Merge from vendor branch READLINE:
[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.3 2003/08/26 23:52:50 drhodus 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 one_move_rogue(dirch, pickup)
66 short dirch, pickup;
67 {
68         short row, col;
69         object *obj;
70         char desc[DCOLS];
71         short n, status, d;
72
73         row = rogue.row;
74         col = rogue.col;
75
76         if (confused) {
77                 dirch = gr_dir();
78         }
79         (void) is_direction(dirch, &d);
80         get_dir_rc(d, &row, &col, 1);
81
82         if (!can_move(rogue.row, rogue.col, row, col)) {
83                 return(MOVE_FAILED);
84         }
85         if (being_held || bear_trap) {
86                 if (!(dungeon[row][col] & MONSTER)) {
87                         if (being_held) {
88                                 message("you are being held", 1);
89                         } else {
90                                 message("you are still stuck in the bear trap", 0);
91                                 (void) reg_move();
92                         }
93                         return(MOVE_FAILED);
94                 }
95         }
96         if (r_teleport) {
97                 if (rand_percent(R_TELE_PERCENT)) {
98                         tele();
99                         return(STOPPED_ON_SOMETHING);
100                 }
101         }
102         if (dungeon[row][col] & MONSTER) {
103                 rogue_hit(object_at(&level_monsters, row, col), 0);
104                 (void) reg_move();
105                 return(MOVE_FAILED);
106         }
107         if (dungeon[row][col] & DOOR) {
108                 if (cur_room == PASSAGE) {
109                         cur_room = get_room_number(row, col);
110                         light_up_room(cur_room);
111                         wake_room(cur_room, 1, row, col);
112                 } else {
113                         light_passage(row, col);
114                 }
115         } else if ((dungeon[rogue.row][rogue.col] & DOOR) &&
116                    (dungeon[row][col] & TUNNEL)) {
117                 light_passage(row, col);
118                 wake_room(cur_room, 0, rogue.row, rogue.col);
119                 darken_room(cur_room);
120                 cur_room = PASSAGE;
121         } else if (dungeon[row][col] & TUNNEL) {
122                         light_passage(row, col);
123         }
124         mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
125         mvaddch(row, col, rogue.fchar);
126
127         if (!jump) {
128                 refresh();
129         }
130         rogue.row = row;
131         rogue.col = col;
132         if (dungeon[row][col] & OBJECT) {
133                 if (levitate && pickup) {
134                         return(STOPPED_ON_SOMETHING);
135                 }
136                 if (pickup && !levitate) {
137                         if (obj = pick_up(row, col, &status)) {
138                                 get_desc(obj, desc);
139                                 if (obj->what_is == GOLD) {
140                                         free_object(obj);
141                                         goto NOT_IN_PACK;
142                                 }
143                         } else if (!status) {
144                                 goto MVED;
145                         } else {
146                                 goto MOVE_ON;
147                         }
148                 } else {
149 MOVE_ON:
150                         obj = object_at(&level_objects, row, col);
151                         (void) strcpy(desc, "moved onto ");
152                         get_desc(obj, desc+11);
153                         goto NOT_IN_PACK;
154                 }
155                 n = strlen(desc);
156                 desc[n] = '(';
157                 desc[n+1] = obj->ichar;
158                 desc[n+2] = ')';
159                 desc[n+3] = 0;
160 NOT_IN_PACK:
161                 message(desc, 1);
162                 (void) reg_move();
163                 return(STOPPED_ON_SOMETHING);
164         }
165         if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) {
166                 if ((!levitate) && (dungeon[row][col] & TRAP)) {
167                         trap_player(row, col);
168                 }
169                 (void) reg_move();
170                 return(STOPPED_ON_SOMETHING);
171         }
172 MVED:   if (reg_move()) {                       /* fainted from hunger */
173                         return(STOPPED_ON_SOMETHING);
174         }
175         return((confused ? STOPPED_ON_SOMETHING : MOVED));
176 }
177
178 multiple_move_rogue(dirch)
179 short dirch;
180 {
181         short row, col;
182         short m;
183
184         switch(dirch) {
185         case '\010':
186         case '\012':
187         case '\013':
188         case '\014':
189         case '\031':
190         case '\025':
191         case '\016':
192         case '\002':
193                 do {
194                         row = rogue.row;
195                         col = rogue.col;
196                         if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) ||
197                                 (m == STOPPED_ON_SOMETHING) ||
198                                 interrupted) {
199                                 break;
200                         }
201                 } while (!next_to_something(row, col));
202                 if (    (!interrupted) && passgo && (m == MOVE_FAILED) &&
203                                 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
204                         turn_passage(dirch + 96, 0);
205                 }
206                 break;
207         case 'H':
208         case 'J':
209         case 'K':
210         case 'L':
211         case 'B':
212         case 'Y':
213         case 'U':
214         case 'N':
215                 while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) ;
216
217                 if (    (!interrupted) && passgo &&
218                                 (dungeon[rogue.row][rogue.col] & TUNNEL)) {
219                         turn_passage(dirch + 32, 1);
220                 }
221                 break;
222         }
223 }
224
225 is_passable(row, col)
226 int row, col;
227 {
228         if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) ||
229                 (col > (DCOLS-1))) {
230                 return(0);
231         }
232         if (dungeon[row][col] & HIDDEN) {
233                 return((dungeon[row][col] & TRAP) ? 1 : 0);
234         }
235         return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP));
236 }
237
238 next_to_something(drow, dcol)
239 int drow, dcol;
240 {
241         short i, j, i_end, j_end, row, col;
242         short pass_count = 0;
243         unsigned short s;
244
245         if (confused) {
246                 return(1);
247         }
248         if (blind) {
249                 return(0);
250         }
251         i_end = (rogue.row < (DROWS-2)) ? 1 : 0;
252         j_end = (rogue.col < (DCOLS-1)) ? 1 : 0;
253
254         for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
255                 for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) {
256                         if ((i == 0) && (j == 0)) {
257                                 continue;
258                         }
259                         if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) {
260                                 continue;
261                         }
262                         row = rogue.row + i;
263                         col = rogue.col + j;
264                         s = dungeon[row][col];
265                         if (s & HIDDEN) {
266                                 continue;
267                         }
268                         /* If the rogue used to be right, up, left, down, or right of
269                          * row,col, and now isn't, then don't stop */
270                         if (s & (MONSTER | OBJECT | STAIRS)) {
271                                 if (((row == drow) || (col == dcol)) &&
272                                         (!((row == rogue.row) || (col == rogue.col)))) {
273                                         continue;
274                                 }
275                                 return(1);
276                         }
277                         if (s & TRAP) {
278                                 if (!(s & HIDDEN)) {
279                                         if (((row == drow) || (col == dcol)) &&
280                                                 (!((row == rogue.row) || (col == rogue.col)))) {
281                                                 continue;
282                                         }
283                                         return(1);
284                                 }
285                         }
286                         if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) {
287                                 if (++pass_count > 1) {
288                                         return(1);
289                                 }
290                         }
291                         if ((s & DOOR) && ((i == 0) || (j == 0))) {
292                                         return(1);
293                         }
294                 }
295         }
296         return(0);
297 }
298
299 can_move(row1, col1, row2, 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 move_onto()
316 {
317         short ch, d;
318         boolean first_miss = 1;
319
320         while (!is_direction(ch = rgetchar(), &d)) {
321                 sound_bell();
322                 if (first_miss) {
323                         message("direction? ", 0);
324                         first_miss = 0;
325                 }
326         }
327         check_message();
328         if (ch != CANCEL) {
329                 (void) one_move_rogue(ch, 0);
330         }
331 }
332
333 boolean
334 is_direction(c, d)
335 short c;
336 short *d;
337 {
338         switch(c) {
339         case 'h':
340                 *d = LEFT;
341                 break;
342         case 'j':
343                 *d = DOWN;
344                 break;
345         case 'k':
346                 *d = UPWARD;
347                 break;
348         case 'l':
349                 *d = RIGHT;
350                 break;
351         case 'b':
352                 *d = DOWNLEFT;
353                 break;
354         case 'y':
355                 *d = UPLEFT;
356                 break;
357         case 'u':
358                 *d = UPRIGHT;
359                 break;
360         case 'n':
361                 *d = DOWNRIGHT;
362                 break;
363         case CANCEL:
364                 break;
365         default:
366                 return(0);
367         }
368         return(1);
369 }
370
371 boolean
372 check_hunger(msg_only)
373 boolean msg_only;
374 {
375         short i, n;
376         boolean fainted = 0;
377
378         if (rogue.moves_left == HUNGRY) {
379                 (void) strcpy(hunger_str, "hungry");
380                 message(hunger_str, 0);
381                 print_stats(STAT_HUNGER);
382         }
383         if (rogue.moves_left == WEAK) {
384                 (void) strcpy(hunger_str, "weak");
385                 message(hunger_str, 1);
386                 print_stats(STAT_HUNGER);
387         }
388         if (rogue.moves_left <= FAINT) {
389                 if (rogue.moves_left == FAINT) {
390                         (void) strcpy(hunger_str, "faint");
391                         message(hunger_str, 1);
392                         print_stats(STAT_HUNGER);
393                 }
394                 n = get_rand(0, (FAINT - rogue.moves_left));
395                 if (n > 0) {
396                         fainted = 1;
397                         if (rand_percent(40)) {
398                                 rogue.moves_left++;
399                         }
400                         message("you faint", 1);
401                         for (i = 0; i < n; i++) {
402                                 if (coin_toss()) {
403                                         mv_mons();
404                                 }
405                         }
406                         message(you_can_move_again, 1);
407                 }
408         }
409         if (msg_only) {
410                 return(fainted);
411         }
412         if (rogue.moves_left <= STARVE) {
413                 killed_by((object *) 0, STARVATION);
414         }
415
416         switch(e_rings) {
417         /*case -2:
418                 Subtract 0, i.e. do nothing.
419                 break;*/
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                 (void) check_hunger(1);
429                 rogue.moves_left -= (rogue.moves_left % 2);
430                 break;
431         case 2:
432                 rogue.moves_left--;
433                 (void) check_hunger(1);
434                 rogue.moves_left--;
435                 break;
436         }
437         return(fainted);
438 }
439
440 boolean
441 reg_move()
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 rest(count)
498 {
499         int i;
500
501         interrupted = 0;
502
503         for (i = 0; i < count; i++) {
504                 if (interrupted) {
505                         break;
506                 }
507                 (void) reg_move();
508         }
509 }
510
511 gr_dir()
512 {
513         short d;
514
515         d = get_rand(1, 8);
516
517         switch(d) {
518                 case 1:
519                         d = 'j';
520                         break;
521                 case 2:
522                         d = 'k';
523                         break;
524                 case 3:
525                         d = 'l';
526                         break;
527                 case 4:
528                         d = 'h';
529                         break;
530                 case 5:
531                         d = 'y';
532                         break;
533                 case 6:
534                         d = 'u';
535                         break;
536                 case 7:
537                         d = 'b';
538                         break;
539                 case 8:
540                         d = 'n';
541                         break;
542         }
543         return(d);
544 }
545
546 heal()
547 {
548         static short heal_exp = -1, n, c = 0;
549         static boolean alt;
550
551         if (rogue.hp_current == rogue.hp_max) {
552                 c = 0;
553                 return;
554         }
555         if (rogue.exp != heal_exp) {
556                 heal_exp = rogue.exp;
557
558                 switch(heal_exp) {
559                 case 1:
560                         n = 20;
561                         break;
562                 case 2:
563                         n = 18;
564                         break;
565                 case 3:
566                         n = 17;
567                         break;
568                 case 4:
569                         n = 14;
570                         break;
571                 case 5:
572                         n = 13;
573                         break;
574                 case 6:
575                         n = 10;
576                         break;
577                 case 7:
578                         n = 9;
579                         break;
580                 case 8:
581                         n = 8;
582                         break;
583                 case 9:
584                         n = 7;
585                         break;
586                 case 10:
587                         n = 4;
588                         break;
589                 case 11:
590                         n = 3;
591                         break;
592                 case 12:
593                 default:
594                         n = 2;
595                 }
596         }
597         if (++c >= n) {
598                 c = 0;
599                 rogue.hp_current++;
600                 if (alt = !alt) {
601                         rogue.hp_current++;
602                 }
603                 if ((rogue.hp_current += regeneration) > rogue.hp_max) {
604                         rogue.hp_current = rogue.hp_max;
605                 }
606                 print_stats(STAT_HP);
607         }
608 }
609
610 static boolean
611 can_turn(nrow, ncol)
612 short nrow, ncol;
613 {
614         if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) {
615                 return(1);
616         }
617         return(0);
618 }
619
620 turn_passage(dir, fast)
621 short dir;
622 boolean fast;
623 {
624         short crow = rogue.row, ccol = rogue.col, turns = 0;
625         short ndir;
626
627         if ((dir != 'h') && can_turn(crow, ccol + 1)) {
628                 turns++;
629                 ndir = 'l';
630         }
631         if ((dir != 'l') && can_turn(crow, ccol - 1)) {
632                 turns++;
633                 ndir = 'h';
634         }
635         if ((dir != 'k') && can_turn(crow + 1, ccol)) {
636                 turns++;
637                 ndir = 'j';
638         }
639         if ((dir != 'j') && can_turn(crow - 1, ccol)) {
640                 turns++;
641                 ndir = 'k';
642         }
643         if (turns == 1) {
644                 multiple_move_rogue(ndir - (fast ? 32 : 96));
645         }
646 }