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:
@@ -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
|
||||
|
||||
@@ -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) */
|
||||
|
||||
198
src/sp_lev.c
198
src/sp_lev.c
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user