xref: /dragonfly/games/rogue/move.c (revision 3536f98c1c75c45bd29f481cf18b0f90cea395f7)
1 /*-
2  * Copyright (c) 1988, 1993
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Timothy C. Stoehr.
7  *
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.
19  *
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.
31  *
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  */
35 
36 /*
37  * move.c
38  *
39  * This source herein may be modified and/or distributed by anybody who
40  * so desires, with the following restrictions:
41  *    1.)  No portion of this notice shall be removed.
42  *    2.)  Credit shall not be taken for the creation of this source.
43  *    3.)  This code is not to be traded, sold, or used for personal
44  *         gain or profit.
45  *
46  */
47 
48 #include "rogue.h"
49 
50 short m_moves = 0;
51 boolean jump = 0;
52 const char you_can_move_again[] = "you can move again";
53 
54 static boolean can_turn(short, short);
55 static boolean check_hunger(boolean);
56 static short gr_dir(void);
57 static void heal(void);
58 static boolean next_to_something(int, int);
59 static void turn_passage(short, boolean);
60 
61 short
one_move_rogue(short dirch,short pickup)62 one_move_rogue(short dirch, short pickup)
63 {
64           short row, col;
65           object *obj;
66           char desc[DCOLS];
67           short n, status, d;
68 
69           row = rogue.row;
70           col = rogue.col;
71 
72           if (confused) {
73                     dirch = gr_dir();
74           }
75           is_direction(dirch, &d);
76           get_dir_rc(d, &row, &col, 1);
77 
78           if (!can_move(rogue.row, rogue.col, row, col)) {
79                     return(MOVE_FAILED);
80           }
81           if (being_held || bear_trap) {
82                     if (!(dungeon[row][col] & MONSTER)) {
83                               if (being_held) {
84                                         message("you are being held", 1);
85                               } else {
86                                         message("you are still stuck in the bear trap", 0);
87                                         reg_move();
88                               }
89                               return(MOVE_FAILED);
90                     }
91           }
92           if (r_teleport) {
93                     if (rand_percent(R_TELE_PERCENT)) {
94                               tele();
95                               return(STOPPED_ON_SOMETHING);
96                     }
97           }
98           if (dungeon[row][col] & MONSTER) {
99                     rogue_hit(object_at(&level_monsters, row, col), 0);
100                     reg_move();
101                     return(MOVE_FAILED);
102           }
103           if (dungeon[row][col] & DOOR) {
104                     if (cur_room == PASSAGE) {
105                               cur_room = get_room_number(row, col);
106                               if (cur_room == NO_ROOM)
107                                         clean_up("one_move_rogue: door to nowhere");
108                               light_up_room(cur_room);
109                               wake_room(cur_room, 1, row, col);
110                     } else {
111                               light_passage(row, col);
112                     }
113           } else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
114                        (dungeon[row][col] & TUNNEL)) {
115                     light_passage(row, col);
116                     wake_room(cur_room, 0, rogue.row, rogue.col);
117                     darken_room(cur_room);
118                     cur_room = PASSAGE;
119           } else if (dungeon[row][col] & TUNNEL) {
120                               light_passage(row, col);
121           }
122           mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
123           mvaddch(row, col, rogue.fchar);
124 
125           if (!jump) {
126                     refresh();
127           }
128           rogue.row = row;
129           rogue.col = col;
130           if (dungeon[row][col] & OBJECT) {
131                     if (levitate && pickup) {
132                               return(STOPPED_ON_SOMETHING);
133                     }
134                     if (pickup && !levitate) {
135                               if ((obj = pick_up(row, col, &status)) != NULL) {
136                                         get_desc(obj, desc);
137                                         if (obj->what_is == GOLD) {
138                                                   free_object(obj);
139                                                   goto NOT_IN_PACK;
140                                         }
141                               } else if (!status) {
142                                         goto MVED;
143                               } else {
144                                         goto MOVE_ON;
145                               }
146                     } else {
147 MOVE_ON:
148                               obj = object_at(&level_objects, row, col);
149                               strcpy(desc, "moved onto ");
150                               get_desc(obj, desc+11);
151                               goto NOT_IN_PACK;
152                     }
153                     n = strlen(desc);
154                     desc[n] = '(';
155                     desc[n+1] = obj->ichar;
156                     desc[n+2] = ')';
157                     desc[n+3] = 0;
158 NOT_IN_PACK:
159                     message(desc, 1);
160                     reg_move();
161                     return(STOPPED_ON_SOMETHING);
162           }
163           if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
164                     if ((!levitate) && (dungeon[row][col] & TRAP)) {
165                               trap_player(row, col);
166                     }
167                     reg_move();
168                     return(STOPPED_ON_SOMETHING);
169           }
170 MVED:     if (reg_move()) {                       /* fainted from hunger */
171                               return(STOPPED_ON_SOMETHING);
172           }
173           return((confused ? STOPPED_ON_SOMETHING : MOVED));
174 }
175 
176 void
multiple_move_rogue(short dirch)177 multiple_move_rogue(short dirch)
178 {
179           short row, col;
180           short m;
181 
182           switch(dirch) {
183           case '\010':
184           case '\012':
185           case '\013':
186           case '\014':
187           case '\031':
188           case '\025':
189           case '\016':
190           case '\002':
191                     do {
192                               row = rogue.row;
193                               col = rogue.col;
194                               if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
195                                         (m == STOPPED_ON_SOMETHING) ||
196                                         interrupted) {
197                                         break;
198                               }
199                     } while (!next_to_something(row, col));
200                     if (      (!interrupted) && passgo && (m == MOVE_FAILED) &&
201                                         (dungeon[rogue.row][rogue.col] & TUNNEL)) {
202                               turn_passage(dirch + 96, 0);
203                     }
204                     break;
205           case 'H':
206           case 'J':
207           case 'K':
208           case 'L':
209           case 'B':
210           case 'Y':
211           case 'U':
212           case 'N':
213                     while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED))
214                               ;
215 
216                     if (      (!interrupted) && passgo &&
217                                         (dungeon[rogue.row][rogue.col] & TUNNEL)) {
218                               turn_passage(dirch + 32, 1);
219                     }
220                     break;
221           }
222 }
223 
224 boolean
is_passable(int row,int col)225 is_passable(int row, int 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 static boolean
next_to_something(int drow,int dcol)238 next_to_something(int drow, int dcol)
239 {
240           short i, j, i_end, j_end, row, col;
241           short pass_count = 0;
242           unsigned short s;
243 
244           if (confused) {
245                     return(1);
246           }
247           if (blind) {
248                     return(0);
249           }
250           i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
251           j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
252 
253           for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
254                     for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
255                               if ((i == 0) && (j == 0)) {
256                                         continue;
257                               }
258                               if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
259                                         continue;
260                               }
261                               row = rogue.row + i;
262                               col = rogue.col + j;
263                               s = dungeon[row][col];
264                               if (s & HIDDEN) {
265                                         continue;
266                               }
267                               /* If the rogue used to be right, up, left, down, or right of
268                                * row,col, and now isn't, then don't stop */
269                               if (s & (MONSTER | OBJECT | STAIRS)) {
270                                         if (((row == drow) || (col == dcol)) &&
271                                                   (!((row == rogue.row) || (col == rogue.col)))) {
272                                                   continue;
273                                         }
274                                         return(1);
275                               }
276                               if (s & TRAP) {
277                                         if (!(s & HIDDEN)) {
278                                                   if (((row == drow) || (col == dcol)) &&
279                                                             (!((row == rogue.row) || (col == rogue.col)))) {
280                                                             continue;
281                                                   }
282                                                   return(1);
283                                         }
284                               }
285                               if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
286                                         if (++pass_count > 1) {
287                                                   return(1);
288                                         }
289                               }
290                               if ((s & DOOR) && ((i == 0) || (j == 0))) {
291                                                   return(1);
292                               }
293                     }
294           }
295           return(0);
296 }
297 
298 boolean
can_move(short row1,short col1,short row2,short col2)299 can_move(short row1, short col1, short row2, short col2)
300 {
301           if (!is_passable(row2, col2)) {
302                     return(0);
303           }
304           if ((row1 != row2) && (col1 != col2)) {
305                     if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
306                               return(0);
307                     }
308                     if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
309                               return(0);
310                     }
311           }
312           return(1);
313 }
314 
315 void
move_onto(void)316 move_onto(void)
317 {
318           short ch, d;
319           boolean first_miss = 1;
320 
321           while (!is_direction(ch = rgetchar(), &d)) {
322                     sound_bell();
323                     if (first_miss) {
324                               message("direction? ", 0);
325                               first_miss = 0;
326                     }
327           }
328           check_message();
329           if (ch != CANCEL) {
330                     one_move_rogue(ch, 0);
331           }
332 }
333 
334 boolean
is_direction(short c,short * d)335 is_direction(short c, short *d)
336 {
337           switch(c) {
338           case 'h':
339                     *d = LEFT;
340                     break;
341           case 'j':
342                     *d = DOWN;
343                     break;
344           case 'k':
345                     *d = UPWARD;
346                     break;
347           case 'l':
348                     *d = RIGHT;
349                     break;
350           case 'b':
351                     *d = DOWNLEFT;
352                     break;
353           case 'y':
354                     *d = UPLEFT;
355                     break;
356           case 'u':
357                     *d = UPRIGHT;
358                     break;
359           case 'n':
360                     *d = DOWNRIGHT;
361                     break;
362           case CANCEL:
363                     break;
364           default:
365                     return(0);
366           }
367           return(1);
368 }
369 
370 static boolean
check_hunger(boolean msg_only)371 check_hunger(boolean msg_only)
372 {
373           short i, n;
374           boolean fainted = 0;
375 
376           if (rogue.moves_left == HUNGRY) {
377                     strcpy(hunger_str, "hungry");
378                     message(hunger_str, 0);
379                     print_stats(STAT_HUNGER);
380           }
381           if (rogue.moves_left == WEAK) {
382                     strcpy(hunger_str, "weak");
383                     message(hunger_str, 1);
384                     print_stats(STAT_HUNGER);
385           }
386           if (rogue.moves_left <= FAINT) {
387                     if (rogue.moves_left == FAINT) {
388                               strcpy(hunger_str, "faint");
389                               message(hunger_str, 1);
390                               print_stats(STAT_HUNGER);
391                     }
392                     n = get_rand(0, (FAINT - rogue.moves_left));
393                     if (n > 0) {
394                               fainted = 1;
395                               if (rand_percent(40)) {
396                                         rogue.moves_left++;
397                               }
398                               message("you faint", 1);
399                               for (i = 0; i < n; i++) {
400                                         if (coin_toss()) {
401                                                   mv_mons();
402                                         }
403                               }
404                               message(you_can_move_again, 1);
405                     }
406           }
407           if (msg_only) {
408                     return(fainted);
409           }
410           if (rogue.moves_left <= STARVE) {
411                     killed_by(NULL, STARVATION);
412           }
413 
414           switch(e_rings) {
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                     check_hunger(1);
424                     rogue.moves_left -= (rogue.moves_left % 2);
425                     break;
426           case 2:
427                     rogue.moves_left--;
428                     check_hunger(1);
429                     rogue.moves_left--;
430                     break;
431           }
432           return(fainted);
433 }
434 
435 boolean
reg_move(void)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                               message("you float gently to the ground", 1);
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                               message("you feel yourself slowing down", 0);
483                     }
484           }
485           heal();
486           if (auto_search > 0) {
487                     search(auto_search, auto_search);
488           }
489           return(fainted);
490 }
491 
492 void
rest(int count)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                     reg_move();
504           }
505 }
506 
507 static short
gr_dir(void)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 static void
heal(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 static boolean
can_turn(short nrow,short ncol)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 static void
turn_passage(short dir,boolean fast)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