Add efidev.4 and efirt.9 manual pages from FreeBSD.
[dragonfly.git] / games / rogue / move.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }