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