1 /*        $NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg 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.13 2011/05/23 23:01:17 joerg 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 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);
68 
69 int
one_move_rogue(short dirch,short pickup)70 one_move_rogue(short dirch, short pickup)
71 {
72           short row, col;
73           object *obj;
74           char desc[DCOLS];
75           short status, d = 0;          /* XXX: GCC */
76 
77           row = rogue.row;
78           col = rogue.col;
79 
80           if (confused) {
81                     dirch = gr_dir();
82           }
83           (void)is_direction(dirch, &d);
84           get_dir_rc(d, &row, &col, 1);
85 
86           if (!can_move(rogue.row, rogue.col, row, col)) {
87                     return(MOVE_FAILED);
88           }
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();
96                               }
97                               return(MOVE_FAILED);
98                     }
99           }
100           if (r_teleport) {
101                     if (rand_percent(R_TELE_PERCENT)) {
102                               tele();
103                               return(STOPPED_ON_SOMETHING);
104                     }
105           }
106           if (dungeon[row][col] & MONSTER) {
107                     rogue_hit(object_at(&level_monsters, row, col), 0);
108                     (void)reg_move();
109                     return(MOVE_FAILED);
110           }
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);
120                     }
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);
129           }
130           mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
131           mvaddch(row, col, rogue.fchar);
132 
133           if (!jump) {
134                     refresh();
135           }
136           rogue.row = row;
137           rogue.col = col;
138           if (dungeon[row][col] & OBJECT) {
139                     if (levitate && pickup) {
140                               return(STOPPED_ON_SOMETHING);
141                     }
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;
149                                         }
150                               } else if (!status) {
151                                         goto MVED;
152                               } else {
153                                         goto MOVE_ON;
154                               }
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;
161                     }
162                     messagef(1, "%s(%c)", desc, obj->ichar);
163 NOT_IN_PACK:
164                     (void)reg_move();
165                     return(STOPPED_ON_SOMETHING);
166           }
167           if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
168                     if ((!levitate) && (dungeon[row][col] & TRAP)) {
169                               trap_player(row, col);
170                     }
171                     (void)reg_move();
172                     return(STOPPED_ON_SOMETHING);
173           }
174 MVED:     if (reg_move()) {                       /* fainted from hunger */
175                               return(STOPPED_ON_SOMETHING);
176           }
177           return((confused ? STOPPED_ON_SOMETHING : MOVED));
178 }
179 
180 void
multiple_move_rogue(short dirch)181 multiple_move_rogue(short dirch)
182 {
183           short row, col;
184           short m;
185 
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;
202                               }
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);
207                     }
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))
218                               ;
219 
220                     if (      (!interrupted) && passgo &&
221                                         (dungeon[rogue.row][rogue.col] & TUNNEL)) {
222                               turn_passage(dirch + 32, 1);
223                     }
224                     break;
225           }
226 }
227 
228 boolean
is_passable(int row,int col)229 is_passable(int row, int col)
230 {
231           if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
232                     (col > (DCOLS-1))) {
233                     return(0);
234           }
235           if (dungeon[row][col] & HIDDEN) {
236                     return((dungeon[row][col] & TRAP) ? 1 : 0);
237           }
238           return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
239 }
240 
241 static boolean
next_to_something(int drow,int dcol)242 next_to_something(int drow, int dcol)
243 {
244           short i, j, i_end, j_end, row, col;
245           short pass_count = 0;
246           unsigned short s;
247 
248           if (confused) {
249                     return(1);
250           }
251           if (blind) {
252                     return(0);
253           }
254           i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
255           j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
256 
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;
261                               }
262                               if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
263                                         continue;
264                               }
265                               row = rogue.row + i;
266                               col = rogue.col + j;
267                               s = dungeon[row][col];
268                               if (s & HIDDEN) {
269                                         continue;
270                               }
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;
277                                         }
278                                         return(1);
279                               }
280                               if (s & TRAP) {
281                                         if (!(s & HIDDEN)) {
282                                                   if (((row == drow) || (col == dcol)) &&
283                                                             (!((row == rogue.row) || (col == rogue.col)))) {
284                                                             continue;
285                                                   }
286                                                   return(1);
287                                         }
288                               }
289                               if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
290                                         if (++pass_count > 1) {
291                                                   return(1);
292                                         }
293                               }
294                               if ((s & DOOR) && ((i == 0) || (j == 0))) {
295                                                   return(1);
296                               }
297                     }
298           }
299           return(0);
300 }
301 
302 boolean
can_move(int row1,int col1,int row2,int col2)303 can_move(int row1, int col1, int row2, int col2)
304 {
305           if (!is_passable(row2, col2)) {
306                     return(0);
307           }
308           if ((row1 != row2) && (col1 != col2)) {
309                     if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
310                               return(0);
311                     }
312                     if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
313                               return(0);
314                     }
315           }
316           return(1);
317 }
318 
319 void
move_onto(void)320 move_onto(void)
321 {
322           short ch, d;
323           boolean first_miss = 1;
324 
325           while (!is_direction(ch = rgetchar(), &d)) {
326                     sound_bell();
327                     if (first_miss) {
328                               messagef(0, "direction? ");
329                               first_miss = 0;
330                     }
331           }
332           check_message();
333           if (ch != CANCEL) {
334                     (void)one_move_rogue(ch, 0);
335           }
336 }
337 
338 boolean
is_direction(short c,short * d)339 is_direction(short c, 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 static boolean
check_hunger(boolean msg_only)375 check_hunger(boolean msg_only)
376 {
377           short i, n;
378           boolean fainted = 0;
379 
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);
384           }
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);
389           }
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);
395                     }
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++;
401                               }
402                               messagef(1, "you faint");
403                               for (i = 0; i < n; i++) {
404                                         if (coin_toss()) {
405                                                   mv_mons();
406                                         }
407                               }
408                               messagef(1, "%s", you_can_move_again);
409                     }
410           }
411           if (msg_only) {
412                     return(fainted);
413           }
414           if (rogue.moves_left <= STARVE) {
415                     killed_by(NULL, STARVATION);
416           }
417 
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;
438           }
439           return(fainted);
440 }
441 
442 boolean
reg_move(void)443 reg_move(void)
444 {
445           boolean fainted;
446 
447           if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
448                     fainted = check_hunger(0);
449           } else {
450                     fainted = 0;
451           }
452 
453           mv_mons();
454 
455           if (++m_moves >= 120) {
456                     m_moves = 0;
457                     wanderer();
458           }
459           if (halluc) {
460                     if (!(--halluc)) {
461                               unhallucinate();
462                     } else {
463                               hallucinate();
464                     }
465           }
466           if (blind) {
467                     if (!(--blind)) {
468                               unblind();
469                     }
470           }
471           if (confused) {
472                     if (!(--confused)) {
473                               unconfuse();
474                     }
475           }
476           if (bear_trap) {
477                     bear_trap--;
478           }
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);
484                               }
485                     }
486           }
487           if (haste_self) {
488                     if (!(--haste_self)) {
489                               messagef(0, "you feel yourself slowing down");
490                     }
491           }
492           heal();
493           if (auto_search > 0) {
494                     search(auto_search, auto_search);
495           }
496           return(fainted);
497 }
498 
499 void
rest(int count)500 rest(int count)
501 {
502           int i;
503 
504           interrupted = 0;
505 
506           for (i = 0; i < count; i++) {
507                     if (interrupted) {
508                               break;
509                     }
510                     (void)reg_move();
511           }
512 }
513 
514 static char
gr_dir(void)515 gr_dir(void)
516 {
517           short d;
518 
519           d = get_rand(1, 8);
520 
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;
546           }
547           return(d);
548 }
549 
550 static void
heal(void)551 heal(void)
552 {
553           static short heal_exp = -1, n, c = 0;
554           static boolean alt;
555 
556           if (rogue.hp_current == rogue.hp_max) {
557                     c = 0;
558                     return;
559           }
560           if (rogue.exp != heal_exp) {
561                     heal_exp = rogue.exp;
562 
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;
600                     }
601           }
602           if (++c >= n) {
603                     c = 0;
604                     rogue.hp_current++;
605                     if ((alt = !alt) != 0) {
606                               rogue.hp_current++;
607                     }
608                     if ((rogue.hp_current += regeneration) > rogue.hp_max) {
609                               rogue.hp_current = rogue.hp_max;
610                     }
611                     print_stats(STAT_HP);
612           }
613 }
614 
615 static boolean
can_turn(short nrow,short ncol)616 can_turn(short nrow, short ncol)
617 {
618           if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
619                     return(1);
620           }
621           return(0);
622 }
623 
624 static void
turn_passage(short dir,boolean fast)625 turn_passage(short dir, boolean fast)
626 {
627           short crow = rogue.row, ccol = rogue.col, turns = 0;
628           short ndir = 0;
629 
630           if ((dir != 'h') && can_turn(crow, ccol + 1)) {
631                     turns++;
632                     ndir = 'l';
633           }
634           if ((dir != 'l') && can_turn(crow, ccol - 1)) {
635                     turns++;
636                     ndir = 'h';
637           }
638           if ((dir != 'k') && can_turn(crow + 1, ccol)) {
639                     turns++;
640                     ndir = 'j';
641           }
642           if ((dir != 'j') && can_turn(crow - 1, ccol)) {
643                     turns++;
644                     ndir = 'k';
645           }
646           if (turns == 1) {
647                     multiple_move_rogue(ndir - (fast ? 32 : 96));
648           }
649 }
650