Fix rare door in corner of room bug
There were couple reports of doors being generated in a corner of a room. This happened for randomly generated irregular rooms, because the code that was deciding if a corridor starting or ending location was good did not handle irregular rooms at all. This changes the corridor code so it can now generate start and end points properly for any valid position in irregular rooms, instead of only on the edges. This means the corridor sometimes meanders a bit more than before, because it tries to find the end point away from the edge of the room rectangle. Also added is sanity checking for the randomly generated rooms and corridors level, testing for door placement and room connectivity. And another fix for a rare special case where dig_corridor created a zero-tile long corridor; the entrance door was placed, but there was nothing behind it. Fixes github #1269 and #1385
This commit is contained in:
@@ -2972,7 +2972,7 @@ extern void flip_level_rnd(int, boolean);
|
||||
extern boolean check_room(coordxy *, coordxy *, coordxy *, coordxy *, boolean) NONNULLPTRS;
|
||||
extern boolean create_room(coordxy, coordxy, coordxy, coordxy,
|
||||
coordxy, coordxy, xint16, xint16);
|
||||
extern boolean dig_corridor(coord *, coord *, boolean, schar, schar) NONNULLARG12;
|
||||
extern boolean dig_corridor(coord *, coord *, int *, boolean, schar, schar) NONNULLARG12;
|
||||
extern void fill_special_room(struct mkroom *) NO_NNARGS;
|
||||
extern void wallify_map(coordxy, coordxy, coordxy, coordxy);
|
||||
extern boolean load_special(const char *) NONNULLARG1;
|
||||
|
||||
246
src/mklev.c
246
src/mklev.c
@@ -23,6 +23,8 @@ staticfn void mk_knox_portal(coordxy, coordxy);
|
||||
staticfn void makevtele(void);
|
||||
staticfn void fill_ordinary_room(struct mkroom *, boolean) NONNULLARG1;
|
||||
staticfn void themerooms_post_level_generate(void);
|
||||
staticfn boolean chk_okdoor(coordxy, coordxy);
|
||||
staticfn void mklev_sanity_check(void);
|
||||
staticfn void makelevel(void);
|
||||
staticfn boolean bydoor(coordxy, coordxy);
|
||||
staticfn void mktrap_victim(struct trap *);
|
||||
@@ -42,8 +44,10 @@ staticfn void do_room_or_subroom(struct mkroom *,
|
||||
coordxy, coordxy, coordxy, coordxy,
|
||||
boolean, schar, boolean, boolean);
|
||||
staticfn void makerooms(void);
|
||||
staticfn boolean door_into_nonjoined(coordxy, coordxy);
|
||||
staticfn boolean finddpos(coord *, coordxy, coordxy, coordxy, coordxy);
|
||||
staticfn boolean good_rm_wall_doorpos(coordxy, coordxy, int, struct mkroom *);
|
||||
staticfn boolean finddpos_shift(coordxy *, coordxy *, int, struct mkroom *);
|
||||
|
||||
staticfn boolean finddpos(coord *, int, struct mkroom *);
|
||||
|
||||
#define create_vault() create_room(-1, -1, 2, 2, -1, -1, VAULT, TRUE)
|
||||
#define init_vault() gv.vault_x = -1
|
||||
@@ -63,57 +67,135 @@ mkroom_cmp(const genericptr vx, const genericptr vy)
|
||||
return (x->lx > y->lx);
|
||||
}
|
||||
|
||||
/* Return TRUE if a door placed at (x, y) which otherwise passes okdoor()
|
||||
* checks would be connecting into an area that was declared as joined=false.
|
||||
* Checking for this in finddpos() enables us to have rooms with sub-areas
|
||||
* (such as shops) that will never randomly generate unwanted doors in order
|
||||
* to connect them up to other areas.
|
||||
*/
|
||||
/* is x,y a good location for a door into room? */
|
||||
staticfn boolean
|
||||
door_into_nonjoined(coordxy x, coordxy y)
|
||||
good_rm_wall_doorpos(coordxy x, coordxy y, int dir, struct mkroom *room)
|
||||
{
|
||||
coordxy tx, ty, i;
|
||||
coordxy tx, ty;
|
||||
int rmno;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
tx = x + xdir[dirs_ord[i]];
|
||||
ty = y + ydir[dirs_ord[i]];
|
||||
if (!isok(tx, ty) || IS_OBSTRUCTED(levl[tx][ty].typ))
|
||||
continue;
|
||||
if (!isok(x, y) || !room->needjoining)
|
||||
return FALSE;
|
||||
|
||||
/* Is this connecting to a room that doesn't want joining? */
|
||||
if (levl[tx][ty].roomno >= ROOMOFFSET
|
||||
&& !svr.rooms[levl[tx][ty].roomno - ROOMOFFSET].needjoining) {
|
||||
return TRUE;
|
||||
if (!(levl[x][y].typ == HWALL
|
||||
|| levl[x][y].typ == VWALL
|
||||
|| IS_DOOR(levl[x][y].typ)
|
||||
|| levl[x][y].typ == SDOOR))
|
||||
return FALSE;
|
||||
|
||||
if (bydoor(x, y))
|
||||
return FALSE;
|
||||
|
||||
tx = x + xdir[dir];
|
||||
ty = y + ydir[dir];
|
||||
|
||||
if (!isok(tx,ty) || IS_OBSTRUCTED(levl[tx][ty].typ))
|
||||
return FALSE;
|
||||
|
||||
rmno = (room - svr.rooms) + ROOMOFFSET;
|
||||
|
||||
if (rmno != levl[tx][ty].roomno)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* starting from x,y going towards dir, find a good location for a door */
|
||||
staticfn boolean
|
||||
finddpos_shift(coordxy *x, coordxy *y, int dir, struct mkroom *aroom)
|
||||
{
|
||||
coordxy dx, dy;
|
||||
|
||||
dir = DIR_180(dir);
|
||||
|
||||
dx = xdir[dir];
|
||||
dy = ydir[dir];
|
||||
|
||||
if (good_rm_wall_doorpos(*x, *y, dir, aroom))
|
||||
return TRUE;
|
||||
|
||||
/* irregular rooms may have the room wall away from the room rectangular
|
||||
area; go into the area until we encounter something */
|
||||
if (aroom->irregular) {
|
||||
coordxy rx = *x, ry = *y;
|
||||
boolean fail = FALSE;
|
||||
|
||||
while (!fail && isok(rx, ry)
|
||||
&& (levl[rx][ry].typ == STONE || levl[rx][ry].typ == CORR)) {
|
||||
rx += dx;
|
||||
ry += dy;
|
||||
if (good_rm_wall_doorpos(rx, ry, dir, aroom)) {
|
||||
*x = rx;
|
||||
*y = ry;
|
||||
return TRUE;
|
||||
}
|
||||
if (!(levl[rx][ry].typ == STONE || levl[rx][ry].typ == CORR))
|
||||
fail = TRUE;
|
||||
if (rx < aroom->lx || rx > aroom->hx
|
||||
|| ry < aroom->ly || ry > aroom->hy)
|
||||
fail = TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* find a valid door position at room edge.
|
||||
dir is the preferred edge of the room.
|
||||
if found, returns TRUE and the coordinate in cc */
|
||||
staticfn boolean
|
||||
finddpos(
|
||||
coord *cc,
|
||||
coordxy xl, coordxy yl,
|
||||
coordxy xh, coordxy yh)
|
||||
finddpos(coord *cc, int dir, struct mkroom *aroom)
|
||||
{
|
||||
coordxy x, y;
|
||||
coordxy x1, y1, x2, y2;
|
||||
int tryct = 0;
|
||||
|
||||
x = rn1(xh - xl + 1, xl);
|
||||
y = rn1(yh - yl + 1, yl);
|
||||
if (okdoor(x, y) && !door_into_nonjoined(x, y))
|
||||
goto gotit;
|
||||
switch (dir) {
|
||||
case DIR_N:
|
||||
x1 = aroom->lx;
|
||||
x2 = aroom->hx;
|
||||
y1 = aroom->ly - 1;
|
||||
y2 = aroom->ly - 1;
|
||||
break;
|
||||
case DIR_S:
|
||||
x1 = aroom->lx;
|
||||
x2 = aroom->hx;
|
||||
y1 = aroom->hy + 1;
|
||||
y2 = aroom->hy + 1;
|
||||
break;
|
||||
case DIR_W:
|
||||
x1 = aroom->lx - 1;
|
||||
x2 = aroom->lx - 1;
|
||||
y1 = aroom->ly;
|
||||
y2 = aroom->hy;
|
||||
break;
|
||||
case DIR_E:
|
||||
x1 = aroom->hx + 1;
|
||||
x2 = aroom->hx + 1;
|
||||
y1 = aroom->ly;
|
||||
y2 = aroom->hy;
|
||||
break;
|
||||
default:
|
||||
impossible("finddpos: illegal dir");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (x = xl; x <= xh; x++)
|
||||
for (y = yl; y <= yh; y++)
|
||||
if (okdoor(x, y) && !door_into_nonjoined(x, y))
|
||||
/* try random points */
|
||||
do {
|
||||
x = (x2 - x1) ? rn1(x2 - x1 + 1, x1) : x1;
|
||||
y = (y2 - y1) ? rn1(y2 - y1 + 1, y1) : y1;
|
||||
if (finddpos_shift(&x, &y, dir, aroom))
|
||||
goto gotit;
|
||||
} while (++tryct < 20);
|
||||
|
||||
/* try all the points */
|
||||
for (x = x1; x <= x2; x++)
|
||||
for (y = y1; y <= y2; y++)
|
||||
if (finddpos_shift(&x, &y, dir, aroom))
|
||||
goto gotit;
|
||||
|
||||
for (x = xl; x <= xh; x++)
|
||||
for (y = yl; y <= yh; y++)
|
||||
if (IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR)
|
||||
goto gotit;
|
||||
/* cannot find something reasonable -- strange */
|
||||
cc->x = xl;
|
||||
cc->y = yh;
|
||||
cc->x = x1;
|
||||
cc->y = y1;
|
||||
return FALSE;
|
||||
gotit:
|
||||
cc->x = x;
|
||||
@@ -349,6 +431,8 @@ join(int a, int b, boolean nxcor)
|
||||
coordxy tx, ty, xx, yy;
|
||||
struct mkroom *croom, *troom;
|
||||
int dx, dy;
|
||||
int npoints;
|
||||
boolean dig_result;
|
||||
|
||||
croom = &svr.rooms[a];
|
||||
troom = &svr.rooms[b];
|
||||
@@ -366,36 +450,36 @@ join(int a, int b, boolean nxcor)
|
||||
dy = 0;
|
||||
xx = croom->hx + 1;
|
||||
tx = troom->lx - 1;
|
||||
if (!finddpos(&cc, xx, croom->ly, xx, croom->hy))
|
||||
if (!finddpos(&cc, DIR_E, croom))
|
||||
return;
|
||||
if (!finddpos(&tt, tx, troom->ly, tx, troom->hy))
|
||||
if (!finddpos(&tt, DIR_W, troom))
|
||||
return;
|
||||
} else if (troom->hy < croom->ly) {
|
||||
dy = -1;
|
||||
dx = 0;
|
||||
yy = croom->ly - 1;
|
||||
ty = troom->hy + 1;
|
||||
if (!finddpos(&cc, croom->lx, yy, croom->hx, yy))
|
||||
if (!finddpos(&cc, DIR_N, croom))
|
||||
return;
|
||||
if (!finddpos(&tt, troom->lx, ty, troom->hx, ty))
|
||||
if (!finddpos(&tt, DIR_S, troom))
|
||||
return;
|
||||
} else if (troom->hx < croom->lx) {
|
||||
dx = -1;
|
||||
dy = 0;
|
||||
xx = croom->lx - 1;
|
||||
tx = troom->hx + 1;
|
||||
if (!finddpos(&cc, xx, croom->ly, xx, croom->hy))
|
||||
if (!finddpos(&cc, DIR_W, croom))
|
||||
return;
|
||||
if (!finddpos(&tt, tx, troom->ly, tx, troom->hy))
|
||||
if (!finddpos(&tt, DIR_E, troom))
|
||||
return;
|
||||
} else {
|
||||
dy = 1;
|
||||
dx = 0;
|
||||
yy = croom->hy + 1;
|
||||
ty = troom->ly - 1;
|
||||
if (!finddpos(&cc, croom->lx, yy, croom->hx, yy))
|
||||
if (!finddpos(&cc, DIR_S, croom))
|
||||
return;
|
||||
if (!finddpos(&tt, troom->lx, ty, troom->hx, ty))
|
||||
if (!finddpos(&tt, DIR_N, troom))
|
||||
return;
|
||||
}
|
||||
xx = cc.x;
|
||||
@@ -404,16 +488,20 @@ join(int a, int b, boolean nxcor)
|
||||
ty = tt.y - dy;
|
||||
if (nxcor && levl[xx + dx][yy + dy].typ != STONE)
|
||||
return;
|
||||
if (okdoor(xx, yy) || !nxcor)
|
||||
dodoor(xx, yy, croom);
|
||||
|
||||
org.x = xx + dx;
|
||||
org.y = yy + dy;
|
||||
dest.x = tx;
|
||||
dest.y = ty;
|
||||
|
||||
if (!dig_corridor(&org, &dest, nxcor,
|
||||
svl.level.flags.arboreal ? ROOM : CORR, STONE))
|
||||
dig_result = dig_corridor(&org, &dest, &npoints, nxcor,
|
||||
svl.level.flags.arboreal ? ROOM : CORR, STONE);
|
||||
|
||||
/* we created at least 1 tile of corridor, even if it failed */
|
||||
if ((npoints > 0) && (okdoor(xx, yy) || !nxcor))
|
||||
dodoor(xx, yy, croom);
|
||||
|
||||
if (!dig_result)
|
||||
return;
|
||||
|
||||
/* we succeeded in digging the corridor */
|
||||
@@ -616,13 +704,11 @@ place_niche(
|
||||
|
||||
if (rn2(2)) {
|
||||
*dy = 1;
|
||||
if (!finddpos(&dd, aroom->lx, aroom->hy + 1,
|
||||
aroom->hx, aroom->hy + 1))
|
||||
if (!finddpos(&dd, DIR_S, aroom))
|
||||
return FALSE;
|
||||
} else {
|
||||
*dy = -1;
|
||||
if (!finddpos(&dd, aroom->lx, aroom->ly - 1,
|
||||
aroom->hx, aroom->ly - 1))
|
||||
if (!finddpos(&dd, DIR_N, aroom))
|
||||
return FALSE;
|
||||
}
|
||||
*xx = dd.x;
|
||||
@@ -1103,6 +1189,60 @@ themerooms_post_level_generate(void)
|
||||
lua_gc(themes, LUA_GCCOLLECT);
|
||||
}
|
||||
|
||||
/* if x,y is door, does it open into solid terrain */
|
||||
staticfn boolean
|
||||
chk_okdoor(coordxy x, coordxy y)
|
||||
{
|
||||
if (IS_DOOR(levl[x][y].typ)) {
|
||||
if (levl[x][y].horizontal) {
|
||||
if ((isok(x, y-1) && (levl[x][y-1].typ > TREE))
|
||||
&& (isok(x, y+1) && (levl[x][y+1].typ <= TREE)))
|
||||
return FALSE;
|
||||
if ((isok(x, y-1) && (levl[x][y-1].typ <= TREE))
|
||||
&& (isok(x, y+1) && (levl[x][y+1].typ > TREE)))
|
||||
return FALSE;
|
||||
} else {
|
||||
if ((isok(x-1, y) && (levl[x-1][y].typ > TREE))
|
||||
&& (isok(x+1, y) && (levl[x+1][y].typ <= TREE)))
|
||||
return FALSE;
|
||||
if ((isok(x-1, y) && (levl[x-1][y].typ <= TREE))
|
||||
&& (isok(x+1, y) && (levl[x+1][y].typ > TREE)))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* check mklev created level sanity */
|
||||
staticfn void
|
||||
mklev_sanity_check(void)
|
||||
{
|
||||
coordxy x, y;
|
||||
int i;
|
||||
int rmno = -1;
|
||||
|
||||
if (!(iflags.sanity_check || iflags.debug_fuzzer))
|
||||
return;
|
||||
|
||||
for (y = 0; y < ROWNO; y++) {
|
||||
for (x = 1; x < COLNO; x++) {
|
||||
if (!chk_okdoor(x,y))
|
||||
impossible("levl[%i][%i] door not ok", x, y);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < svn.nroom; i++) {
|
||||
if (!svr.rooms[i].needjoining)
|
||||
continue;
|
||||
if (rmno == -1)
|
||||
rmno = gs.smeq[i];
|
||||
if (rmno != -1 && gs.smeq[i] != rmno)
|
||||
impossible("room %i not connected?", i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
staticfn void
|
||||
makelevel(void)
|
||||
{
|
||||
@@ -1166,6 +1306,8 @@ makelevel(void)
|
||||
makecorridors();
|
||||
make_niches();
|
||||
|
||||
mklev_sanity_check();
|
||||
|
||||
/* make a secret treasure vault, not connected to the rest */
|
||||
if (do_vault()) {
|
||||
coordxy w, h;
|
||||
|
||||
@@ -313,7 +313,7 @@ join_map(schar bg_typ, schar fg_typ)
|
||||
em.y = croom2->ly + ((croom2->hy - croom2->ly) / 2);
|
||||
}
|
||||
|
||||
(void) dig_corridor(&sm, &em, FALSE, fg_typ, bg_typ);
|
||||
(void) dig_corridor(&sm, &em, NULL, FALSE, fg_typ, bg_typ);
|
||||
|
||||
/* choose next region to join */
|
||||
/* only increment croom if croom and croom2 are non-overlapping */
|
||||
|
||||
10
src/sp_lev.c
10
src/sp_lev.c
@@ -2535,10 +2535,12 @@ search_door(
|
||||
* Dig a corridor between two points, using terrain ftyp.
|
||||
* if nxcor is TRUE, he corridor may be blocked by a boulder,
|
||||
* or just end without reaching the destination.
|
||||
* if not null, npoints has the number of map locations used
|
||||
*/
|
||||
boolean
|
||||
dig_corridor(
|
||||
coord *org, coord *dest,
|
||||
int *npoints,
|
||||
boolean nxcor,
|
||||
schar ftyp, schar btyp)
|
||||
{
|
||||
@@ -2546,6 +2548,8 @@ dig_corridor(
|
||||
struct rm *crm;
|
||||
int tx, ty, xx, yy;
|
||||
|
||||
if (npoints)
|
||||
*npoints = 0;
|
||||
xx = org->x;
|
||||
yy = org->y;
|
||||
tx = dest->x;
|
||||
@@ -2582,8 +2586,12 @@ dig_corridor(
|
||||
crm = &levl[xx][yy];
|
||||
if (crm->typ == btyp) {
|
||||
if (ftyp == CORR && maybe_sdoor(100)) {
|
||||
if (npoints)
|
||||
(*npoints)++;
|
||||
crm->typ = SCORR;
|
||||
} else {
|
||||
if (npoints)
|
||||
(*npoints)++;
|
||||
crm->typ = ftyp;
|
||||
if (nxcor && !rn2(50))
|
||||
(void) mksobj_at(BOULDER, xx, yy, TRUE, FALSE);
|
||||
@@ -2704,7 +2712,7 @@ create_corridor(corridor *c)
|
||||
dest.x++;
|
||||
break;
|
||||
}
|
||||
(void) dig_corridor(&org, &dest, FALSE, CORR, STONE);
|
||||
(void) dig_corridor(&org, &dest, NULL, FALSE, CORR, STONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user