047472aa61ca8767849bb4a1efea6b84d09cd4db
[dragonfly.git] / games / rogue / room.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  * @(#)room.c   8.1 (Berkeley) 5/31/93
37  * $FreeBSD: src/games/rogue/room.c,v 1.7 1999/11/30 03:49:26 billf Exp $
38  * $DragonFly: src/games/rogue/room.c,v 1.3 2006/09/02 19:31:07 pavalos Exp $
39  */
40
41 /*
42  * room.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 room rooms[MAXROOMS];
56 boolean rooms_visited[MAXROOMS];
57
58 extern short blind;
59 extern boolean detect_monster, jump, passgo, no_skull, ask_quit, flush;
60 extern char *nick_name, *fruit, *save_file, *press_space;
61
62 #define NOPTS 8
63
64 struct option {
65         const char *prompt;
66         boolean is_bool;
67         char **strval;
68         boolean *bval;
69 } options[NOPTS] = {
70         {
71                 "Flush typeahead during battle (\"flush\"): ",
72                 1, (char **) 0, &flush
73         },
74         {
75                 "Show position only at end of run (\"jump\"): ",
76                 1, (char **) 0, &jump
77         },
78         {
79                 "Follow turnings in passageways (\"passgo\"): ",
80                 1, (char **) 0, &passgo
81         },
82         {
83                 "Don't print skull when killed (\"noskull\" or \"notombstone\"): ",
84                 1, (char **) 0, &no_skull
85         },
86         {
87                 "Ask player before saying 'Okay, bye-bye!' (\"askquit\"): ",
88                 1, (char **) 0, &ask_quit
89         },
90         {
91                 "Name (\"name\"): ",
92                 0, &nick_name, NULL
93         },
94         {
95                 "Fruit (\"fruit\"): ",
96                 0, &fruit, NULL
97         },
98         {
99                 "Save file (\"file\"): ",
100                 0, &save_file, NULL
101         }
102 };
103
104 static void     visit_rooms(int);
105 static boolean  get_oth_room(short, short *, short *);
106 static void     opt_show(int);
107 static void     opt_erase(int);
108 static void     opt_go(int);
109
110 void
111 light_up_room(int rn)
112 {
113         short i, j;
114
115         if (!blind) {
116                 for (i = rooms[rn].top_row;
117                         i <= rooms[rn].bottom_row; i++) {
118                         for (j = rooms[rn].left_col;
119                                 j <= rooms[rn].right_col; j++) {
120                                 if (dungeon[i][j] & MONSTER) {
121                                         object *monster;
122
123                                         if ((monster = object_at(&level_monsters, i, j))) {
124                                                 dungeon[monster->row][monster->col] &= (~MONSTER);
125                                                 monster->trail_char =
126                                                         get_dungeon_char(monster->row, monster->col);
127                                                 dungeon[monster->row][monster->col] |= MONSTER;
128                                         }
129                                 }
130                                 mvaddch(i, j, get_dungeon_char(i, j));
131                         }
132                 }
133                 mvaddch(rogue.row, rogue.col, rogue.fchar);
134         }
135 }
136
137 void
138 light_passage(int row, int col)
139 {
140         short i, j, i_end, j_end;
141
142         if (blind) {
143                 return;
144         }
145         i_end = (row < (DROWS-2)) ? 1 : 0;
146         j_end = (col < (DCOLS-1)) ? 1 : 0;
147
148         for (i = ((row > MIN_ROW) ? -1 : 0); i <= i_end; i++) {
149                 for (j = ((col > 0) ? -1 : 0); j <= j_end; j++) {
150                         if (can_move(row, col, row+i, col+j)) {
151                                 mvaddch(row+i, col+j, get_dungeon_char(row+i, col+j));
152                         }
153                 }
154         }
155 }
156
157 void
158 darken_room(short rn)
159 {
160         short i, j;
161
162         for (i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) {
163                 for (j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) {
164                         if (blind) {
165                                 mvaddch(i, j, ' ');
166                         } else {
167                                 if (!(dungeon[i][j] & (OBJECT | STAIRS)) &&
168                                         !(detect_monster && (dungeon[i][j] & MONSTER))) {
169                                         if (!imitating(i, j)) {
170                                                 mvaddch(i, j, ' ');
171                                         }
172                                         if ((dungeon[i][j] & TRAP) && (!(dungeon[i][j] & HIDDEN))) {
173                                                 mvaddch(i, j, '^');
174                                         }
175                                 }
176                         }
177                 }
178         }
179 }
180
181 char
182 get_dungeon_char(int row, int col)
183 {
184         unsigned short mask = dungeon[row][col];
185
186         if (mask & MONSTER) {
187                 return(gmc_row_col(row, col));
188         }
189         if (mask & OBJECT) {
190                 object *obj;
191
192                 obj = object_at(&level_objects, row, col);
193                 return(get_mask_char(obj->what_is));
194         }
195         if (mask & (TUNNEL | STAIRS | HORWALL | VERTWALL | FLOOR | DOOR)) {
196                 if ((mask & (TUNNEL| STAIRS)) && (!(mask & HIDDEN))) {
197                         return(((mask & STAIRS) ? '%' : '#'));
198                 }
199                 if (mask & HORWALL) {
200                         return('-');
201                 }
202                 if (mask & VERTWALL) {
203                         return('|');
204                 }
205                 if (mask & FLOOR) {
206                         if (mask & TRAP) {
207                                 if (!(dungeon[row][col] & HIDDEN)) {
208                                         return('^');
209                                 }
210                         }
211                         return('.');
212                 }
213                 if (mask & DOOR) {
214                         if (mask & HIDDEN) {
215                                 if (((col > 0) && (dungeon[row][col-1] & HORWALL)) ||
216                                         ((col < (DCOLS-1)) && (dungeon[row][col+1] & HORWALL))) {
217                                         return('-');
218                                 } else {
219                                         return('|');
220                                 }
221                         } else {
222                                 return('+');
223                         }
224                 }
225         }
226         return(' ');
227 }
228
229 char
230 get_mask_char(unsigned short mask)
231 {
232                 switch(mask) {
233                 case SCROL:
234                         return('?');
235                 case POTION:
236                         return('!');
237                 case GOLD:
238                         return('*');
239                 case FOOD:
240                         return(':');
241                 case WAND:
242                         return('/');
243                 case ARMOR:
244                         return(']');
245                 case WEAPON:
246                         return(')');
247                 case RING:
248                         return('=');
249                 case AMULET:
250                         return(',');
251                 default:
252                         return('~');    /* unknown, something is wrong */
253                 }
254 }
255
256 void
257 gr_row_col(short *row, short *col, unsigned short mask)
258 {
259         short rn;
260         short r, c;
261
262         do {
263                 r = get_rand(MIN_ROW, DROWS-2);
264                 c = get_rand(0, DCOLS-1);
265                 rn = get_room_number(r, c);
266         } while ((rn == NO_ROOM) ||
267                 (!(dungeon[r][c] & mask)) ||
268                 (dungeon[r][c] & (~mask)) ||
269                 (!(rooms[rn].is_room & (R_ROOM | R_MAZE))) ||
270                 ((r == rogue.row) && (c == rogue.col)));
271
272         *row = r;
273         *col = c;
274 }
275
276 short
277 gr_room(void)
278 {
279         short i;
280
281         do {
282                 i = get_rand(0, MAXROOMS-1);
283         } while (!(rooms[i].is_room & (R_ROOM | R_MAZE)));
284
285         return(i);
286 }
287
288 short
289 party_objects(short rn)
290 {
291         short i, j, nf = 0;
292         object *obj;
293         short n, N, row, col;
294         boolean found;
295
296         N = ((rooms[rn].bottom_row - rooms[rn].top_row) - 1) *
297                 ((rooms[rn].right_col - rooms[rn].left_col) - 1);
298         n =  get_rand(5, 10);
299         if (n > N) {
300                 n = N - 2;
301         }
302         for (i = 0; i < n; i++) {
303                 for (j = found = 0; ((!found) && (j < 250)); j++) {
304                         row = get_rand(rooms[rn].top_row+1,
305                                            rooms[rn].bottom_row-1);
306                         col = get_rand(rooms[rn].left_col+1,
307                                            rooms[rn].right_col-1);
308                         if ((dungeon[row][col] == FLOOR) || (dungeon[row][col] == TUNNEL)) {
309                                 found = 1;
310                         }
311                 }
312                 if (found) {
313                         obj = gr_object();
314                         place_at(obj, row, col);
315                         nf++;
316                 }
317         }
318         return(nf);
319 }
320
321 short
322 get_room_number(int row, int col)
323 {
324         short i;
325
326         for (i = 0; i < MAXROOMS; i++) {
327                 if ((row >= rooms[i].top_row) && (row <= rooms[i].bottom_row) &&
328                         (col >= rooms[i].left_col) && (col <= rooms[i].right_col)) {
329                         return(i);
330                 }
331         }
332         return(NO_ROOM);
333 }
334
335 boolean
336 is_all_connected(void)
337 {
338         short i, starting_room = 0;
339
340         for (i = 0; i < MAXROOMS; i++) {
341                 rooms_visited[i] = 0;
342                 if (rooms[i].is_room & (R_ROOM | R_MAZE)) {
343                         starting_room = i;
344                 }
345         }
346
347         visit_rooms(starting_room);
348
349         for (i = 0; i < MAXROOMS; i++) {
350                 if ((rooms[i].is_room & (R_ROOM | R_MAZE)) && (!rooms_visited[i])) {
351                         return(0);
352                 }
353         }
354         return(1);
355 }
356
357 static void
358 visit_rooms(int rn)
359 {
360         short i;
361         short oth_rn;
362
363         rooms_visited[rn] = 1;
364
365         for (i = 0; i < 4; i++) {
366                 oth_rn = rooms[rn].doors[i].oth_room;
367                 if ((oth_rn >= 0) && (!rooms_visited[oth_rn])) {
368                         visit_rooms(oth_rn);
369                 }
370         }
371 }
372
373 void
374 draw_magic_map(void)
375 {
376         short i, j, ch, och;
377         unsigned short mask = (HORWALL | VERTWALL | DOOR | TUNNEL | TRAP | STAIRS |
378                         MONSTER);
379         unsigned short s;
380
381         for (i = 0; i < DROWS; i++) {
382                 for (j = 0; j < DCOLS; j++) {
383                         s = dungeon[i][j];
384                         if (s & mask) {
385                                 if (((ch = mvinch(i, j)) == ' ') ||
386                                         ((ch >= 'A') && (ch <= 'Z')) || (s & (TRAP | HIDDEN))) {
387                                         och = ch;
388                                         dungeon[i][j] &= (~HIDDEN);
389                                         if (s & HORWALL) {
390                                                 ch = '-';
391                                         } else if (s & VERTWALL) {
392                                                 ch = '|';
393                                         } else if (s & DOOR) {
394                                                 ch = '+';
395                                         } else if (s & TRAP) {
396                                                 ch = '^';
397                                         } else if (s & STAIRS) {
398                                                 ch = '%';
399                                         } else if (s & TUNNEL) {
400                                                 ch = '#';
401                                         } else {
402                                                 continue;
403                                         }
404                                         if ((!(s & MONSTER)) || (och == ' ')) {
405                                                 addch(ch);
406                                         }
407                                         if (s & MONSTER) {
408                                                 object *monster;
409
410                                                 if ((monster = object_at(&level_monsters, i, j))) {
411                                                         monster->trail_char = ch;
412                                                 }
413                                         }
414                                 }
415                         }
416                 }
417         }
418 }
419
420 void
421 dr_course(object *monster, boolean entering, short row, short col)
422 {
423         short i, j, k, rn;
424         short r, rr;
425
426         monster->row = row;
427         monster->col = col;
428
429         if (mon_sees(monster, rogue.row, rogue.col)) {
430                 monster->trow = NO_ROOM;
431                 return;
432         }
433         rn = get_room_number(row, col);
434
435         if (entering) {         /* entering room */
436                 /* look for door to some other room */
437                 r = get_rand(0, MAXROOMS-1);
438                 for (i = 0; i < MAXROOMS; i++) {
439                         rr = (r + i) % MAXROOMS;
440                         if ((!(rooms[rr].is_room & (R_ROOM | R_MAZE))) || (rr == rn)) {
441                                 continue;
442                         }
443                         for (k = 0; k < 4; k++) {
444                                 if (rooms[rr].doors[k].oth_room == rn) {
445                                         monster->trow = rooms[rr].doors[k].oth_row;
446                                         monster->tcol = rooms[rr].doors[k].oth_col;
447                                         if ((monster->trow == row) &&
448                                                 (monster->tcol == col)) {
449                                                 continue;
450                                         }
451                                         return;
452                                 }
453                         }
454                 }
455                 /* look for door to dead end */
456                 for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) {
457                         for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) {
458                                 if ((i != monster->row) && (j != monster->col) &&
459                                         (dungeon[i][j] & DOOR)) {
460                                         monster->trow = i;
461                                         monster->tcol = j;
462                                         return;
463                                 }
464                         }
465                 }
466                 /* return monster to room that he came from */
467                 for (i = 0; i < MAXROOMS; i++) {
468                         for (j = 0; j < 4; j++) {
469                                 if (rooms[i].doors[j].oth_room == rn) {
470                                         for (k = 0; k < 4; k++) {
471                                                 if (rooms[rn].doors[k].oth_room == i) {
472                                                         monster->trow = rooms[rn].doors[k].oth_row;
473                                                         monster->tcol = rooms[rn].doors[k].oth_col;
474                                                         return;
475                                                 }
476                                         }
477                                 }
478                         }
479                 }
480                 /* no place to send monster */
481                 monster->trow = NO_ROOM;
482         } else {                /* exiting room */
483                 if (!get_oth_room(rn, &row, &col)) {
484                         monster->trow = NO_ROOM;
485                 } else {
486                         monster->trow = row;
487                         monster->tcol = col;
488                 }
489         }
490 }
491
492 static boolean
493 get_oth_room(short rn, short *row, short *col)
494 {
495         short d = -1;
496
497         if (*row == rooms[rn].top_row) {
498                 d = UPWARD/2;
499         } else if (*row == rooms[rn].bottom_row) {
500                 d = DOWN/2;
501         } else if (*col == rooms[rn].left_col) {
502                 d = LEFT/2;
503         } else if (*col == rooms[rn].right_col) {
504                 d = RIGHT/2;
505         }
506         if ((d != -1) && (rooms[rn].doors[d].oth_room >= 0)) {
507                 *row = rooms[rn].doors[d].oth_row;
508                 *col = rooms[rn].doors[d].oth_col;
509                 return(1);
510         }
511         return(0);
512 }
513
514 void
515 edit_opts(void)
516 {
517         char save[NOPTS+1][DCOLS];
518         short i, j;
519         short ch;
520         boolean done = 0;
521         char buf[MAX_OPT_LEN + 2];
522
523         for (i = 0; i < NOPTS+1; i++) {
524                 for (j = 0; j < DCOLS; j++) {
525                         save[i][j] = mvinch(i, j);
526                 }
527                 if (i < NOPTS) {
528                         opt_show(i);
529                 }
530         }
531         opt_go(0);
532         i = 0;
533
534         while (!done) {
535                 refresh();
536                 ch = rgetchar();
537 CH:
538                 switch(ch) {
539                 case '\033':
540                         done = 1;
541                         break;
542                 case '\012':
543                 case '\015':
544                         if (i == (NOPTS - 1)) {
545                                 mvaddstr(NOPTS, 0, press_space);
546                                 refresh();
547                                 wait_for_ack();
548                                 done = 1;
549                         } else {
550                                 i++;
551                                 opt_go(i);
552                         }
553                         break;
554                 case '-':
555                         if (i > 0) {
556                                 opt_go(--i);
557                         } else {
558                                 sound_bell();
559                         }
560                         break;
561                 case 't':
562                 case 'T':
563                 case 'f':
564                 case 'F':
565                         if (options[i].is_bool) {
566                                 *(options[i].bval) = (((ch == 't') || (ch == 'T')) ? 1 : 0);
567                                 opt_show(i);
568                                 opt_go(++i);
569                                 break;
570                         }
571                 default:
572                         if (options[i].is_bool) {
573                                 sound_bell();
574                                 break;
575                         }
576                         j = 0;
577                         if ((ch == '\010') || ((ch >= ' ') && (ch <= '~'))) {
578                                 opt_erase(i);
579                                 do {
580                                         if ((ch >= ' ') && (ch <= '~') && (j < MAX_OPT_LEN)) {
581                                                 buf[j++] = ch;
582                                                 buf[j] = '\0';
583                                                 addch(ch);
584                                         } else if ((ch == '\010') && (j > 0)) {
585                                                 buf[--j] = '\0';
586                                                 move(i, j + strlen(options[i].prompt));
587                                                 addch(' ');
588                                                 move(i, j + strlen(options[i].prompt));
589                                         }
590                                         refresh();
591                                         ch = rgetchar();
592                                 } while ((ch != '\012') && (ch != '\015') && (ch != '\033'));
593                                 if (j != 0) {
594                                         strcpy(*(options[i].strval), buf);
595                                 }
596                                 opt_show(i);
597                                 goto CH;
598                         } else {
599                                 sound_bell();
600                         }
601                         break;
602                 }
603         }
604
605         for (i = 0; i < NOPTS+1; i++) {
606                 move(i, 0);
607                 for (j = 0; j < DCOLS; j++) {
608                         addch(save[i][j]);
609                 }
610         }
611 }
612
613 static void
614 opt_show(int i)
615 {
616         const char *s;
617         struct option *opt = &options[i];
618
619         opt_erase(i);
620
621         if (opt->is_bool) {
622                 s = *(opt->bval) ? "True" : "False";
623         } else {
624                 s = *(opt->strval);
625         }
626         addstr(s);
627 }
628
629 static void
630 opt_erase(int i)
631 {
632         struct option *opt = &options[i];
633
634         mvaddstr(i, 0, opt->prompt);
635         clrtoeol();
636 }
637
638 static void
639 opt_go(int i)
640 {
641         move(i, strlen(options[i].prompt));
642 }
643
644 void
645 do_shell(void)
646 {
647 #ifdef UNIX
648         const char *sh;
649
650         md_ignore_signals();
651         if (!(sh = md_getenv("SHELL"))) {
652                 sh = "/bin/sh";
653         }
654         move(LINES-1, 0);
655         refresh();
656         stop_window();
657         printf("\nCreating new shell...\n");
658         md_shell(sh);
659         start_window();
660         wrefresh(curscr);
661         md_heed_signals();
662 #endif
663 }