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