Fix C343-341: Stuck in minetown room

Bustling Town can be generated with inaccessible areas outside the
top edge of the fixed town map.  If you end up in one of those area
without any way to dig or teleport, you're stuck.

This adds a new level flag "inaccessibles" to force checking for
such inaccessible areas, and add secret doors, holes/trapdoors,
or some random escape item into the areas.
This commit is contained in:
Pasi Kallinen
2015-10-13 19:00:18 +03:00
parent 1703818a11
commit 65a30d2ba7
4 changed files with 182 additions and 21 deletions

View File

@@ -639,6 +639,7 @@ ALTAR:(31,03),align[0],shrine
# "Bustling Town" by Kelly Bailey
#
MAZE: "minetn-6",' '
FLAGS: inaccessibles
INIT_MAP:mines,'.','-',true,true,lit,true
GEOMETRY:center,top
MAP

View File

@@ -30,6 +30,8 @@
*/
#define SOLIDIFY 0x00000400L /* outer areas are nondiggable & nonpasswall */
#define CORRMAZE 0x00000800L /* for maze levels only */
#define CHECK_INACCESSIBLES 0x00001000L /* check for inaccessible areas and
generate ways to escape from them */
/* different level layout initializers */
#define LVLINIT_NONE 0
@@ -291,6 +293,7 @@ struct sp_coder {
int n_subroom;
boolean exit_script;
int lvl_is_joined;
boolean check_inaccessibles;
int opcode; /* current opcode */
struct opvar *opdat; /* current push data (req. opcode == SPO_PUSH) */

View File

@@ -112,9 +112,9 @@ STATIC_DCL struct opvar *FDECL(selection_logical_oper,
STATIC_DCL struct opvar *FDECL(selection_filter_mapchar,
(struct opvar *, struct opvar *));
STATIC_DCL void FDECL(selection_filter_percent, (struct opvar *, int));
STATIC_DCL int FDECL(selection_rndcoord, (struct opvar *, schar *, schar *));
STATIC_DCL int FDECL(selection_rndcoord, (struct opvar *, schar *, schar *, boolean));
STATIC_DCL void FDECL(selection_do_grow, (struct opvar *, int));
STATIC_DCL void FDECL(selection_floodfill, (struct opvar *, int, int));
STATIC_DCL void FDECL(selection_floodfill, (struct opvar *, int, int, boolean));
STATIC_DCL void FDECL(selection_do_ellipse,
(struct opvar *, int, int, int, int, int));
STATIC_DCL long FDECL(line_dist_coord, (long, long, long, long, long, long));
@@ -3163,6 +3163,8 @@ struct sp_coder *coder;
coder->solidify = TRUE;
if (lflags & CORRMAZE)
level.flags.corrmaze = TRUE;
if (lflags & CHECK_INACCESSIBLES)
coder->check_inaccessibles = TRUE;
opvar_free(flagdata);
}
@@ -3645,9 +3647,10 @@ int percent;
}
int
selection_rndcoord(ov, x, y)
selection_rndcoord(ov, x, y, removeit)
struct opvar *ov;
schar *x, *y;
boolean removeit;
{
int idx = 0;
int c;
@@ -3666,6 +3669,7 @@ schar *x, *y;
if (!c) {
*x = dx;
*y = dy;
if (removeit) selection_setpoint(dx, dy, ov, 0);
return 1;
}
c--;
@@ -3727,12 +3731,40 @@ int dir;
selection_setpoint(x, y, ov, 1);
}
int (*selection_flood_check_func)(int, int) = NULL;
void
selection_floodfill(ov, x, y)
set_selection_floodfill_checker(f)
int (*f)(int, int);
{
selection_flood_check_func = f;
}
static schar floodfill_checker_match_under_typ;
int
floodfill_checker_match_under(x,y)
int x,y;
{
return (floodfill_checker_match_under_typ == levl[x][y].typ);
}
int
floodfill_checker_match_accessible(x,y)
int x,y;
{
return (ACCESSIBLE(levl[x][y].typ)
|| levl[x][y].typ == SDOOR
|| levl[x][y].typ == SCORR);
}
void
selection_floodfill(ov, x, y, diagonals)
struct opvar *ov;
int x, y;
boolean diagonals;
{
static const char nhFunc[] = "selection_floorfill";
static const char nhFunc[] = "selection_floodfill";
struct opvar *tmp = selection_opvar(NULL);
#define SEL_FLOOD_STACK (COLNO * ROWNO)
#define SEL_FLOOD(nx, ny) \
@@ -3744,11 +3776,19 @@ int x, y;
} else \
panic(floodfill_stack_overrun); \
} while (0)
static const char floodfill_stack_overrun[] = "floorfill stack overrun";
#define SEL_FLOOD_CHKDIR(mx,my,sel) \
if (isok((mx), (my)) \
&& (*selection_flood_check_func)((mx),(my)) \
&& !selection_getpoint((mx), (my), (sel))) \
SEL_FLOOD((mx), (my))
static const char floodfill_stack_overrun[] = "floodfill stack overrun";
int idx = 0;
xchar dx[SEL_FLOOD_STACK];
xchar dy[SEL_FLOOD_STACK];
schar under = levl[x][y].typ;
if (selection_flood_check_func == NULL) {
opvar_free(tmp);
return;
}
SEL_FLOOD(x, y);
do {
idx--;
@@ -3758,21 +3798,20 @@ int x, y;
selection_setpoint(x, y, ov, 1);
selection_setpoint(x, y, tmp, 1);
}
if (isok(x + 1, y) && (levl[x + 1][y].typ == under)
&& !selection_getpoint(x + 1, y, tmp))
SEL_FLOOD(x + 1, y);
if (isok(x - 1, y) && (levl[x - 1][y].typ == under)
&& !selection_getpoint(x - 1, y, tmp))
SEL_FLOOD(x - 1, y);
if (isok(x, y + 1) && (levl[x][y + 1].typ == under)
&& !selection_getpoint(x, y + 1, tmp))
SEL_FLOOD(x, y + 1);
if (isok(x, y - 1) && (levl[x][y - 1].typ == under)
&& !selection_getpoint(x, y - 1, tmp))
SEL_FLOOD(x, y - 1);
SEL_FLOOD_CHKDIR((x + 1), y, tmp);
SEL_FLOOD_CHKDIR((x - 1), y, tmp);
SEL_FLOOD_CHKDIR(x, (y + 1), tmp);
SEL_FLOOD_CHKDIR(x, (y - 1), tmp);
if (diagonals) {
SEL_FLOOD_CHKDIR((x + 1), (y + 1), tmp);
SEL_FLOOD_CHKDIR((x - 1), (y - 1), tmp);
SEL_FLOOD_CHKDIR((x - 1), (y + 1), tmp);
SEL_FLOOD_CHKDIR((x + 1), (y - 1), tmp);
}
} while (idx > 0);
#undef SEL_FLOOD
#undef SEL_FLOOD_STACK
#undef SEL_FLOOD_CHKDIR
opvar_free(tmp);
}
@@ -4189,6 +4228,117 @@ struct sp_coder *coder;
opvar_free(chance);
}
boolean
generate_way_out_method(nx,ny, ov)
int nx,ny;
struct opvar *ov;
{
static const char nhFunc[] = "generate_way_out_method";
const int escapeitems[] = { PICK_AXE,
DWARVISH_MATTOCK,
WAN_DIGGING,
WAN_TELEPORTATION,
SCR_TELEPORTATION,
RIN_TELEPORTATION };
struct opvar *ov2 = selection_opvar(NULL), *ov3;
schar x, y;
boolean res = TRUE;
selection_floodfill(ov2, nx, ny, TRUE);
ov3 = opvar_clone(ov2);
/* try to make a secret door */
while (selection_rndcoord(ov3, &x, &y, TRUE)) {
if (isok(x+1, y) && !selection_getpoint(x+1, y, ov) && IS_WALL(levl[x+1][y].typ) &&
isok(x+2, y) && selection_getpoint(x+2, y, ov) && ACCESSIBLE(levl[x+2][y].typ)) {
levl[x+1][y].typ = SDOOR;
goto gotitdone;
}
if (isok(x-1, y) && !selection_getpoint(x-1, y, ov) && IS_WALL(levl[x-1][y].typ) &&
isok(x-2, y) && selection_getpoint(x-2, y, ov) && ACCESSIBLE(levl[x-2][y].typ)) {
levl[x-1][y].typ = SDOOR;
goto gotitdone;
}
if (isok(x, y+1) && !selection_getpoint(x, y+1, ov) && IS_WALL(levl[x][y+1].typ) &&
isok(x, y+2) && selection_getpoint(x, y+2, ov) && ACCESSIBLE(levl[x][y+2].typ)) {
levl[x][y+1].typ = SDOOR;
goto gotitdone;
}
if (isok(x, y-1) && !selection_getpoint(x, y-1, ov) && IS_WALL(levl[x][y-1].typ) &&
isok(x, y-2) && selection_getpoint(x, y-2, ov) && ACCESSIBLE(levl[x][y-2].typ)) {
levl[x][y-1].typ = SDOOR;
goto gotitdone;
}
}
/* try to make a hole or a trapdoor */
if (Can_fall_thru(&u.uz)) {
opvar_free(ov3);
ov3 = opvar_clone(ov2);
while (selection_rndcoord(ov3, &x, &y, TRUE)) {
if (maketrap(x,y, rn2(2) ? HOLE : TRAPDOOR))
goto gotitdone;
}
}
/* generate one of the escape items */
if (selection_rndcoord(ov2, &x, &y, FALSE)) {
mksobj_at(escapeitems[rn2(SIZE(escapeitems))], x, y, TRUE, FALSE);
goto gotitdone;
}
res = FALSE;
gotitdone:
opvar_free(ov2);
opvar_free(ov3);
return res;
}
void
ensure_way_out()
{
static const char nhFunc[] = "ensure_way_out";
struct opvar *ov = selection_opvar(NULL);
struct trap *ttmp = ftrap;
int x,y;
boolean ret = TRUE;
set_selection_floodfill_checker(floodfill_checker_match_accessible);
if (xupstair && !selection_getpoint(xupstair, yupstair, ov))
selection_floodfill(ov, xupstair, yupstair, TRUE);
if (xdnstair && !selection_getpoint(xdnstair, ydnstair, ov))
selection_floodfill(ov, xdnstair, ydnstair, TRUE);
if (xupladder && !selection_getpoint(xupladder, yupladder, ov))
selection_floodfill(ov, xupladder, yupladder, TRUE);
if (xdnladder && !selection_getpoint(xdnladder, ydnladder, ov))
selection_floodfill(ov, xdnladder, ydnladder, TRUE);
while (ttmp) {
if ((ttmp->ttyp == MAGIC_PORTAL || ttmp->ttyp == VIBRATING_SQUARE
|| ttmp->ttyp == HOLE || ttmp->ttyp == TRAPDOOR)
&& !selection_getpoint(ttmp->tx, ttmp->ty, ov))
selection_floodfill(ov, ttmp->tx, ttmp->ty, TRUE);
ttmp = ttmp->ntrap;
}
do {
ret = TRUE;
for (x = 0; x < COLNO; x++)
for (y = 0; y < ROWNO; y++)
if (ACCESSIBLE(levl[x][y].typ)
&& !selection_getpoint(x, y, ov)) {
if (generate_way_out_method(x,y, ov))
selection_floodfill(ov, x,y, TRUE);
ret = FALSE;
goto outhere;
}
outhere: ;
} while (!ret);
opvar_free(ov);
}
void
spo_levregion(coder)
struct sp_coder *coder;
@@ -4977,6 +5127,7 @@ sp_lev *lvl;
coder->stack = NULL;
coder->premapped = FALSE;
coder->solidify = FALSE;
coder->check_inaccessibles = FALSE;
coder->croom = NULL;
coder->n_subroom = 1;
coder->exit_script = FALSE;
@@ -5509,7 +5660,9 @@ sp_lev *lvl;
get_location_coord(&x, &y, ANY_LOC, coder->croom, OV_i(tmp));
if (isok(x, y)) {
struct opvar *pt = selection_opvar(NULL);
selection_floodfill(pt, x, y);
set_selection_floodfill_checker(floodfill_checker_match_under);
floodfill_checker_match_under_typ = levl[x][y].typ;
selection_floodfill(pt, x, y, FALSE);
splev_stack_push(coder->stack, pt);
}
opvar_free(tmp);
@@ -5519,7 +5672,7 @@ sp_lev *lvl;
schar x, y;
if (!OV_pop_typ(pt, SPOVAR_SEL))
panic("no selection for rndcoord");
if (selection_rndcoord(pt, &x, &y)) {
if (selection_rndcoord(pt, &x, &y, FALSE)) {
x -= xstart;
y -= ystart;
}
@@ -5589,6 +5742,9 @@ sp_lev *lvl;
link_doors_rooms();
fill_rooms();
remove_boundary_syms();
if (coder->check_inaccessibles)
ensure_way_out();
/* FIXME: Ideally, we want this call to only cover areas of the map
* which were not inserted directly by the special level file (see
* the insect legs on Baalzebub's level, for instance). Since that

View File

@@ -296,6 +296,7 @@ graveyard { savetoken(yytext); yylval.i=GRAVEYARD; return FLAG_TYPE; }
icedpools { savetoken(yytext); yylval.i=ICEDPOOLS; return FLAG_TYPE; }
solidify { savetoken(yytext); yylval.i=SOLIDIFY; return FLAG_TYPE; }
corrmaze { savetoken(yytext); yylval.i=CORRMAZE; return FLAG_TYPE; }
inaccessibles { savetoken(yytext); yylval.i=CHECK_INACCESSIBLES; return FLAG_TYPE; }
[0-9]+d[0-9]+ { char *p = strchr(yytext, 'd');
savetoken(yytext);
if (p) {