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