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