Initial import from FreeBSD RELENG_4:
[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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)move.c      8.1 (Berkeley) 5/31/93";
40 #endif
41 static const char rcsid[] =
42  "$FreeBSD: src/games/rogue/move.c,v 1.7 1999/11/30 03:49:24 billf Exp $";
43 #endif /* not lint */
44
45 /*
46  * move.c
47  *
48  * This source herein may be modified and/or distributed by anybody who
49  * so desires, with the following restrictions:
50  *    1.)  No portion of this notice shall be removed.
51  *    2.)  Credit shall not be taken for the creation of this source.
52  *    3.)  This code is not to be traded, sold, or used for personal
53  *         gain or profit.
54  *
55  */
56
57 #include "rogue.h"
58
59 short m_moves = 0;
60 boolean jump = 0;
61 const char *you_can_move_again = "you can move again";
62
63 extern short cur_room, halluc, blind, levitate;
64 extern short cur_level, max_level;
65 extern short bear_trap, haste_self, confused;
66 extern short e_rings, regeneration, auto_search;
67 extern char hunger_str[];
68 extern boolean being_held, interrupted, r_teleport, passgo;
69
70 one_move_rogue(dirch, pickup)
71 short dirch, pickup;
72 {
73         short row, col;
74         object *obj;
75         char desc[DCOLS];
76         short n, status, d;
77
78         row = rogue.row;
79         col = rogue.col;
80
81         if (confused) {
82                 dirch = gr_dir();
83         }
84         (void) is_direction(dirch, &d);
85         get_dir_rc(d, &row, &col, 1);
86
87         if (!can_move(rogue.row, rogue.col, row, col)) {
88                 return(MOVE_FAILED);
89         }
90         if (being_held || bear_trap) {
91                 if (!(dungeon[row][col] & MONSTER)) {
92                         if (being_held) {
93                                 message("you are being held", 1);
94                         } else {
95                                 message("you are still stuck in the bear trap", 0);
96                                 (void) reg_move();
97                         }
98                         return(MOVE_FAILED);
99                 }
100         }
101         if (r_teleport) {
102                 if (rand_percent(R_TELE_PERCENT)) {
103                         tele();
104                         return(STOPPED_ON_SOMETHING);
105                 }
106         }
107         if (dungeon[row][col] & MONSTER) {
108                 rogue_hit(object_at(&level_monsters, row, col), 0);
109                 (void) reg_move();
110                 return(MOVE_FAILED);
111         }
112         if (dungeon[row][col] & DOOR) {
113                 if (cur_room == PASSAGE) {
114                         cur_room = get_room_number(row, col);
115                         light_up_room(cur_room);
116                         wake_room(cur_room, 1, row, col);
117                 } else {
118                         light_passage(row, col);
119                 }
120         } else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
121                    (dungeon[row][col] & TUNNEL)) {
122                 light_passage(row, col);
123                 wake_room(cur_room, 0, rogue.row, rogue.col);
124                 darken_room(cur_room);
125                 cur_room = PASSAGE;
126         } else if (dungeon[row][col] & TUNNEL) {
127                         light_passage(row, col);
128         }
129         mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
130         mvaddch(row, col, rogue.fchar);
131
132         if (!jump) {
133                 refresh();
134         }
135         rogue.row = row;
136         rogue.col = col;
137         if (dungeon[row][col] & OBJECT) {
138                 if (levitate && pickup) {
139                         return(STOPPED_ON_SOMETHING);
140                 }
141                 if (pickup && !levitate) {
142                         if (obj = pick_up(row, col, &status)) {
143                                 get_desc(obj, desc);
144                                 if (obj->what_is == GOLD) {
145                                         free_object(obj);
146                                         goto NOT_IN_PACK;
147                                 }
148                         } else if (!status) {
149                                 goto MVED;
150                         } else {
151                                 goto MOVE_ON;
152                         }
153                 } else {
154 MOVE_ON:
155                         obj = object_at(&level_objects, row, col);
156                         (void) strcpy(desc, "moved onto ");
157                         get_desc(obj, desc+11);
158                         goto NOT_IN_PACK;
159                 }
160                 n = strlen(desc);
161                 desc[n] = '(';
162                 desc[n+1] = obj->ichar;
163                 desc[n+2] = ')';
164                 desc[n+3] = 0;
165 NOT_IN_PACK:
166                 message(desc, 1);
167                 (void) reg_move();
168                 return(STOPPED_ON_SOMETHING);
169         }
170         if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
171                 if ((!levitate) && (dungeon[row][col] & TRAP)) {
172                         trap_player(row, col);
173                 }
174                 (void) reg_move();
175                 return(STOPPED_ON_SOMETHING);
176         }
177 MVED:   if (reg_move()) {                       /* fainted from hunger */
178                         return(STOPPED_ON_SOMETHING);
179         }
180         return((confused ? STOPPED_ON_SOMETHING : MOVED));
181 }
182
183 multiple_move_rogue(dirch)
184 short dirch;
185 {
186         short row, col;
187         short m;
188
189         switch(dirch) {
190         case '\010':
191         case '\012':
192         case '\013':
193         case '\014':
194         case '\031':
195         case '\025':
196         case '\016':
197         case '\002':
198                 do {
199                         row = rogue.row;
200                         col = rogue.col;
201                         if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
202                                 (m == STOPPED_ON_SOMETHING) ||
203                                 interrupted) {
204                                 break;
205                         }
206                 } while (!next_to_something(row, col));
207                 if (    (!interrupted) && passgo && (m == MOVE_FAILED) &&
208                                 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
209                         turn_passage(dirch + 96, 0);
210                 }
211                 break;
212         case 'H':
213         case 'J':
214         case 'K':
215         case 'L':
216         case 'B':
217         case 'Y':
218         case 'U':
219         case 'N':
220                 while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) ;
221
222                 if (    (!interrupted) && passgo &&
223                                 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
224                         turn_passage(dirch + 32, 1);
225                 }
226                 break;
227         }
228 }
229
230 is_passable(row, col)
231 int row, col;
232 {
233         if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
234                 (col > (DCOLS-1))) {
235                 return(0);
236         }
237         if (dungeon[row][col] & HIDDEN) {
238                 return((dungeon[row][col] & TRAP) ? 1 : 0);
239         }
240         return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
241 }
242
243 next_to_something(drow, dcol)
244 int drow, dcol;
245 {
246         short i, j, i_end, j_end, row, col;
247         short pass_count = 0;
248         unsigned short s;
249
250         if (confused) {
251                 return(1);
252         }
253         if (blind) {
254                 return(0);
255         }
256         i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
257         j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
258
259         for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
260                 for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
261                         if ((i == 0) && (j == 0)) {
262                                 continue;
263                         }
264                         if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
265                                 continue;
266                         }
267                         row = rogue.row + i;
268                         col = rogue.col + j;
269                         s = dungeon[row][col];
270                         if (s & HIDDEN) {
271                                 continue;
272                         }
273                         /* If the rogue used to be right, up, left, down, or right of
274                          * row,col, and now isn't, then don't stop */
275                         if (s & (MONSTER | OBJECT | STAIRS)) {
276                                 if (((row == drow) || (col == dcol)) &&
277                                         (!((row == rogue.row) || (col == rogue.col)))) {
278                                         continue;
279                                 }
280                                 return(1);
281                         }
282                         if (s & TRAP) {
283                                 if (!(s & HIDDEN)) {
284                                         if (((row == drow) || (col == dcol)) &&
285                                                 (!((row == rogue.row) || (col == rogue.col)))) {
286                                                 continue;
287                                         }
288                                         return(1);
289                                 }
290                         }
291                         if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
292                                 if (++pass_count > 1) {
293                                         return(1);
294                                 }
295                         }
296                         if ((s & DOOR) && ((i == 0) || (j == 0))) {
297                                         return(1);
298                         }
299                 }
300         }
301         return(0);
302 }
303
304 can_move(row1, col1, row2, col2)
305 {
306         if (!is_passable(row2, col2)) {
307                 return(0);
308         }
309         if ((row1 != row2) && (col1 != col2)) {
310                 if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) {
311                         return(0);
312                 }
313                 if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) {
314                         return(0);
315                 }
316         }
317         return(1);
318 }
319
320 move_onto()
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                         message("direction? ", 0);
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
339 is_direction(c, d)
340 short c;
341 short *d;
342 {
343         switch(c) {
344         case 'h':
345                 *d = LEFT;
346                 break;
347         case 'j':
348                 *d = DOWN;
349                 break;
350         case 'k':
351                 *d = UPWARD;
352                 break;
353         case 'l':
354                 *d = RIGHT;
355                 break;
356         case 'b':
357                 *d = DOWNLEFT;
358                 break;
359         case 'y':
360                 *d = UPLEFT;
361                 break;
362         case 'u':
363                 *d = UPRIGHT;
364                 break;
365         case 'n':
366                 *d = DOWNRIGHT;
367                 break;
368         case CANCEL:
369                 break;
370         default:
371                 return(0);
372         }
373         return(1);
374 }
375
376 boolean
377 check_hunger(msg_only)
378 boolean msg_only;
379 {
380         short i, n;
381         boolean fainted = 0;
382
383         if (rogue.moves_left == HUNGRY) {
384                 (void) strcpy(hunger_str, "hungry");
385                 message(hunger_str, 0);
386                 print_stats(STAT_HUNGER);
387         }
388         if (rogue.moves_left == WEAK) {
389                 (void) strcpy(hunger_str, "weak");
390                 message(hunger_str, 1);
391                 print_stats(STAT_HUNGER);
392         }
393         if (rogue.moves_left <= FAINT) {
394                 if (rogue.moves_left == FAINT) {
395                         (void) strcpy(hunger_str, "faint");
396                         message(hunger_str, 1);
397                         print_stats(STAT_HUNGER);
398                 }
399                 n = get_rand(0, (FAINT - rogue.moves_left));
400                 if (n > 0) {
401                         fainted = 1;
402                         if (rand_percent(40)) {
403                                 rogue.moves_left++;
404                         }
405                         message("you faint", 1);
406                         for (i = 0; i < n; i++) {
407                                 if (coin_toss()) {
408                                         mv_mons();
409                                 }
410                         }
411                         message(you_can_move_again, 1);
412                 }
413         }
414         if (msg_only) {
415                 return(fainted);
416         }
417         if (rogue.moves_left <= STARVE) {
418                 killed_by((object *) 0, STARVATION);
419         }
420
421         switch(e_rings) {
422         /*case -2:
423                 Subtract 0, i.e. do nothing.
424                 break;*/
425         case -1:
426                 rogue.moves_left -= (rogue.moves_left % 2);
427                 break;
428         case 0:
429                 rogue.moves_left--;
430                 break;
431         case 1:
432                 rogue.moves_left--;
433                 (void) check_hunger(1);
434                 rogue.moves_left -= (rogue.moves_left % 2);
435                 break;
436         case 2:
437                 rogue.moves_left--;
438                 (void) check_hunger(1);
439                 rogue.moves_left--;
440                 break;
441         }
442         return(fainted);
443 }
444
445 boolean
446 reg_move()
447 {
448         boolean fainted;
449
450         if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) {
451                 fainted = check_hunger(0);
452         } else {
453                 fainted = 0;
454         }
455
456         mv_mons();
457
458         if (++m_moves >= 120) {
459                 m_moves = 0;
460                 wanderer();
461         }
462         if (halluc) {
463                 if (!(--halluc)) {
464                         unhallucinate();
465                 } else {
466                         hallucinate();
467                 }
468         }
469         if (blind) {
470                 if (!(--blind)) {
471                         unblind();
472                 }
473         }
474         if (confused) {
475                 if (!(--confused)) {
476                         unconfuse();
477                 }
478         }
479         if (bear_trap) {
480                 bear_trap--;
481         }
482         if (levitate) {
483                 if (!(--levitate)) {
484                         message("you float gently to the ground", 1);
485                         if (dungeon[rogue.row][rogue.col] & TRAP) {
486                                 trap_player(rogue.row, rogue.col);
487                         }
488                 }
489         }
490         if (haste_self) {
491                 if (!(--haste_self)) {
492                         message("you feel yourself slowing down", 0);
493                 }
494         }
495         heal();
496         if (auto_search > 0) {
497                 search(auto_search, auto_search);
498         }
499         return(fainted);
500 }
501
502 rest(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 gr_dir()
517 {
518         short d;
519
520         d = get_rand(1, 8);
521
522         switch(d) {
523                 case 1:
524                         d = 'j';
525                         break;
526                 case 2:
527                         d = 'k';
528                         break;
529                 case 3:
530                         d = 'l';
531                         break;
532                 case 4:
533                         d = 'h';
534                         break;
535                 case 5:
536                         d = 'y';
537                         break;
538                 case 6:
539                         d = 'u';
540                         break;
541                 case 7:
542                         d = 'b';
543                         break;
544                 case 8:
545                         d = 'n';
546                         break;
547         }
548         return(d);
549 }
550
551 heal()
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) {
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
616 can_turn(nrow, ncol)
617 short nrow, ncol;
618 {
619         if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
620                 return(1);
621         }
622         return(0);
623 }
624
625 turn_passage(dir, fast)
626 short dir;
627 boolean fast;
628 {
629         short crow = rogue.row, ccol = rogue.col, turns = 0;
630         short ndir;
631
632         if ((dir != 'h') && can_turn(crow, ccol + 1)) {
633                 turns++;
634                 ndir = 'l';
635         }
636         if ((dir != 'l') && can_turn(crow, ccol - 1)) {
637                 turns++;
638                 ndir = 'h';
639         }
640         if ((dir != 'k') && can_turn(crow + 1, ccol)) {
641                 turns++;
642                 ndir = 'j';
643         }
644         if ((dir != 'j') && can_turn(crow - 1, ccol)) {
645                 turns++;
646                 ndir = 'k';
647         }
648         if (turns == 1) {
649                 multiple_move_rogue(ndir - (fast ? 32 : 96));
650         }
651 }