Error out if no volumes are specified instead of core-dumping.
[dragonfly.git] / games / rogue / move.c
blob757ab93ed9bb92784252aa069a0c94032294b177
1 /*
2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Timothy C. Stoehr.
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.
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.
36 * @(#)move.c 8.1 (Berkeley) 5/31/93
37 * $FreeBSD: src/games/rogue/move.c,v 1.7 1999/11/30 03:49:24 billf Exp $
38 * $DragonFly: src/games/rogue/move.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
42 * move.c
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.
53 #include "rogue.h"
55 short m_moves = 0;
56 boolean jump = 0;
57 const char *you_can_move_again = "you can move again";
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 boolean being_held, interrupted, r_teleport, passgo;
65 static boolean next_to_something(int, int);
66 static boolean check_hunger(boolean);
67 static short gr_dir(void);
68 static void heal(void);
69 static boolean can_turn(short, short);
70 static void turn_passage(short, boolean);
72 short
73 one_move_rogue(short dirch, short pickup)
75 short row, col;
76 object *obj;
77 char desc[DCOLS];
78 short n, status, d;
80 row = rogue.row;
81 col = rogue.col;
83 if (confused) {
84 dirch = gr_dir();
86 is_direction(dirch, &d);
87 get_dir_rc(d, &row, &col, 1);
89 if (!can_move(rogue.row, rogue.col, row, col)) {
90 return(MOVE_FAILED);
92 if (being_held || bear_trap) {
93 if (!(dungeon[row][col] & MONSTER)) {
94 if (being_held) {
95 message("you are being held", 1);
96 } else {
97 message("you are still stuck in the bear trap", 0);
98 reg_move();
100 return(MOVE_FAILED);
103 if (r_teleport) {
104 if (rand_percent(R_TELE_PERCENT)) {
105 tele();
106 return(STOPPED_ON_SOMETHING);
109 if (dungeon[row][col] & MONSTER) {
110 rogue_hit(object_at(&level_monsters, row, col), 0);
111 reg_move();
112 return(MOVE_FAILED);
114 if (dungeon[row][col] & DOOR) {
115 if (cur_room == PASSAGE) {
116 cur_room = get_room_number(row, col);
117 light_up_room(cur_room);
118 wake_room(cur_room, 1, row, col);
119 } else {
120 light_passage(row, col);
122 } else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
123 (dungeon[row][col] & TUNNEL)) {
124 light_passage(row, col);
125 wake_room(cur_room, 0, rogue.row, rogue.col);
126 darken_room(cur_room);
127 cur_room = PASSAGE;
128 } else if (dungeon[row][col] & TUNNEL) {
129 light_passage(row, col);
131 mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
132 mvaddch(row, col, rogue.fchar);
134 if (!jump) {
135 refresh();
137 rogue.row = row;
138 rogue.col = col;
139 if (dungeon[row][col] & OBJECT) {
140 if (levitate && pickup) {
141 return(STOPPED_ON_SOMETHING);
143 if (pickup && !levitate) {
144 if ((obj = pick_up(row, col, &status))) {
145 get_desc(obj, desc);
146 if (obj->what_is == GOLD) {
147 free_object(obj);
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 strcpy(desc, "moved onto ");
159 get_desc(obj, desc+11);
160 goto NOT_IN_PACK;
162 n = strlen(desc);
163 desc[n] = '(';
164 desc[n+1] = obj->ichar;
165 desc[n+2] = ')';
166 desc[n+3] = 0;
167 NOT_IN_PACK:
168 message(desc, 1);
169 reg_move();
170 return(STOPPED_ON_SOMETHING);
172 if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
173 if ((!levitate) && (dungeon[row][col] & TRAP)) {
174 trap_player(row, col);
176 reg_move();
177 return(STOPPED_ON_SOMETHING);
179 MVED: if (reg_move()) { /* fainted from hunger */
180 return(STOPPED_ON_SOMETHING);
182 return((confused ? STOPPED_ON_SOMETHING : MOVED));
185 void
186 multiple_move_rogue(short dirch)
188 short row, col;
189 short m;
191 switch(dirch) {
192 case '\010':
193 case '\012':
194 case '\013':
195 case '\014':
196 case '\031':
197 case '\025':
198 case '\016':
199 case '\002':
200 do {
201 row = rogue.row;
202 col = rogue.col;
203 if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
204 (m == STOPPED_ON_SOMETHING) ||
205 interrupted) {
206 break;
208 } while (!next_to_something(row, col));
209 if ( (!interrupted) && passgo && (m == MOVE_FAILED) &&
210 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
211 turn_passage(dirch + 96, 0);
213 break;
214 case 'H':
215 case 'J':
216 case 'K':
217 case 'L':
218 case 'B':
219 case 'Y':
220 case 'U':
221 case 'N':
222 while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) ;
224 if ( (!interrupted) && passgo &&
225 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
226 turn_passage(dirch + 32, 1);
228 break;
232 boolean
233 is_passable(int row, int col)
235 if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
236 (col > (DCOLS-1))) {
237 return(0);
239 if (dungeon[row][col] & HIDDEN) {
240 return((dungeon[row][col] & TRAP) ? 1 : 0);
242 return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
245 static boolean
246 next_to_something(int drow, int dcol)
248 short i, j, i_end, j_end, row, col;
249 short pass_count = 0;
250 unsigned short s;
252 if (confused) {
253 return(1);
255 if (blind) {
256 return(0);
258 i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
259 j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
261 for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
262 for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
263 if ((i == 0) && (j == 0)) {
264 continue;
266 if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
267 continue;
269 row = rogue.row + i;
270 col = rogue.col + j;
271 s = dungeon[row][col];
272 if (s & HIDDEN) {
273 continue;
275 /* If the rogue used to be right, up, left, down, or right of
276 * row,col, and now isn't, then don't stop */
277 if (s & (MONSTER | OBJECT | STAIRS)) {
278 if (((row == drow) || (col == dcol)) &&
279 (!((row == rogue.row) || (col == rogue.col)))) {
280 continue;
282 return(1);
284 if (s & TRAP) {
285 if (!(s & HIDDEN)) {
286 if (((row == drow) || (col == dcol)) &&
287 (!((row == rogue.row) || (col == rogue.col)))) {
288 continue;
290 return(1);
293 if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
294 if (++pass_count > 1) {
295 return(1);
298 if ((s & DOOR) && ((i == 0) || (j == 0))) {
299 return(1);
303 return(0);
306 boolean
307 can_move(short row1, short col1, short row2, short col2)
309 if (!is_passable(row2, col2)) {
310 return(0);
312 if ((row1 != row2) && (col1 != col2)) {
313 if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
314 return(0);
316 if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
317 return(0);
320 return(1);
323 void
324 move_onto(void)
326 short ch, d;
327 boolean first_miss = 1;
329 while (!is_direction(ch = rgetchar(), &d)) {
330 sound_bell();
331 if (first_miss) {
332 message("direction? ", 0);
333 first_miss = 0;
336 check_message();
337 if (ch != CANCEL) {
338 one_move_rogue(ch, 0);
342 boolean
343 is_direction(short c, short *d)
345 switch(c) {
346 case 'h':
347 *d = LEFT;
348 break;
349 case 'j':
350 *d = DOWN;
351 break;
352 case 'k':
353 *d = UPWARD;
354 break;
355 case 'l':
356 *d = RIGHT;
357 break;
358 case 'b':
359 *d = DOWNLEFT;
360 break;
361 case 'y':
362 *d = UPLEFT;
363 break;
364 case 'u':
365 *d = UPRIGHT;
366 break;
367 case 'n':
368 *d = DOWNRIGHT;
369 break;
370 case CANCEL:
371 break;
372 default:
373 return(0);
375 return(1);
378 static boolean
379 check_hunger(boolean msg_only)
381 short i, n;
382 boolean fainted = 0;
384 if (rogue.moves_left == HUNGRY) {
385 strcpy(hunger_str, "hungry");
386 message(hunger_str, 0);
387 print_stats(STAT_HUNGER);
389 if (rogue.moves_left == WEAK) {
390 strcpy(hunger_str, "weak");
391 message(hunger_str, 1);
392 print_stats(STAT_HUNGER);
394 if (rogue.moves_left <= FAINT) {
395 if (rogue.moves_left == FAINT) {
396 strcpy(hunger_str, "faint");
397 message(hunger_str, 1);
398 print_stats(STAT_HUNGER);
400 n = get_rand(0, (FAINT - rogue.moves_left));
401 if (n > 0) {
402 fainted = 1;
403 if (rand_percent(40)) {
404 rogue.moves_left++;
406 message("you faint", 1);
407 for (i = 0; i < n; i++) {
408 if (coin_toss()) {
409 mv_mons();
412 message(you_can_move_again, 1);
415 if (msg_only) {
416 return(fainted);
418 if (rogue.moves_left <= STARVE) {
419 killed_by((object *) 0, STARVATION);
422 switch(e_rings) {
423 case -1:
424 rogue.moves_left -= (rogue.moves_left % 2);
425 break;
426 case 0:
427 rogue.moves_left--;
428 break;
429 case 1:
430 rogue.moves_left--;
431 check_hunger(1);
432 rogue.moves_left -= (rogue.moves_left % 2);
433 break;
434 case 2:
435 rogue.moves_left--;
436 check_hunger(1);
437 rogue.moves_left--;
438 break;
440 return(fainted);
443 boolean
444 reg_move(void)
446 boolean fainted;
448 if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
449 fainted = check_hunger(0);
450 } else {
451 fainted = 0;
454 mv_mons();
456 if (++m_moves >= 120) {
457 m_moves = 0;
458 wanderer();
460 if (halluc) {
461 if (!(--halluc)) {
462 unhallucinate();
463 } else {
464 hallucinate();
467 if (blind) {
468 if (!(--blind)) {
469 unblind();
472 if (confused) {
473 if (!(--confused)) {
474 unconfuse();
477 if (bear_trap) {
478 bear_trap--;
480 if (levitate) {
481 if (!(--levitate)) {
482 message("you float gently to the ground", 1);
483 if (dungeon[rogue.row][rogue.col] & TRAP) {
484 trap_player(rogue.row, rogue.col);
488 if (haste_self) {
489 if (!(--haste_self)) {
490 message("you feel yourself slowing down", 0);
493 heal();
494 if (auto_search > 0) {
495 search(auto_search, auto_search);
497 return(fainted);
500 void
501 rest(int count)
503 int i;
505 interrupted = 0;
507 for (i = 0; i < count; i++) {
508 if (interrupted) {
509 break;
511 reg_move();
515 static short
516 gr_dir(void)
518 short d;
520 d = get_rand(1, 8);
522 switch(d) {
523 case 1:
524 d = 'j';
525 break;
526 case 2:
527 d = 'k';
528 break;
529 case 3:
530 d = 'l';
531 break;
532 case 4:
533 d = 'h';
534 break;
535 case 5:
536 d = 'y';
537 break;
538 case 6:
539 d = 'u';
540 break;
541 case 7:
542 d = 'b';
543 break;
544 case 8:
545 d = 'n';
546 break;
548 return(d);
551 static void
552 heal(void)
554 static short heal_exp = -1, n, c = 0;
555 static boolean alt;
557 if (rogue.hp_current == rogue.hp_max) {
558 c = 0;
559 return;
561 if (rogue.exp != heal_exp) {
562 heal_exp = rogue.exp;
564 switch(heal_exp) {
565 case 1:
566 n = 20;
567 break;
568 case 2:
569 n = 18;
570 break;
571 case 3:
572 n = 17;
573 break;
574 case 4:
575 n = 14;
576 break;
577 case 5:
578 n = 13;
579 break;
580 case 6:
581 n = 10;
582 break;
583 case 7:
584 n = 9;
585 break;
586 case 8:
587 n = 8;
588 break;
589 case 9:
590 n = 7;
591 break;
592 case 10:
593 n = 4;
594 break;
595 case 11:
596 n = 3;
597 break;
598 case 12:
599 default:
600 n = 2;
603 if (++c >= n) {
604 c = 0;
605 rogue.hp_current++;
606 if ((alt = !alt)) {
607 rogue.hp_current++;
609 if ((rogue.hp_current += regeneration) > rogue.hp_max) {
610 rogue.hp_current = rogue.hp_max;
612 print_stats(STAT_HP);
616 static boolean
617 can_turn(short nrow, short ncol)
619 if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
620 return(1);
622 return(0);
625 static void
626 turn_passage(short dir, boolean fast)
628 short crow = rogue.row, ccol = rogue.col, turns = 0;
629 short ndir = 0;
631 if ((dir != 'h') && can_turn(crow, ccol + 1)) {
632 turns++;
633 ndir = 'l';
635 if ((dir != 'l') && can_turn(crow, ccol - 1)) {
636 turns++;
637 ndir = 'h';
639 if ((dir != 'k') && can_turn(crow + 1, ccol)) {
640 turns++;
641 ndir = 'j';
643 if ((dir != 'j') && can_turn(crow - 1, ccol)) {
644 turns++;
645 ndir = 'k';
647 if (turns == 1) {
648 multiple_move_rogue(ndir - (fast ? 32 : 96));