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