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