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