xref: /minix/games/rogue/move.c (revision e3b78ef1)
1 /*	$NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg 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.13 2011/05/23 23:01:17 joerg 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 static boolean can_turn(short, short);
63 static boolean check_hunger(boolean);
64 static char gr_dir(void);
65 static void heal(void);
66 static boolean next_to_something(int, int);
67 static void turn_passage(short, boolean);
68 
69 int
one_move_rogue(short dirch,short pickup)70 one_move_rogue(short dirch, short pickup)
71 {
72 	short row, col;
73 	object *obj;
74 	char desc[DCOLS];
75 	short status, d = 0;	/* XXX: GCC */
76 
77 	row = rogue.row;
78 	col = rogue.col;
79 
80 	if (confused) {
81 		dirch = gr_dir();
82 	}
83 	(void)is_direction(dirch, &d);
84 	get_dir_rc(d, &row, &col, 1);
85 
86 	if (!can_move(rogue.row, rogue.col, row, col)) {
87 		return(MOVE_FAILED);
88 	}
89 	if (being_held || bear_trap) {
90 		if (!(dungeon[row][col] & MONSTER)) {
91 			if (being_held) {
92 				messagef(1, "you are being held");
93 			} else {
94 				messagef(0, "you are still stuck in the bear trap");
95 				(void)reg_move();
96 			}
97 			return(MOVE_FAILED);
98 		}
99 	}
100 	if (r_teleport) {
101 		if (rand_percent(R_TELE_PERCENT)) {
102 			tele();
103 			return(STOPPED_ON_SOMETHING);
104 		}
105 	}
106 	if (dungeon[row][col] & MONSTER) {
107 		rogue_hit(object_at(&level_monsters, row, col), 0);
108 		(void)reg_move();
109 		return(MOVE_FAILED);
110 	}
111 	if (dungeon[row][col] & DOOR) {
112 		if (cur_room == PASSAGE) {
113 			cur_room = get_room_number(row, col);
114 			if (cur_room == NO_ROOM)
115 				clean_up("one_move_rogue: door to nowhere");
116 			light_up_room(cur_room);
117 			wake_room(cur_room, 1, row, col);
118 		} else {
119 			light_passage(row, col);
120 		}
121 	} else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
122 		   (dungeon[row][col] & TUNNEL)) {
123 		light_passage(row, col);
124 		wake_room(cur_room, 0, rogue.row, rogue.col);
125 		darken_room(cur_room);
126 		cur_room = PASSAGE;
127 	} else if (dungeon[row][col] & TUNNEL) {
128 			light_passage(row, col);
129 	}
130 	mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
131 	mvaddch(row, col, rogue.fchar);
132 
133 	if (!jump) {
134 		refresh();
135 	}
136 	rogue.row = row;
137 	rogue.col = col;
138 	if (dungeon[row][col] & OBJECT) {
139 		if (levitate && pickup) {
140 			return(STOPPED_ON_SOMETHING);
141 		}
142 		if (pickup && !levitate) {
143 			if ((obj = pick_up(row, col, &status)) != NULL) {
144 				get_desc(obj, desc, sizeof(desc));
145 				if (obj->what_is == GOLD) {
146 					free_object(obj);
147 					messagef(1, "%s", desc);
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 			get_desc(obj, desc, sizeof(desc));
159 			messagef(1, "moved onto %s", desc);
160 			goto NOT_IN_PACK;
161 		}
162 		messagef(1, "%s(%c)", desc, obj->ichar);
163 NOT_IN_PACK:
164 		(void)reg_move();
165 		return(STOPPED_ON_SOMETHING);
166 	}
167 	if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
168 		if ((!levitate) && (dungeon[row][col] & TRAP)) {
169 			trap_player(row, col);
170 		}
171 		(void)reg_move();
172 		return(STOPPED_ON_SOMETHING);
173 	}
174 MVED:	if (reg_move()) {			/* fainted from hunger */
175 			return(STOPPED_ON_SOMETHING);
176 	}
177 	return((confused ? STOPPED_ON_SOMETHING : MOVED));
178 }
179 
180 void
multiple_move_rogue(short dirch)181 multiple_move_rogue(short dirch)
182 {
183 	short row, col;
184 	short m;
185 
186 	switch(dirch) {
187 	case '\010':
188 	case '\012':
189 	case '\013':
190 	case '\014':
191 	case '\031':
192 	case '\025':
193 	case '\016':
194 	case '\002':
195 		do {
196 			row = rogue.row;
197 			col = rogue.col;
198 			if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
199 				(m == STOPPED_ON_SOMETHING) ||
200 				interrupted) {
201 				break;
202 			}
203 		} while (!next_to_something(row, col));
204 		if (	(!interrupted) && passgo && (m == MOVE_FAILED) &&
205 				(dungeon[rogue.row][rogue.col] & TUNNEL)) {
206 			turn_passage(dirch + 96, 0);
207 		}
208 		break;
209 	case 'H':
210 	case 'J':
211 	case 'K':
212 	case 'L':
213 	case 'B':
214 	case 'Y':
215 	case 'U':
216 	case 'N':
217 		while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED))
218 			;
219 
220 		if (	(!interrupted) && passgo &&
221 				(dungeon[rogue.row][rogue.col] & TUNNEL)) {
222 			turn_passage(dirch + 32, 1);
223 		}
224 		break;
225 	}
226 }
227 
228 boolean
is_passable(int row,int col)229 is_passable(int row, int col)
230 {
231 	if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
232 		(col > (DCOLS-1))) {
233 		return(0);
234 	}
235 	if (dungeon[row][col] & HIDDEN) {
236 		return((dungeon[row][col] & TRAP) ? 1 : 0);
237 	}
238 	return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
239 }
240 
241 static boolean
next_to_something(int drow,int dcol)242 next_to_something(int drow, int dcol)
243 {
244 	short i, j, i_end, j_end, row, col;
245 	short pass_count = 0;
246 	unsigned short s;
247 
248 	if (confused) {
249 		return(1);
250 	}
251 	if (blind) {
252 		return(0);
253 	}
254 	i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
255 	j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
256 
257 	for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
258 		for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
259 			if ((i == 0) && (j == 0)) {
260 				continue;
261 			}
262 			if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
263 				continue;
264 			}
265 			row = rogue.row + i;
266 			col = rogue.col + j;
267 			s = dungeon[row][col];
268 			if (s & HIDDEN) {
269 				continue;
270 			}
271 			/* If the rogue used to be right, up, left, down, or right of
272 			 * row,col, and now isn't, then don't stop */
273 			if (s & (MONSTER | OBJECT | STAIRS)) {
274 				if (((row == drow) || (col == dcol)) &&
275 					(!((row == rogue.row) || (col == rogue.col)))) {
276 					continue;
277 				}
278 				return(1);
279 			}
280 			if (s & TRAP) {
281 				if (!(s & HIDDEN)) {
282 					if (((row == drow) || (col == dcol)) &&
283 						(!((row == rogue.row) || (col == rogue.col)))) {
284 						continue;
285 					}
286 					return(1);
287 				}
288 			}
289 			if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
290 				if (++pass_count > 1) {
291 					return(1);
292 				}
293 			}
294 			if ((s & DOOR) && ((i == 0) || (j == 0))) {
295 					return(1);
296 			}
297 		}
298 	}
299 	return(0);
300 }
301 
302 boolean
can_move(int row1,int col1,int row2,int col2)303 can_move(int row1, int col1, int row2, int col2)
304 {
305 	if (!is_passable(row2, col2)) {
306 		return(0);
307 	}
308 	if ((row1 != row2) && (col1 != col2)) {
309 		if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
310 			return(0);
311 		}
312 		if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
313 			return(0);
314 		}
315 	}
316 	return(1);
317 }
318 
319 void
move_onto(void)320 move_onto(void)
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 			messagef(0, "direction? ");
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
is_direction(short c,short * d)339 is_direction(short c, short *d)
340 {
341 	switch(c) {
342 	case 'h':
343 		*d = LEFT;
344 		break;
345 	case 'j':
346 		*d = DOWN;
347 		break;
348 	case 'k':
349 		*d = UPWARD;
350 		break;
351 	case 'l':
352 		*d = RIGHT;
353 		break;
354 	case 'b':
355 		*d = DOWNLEFT;
356 		break;
357 	case 'y':
358 		*d = UPLEFT;
359 		break;
360 	case 'u':
361 		*d = UPRIGHT;
362 		break;
363 	case 'n':
364 		*d = DOWNRIGHT;
365 		break;
366 	case CANCEL:
367 		break;
368 	default:
369 		return(0);
370 	}
371 	return(1);
372 }
373 
374 static boolean
check_hunger(boolean msg_only)375 check_hunger(boolean msg_only)
376 {
377 	short i, n;
378 	boolean fainted = 0;
379 
380 	if (rogue.moves_left == HUNGRY) {
381 		(void)strlcpy(hunger_str, "hungry", sizeof(hunger_str));
382 		messagef(0, "%s", hunger_str);
383 		print_stats(STAT_HUNGER);
384 	}
385 	if (rogue.moves_left == WEAK) {
386 		(void)strlcpy(hunger_str, "weak", sizeof(hunger_str));
387 		messagef(1, "%s", hunger_str);
388 		print_stats(STAT_HUNGER);
389 	}
390 	if (rogue.moves_left <= FAINT) {
391 		if (rogue.moves_left == FAINT) {
392 			(void)strlcpy(hunger_str, "faint", sizeof(hunger_str));
393 			messagef(1, "%s", hunger_str);
394 			print_stats(STAT_HUNGER);
395 		}
396 		n = get_rand(0, (FAINT - rogue.moves_left));
397 		if (n > 0) {
398 			fainted = 1;
399 			if (rand_percent(40)) {
400 				rogue.moves_left++;
401 			}
402 			messagef(1, "you faint");
403 			for (i = 0; i < n; i++) {
404 				if (coin_toss()) {
405 					mv_mons();
406 				}
407 			}
408 			messagef(1, "%s", you_can_move_again);
409 		}
410 	}
411 	if (msg_only) {
412 		return(fainted);
413 	}
414 	if (rogue.moves_left <= STARVE) {
415 		killed_by(NULL, STARVATION);
416 	}
417 
418 	switch(e_rings) {
419 	/*case -2:
420 		Subtract 0, i.e. do nothing.
421 		break;*/
422 	case -1:
423 		rogue.moves_left -= (rogue.moves_left % 2);
424 		break;
425 	case 0:
426 		rogue.moves_left--;
427 		break;
428 	case 1:
429 		rogue.moves_left--;
430 		(void)check_hunger(1);
431 		rogue.moves_left -= (rogue.moves_left % 2);
432 		break;
433 	case 2:
434 		rogue.moves_left--;
435 		(void)check_hunger(1);
436 		rogue.moves_left--;
437 		break;
438 	}
439 	return(fainted);
440 }
441 
442 boolean
reg_move(void)443 reg_move(void)
444 {
445 	boolean fainted;
446 
447 	if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
448 		fainted = check_hunger(0);
449 	} else {
450 		fainted = 0;
451 	}
452 
453 	mv_mons();
454 
455 	if (++m_moves >= 120) {
456 		m_moves = 0;
457 		wanderer();
458 	}
459 	if (halluc) {
460 		if (!(--halluc)) {
461 			unhallucinate();
462 		} else {
463 			hallucinate();
464 		}
465 	}
466 	if (blind) {
467 		if (!(--blind)) {
468 			unblind();
469 		}
470 	}
471 	if (confused) {
472 		if (!(--confused)) {
473 			unconfuse();
474 		}
475 	}
476 	if (bear_trap) {
477 		bear_trap--;
478 	}
479 	if (levitate) {
480 		if (!(--levitate)) {
481 			messagef(1, "you float gently to the ground");
482 			if (dungeon[rogue.row][rogue.col] & TRAP) {
483 				trap_player(rogue.row, rogue.col);
484 			}
485 		}
486 	}
487 	if (haste_self) {
488 		if (!(--haste_self)) {
489 			messagef(0, "you feel yourself slowing down");
490 		}
491 	}
492 	heal();
493 	if (auto_search > 0) {
494 		search(auto_search, auto_search);
495 	}
496 	return(fainted);
497 }
498 
499 void
rest(int count)500 rest(int count)
501 {
502 	int i;
503 
504 	interrupted = 0;
505 
506 	for (i = 0; i < count; i++) {
507 		if (interrupted) {
508 			break;
509 		}
510 		(void)reg_move();
511 	}
512 }
513 
514 static char
gr_dir(void)515 gr_dir(void)
516 {
517 	short d;
518 
519 	d = get_rand(1, 8);
520 
521 	switch(d) {
522 		case 1:
523 			d = 'j';
524 			break;
525 		case 2:
526 			d = 'k';
527 			break;
528 		case 3:
529 			d = 'l';
530 			break;
531 		case 4:
532 			d = 'h';
533 			break;
534 		case 5:
535 			d = 'y';
536 			break;
537 		case 6:
538 			d = 'u';
539 			break;
540 		case 7:
541 			d = 'b';
542 			break;
543 		case 8:
544 			d = 'n';
545 			break;
546 	}
547 	return(d);
548 }
549 
550 static void
heal(void)551 heal(void)
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) != 0) {
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
can_turn(short nrow,short ncol)616 can_turn(short nrow, short ncol)
617 {
618 	if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
619 		return(1);
620 	}
621 	return(0);
622 }
623 
624 static void
turn_passage(short dir,boolean fast)625 turn_passage(short dir, boolean fast)
626 {
627 	short crow = rogue.row, ccol = rogue.col, turns = 0;
628 	short ndir = 0;
629 
630 	if ((dir != 'h') && can_turn(crow, ccol + 1)) {
631 		turns++;
632 		ndir = 'l';
633 	}
634 	if ((dir != 'l') && can_turn(crow, ccol - 1)) {
635 		turns++;
636 		ndir = 'h';
637 	}
638 	if ((dir != 'k') && can_turn(crow + 1, ccol)) {
639 		turns++;
640 		ndir = 'j';
641 	}
642 	if ((dir != 'j') && can_turn(crow - 1, ccol)) {
643 		turns++;
644 		ndir = 'k';
645 	}
646 	if (turns == 1) {
647 		multiple_move_rogue(ndir - (fast ? 32 : 96));
648 	}
649 }
650