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