w(1): Limit affect of locale change
[dragonfly.git] / games / rogue / move.c
blob19790f186d163393ba88d6e434ad60fd033b4bb1
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. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
32 * @(#)move.c 8.1 (Berkeley) 5/31/93
33 * $FreeBSD: src/games/rogue/move.c,v 1.7 1999/11/30 03:49:24 billf Exp $
34 * $DragonFly: src/games/rogue/move.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
38 * move.c
40 * This source herein may be modified and/or distributed by anybody who
41 * so desires, with the following restrictions:
42 * 1.) No portion of this notice shall be removed.
43 * 2.) Credit shall not be taken for the creation of this source.
44 * 3.) This code is not to be traded, sold, or used for personal
45 * gain or profit.
49 #include "rogue.h"
51 short m_moves = 0;
52 boolean jump = 0;
53 const char *you_can_move_again = "you can move again";
55 extern short cur_room, halluc, blind, levitate;
56 extern short cur_level, max_level;
57 extern short bear_trap, haste_self, confused;
58 extern short e_rings, regeneration, auto_search;
59 extern boolean being_held, interrupted, r_teleport, passgo;
61 static boolean can_turn(short, short);
62 static boolean check_hunger(boolean);
63 static short gr_dir(void);
64 static void heal(void);
65 static boolean next_to_something(int, int);
66 static void turn_passage(short, boolean);
68 short
69 one_move_rogue(short dirch, short pickup)
71 short row, col;
72 object *obj;
73 char desc[DCOLS];
74 short n, status, d;
76 row = rogue.row;
77 col = rogue.col;
79 if (confused) {
80 dirch = gr_dir();
82 is_direction(dirch, &d);
83 get_dir_rc(d, &row, &col, 1);
85 if (!can_move(rogue.row, rogue.col, row, col)) {
86 return(MOVE_FAILED);
88 if (being_held || bear_trap) {
89 if (!(dungeon[row][col] & MONSTER)) {
90 if (being_held) {
91 message("you are being held", 1);
92 } else {
93 message("you are still stuck in the bear trap", 0);
94 reg_move();
96 return(MOVE_FAILED);
99 if (r_teleport) {
100 if (rand_percent(R_TELE_PERCENT)) {
101 tele();
102 return(STOPPED_ON_SOMETHING);
105 if (dungeon[row][col] & MONSTER) {
106 rogue_hit(object_at(&level_monsters, row, col), 0);
107 reg_move();
108 return(MOVE_FAILED);
110 if (dungeon[row][col] & DOOR) {
111 if (cur_room == PASSAGE) {
112 cur_room = get_room_number(row, col);
113 light_up_room(cur_room);
114 wake_room(cur_room, 1, row, col);
115 } else {
116 light_passage(row, col);
118 } else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
119 (dungeon[row][col] & TUNNEL)) {
120 light_passage(row, col);
121 wake_room(cur_room, 0, rogue.row, rogue.col);
122 darken_room(cur_room);
123 cur_room = PASSAGE;
124 } else if (dungeon[row][col] & TUNNEL) {
125 light_passage(row, col);
127 mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
128 mvaddch(row, col, rogue.fchar);
130 if (!jump) {
131 refresh();
133 rogue.row = row;
134 rogue.col = col;
135 if (dungeon[row][col] & OBJECT) {
136 if (levitate && pickup) {
137 return(STOPPED_ON_SOMETHING);
139 if (pickup && !levitate) {
140 if ((obj = pick_up(row, col, &status)) != NULL) {
141 get_desc(obj, desc);
142 if (obj->what_is == GOLD) {
143 free_object(obj);
144 goto NOT_IN_PACK;
146 } else if (!status) {
147 goto MVED;
148 } else {
149 goto MOVE_ON;
151 } else {
152 MOVE_ON:
153 obj = object_at(&level_objects, row, col);
154 strcpy(desc, "moved onto ");
155 get_desc(obj, desc+11);
156 goto NOT_IN_PACK;
158 n = strlen(desc);
159 desc[n] = '(';
160 desc[n+1] = obj->ichar;
161 desc[n+2] = ')';
162 desc[n+3] = 0;
163 NOT_IN_PACK:
164 message(desc, 1);
165 reg_move();
166 return(STOPPED_ON_SOMETHING);
168 if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
169 if ((!levitate) && (dungeon[row][col] & TRAP)) {
170 trap_player(row, col);
172 reg_move();
173 return(STOPPED_ON_SOMETHING);
175 MVED: if (reg_move()) { /* fainted from hunger */
176 return(STOPPED_ON_SOMETHING);
178 return((confused ? STOPPED_ON_SOMETHING : MOVED));
181 void
182 multiple_move_rogue(short dirch)
184 short row, col;
185 short m;
187 switch(dirch) {
188 case '\010':
189 case '\012':
190 case '\013':
191 case '\014':
192 case '\031':
193 case '\025':
194 case '\016':
195 case '\002':
196 do {
197 row = rogue.row;
198 col = rogue.col;
199 if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
200 (m == STOPPED_ON_SOMETHING) ||
201 interrupted) {
202 break;
204 } while (!next_to_something(row, col));
205 if ( (!interrupted) && passgo && (m == MOVE_FAILED) &&
206 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
207 turn_passage(dirch + 96, 0);
209 break;
210 case 'H':
211 case 'J':
212 case 'K':
213 case 'L':
214 case 'B':
215 case 'Y':
216 case 'U':
217 case 'N':
218 while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED))
221 if ( (!interrupted) && passgo &&
222 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
223 turn_passage(dirch + 32, 1);
225 break;
229 boolean
230 is_passable(int row, int col)
232 if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
233 (col > (DCOLS-1))) {
234 return(0);
236 if (dungeon[row][col] & HIDDEN) {
237 return((dungeon[row][col] & TRAP) ? 1 : 0);
239 return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
242 static boolean
243 next_to_something(int drow, int dcol)
245 short i, j, i_end, j_end, row, col;
246 short pass_count = 0;
247 unsigned short s;
249 if (confused) {
250 return(1);
252 if (blind) {
253 return(0);
255 i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
256 j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
258 for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
259 for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
260 if ((i == 0) && (j == 0)) {
261 continue;
263 if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
264 continue;
266 row = rogue.row + i;
267 col = rogue.col + j;
268 s = dungeon[row][col];
269 if (s & HIDDEN) {
270 continue;
272 /* If the rogue used to be right, up, left, down, or right of
273 * row,col, and now isn't, then don't stop */
274 if (s & (MONSTER | OBJECT | STAIRS)) {
275 if (((row == drow) || (col == dcol)) &&
276 (!((row == rogue.row) || (col == rogue.col)))) {
277 continue;
279 return(1);
281 if (s & TRAP) {
282 if (!(s & HIDDEN)) {
283 if (((row == drow) || (col == dcol)) &&
284 (!((row == rogue.row) || (col == rogue.col)))) {
285 continue;
287 return(1);
290 if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
291 if (++pass_count > 1) {
292 return(1);
295 if ((s & DOOR) && ((i == 0) || (j == 0))) {
296 return(1);
300 return(0);
303 boolean
304 can_move(short row1, short col1, short row2, short col2)
306 if (!is_passable(row2, col2)) {
307 return(0);
309 if ((row1 != row2) && (col1 != col2)) {
310 if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
311 return(0);
313 if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
314 return(0);
317 return(1);
320 void
321 move_onto(void)
323 short ch, d;
324 boolean first_miss = 1;
326 while (!is_direction(ch = rgetchar(), &d)) {
327 sound_bell();
328 if (first_miss) {
329 message("direction? ", 0);
330 first_miss = 0;
333 check_message();
334 if (ch != CANCEL) {
335 one_move_rogue(ch, 0);
339 boolean
340 is_direction(short c, short *d)
342 switch(c) {
343 case 'h':
344 *d = LEFT;
345 break;
346 case 'j':
347 *d = DOWN;
348 break;
349 case 'k':
350 *d = UPWARD;
351 break;
352 case 'l':
353 *d = RIGHT;
354 break;
355 case 'b':
356 *d = DOWNLEFT;
357 break;
358 case 'y':
359 *d = UPLEFT;
360 break;
361 case 'u':
362 *d = UPRIGHT;
363 break;
364 case 'n':
365 *d = DOWNRIGHT;
366 break;
367 case CANCEL:
368 break;
369 default:
370 return(0);
372 return(1);
375 static boolean
376 check_hunger(boolean msg_only)
378 short i, n;
379 boolean fainted = 0;
381 if (rogue.moves_left == HUNGRY) {
382 strcpy(hunger_str, "hungry");
383 message(hunger_str, 0);
384 print_stats(STAT_HUNGER);
386 if (rogue.moves_left == WEAK) {
387 strcpy(hunger_str, "weak");
388 message(hunger_str, 1);
389 print_stats(STAT_HUNGER);
391 if (rogue.moves_left <= FAINT) {
392 if (rogue.moves_left == FAINT) {
393 strcpy(hunger_str, "faint");
394 message(hunger_str, 1);
395 print_stats(STAT_HUNGER);
397 n = get_rand(0, (FAINT - rogue.moves_left));
398 if (n > 0) {
399 fainted = 1;
400 if (rand_percent(40)) {
401 rogue.moves_left++;
403 message("you faint", 1);
404 for (i = 0; i < n; i++) {
405 if (coin_toss()) {
406 mv_mons();
409 message(you_can_move_again, 1);
412 if (msg_only) {
413 return(fainted);
415 if (rogue.moves_left <= STARVE) {
416 killed_by(NULL, STARVATION);
419 switch(e_rings) {
420 case -1:
421 rogue.moves_left -= (rogue.moves_left % 2);
422 break;
423 case 0:
424 rogue.moves_left--;
425 break;
426 case 1:
427 rogue.moves_left--;
428 check_hunger(1);
429 rogue.moves_left -= (rogue.moves_left % 2);
430 break;
431 case 2:
432 rogue.moves_left--;
433 check_hunger(1);
434 rogue.moves_left--;
435 break;
437 return(fainted);
440 boolean
441 reg_move(void)
443 boolean fainted;
445 if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
446 fainted = check_hunger(0);
447 } else {
448 fainted = 0;
451 mv_mons();
453 if (++m_moves >= 120) {
454 m_moves = 0;
455 wanderer();
457 if (halluc) {
458 if (!(--halluc)) {
459 unhallucinate();
460 } else {
461 hallucinate();
464 if (blind) {
465 if (!(--blind)) {
466 unblind();
469 if (confused) {
470 if (!(--confused)) {
471 unconfuse();
474 if (bear_trap) {
475 bear_trap--;
477 if (levitate) {
478 if (!(--levitate)) {
479 message("you float gently to the ground", 1);
480 if (dungeon[rogue.row][rogue.col] & TRAP) {
481 trap_player(rogue.row, rogue.col);
485 if (haste_self) {
486 if (!(--haste_self)) {
487 message("you feel yourself slowing down", 0);
490 heal();
491 if (auto_search > 0) {
492 search(auto_search, auto_search);
494 return(fainted);
497 void
498 rest(int count)
500 int i;
502 interrupted = 0;
504 for (i = 0; i < count; i++) {
505 if (interrupted) {
506 break;
508 reg_move();
512 static short
513 gr_dir(void)
515 short d;
517 d = get_rand(1, 8);
519 switch(d) {
520 case 1:
521 d = 'j';
522 break;
523 case 2:
524 d = 'k';
525 break;
526 case 3:
527 d = 'l';
528 break;
529 case 4:
530 d = 'h';
531 break;
532 case 5:
533 d = 'y';
534 break;
535 case 6:
536 d = 'u';
537 break;
538 case 7:
539 d = 'b';
540 break;
541 case 8:
542 d = 'n';
543 break;
545 return(d);
548 static void
549 heal(void)
551 static short heal_exp = -1, n, c = 0;
552 static boolean alt;
554 if (rogue.hp_current == rogue.hp_max) {
555 c = 0;
556 return;
558 if (rogue.exp != heal_exp) {
559 heal_exp = rogue.exp;
561 switch(heal_exp) {
562 case 1:
563 n = 20;
564 break;
565 case 2:
566 n = 18;
567 break;
568 case 3:
569 n = 17;
570 break;
571 case 4:
572 n = 14;
573 break;
574 case 5:
575 n = 13;
576 break;
577 case 6:
578 n = 10;
579 break;
580 case 7:
581 n = 9;
582 break;
583 case 8:
584 n = 8;
585 break;
586 case 9:
587 n = 7;
588 break;
589 case 10:
590 n = 4;
591 break;
592 case 11:
593 n = 3;
594 break;
595 case 12:
596 default:
597 n = 2;
600 if (++c >= n) {
601 c = 0;
602 rogue.hp_current++;
603 if ((alt = !alt) != 0) {
604 rogue.hp_current++;
606 if ((rogue.hp_current += regeneration) > rogue.hp_max) {
607 rogue.hp_current = rogue.hp_max;
609 print_stats(STAT_HP);
613 static boolean
614 can_turn(short nrow, short ncol)
616 if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
617 return(1);
619 return(0);
622 static void
623 turn_passage(short dir, boolean fast)
625 short crow = rogue.row, ccol = rogue.col, turns = 0;
626 short ndir = 0;
628 if ((dir != 'h') && can_turn(crow, ccol + 1)) {
629 turns++;
630 ndir = 'l';
632 if ((dir != 'l') && can_turn(crow, ccol - 1)) {
633 turns++;
634 ndir = 'h';
636 if ((dir != 'k') && can_turn(crow + 1, ccol)) {
637 turns++;
638 ndir = 'j';
640 if ((dir != 'j') && can_turn(crow - 1, ccol)) {
641 turns++;
642 ndir = 'k';
644 if (turns == 1) {
645 multiple_move_rogue(ndir - (fast ? 32 : 96));