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