e3b78ef14f
Change-Id: Id4aef4950f250edef2d507910877aabc6b9580ea
671 lines
14 KiB
C
671 lines
14 KiB
C
/* $NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)room.c 8.1 (Berkeley) 5/31/93";
|
|
#else
|
|
__RCSID("$NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
/*
|
|
* 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];
|
|
|
|
static boolean rooms_visited[MAXROOMS];
|
|
|
|
#define NOPTS 7
|
|
static const struct option {
|
|
const char *prompt;
|
|
boolean is_bool;
|
|
char **strval;
|
|
boolean *bval;
|
|
} options[NOPTS] = {
|
|
{
|
|
"Show position only at end of run (\"jump\"): ",
|
|
1, NULL, &jump
|
|
},
|
|
{
|
|
"Follow turnings in passageways (\"passgo\"): ",
|
|
1, NULL, &passgo
|
|
},
|
|
{
|
|
"Don't print skull when killed (\"noskull\" or \"notombstone\"): ",
|
|
1, NULL, &no_skull
|
|
},
|
|
{
|
|
"Ask player before saying 'Okay, bye-bye!' (\"askquit\"): ",
|
|
1, NULL, &ask_quit
|
|
},
|
|
{
|
|
"Name (\"name\"): ",
|
|
0, &nick_name, NULL
|
|
},
|
|
{
|
|
"Fruit (\"fruit\"): ",
|
|
0, &fruit, NULL
|
|
},
|
|
{
|
|
"Save file (\"file\"): ",
|
|
0, &save_file, NULL
|
|
}
|
|
};
|
|
|
|
static boolean get_oth_room(short, short *, short *);
|
|
static void opt_erase(int);
|
|
static void opt_go(int);
|
|
static void opt_show(int);
|
|
static void visit_rooms(int);
|
|
|
|
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)) != NULL) {
|
|
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(int row, int 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;
|
|
|
|
row = col = 0;
|
|
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(int row, int 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);
|
|
}
|
|
|
|
static 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))
|
|
!= NULL) {
|
|
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 */
|
|
if (rn == NO_ROOM)
|
|
clean_up("dr_course: monster not in room");
|
|
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 (rn == NO_ROOM || !get_oth_room(rn, &row, &col)) {
|
|
monster->trow = NO_ROOM;
|
|
} else {
|
|
monster->trow = row;
|
|
monster->tcol = col;
|
|
}
|
|
}
|
|
}
|
|
|
|
static 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 {
|
|
sound_bell();
|
|
}
|
|
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) {
|
|
sound_bell();
|
|
break;
|
|
}
|
|
j = 0;
|
|
if ((ch == '\010') || ((ch >= ' ') && (ch <= '~'))) {
|
|
opt_erase(i);
|
|
do {
|
|
if ((ch >= ' ') && (ch <= '~') && (j < MAX_OPT_LEN)) {
|
|
buf[j++] = ch;
|
|
buf[j] = '\0';
|
|
addch(ch);
|
|
} else if ((ch == '\010') && (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)strcpy(*(options[i].strval), buf);
|
|
}
|
|
opt_show(i);
|
|
goto CH;
|
|
} else {
|
|
sound_bell();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < NOPTS+1; i++) {
|
|
move(i, 0);
|
|
for (j = 0; j < DCOLS; j++) {
|
|
addch(save[i][j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static 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);
|
|
}
|
|
|
|
static void
|
|
opt_erase(int i)
|
|
{
|
|
const struct option *opt = &options[i];
|
|
|
|
mvaddstr(i, 0, opt->prompt);
|
|
clrtoeol();
|
|
}
|
|
|
|
static void
|
|
opt_go(int i)
|
|
{
|
|
move(i, strlen(options[i].prompt));
|
|
}
|
|
|
|
void
|
|
do_shell(void)
|
|
{
|
|
#ifdef UNIX
|
|
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();
|
|
#endif
|
|
}
|