/* $OpenBSD: room.c,v 1.10 2009/10/27 23:59:26 deraadt Exp $ */ /* $NetBSD: room.c,v 1.3 1995/04/22 10:28:17 cgd Exp $ */ /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Timothy C. Stoehr. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * room.c * * This source herein may be modified and/or distributed by anybody who * so desires, with the following restrictions: * 1.) No portion of this notice shall be removed. * 2.) Credit shall not be taken for the creation of this source. * 3.) This code is not to be traded, sold, or used for personal * gain or profit. * */ #include "rogue.h" room rooms[MAXROOMS]; boolean rooms_visited[MAXROOMS]; #define NOPTS 7 const struct option { const char *prompt; boolean is_bool; char **strval; boolean *bval; } options[NOPTS] = { { "Show position only at end of run (\"jump\"): ", 1, (char **) NULL, &jump }, { "Follow turnings in passageways (\"passgo\"): ", 1, (char **) NULL, &passgo }, { "Don't print skull when killed (\"noskull\" or \"notombstone\"): ", 1, (char **) NULL, &no_skull }, { "Ask player before saying 'Okay, bye-bye!' (\"askquit\"): ", 1, (char **) NULL, &ask_quit }, { "Name (\"name\"): ", 0, &nick_name, (boolean *) NULL }, { "Fruit (\"fruit\"): ", 0, &fruit, (boolean *) NULL }, { "Save file (\"file\"): ", 0, &save_file, (boolean *) NULL } }; void light_up_room(int rn) { short i, j; if (!blind) { for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { if (dungeon[i][j] & MONSTER) { object *monster; if ((monster = object_at(&level_monsters, i, j))) { dungeon[monster->row][monster->col] &= (~MONSTER); monster->trail_char = get_dungeon_char(monster->row, monster->col); dungeon[monster->row][monster->col] |= MONSTER; } } mvaddch(i, j, get_dungeon_char(i, j)); } } mvaddch(rogue.row, rogue.col, rogue.fchar); } } void light_passage(short row, short col) { short i, j, i_end, j_end; if (blind) { return; } i_end = (row < (DROWS-2)) ? 1 : 0; j_end = (col < (DCOLS-1)) ? 1 : 0; for (i = ((row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { for (j = ((col > 0) ? -1 : 0); j <= j_end; j++) { if (can_move(row, col, row+i, col+j)) { mvaddch(row+i, col+j, get_dungeon_char(row+i, col+j)); } } } } void darken_room(short rn) { short i, j; for (i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) { for (j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) { if (blind) { mvaddch(i, j, ' '); } else { if (!(dungeon[i][j] & (OBJECT | STAIRS)) && !(detect_monster && (dungeon[i][j] & MONSTER))) { if (!imitating(i, j)) { mvaddch(i, j, ' '); } if ((dungeon[i][j] & TRAP) && (!(dungeon[i][j] & HIDDEN))) { mvaddch(i, j, '^'); } } } } } } char get_dungeon_char(short row, short col) { unsigned short mask = dungeon[row][col]; if (mask & MONSTER) { return(gmc_row_col(row, col)); } if (mask & OBJECT) { object *obj; obj = object_at(&level_objects, row, col); return(get_mask_char(obj->what_is)); } if (mask & (TUNNEL | STAIRS | HORWALL | VERTWALL | FLOOR | DOOR)) { if ((mask & (TUNNEL| STAIRS)) && (!(mask & HIDDEN))) { return(((mask & STAIRS) ? '%' : '#')); } if (mask & HORWALL) { return('-'); } if (mask & VERTWALL) { return('|'); } if (mask & FLOOR) { if (mask & TRAP) { if (!(dungeon[row][col] & HIDDEN)) { return('^'); } } return('.'); } if (mask & DOOR) { if (mask & HIDDEN) { if (((col > 0) && (dungeon[row][col-1] & HORWALL)) || ((col < (DCOLS-1)) && (dungeon[row][col+1] & HORWALL))) { return('-'); } else { return('|'); } } else { return('+'); } } } return(' '); } char get_mask_char(unsigned short mask) { switch(mask) { case SCROL: return('?'); case POTION: return('!'); case GOLD: return('*'); case FOOD: return(':'); case WAND: return('/'); case ARMOR: return(']'); case WEAPON: return(')'); case RING: return('='); case AMULET: return(','); default: return('~'); /* unknown, something is wrong */ } } void gr_row_col(short *row, short *col, unsigned short mask) { short rn; short r, c; do { r = get_rand(MIN_ROW, DROWS-2); c = get_rand(0, DCOLS-1); rn = get_room_number(r, c); } while ((rn == NO_ROOM) || (!(dungeon[r][c] & mask)) || (dungeon[r][c] & (~mask)) || (!(rooms[rn].is_room & (R_ROOM | R_MAZE))) || ((r == rogue.row) && (c == rogue.col))); *row = r; *col = c; } short gr_room(void) { short i; do { i = get_rand(0, MAXROOMS-1); } while (!(rooms[i].is_room & (R_ROOM | R_MAZE))); return(i); } short party_objects(int rn) { short i, j, nf = 0; object *obj; short n, N, row, col; boolean found; N = ((rooms[rn].bottom_row - rooms[rn].top_row) - 1) * ((rooms[rn].right_col - rooms[rn].left_col) - 1); n = get_rand(5, 10); if (n > N) { n = N - 2; } for (i = 0; i < n; i++) { for (j = found = 0; ((!found) && (j < 250)); j++) { row = get_rand(rooms[rn].top_row+1, rooms[rn].bottom_row-1); col = get_rand(rooms[rn].left_col+1, rooms[rn].right_col-1); if ((dungeon[row][col] == FLOOR) || (dungeon[row][col] == TUNNEL)) { found = 1; } } if (found) { obj = gr_object(); place_at(obj, row, col); nf++; } } return(nf); } short get_room_number(short row, short col) { short i; for (i = 0; i < MAXROOMS; i++) { if ((row >= rooms[i].top_row) && (row <= rooms[i].bottom_row) && (col >= rooms[i].left_col) && (col <= rooms[i].right_col)) { return(i); } } return(NO_ROOM); } boolean is_all_connected(void) { short i, starting_room; starting_room = 0; for (i = 0; i < MAXROOMS; i++) { rooms_visited[i] = 0; if (rooms[i].is_room & (R_ROOM | R_MAZE)) { starting_room = i; } } visit_rooms(starting_room); for (i = 0; i < MAXROOMS; i++) { if ((rooms[i].is_room & (R_ROOM | R_MAZE)) && (!rooms_visited[i])) { return(0); } } return(1); } void visit_rooms(int rn) { short i; short oth_rn; rooms_visited[rn] = 1; for (i = 0; i < 4; i++) { oth_rn = rooms[rn].doors[i].oth_room; if ((oth_rn >= 0) && (!rooms_visited[oth_rn])) { visit_rooms(oth_rn); } } } void draw_magic_map(void) { short i, j, ch, och; unsigned short mask = (HORWALL | VERTWALL | DOOR | TUNNEL | TRAP | STAIRS | MONSTER); unsigned short s; for (i = 0; i < DROWS; i++) { for (j = 0; j < DCOLS; j++) { s = dungeon[i][j]; if (s & mask) { if (((ch = mvinch(i, j)) == ' ') || ((ch >= 'A') && (ch <= 'Z')) || (s & (TRAP | HIDDEN))) { och = ch; dungeon[i][j] &= (~HIDDEN); if (s & HORWALL) { ch = '-'; } else if (s & VERTWALL) { ch = '|'; } else if (s & DOOR) { ch = '+'; } else if (s & TRAP) { ch = '^'; } else if (s & STAIRS) { ch = '%'; } else if (s & TUNNEL) { ch = '#'; } else { continue; } if ((!(s & MONSTER)) || (och == ' ')) { addch(ch); } if (s & MONSTER) { object *monster; if ((monster = object_at(&level_monsters, i, j))) { monster->trail_char = ch; } } } } } } } void dr_course(object *monster, boolean entering, short row, short col) { short i, j, k, rn; short r, rr; monster->row = row; monster->col = col; if (mon_sees(monster, rogue.row, rogue.col)) { monster->trow = NO_ROOM; return; } rn = get_room_number(row, col); if (entering) { /* entering room */ /* look for door to some other room */ r = get_rand(0, MAXROOMS-1); for (i = 0; i < MAXROOMS; i++) { rr = (r + i) % MAXROOMS; if ((!(rooms[rr].is_room & (R_ROOM | R_MAZE))) || (rr == rn)) { continue; } for (k = 0; k < 4; k++) { if (rooms[rr].doors[k].oth_room == rn) { monster->trow = rooms[rr].doors[k].oth_row; monster->tcol = rooms[rr].doors[k].oth_col; if ((monster->trow == row) && (monster->tcol == col)) { continue; } return; } } } /* look for door to dead end */ for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { if ((i != monster->row) && (j != monster->col) && (dungeon[i][j] & DOOR)) { monster->trow = i; monster->tcol = j; return; } } } /* return monster to room that he came from */ for (i = 0; i < MAXROOMS; i++) { for (j = 0; j < 4; j++) { if (rooms[i].doors[j].oth_room == rn) { for (k = 0; k < 4; k++) { if (rooms[rn].doors[k].oth_room == i) { monster->trow = rooms[rn].doors[k].oth_row; monster->tcol = rooms[rn].doors[k].oth_col; return; } } } } } /* no place to send monster */ monster->trow = NO_ROOM; } else { /* exiting room */ if (!get_oth_room(rn, &row, &col)) { monster->trow = NO_ROOM; } else { monster->trow = row; monster->tcol = col; } } } boolean get_oth_room(short rn, short *row, short *col) { short d = -1; if (*row == rooms[rn].top_row) { d = UPWARD/2; } else if (*row == rooms[rn].bottom_row) { d = DOWN/2; } else if (*col == rooms[rn].left_col) { d = LEFT/2; } else if (*col == rooms[rn].right_col) { d = RIGHT/2; } if ((d != -1) && (rooms[rn].doors[d].oth_room >= 0)) { *row = rooms[rn].doors[d].oth_row; *col = rooms[rn].doors[d].oth_col; return(1); } return(0); } void edit_opts(void) { char save[NOPTS+1][DCOLS]; short i, j; short ch; boolean done = 0; char buf[MAX_OPT_LEN + 2]; for (i = 0; i < NOPTS+1; i++) { for (j = 0; j < DCOLS; j++) { save[i][j] = mvinch(i, j); } if (i < NOPTS) { opt_show(i); } } opt_go(0); i = 0; while (!done) { refresh(); ch = rgetchar(); CH: switch(ch) { case '\033': done = 1; break; case '\012': case '\015': if (i == (NOPTS - 1)) { mvaddstr(NOPTS, 0, press_space); refresh(); wait_for_ack(); done = 1; } else { i++; opt_go(i); } break; case '-': if (i > 0) { opt_go(--i); } else { beep(); } break; case 't': case 'T': case 'f': case 'F': if (options[i].is_bool) { *(options[i].bval) = (((ch == 't') || (ch == 'T')) ? 1 : 0); opt_show(i); opt_go(++i); break; } default: if (options[i].is_bool) { beep(); break; } j = 0; if ((ch == '\b') || ((ch >= ' ') && (ch <= '~'))) { opt_erase(i); do { if ((ch >= ' ') && (ch <= '~') && (j < MAX_OPT_LEN)) { buf[j++] = ch; buf[j] = '\0'; addch(ch); } else if ((ch == '\b' || ch == erasechar()) && (j > 0)) { buf[--j] = '\0'; move(i, j + strlen(options[i].prompt)); addch(' '); move(i, j + strlen(options[i].prompt)); } refresh(); ch = rgetchar(); } while ((ch != '\012') && (ch != '\015') && (ch != '\033')); if (j != 0) { /* We rely on the option string being * allocated to hold MAX_OPT_LEN+2 * bytes. This is arranged in init.c. */ (void) strlcpy(*(options[i].strval), buf, MAX_OPT_LEN+2); } opt_show(i); goto CH; } else { beep(); } break; } } for (i = 0; i < NOPTS+1; i++) { move(i, 0); for (j = 0; j < DCOLS; j++) { addch(save[i][j]); } } } void opt_show(int i) { const char *s; const struct option *opt = &options[i]; opt_erase(i); if (opt->is_bool) { s = *(opt->bval) ? "True" : "False"; } else { s = *(opt->strval); } addstr(s); } void opt_erase(int i) { const struct option *opt = &options[i]; mvaddstr(i, 0, opt->prompt); clrtoeol(); } void opt_go(int i) { move(i, strlen(options[i].prompt)); } void do_shell(void) { const char *sh; md_ignore_signals(); if (!(sh = md_getenv("SHELL"))) { sh = "/bin/sh"; } move(LINES-1, 0); refresh(); stop_window(); printf("\nCreating new shell...\n"); md_shell(sh); start_window(); wrefresh(curscr); md_heed_signals(); }