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