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