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