xref: /dragonfly/games/rogue/move.c (revision 0db87cb7)
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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)move.c	8.1 (Berkeley) 5/31/93
33  * $FreeBSD: src/games/rogue/move.c,v 1.7 1999/11/30 03:49:24 billf Exp $
34  * $DragonFly: src/games/rogue/move.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
35  */
36 
37 /*
38  * move.c
39  *
40  * This source herein may be modified and/or distributed by anybody who
41  * so desires, with the following restrictions:
42  *    1.)  No portion of this notice shall be removed.
43  *    2.)  Credit shall not be taken for the creation of this source.
44  *    3.)  This code is not to be traded, sold, or used for personal
45  *         gain or profit.
46  *
47  */
48 
49 #include "rogue.h"
50 
51 short m_moves = 0;
52 boolean jump = 0;
53 const char *you_can_move_again = "you can move again";
54 
55 extern short cur_room, halluc, blind, levitate;
56 extern short cur_level, max_level;
57 extern short bear_trap, haste_self, confused;
58 extern short e_rings, regeneration, auto_search;
59 extern boolean being_held, interrupted, r_teleport, passgo;
60 
61 static boolean can_turn(short, short);
62 static boolean check_hunger(boolean);
63 static short gr_dir(void);
64 static void heal(void);
65 static boolean next_to_something(int, int);
66 static void turn_passage(short, boolean);
67 
68 short
69 one_move_rogue(short dirch, short pickup)
70 {
71 	short row, col;
72 	object *obj;
73 	char desc[DCOLS];
74 	short n, status, d;
75 
76 	row = rogue.row;
77 	col = rogue.col;
78 
79 	if (confused) {
80 		dirch = gr_dir();
81 	}
82 	is_direction(dirch, &d);
83 	get_dir_rc(d, &row, &col, 1);
84 
85 	if (!can_move(rogue.row, rogue.col, row, col)) {
86 		return(MOVE_FAILED);
87 	}
88 	if (being_held || bear_trap) {
89 		if (!(dungeon[row][col] & MONSTER)) {
90 			if (being_held) {
91 				message("you are being held", 1);
92 			} else {
93 				message("you are still stuck in the bear trap", 0);
94 				reg_move();
95 			}
96 			return(MOVE_FAILED);
97 		}
98 	}
99 	if (r_teleport) {
100 		if (rand_percent(R_TELE_PERCENT)) {
101 			tele();
102 			return(STOPPED_ON_SOMETHING);
103 		}
104 	}
105 	if (dungeon[row][col] & MONSTER) {
106 		rogue_hit(object_at(&level_monsters, row, col), 0);
107 		reg_move();
108 		return(MOVE_FAILED);
109 	}
110 	if (dungeon[row][col] & DOOR) {
111 		if (cur_room == PASSAGE) {
112 			cur_room = get_room_number(row, col);
113 			light_up_room(cur_room);
114 			wake_room(cur_room, 1, row, col);
115 		} else {
116 			light_passage(row, col);
117 		}
118 	} else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
119 		   (dungeon[row][col] & TUNNEL)) {
120 		light_passage(row, col);
121 		wake_room(cur_room, 0, rogue.row, rogue.col);
122 		darken_room(cur_room);
123 		cur_room = PASSAGE;
124 	} else if (dungeon[row][col] & TUNNEL) {
125 			light_passage(row, col);
126 	}
127 	mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
128 	mvaddch(row, col, rogue.fchar);
129 
130 	if (!jump) {
131 		refresh();
132 	}
133 	rogue.row = row;
134 	rogue.col = col;
135 	if (dungeon[row][col] & OBJECT) {
136 		if (levitate && pickup) {
137 			return(STOPPED_ON_SOMETHING);
138 		}
139 		if (pickup && !levitate) {
140 			if ((obj = pick_up(row, col, &status)) != NULL) {
141 				get_desc(obj, desc);
142 				if (obj->what_is == GOLD) {
143 					free_object(obj);
144 					goto NOT_IN_PACK;
145 				}
146 			} else if (!status) {
147 				goto MVED;
148 			} else {
149 				goto MOVE_ON;
150 			}
151 		} else {
152 MOVE_ON:
153 			obj = object_at(&level_objects, row, col);
154 			strcpy(desc, "moved onto ");
155 			get_desc(obj, desc+11);
156 			goto NOT_IN_PACK;
157 		}
158 		n = strlen(desc);
159 		desc[n] = '(';
160 		desc[n+1] = obj->ichar;
161 		desc[n+2] = ')';
162 		desc[n+3] = 0;
163 NOT_IN_PACK:
164 		message(desc, 1);
165 		reg_move();
166 		return(STOPPED_ON_SOMETHING);
167 	}
168 	if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
169 		if ((!levitate) && (dungeon[row][col] & TRAP)) {
170 			trap_player(row, col);
171 		}
172 		reg_move();
173 		return(STOPPED_ON_SOMETHING);
174 	}
175 MVED:	if (reg_move()) {			/* fainted from hunger */
176 			return(STOPPED_ON_SOMETHING);
177 	}
178 	return((confused ? STOPPED_ON_SOMETHING : MOVED));
179 }
180 
181 void
182 multiple_move_rogue(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 
221 		if (	(!interrupted) && passgo &&
222 				(dungeon[rogue.row][rogue.col] & TUNNEL)) {
223 			turn_passage(dirch + 32, 1);
224 		}
225 		break;
226 	}
227 }
228 
229 boolean
230 is_passable(int row, int 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 static boolean
243 next_to_something(int drow, int dcol)
244 {
245 	short i, j, i_end, j_end, row, col;
246 	short pass_count = 0;
247 	unsigned short s;
248 
249 	if (confused) {
250 		return(1);
251 	}
252 	if (blind) {
253 		return(0);
254 	}
255 	i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
256 	j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
257 
258 	for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
259 		for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
260 			if ((i == 0) && (j == 0)) {
261 				continue;
262 			}
263 			if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
264 				continue;
265 			}
266 			row = rogue.row + i;
267 			col = rogue.col + j;
268 			s = dungeon[row][col];
269 			if (s & HIDDEN) {
270 				continue;
271 			}
272 			/* If the rogue used to be right, up, left, down, or right of
273 			 * row,col, and now isn't, then don't stop */
274 			if (s & (MONSTER | OBJECT | STAIRS)) {
275 				if (((row == drow) || (col == dcol)) &&
276 					(!((row == rogue.row) || (col == rogue.col)))) {
277 					continue;
278 				}
279 				return(1);
280 			}
281 			if (s & TRAP) {
282 				if (!(s & HIDDEN)) {
283 					if (((row == drow) || (col == dcol)) &&
284 						(!((row == rogue.row) || (col == rogue.col)))) {
285 						continue;
286 					}
287 					return(1);
288 				}
289 			}
290 			if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
291 				if (++pass_count > 1) {
292 					return(1);
293 				}
294 			}
295 			if ((s & DOOR) && ((i == 0) || (j == 0))) {
296 					return(1);
297 			}
298 		}
299 	}
300 	return(0);
301 }
302 
303 boolean
304 can_move(short row1, short col1, short row2, short 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 void
321 move_onto(void)
322 {
323 	short ch, d;
324 	boolean first_miss = 1;
325 
326 	while (!is_direction(ch = rgetchar(), &d)) {
327 		sound_bell();
328 		if (first_miss) {
329 			message("direction? ", 0);
330 			first_miss = 0;
331 		}
332 	}
333 	check_message();
334 	if (ch != CANCEL) {
335 		one_move_rogue(ch, 0);
336 	}
337 }
338 
339 boolean
340 is_direction(short c, short *d)
341 {
342 	switch(c) {
343 	case 'h':
344 		*d = LEFT;
345 		break;
346 	case 'j':
347 		*d = DOWN;
348 		break;
349 	case 'k':
350 		*d = UPWARD;
351 		break;
352 	case 'l':
353 		*d = RIGHT;
354 		break;
355 	case 'b':
356 		*d = DOWNLEFT;
357 		break;
358 	case 'y':
359 		*d = UPLEFT;
360 		break;
361 	case 'u':
362 		*d = UPRIGHT;
363 		break;
364 	case 'n':
365 		*d = DOWNRIGHT;
366 		break;
367 	case CANCEL:
368 		break;
369 	default:
370 		return(0);
371 	}
372 	return(1);
373 }
374 
375 static boolean
376 check_hunger(boolean msg_only)
377 {
378 	short i, n;
379 	boolean fainted = 0;
380 
381 	if (rogue.moves_left == HUNGRY) {
382 		strcpy(hunger_str, "hungry");
383 		message(hunger_str, 0);
384 		print_stats(STAT_HUNGER);
385 	}
386 	if (rogue.moves_left == WEAK) {
387 		strcpy(hunger_str, "weak");
388 		message(hunger_str, 1);
389 		print_stats(STAT_HUNGER);
390 	}
391 	if (rogue.moves_left <= FAINT) {
392 		if (rogue.moves_left == FAINT) {
393 			strcpy(hunger_str, "faint");
394 			message(hunger_str, 1);
395 			print_stats(STAT_HUNGER);
396 		}
397 		n = get_rand(0, (FAINT - rogue.moves_left));
398 		if (n > 0) {
399 			fainted = 1;
400 			if (rand_percent(40)) {
401 				rogue.moves_left++;
402 			}
403 			message("you faint", 1);
404 			for (i = 0; i < n; i++) {
405 				if (coin_toss()) {
406 					mv_mons();
407 				}
408 			}
409 			message(you_can_move_again, 1);
410 		}
411 	}
412 	if (msg_only) {
413 		return(fainted);
414 	}
415 	if (rogue.moves_left <= STARVE) {
416 		killed_by(NULL, STARVATION);
417 	}
418 
419 	switch(e_rings) {
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 		check_hunger(1);
429 		rogue.moves_left -= (rogue.moves_left % 2);
430 		break;
431 	case 2:
432 		rogue.moves_left--;
433 		check_hunger(1);
434 		rogue.moves_left--;
435 		break;
436 	}
437 	return(fainted);
438 }
439 
440 boolean
441 reg_move(void)
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 void
498 rest(int count)
499 {
500 	int i;
501 
502 	interrupted = 0;
503 
504 	for (i = 0; i < count; i++) {
505 		if (interrupted) {
506 			break;
507 		}
508 		reg_move();
509 	}
510 }
511 
512 static short
513 gr_dir(void)
514 {
515 	short d;
516 
517 	d = get_rand(1, 8);
518 
519 	switch(d) {
520 		case 1:
521 			d = 'j';
522 			break;
523 		case 2:
524 			d = 'k';
525 			break;
526 		case 3:
527 			d = 'l';
528 			break;
529 		case 4:
530 			d = 'h';
531 			break;
532 		case 5:
533 			d = 'y';
534 			break;
535 		case 6:
536 			d = 'u';
537 			break;
538 		case 7:
539 			d = 'b';
540 			break;
541 		case 8:
542 			d = 'n';
543 			break;
544 	}
545 	return(d);
546 }
547 
548 static void
549 heal(void)
550 {
551 	static short heal_exp = -1, n, c = 0;
552 	static boolean alt;
553 
554 	if (rogue.hp_current == rogue.hp_max) {
555 		c = 0;
556 		return;
557 	}
558 	if (rogue.exp != heal_exp) {
559 		heal_exp = rogue.exp;
560 
561 		switch(heal_exp) {
562 		case 1:
563 			n = 20;
564 			break;
565 		case 2:
566 			n = 18;
567 			break;
568 		case 3:
569 			n = 17;
570 			break;
571 		case 4:
572 			n = 14;
573 			break;
574 		case 5:
575 			n = 13;
576 			break;
577 		case 6:
578 			n = 10;
579 			break;
580 		case 7:
581 			n = 9;
582 			break;
583 		case 8:
584 			n = 8;
585 			break;
586 		case 9:
587 			n = 7;
588 			break;
589 		case 10:
590 			n = 4;
591 			break;
592 		case 11:
593 			n = 3;
594 			break;
595 		case 12:
596 		default:
597 			n = 2;
598 		}
599 	}
600 	if (++c >= n) {
601 		c = 0;
602 		rogue.hp_current++;
603 		if ((alt = !alt) != 0) {
604 			rogue.hp_current++;
605 		}
606 		if ((rogue.hp_current += regeneration) > rogue.hp_max) {
607 			rogue.hp_current = rogue.hp_max;
608 		}
609 		print_stats(STAT_HP);
610 	}
611 }
612 
613 static boolean
614 can_turn(short nrow, short ncol)
615 {
616 	if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
617 		return(1);
618 	}
619 	return(0);
620 }
621 
622 static void
623 turn_passage(short dir, boolean fast)
624 {
625 	short crow = rogue.row, ccol = rogue.col, turns = 0;
626 	short ndir = 0;
627 
628 	if ((dir != 'h') && can_turn(crow, ccol + 1)) {
629 		turns++;
630 		ndir = 'l';
631 	}
632 	if ((dir != 'l') && can_turn(crow, ccol - 1)) {
633 		turns++;
634 		ndir = 'h';
635 	}
636 	if ((dir != 'k') && can_turn(crow + 1, ccol)) {
637 		turns++;
638 		ndir = 'j';
639 	}
640 	if ((dir != 'j') && can_turn(crow - 1, ccol)) {
641 		turns++;
642 		ndir = 'k';
643 	}
644 	if (turns == 1) {
645 		multiple_move_rogue(ndir - (fast ? 32 : 96));
646 	}
647 }
648