xref: /netbsd/games/rogue/move.c (revision bf9ec67e)
1 /*	$NetBSD: move.c,v 1.5 1998/11/10 13:01:32 hubertf Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Timothy C. Stoehr.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)move.c	8.1 (Berkeley) 5/31/93";
43 #else
44 __RCSID("$NetBSD: move.c,v 1.5 1998/11/10 13:01:32 hubertf Exp $");
45 #endif
46 #endif /* not lint */
47 
48 /*
49  * move.c
50  *
51  * This source herein may be modified and/or distributed by anybody who
52  * so desires, with the following restrictions:
53  *    1.)  No portion of this notice shall be removed.
54  *    2.)  Credit shall not be taken for the creation of this source.
55  *    3.)  This code is not to be traded, sold, or used for personal
56  *         gain or profit.
57  *
58  */
59 
60 #include "rogue.h"
61 
62 short m_moves = 0;
63 boolean jump = 0;
64 const char *you_can_move_again = "you can move again";
65 
66 int
67 one_move_rogue(dirch, pickup)
68 	short dirch, pickup;
69 {
70 	short row, col;
71 	object *obj;
72 	char desc[DCOLS];
73 	short n, status, d;
74 
75 	row = rogue.row;
76 	col = rogue.col;
77 
78 	if (confused) {
79 		dirch = gr_dir();
80 	}
81 	(void) is_direction(dirch, &d);
82 	get_dir_rc(d, &row, &col, 1);
83 
84 	if (!can_move(rogue.row, rogue.col, row, col)) {
85 		return(MOVE_FAILED);
86 	}
87 	if (being_held || bear_trap) {
88 		if (!(dungeon[row][col] & MONSTER)) {
89 			if (being_held) {
90 				message("you are being held", 1);
91 			} else {
92 				message("you are still stuck in the bear trap", 0);
93 				(void) reg_move();
94 			}
95 			return(MOVE_FAILED);
96 		}
97 	}
98 	if (r_teleport) {
99 		if (rand_percent(R_TELE_PERCENT)) {
100 			tele();
101 			return(STOPPED_ON_SOMETHING);
102 		}
103 	}
104 	if (dungeon[row][col] & MONSTER) {
105 		rogue_hit(object_at(&level_monsters, row, col), 0);
106 		(void) reg_move();
107 		return(MOVE_FAILED);
108 	}
109 	if (dungeon[row][col] & DOOR) {
110 		if (cur_room == PASSAGE) {
111 			cur_room = get_room_number(row, col);
112 			light_up_room(cur_room);
113 			wake_room(cur_room, 1, row, col);
114 		} else {
115 			light_passage(row, col);
116 		}
117 	} else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
118 		   (dungeon[row][col] & TUNNEL)) {
119 		light_passage(row, col);
120 		wake_room(cur_room, 0, rogue.row, rogue.col);
121 		darken_room(cur_room);
122 		cur_room = PASSAGE;
123 	} else if (dungeon[row][col] & TUNNEL) {
124 			light_passage(row, col);
125 	}
126 	mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
127 	mvaddch(row, col, rogue.fchar);
128 
129 	if (!jump) {
130 		refresh();
131 	}
132 	rogue.row = row;
133 	rogue.col = col;
134 	if (dungeon[row][col] & OBJECT) {
135 		if (levitate && pickup) {
136 			return(STOPPED_ON_SOMETHING);
137 		}
138 		if (pickup && !levitate) {
139 			if ((obj = pick_up(row, col, &status)) != NULL) {
140 				get_desc(obj, desc);
141 				if (obj->what_is == GOLD) {
142 					free_object(obj);
143 					goto NOT_IN_PACK;
144 				}
145 			} else if (!status) {
146 				goto MVED;
147 			} else {
148 				goto MOVE_ON;
149 			}
150 		} else {
151 MOVE_ON:
152 			obj = object_at(&level_objects, row, col);
153 			(void) strcpy(desc, "moved onto ");
154 			get_desc(obj, desc+11);
155 			goto NOT_IN_PACK;
156 		}
157 		n = strlen(desc);
158 		desc[n] = '(';
159 		desc[n+1] = obj->ichar;
160 		desc[n+2] = ')';
161 		desc[n+3] = 0;
162 NOT_IN_PACK:
163 		message(desc, 1);
164 		(void) reg_move();
165 		return(STOPPED_ON_SOMETHING);
166 	}
167 	if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
168 		if ((!levitate) && (dungeon[row][col] & TRAP)) {
169 			trap_player(row, col);
170 		}
171 		(void) reg_move();
172 		return(STOPPED_ON_SOMETHING);
173 	}
174 MVED:	if (reg_move()) {			/* fainted from hunger */
175 			return(STOPPED_ON_SOMETHING);
176 	}
177 	return((confused ? STOPPED_ON_SOMETHING : MOVED));
178 }
179 
180 void
181 multiple_move_rogue(dirch)
182 	short dirch;
183 {
184 	short row, col;
185 	short m;
186 
187 	switch(dirch) {
188 	case '\010':
189 	case '\012':
190 	case '\013':
191 	case '\014':
192 	case '\031':
193 	case '\025':
194 	case '\016':
195 	case '\002':
196 		do {
197 			row = rogue.row;
198 			col = rogue.col;
199 			if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
200 				(m == STOPPED_ON_SOMETHING) ||
201 				interrupted) {
202 				break;
203 			}
204 		} while (!next_to_something(row, col));
205 		if (	(!interrupted) && passgo && (m == MOVE_FAILED) &&
206 				(dungeon[rogue.row][rogue.col] & TUNNEL)) {
207 			turn_passage(dirch + 96, 0);
208 		}
209 		break;
210 	case 'H':
211 	case 'J':
212 	case 'K':
213 	case 'L':
214 	case 'B':
215 	case 'Y':
216 	case 'U':
217 	case 'N':
218 		while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) ;
219 
220 		if (	(!interrupted) && passgo &&
221 				(dungeon[rogue.row][rogue.col] & TUNNEL)) {
222 			turn_passage(dirch + 32, 1);
223 		}
224 		break;
225 	}
226 }
227 
228 boolean
229 is_passable(row, col)
230 	int row, col;
231 {
232 	if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
233 		(col > (DCOLS-1))) {
234 		return(0);
235 	}
236 	if (dungeon[row][col] & HIDDEN) {
237 		return((dungeon[row][col] & TRAP) ? 1 : 0);
238 	}
239 	return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
240 }
241 
242 boolean
243 next_to_something(drow, dcol)
244 	int drow, dcol;
245 {
246 	short i, j, i_end, j_end, row, col;
247 	short pass_count = 0;
248 	unsigned short s;
249 
250 	if (confused) {
251 		return(1);
252 	}
253 	if (blind) {
254 		return(0);
255 	}
256 	i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
257 	j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
258 
259 	for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
260 		for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
261 			if ((i == 0) && (j == 0)) {
262 				continue;
263 			}
264 			if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
265 				continue;
266 			}
267 			row = rogue.row + i;
268 			col = rogue.col + j;
269 			s = dungeon[row][col];
270 			if (s & HIDDEN) {
271 				continue;
272 			}
273 			/* If the rogue used to be right, up, left, down, or right of
274 			 * row,col, and now isn't, then don't stop */
275 			if (s & (MONSTER | OBJECT | STAIRS)) {
276 				if (((row == drow) || (col == dcol)) &&
277 					(!((row == rogue.row) || (col == rogue.col)))) {
278 					continue;
279 				}
280 				return(1);
281 			}
282 			if (s & TRAP) {
283 				if (!(s & HIDDEN)) {
284 					if (((row == drow) || (col == dcol)) &&
285 						(!((row == rogue.row) || (col == rogue.col)))) {
286 						continue;
287 					}
288 					return(1);
289 				}
290 			}
291 			if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
292 				if (++pass_count > 1) {
293 					return(1);
294 				}
295 			}
296 			if ((s & DOOR) && ((i == 0) || (j == 0))) {
297 					return(1);
298 			}
299 		}
300 	}
301 	return(0);
302 }
303 
304 boolean
305 can_move(row1, col1, row2, col2)
306 	int row1, col1, row2, col2;
307 {
308 	if (!is_passable(row2, col2)) {
309 		return(0);
310 	}
311 	if ((row1 != row2) && (col1 != col2)) {
312 		if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
313 			return(0);
314 		}
315 		if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
316 			return(0);
317 		}
318 	}
319 	return(1);
320 }
321 
322 void
323 move_onto()
324 {
325 	short ch, d;
326 	boolean first_miss = 1;
327 
328 	while (!is_direction(ch = rgetchar(), &d)) {
329 		sound_bell();
330 		if (first_miss) {
331 			message("direction? ", 0);
332 			first_miss = 0;
333 		}
334 	}
335 	check_message();
336 	if (ch != CANCEL) {
337 		(void) one_move_rogue(ch, 0);
338 	}
339 }
340 
341 boolean
342 is_direction(c, d)
343 	short c;
344 	short *d;
345 {
346 	switch(c) {
347 	case 'h':
348 		*d = LEFT;
349 		break;
350 	case 'j':
351 		*d = DOWN;
352 		break;
353 	case 'k':
354 		*d = UPWARD;
355 		break;
356 	case 'l':
357 		*d = RIGHT;
358 		break;
359 	case 'b':
360 		*d = DOWNLEFT;
361 		break;
362 	case 'y':
363 		*d = UPLEFT;
364 		break;
365 	case 'u':
366 		*d = UPRIGHT;
367 		break;
368 	case 'n':
369 		*d = DOWNRIGHT;
370 		break;
371 	case CANCEL:
372 		break;
373 	default:
374 		return(0);
375 	}
376 	return(1);
377 }
378 
379 boolean
380 check_hunger(msg_only)
381 	boolean msg_only;
382 {
383 	short i, n;
384 	boolean fainted = 0;
385 
386 	if (rogue.moves_left == HUNGRY) {
387 		(void) strcpy(hunger_str, "hungry");
388 		message(hunger_str, 0);
389 		print_stats(STAT_HUNGER);
390 	}
391 	if (rogue.moves_left == WEAK) {
392 		(void) strcpy(hunger_str, "weak");
393 		message(hunger_str, 1);
394 		print_stats(STAT_HUNGER);
395 	}
396 	if (rogue.moves_left <= FAINT) {
397 		if (rogue.moves_left == FAINT) {
398 			(void) strcpy(hunger_str, "faint");
399 			message(hunger_str, 1);
400 			print_stats(STAT_HUNGER);
401 		}
402 		n = get_rand(0, (FAINT - rogue.moves_left));
403 		if (n > 0) {
404 			fainted = 1;
405 			if (rand_percent(40)) {
406 				rogue.moves_left++;
407 			}
408 			message("you faint", 1);
409 			for (i = 0; i < n; i++) {
410 				if (coin_toss()) {
411 					mv_mons();
412 				}
413 			}
414 			message(you_can_move_again, 1);
415 		}
416 	}
417 	if (msg_only) {
418 		return(fainted);
419 	}
420 	if (rogue.moves_left <= STARVE) {
421 		killed_by((object *) 0, STARVATION);
422 	}
423 
424 	switch(e_rings) {
425 	/*case -2:
426 		Subtract 0, i.e. do nothing.
427 		break;*/
428 	case -1:
429 		rogue.moves_left -= (rogue.moves_left % 2);
430 		break;
431 	case 0:
432 		rogue.moves_left--;
433 		break;
434 	case 1:
435 		rogue.moves_left--;
436 		(void) check_hunger(1);
437 		rogue.moves_left -= (rogue.moves_left % 2);
438 		break;
439 	case 2:
440 		rogue.moves_left--;
441 		(void) check_hunger(1);
442 		rogue.moves_left--;
443 		break;
444 	}
445 	return(fainted);
446 }
447 
448 boolean
449 reg_move()
450 {
451 	boolean fainted;
452 
453 	if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
454 		fainted = check_hunger(0);
455 	} else {
456 		fainted = 0;
457 	}
458 
459 	mv_mons();
460 
461 	if (++m_moves >= 120) {
462 		m_moves = 0;
463 		wanderer();
464 	}
465 	if (halluc) {
466 		if (!(--halluc)) {
467 			unhallucinate();
468 		} else {
469 			hallucinate();
470 		}
471 	}
472 	if (blind) {
473 		if (!(--blind)) {
474 			unblind();
475 		}
476 	}
477 	if (confused) {
478 		if (!(--confused)) {
479 			unconfuse();
480 		}
481 	}
482 	if (bear_trap) {
483 		bear_trap--;
484 	}
485 	if (levitate) {
486 		if (!(--levitate)) {
487 			message("you float gently to the ground", 1);
488 			if (dungeon[rogue.row][rogue.col] & TRAP) {
489 				trap_player(rogue.row, rogue.col);
490 			}
491 		}
492 	}
493 	if (haste_self) {
494 		if (!(--haste_self)) {
495 			message("you feel yourself slowing down", 0);
496 		}
497 	}
498 	heal();
499 	if (auto_search > 0) {
500 		search(auto_search, auto_search);
501 	}
502 	return(fainted);
503 }
504 
505 void
506 rest(count)
507 	int count;
508 {
509 	int i;
510 
511 	interrupted = 0;
512 
513 	for (i = 0; i < count; i++) {
514 		if (interrupted) {
515 			break;
516 		}
517 		(void) reg_move();
518 	}
519 }
520 
521 char
522 gr_dir()
523 {
524 	short d;
525 
526 	d = get_rand(1, 8);
527 
528 	switch(d) {
529 		case 1:
530 			d = 'j';
531 			break;
532 		case 2:
533 			d = 'k';
534 			break;
535 		case 3:
536 			d = 'l';
537 			break;
538 		case 4:
539 			d = 'h';
540 			break;
541 		case 5:
542 			d = 'y';
543 			break;
544 		case 6:
545 			d = 'u';
546 			break;
547 		case 7:
548 			d = 'b';
549 			break;
550 		case 8:
551 			d = 'n';
552 			break;
553 	}
554 	return(d);
555 }
556 
557 void
558 heal()
559 {
560 	static short heal_exp = -1, n, c = 0;
561 	static boolean alt;
562 
563 	if (rogue.hp_current == rogue.hp_max) {
564 		c = 0;
565 		return;
566 	}
567 	if (rogue.exp != heal_exp) {
568 		heal_exp = rogue.exp;
569 
570 		switch(heal_exp) {
571 		case 1:
572 			n = 20;
573 			break;
574 		case 2:
575 			n = 18;
576 			break;
577 		case 3:
578 			n = 17;
579 			break;
580 		case 4:
581 			n = 14;
582 			break;
583 		case 5:
584 			n = 13;
585 			break;
586 		case 6:
587 			n = 10;
588 			break;
589 		case 7:
590 			n = 9;
591 			break;
592 		case 8:
593 			n = 8;
594 			break;
595 		case 9:
596 			n = 7;
597 			break;
598 		case 10:
599 			n = 4;
600 			break;
601 		case 11:
602 			n = 3;
603 			break;
604 		case 12:
605 		default:
606 			n = 2;
607 		}
608 	}
609 	if (++c >= n) {
610 		c = 0;
611 		rogue.hp_current++;
612 		if ((alt = !alt) != 0) {
613 			rogue.hp_current++;
614 		}
615 		if ((rogue.hp_current += regeneration) > rogue.hp_max) {
616 			rogue.hp_current = rogue.hp_max;
617 		}
618 		print_stats(STAT_HP);
619 	}
620 }
621 
622 boolean
623 can_turn(nrow, ncol)
624 	short nrow, ncol;
625 {
626 	if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
627 		return(1);
628 	}
629 	return(0);
630 }
631 
632 void
633 turn_passage(dir, fast)
634 	short dir;
635 	boolean fast;
636 {
637 	short crow = rogue.row, ccol = rogue.col, turns = 0;
638 	short ndir = 0;
639 
640 	if ((dir != 'h') && can_turn(crow, ccol + 1)) {
641 		turns++;
642 		ndir = 'l';
643 	}
644 	if ((dir != 'l') && can_turn(crow, ccol - 1)) {
645 		turns++;
646 		ndir = 'h';
647 	}
648 	if ((dir != 'k') && can_turn(crow + 1, ccol)) {
649 		turns++;
650 		ndir = 'j';
651 	}
652 	if ((dir != 'j') && can_turn(crow - 1, ccol)) {
653 		turns++;
654 		ndir = 'k';
655 	}
656 	if (turns == 1) {
657 		multiple_move_rogue(ndir - (fast ? 32 : 96));
658 	}
659 }
660