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