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