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