Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / games / rogue / move.c
blob4e2545b09697ca60140b88ca87036efb7dedd6e0
1 /* $NetBSD: move.c,v 1.11 2008/01/14 03:50:02 dholland Exp $ */
3 /*
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Timothy C. Stoehr.
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.
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.
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.11 2008/01/14 03:50:02 dholland Exp $");
41 #endif
42 #endif /* not lint */
45 * move.c
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.
56 #include "rogue.h"
58 short m_moves = 0;
59 boolean jump = 0;
60 const char *you_can_move_again = "you can move again";
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);
69 int
70 one_move_rogue(short dirch, short pickup)
72 short row, col;
73 object *obj;
74 char desc[DCOLS];
75 short status, d = 0; /* XXX: GCC */
77 row = rogue.row;
78 col = rogue.col;
80 if (confused) {
81 dirch = gr_dir();
83 (void)is_direction(dirch, &d);
84 get_dir_rc(d, &row, &col, 1);
86 if (!can_move(rogue.row, rogue.col, row, col)) {
87 return(MOVE_FAILED);
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();
97 return(MOVE_FAILED);
100 if (r_teleport) {
101 if (rand_percent(R_TELE_PERCENT)) {
102 tele();
103 return(STOPPED_ON_SOMETHING);
106 if (dungeon[row][col] & MONSTER) {
107 rogue_hit(object_at(&level_monsters, row, col), 0);
108 (void)reg_move();
109 return(MOVE_FAILED);
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);
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);
130 mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
131 mvaddch(row, col, rogue.fchar);
133 if (!jump) {
134 refresh();
136 rogue.row = row;
137 rogue.col = col;
138 if (dungeon[row][col] & OBJECT) {
139 if (levitate && pickup) {
140 return(STOPPED_ON_SOMETHING);
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;
150 } else if (!status) {
151 goto MVED;
152 } else {
153 goto MOVE_ON;
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;
162 messagef(1, "%s(%c)", desc, obj->ichar);
163 NOT_IN_PACK:
164 (void)reg_move();
165 return(STOPPED_ON_SOMETHING);
167 if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
168 if ((!levitate) && (dungeon[row][col] & TRAP)) {
169 trap_player(row, col);
171 (void)reg_move();
172 return(STOPPED_ON_SOMETHING);
174 MVED: if (reg_move()) { /* fainted from hunger */
175 return(STOPPED_ON_SOMETHING);
177 return((confused ? STOPPED_ON_SOMETHING : MOVED));
180 void
181 multiple_move_rogue(short dirch)
183 short row, col;
184 short m;
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;
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);
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))
220 if ( (!interrupted) && passgo &&
221 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
222 turn_passage(dirch + 32, 1);
224 break;
228 boolean
229 is_passable(int row, int col)
231 if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
232 (col > (DCOLS-1))) {
233 return(0);
235 if (dungeon[row][col] & HIDDEN) {
236 return((dungeon[row][col] & TRAP) ? 1 : 0);
238 return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
241 static boolean
242 next_to_something(int drow, int dcol)
244 short i, j, i_end, j_end, row, col;
245 short pass_count = 0;
246 unsigned short s;
248 if (confused) {
249 return(1);
251 if (blind) {
252 return(0);
254 i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
255 j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
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;
262 if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
263 continue;
265 row = rogue.row + i;
266 col = rogue.col + j;
267 s = dungeon[row][col];
268 if (s & HIDDEN) {
269 continue;
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;
278 return(1);
280 if (s & TRAP) {
281 if (!(s & HIDDEN)) {
282 if (((row == drow) || (col == dcol)) &&
283 (!((row == rogue.row) || (col == rogue.col)))) {
284 continue;
286 return(1);
289 if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
290 if (++pass_count > 1) {
291 return(1);
294 if ((s & DOOR) && ((i == 0) || (j == 0))) {
295 return(1);
299 return(0);
302 boolean
303 can_move(int row1, int col1, int row2, int col2)
305 if (!is_passable(row2, col2)) {
306 return(0);
308 if ((row1 != row2) && (col1 != col2)) {
309 if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
310 return(0);
312 if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
313 return(0);
316 return(1);
319 void
320 move_onto(void)
322 short ch, d;
323 boolean first_miss = 1;
325 while (!is_direction(ch = rgetchar(), &d)) {
326 sound_bell();
327 if (first_miss) {
328 messagef(0, "direction? ");
329 first_miss = 0;
332 check_message();
333 if (ch != CANCEL) {
334 (void)one_move_rogue(ch, 0);
338 boolean
339 is_direction(short c, short *d)
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);
371 return(1);
374 static boolean
375 check_hunger(boolean msg_only)
377 short i, n;
378 boolean fainted = 0;
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);
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);
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);
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++;
402 messagef(1, "you faint");
403 for (i = 0; i < n; i++) {
404 if (coin_toss()) {
405 mv_mons();
408 messagef(1, you_can_move_again);
411 if (msg_only) {
412 return(fainted);
414 if (rogue.moves_left <= STARVE) {
415 killed_by(NULL, STARVATION);
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;
439 return(fainted);
442 boolean
443 reg_move(void)
445 boolean fainted;
447 if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
448 fainted = check_hunger(0);
449 } else {
450 fainted = 0;
453 mv_mons();
455 if (++m_moves >= 120) {
456 m_moves = 0;
457 wanderer();
459 if (halluc) {
460 if (!(--halluc)) {
461 unhallucinate();
462 } else {
463 hallucinate();
466 if (blind) {
467 if (!(--blind)) {
468 unblind();
471 if (confused) {
472 if (!(--confused)) {
473 unconfuse();
476 if (bear_trap) {
477 bear_trap--;
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);
487 if (haste_self) {
488 if (!(--haste_self)) {
489 messagef(0, "you feel yourself slowing down");
492 heal();
493 if (auto_search > 0) {
494 search(auto_search, auto_search);
496 return(fainted);
499 void
500 rest(int count)
502 int i;
504 interrupted = 0;
506 for (i = 0; i < count; i++) {
507 if (interrupted) {
508 break;
510 (void)reg_move();
514 static char
515 gr_dir(void)
517 short d;
519 d = get_rand(1, 8);
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;
547 return(d);
550 static void
551 heal(void)
553 static short heal_exp = -1, n, c = 0;
554 static boolean alt;
556 if (rogue.hp_current == rogue.hp_max) {
557 c = 0;
558 return;
560 if (rogue.exp != heal_exp) {
561 heal_exp = rogue.exp;
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;
602 if (++c >= n) {
603 c = 0;
604 rogue.hp_current++;
605 if ((alt = !alt) != 0) {
606 rogue.hp_current++;
608 if ((rogue.hp_current += regeneration) > rogue.hp_max) {
609 rogue.hp_current = rogue.hp_max;
611 print_stats(STAT_HP);
615 static boolean
616 can_turn(short nrow, short ncol)
618 if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
619 return(1);
621 return(0);
624 static void
625 turn_passage(short dir, boolean fast)
627 short crow = rogue.row, ccol = rogue.col, turns = 0;
628 short ndir = 0;
630 if ((dir != 'h') && can_turn(crow, ccol + 1)) {
631 turns++;
632 ndir = 'l';
634 if ((dir != 'l') && can_turn(crow, ccol - 1)) {
635 turns++;
636 ndir = 'h';
638 if ((dir != 'k') && can_turn(crow + 1, ccol)) {
639 turns++;
640 ndir = 'j';
642 if ((dir != 'j') && can_turn(crow - 1, ccol)) {
643 turns++;
644 ndir = 'k';
646 if (turns == 1) {
647 multiple_move_rogue(ndir - (fast ? 32 : 96));