fix github issue #606 - shop wall repair

triggering an impossible warning about "wall_angle: unknown" due
to the known conflict between door state and wall info which both
overlay the flags field for map locations.

Reported and diagnosed by vultur-cadens:  if a shop's wall was dug
open, followed by use of locking magic to plug the gap with a door,
and then unlocking that door, the D_CLOSED door flag was left as
invalid wall_info when shop damage was repaired.  Map re-display
complained.  Leaving the door locked or opening it after unlocking
did not result in any complaint because the values for those door
states do not conflict with wall angle values.

The problem was reproducible and is now fixed by adding an extra
field to the shop damage structure.  A similar change has been
made to the vault guard's 'fake corridor' structure but I have no
test case for that so don't know whether it makes any difference.
At least it doesn't seem to have broken anything.

Existing save and bones files are invalidated by the fixes.

Fixes #606
This commit is contained in:
PatR
2021-10-15 15:43:23 -07:00
parent 53bb04d9ad
commit d9bbad8e6e
7 changed files with 107 additions and 46 deletions

View File

@@ -642,6 +642,10 @@ blessed scroll of remove curse read while confused blesses or curses any
can now use m<dir> to try to move to an adjacent boulder's spot without
pushing it; hero poly'd into a giant or a tiny creature or carrying so
little as to be able to squeeze there will succeed, others will fail
breaching a shop wall, using locking magic to put a door there, then unlocking
that door yielded a situation where subsequent shop damage repair
produced invalid map data which resulted in an impossible() warning
about "wall_angle: unknown" during map display
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository

View File

@@ -69,7 +69,9 @@
#define GD_DESTROYGOLD 0x02
struct fakecorridor {
xchar fx, fy, ftyp;
xchar fx, fy;
schar ftyp; /* from struct rm's typ */
uchar flags; /* also from struct rm; an unsigned 5-bit field there */
};
struct egd {

View File

@@ -17,7 +17,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
#define EDITLEVEL 42
#define EDITLEVEL 43
/*
* Development status possibilities.

View File

@@ -114,20 +114,27 @@ enum levl_typ_types {
* a secret door is revealed, the door gets set to D_CLOSED iff
* it isn't set to D_LOCKED (see cvt_sdoor_to_door() in detect.c).
*
* D_TRAPPED conflicts with W_NONDIGGABLE but the latter is not
* expected to be used on door locations.
* D_LOCKED conflicts with W_NONDIGGABLE but the latter is not
* expected to be used on door locations. D_TRAPPED conflicts
* with W_NONPASSWALL but secret doors aren't trapped.
* D_SECRET would not fit within struct rm's 5-bit 'flags' field.
*/
/*
* The 5 possible states of doors.
* For historical reasons they are numbered as mask bits rather than 0..4.
* The trapped flag is OR'd onto the state and only valid if that state
* is closed or locked.
* The no-door state allows egress when moving diagonally, others do not.
*/
#define D_NODOOR 0
#define D_BROKEN 1
#define D_ISOPEN 2
#define D_CLOSED 4
#define D_LOCKED 8
#define D_TRAPPED 16
#define D_SECRET 32 /* only used by sp_lev.c, NOT in rm-struct */
#define D_NODOOR 0x00
#define D_BROKEN 0x01
#define D_ISOPEN 0x02
#define D_CLOSED 0x04
#define D_LOCKED 0x08
#define D_TRAPPED 0x10
#define D_SECRET 0x20 /* only used by sp_lev.c, NOT in rm-struct */
/*
* Thrones should only be looted once.
@@ -213,7 +220,7 @@ enum levl_typ_types {
*/
struct rm {
int glyph; /* what the hero thinks is there */
schar typ; /* what is really there */
schar typ; /* what is really there [why is this signed?] */
uchar seenv; /* seen vector */
Bitfield(flags, 5); /* extra information for typ */
Bitfield(horizontal, 1); /* wall/door/etc is horiz. (more typ info) */
@@ -322,7 +329,8 @@ struct damage {
struct damage *next;
long when, cost;
coord place;
schar typ;
schar typ; /* from struct rm */
uchar flags; /* also from struct rm; an unsigned 5-bit field there */
};
/* for bones levels: identify the dead character, who might have died on

View File

@@ -2971,16 +2971,44 @@ t_warn(struct rm *lev)
static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x";
const char *wname;
if (lev->typ == TUWALL)
/* 3.7: non-T_wall cases added after shop repair (via breaching a wall,
using locking magic to put a door there, then unlocking the door;
D_CLOSED carried over to the wall) triggered warning for "unknown" */
switch (lev->typ) {
case TUWALL:
wname = "tuwall";
else if (lev->typ == TLWALL)
break;
case TLWALL:
wname = "tlwall";
else if (lev->typ == TRWALL)
break;
case TRWALL:
wname = "trwall";
else if (lev->typ == TDWALL)
break;
case TDWALL:
wname = "tdwall";
else
break;
case VWALL:
wname = "vwall";
break;
case HWALL:
wname = "hwall";
break;
case TLCORNER:
wname = "tlcorner";
break;
case TRCORNER:
wname = "trcorner";
break;
case BLCORNER:
wname = "blcorner";
break;
case BRCORNER:
wname = "brcorner";
break;
default:
wname = "unknown";
break;
}
impossible(warn_str, wname, lev->wall_info & WM_MASK,
(unsigned int) lev->seenv);
}

View File

@@ -2720,9 +2720,10 @@ append_honorific(char *buf)
{
/* (chooses among [0]..[3] normally; [1]..[4] after the
Wizard has been killed or invocation ritual performed) */
static const char *const honored[] = { "good", "honored", "most gracious",
"esteemed",
"most renowned and sacred" };
static const char *const honored[] = {
"good", "honored", "most gracious", "esteemed",
"most renowned and sacred"
};
Strcat(buf, honored[rn2(SIZE(honored) - 1) + u.uevent.udemigod]);
if (is_vampire(g.youmonst.data))
@@ -3481,6 +3482,7 @@ add_damage(
tmp_dam->place.y = y;
tmp_dam->cost = cost;
tmp_dam->typ = levl[x][y].typ;
tmp_dam->flags = levl[x][y].flags;
tmp_dam->next = g.level.damagelist;
g.level.damagelist = tmp_dam;
/* If player saw damage, display as a wall forever */
@@ -3570,7 +3572,7 @@ discard_damage_struct(struct damage *dam)
if (prev)
prev->next = dam->next;
}
(void) memset(dam, 0, sizeof(struct damage));
(void) memset(dam, 0, sizeof *dam);
free((genericptr_t) dam);
}
@@ -3802,10 +3804,14 @@ repair_damage(
stop_picking = picking_at(x, y);
/* door or wall repair; trap, if any, is now gone;
restore original terrain type and move any items away */
restore original terrain type and move any items away;
rm.doormask and rm.wall_info are both overlaid on rm.flags
so the new flags value needs to match the restored typ */
levl[x][y].typ = tmp_dam->typ;
if (IS_DOOR(tmp_dam->typ))
levl[x][y].doormask = D_CLOSED; /* arbitrary */
else /* not a door; set rm.wall_info or whatever old flags are relevant */
levl[x][y].flags = tmp_dam->flags;
litter = litter_getpos(&k, x, y, shkp);
litter_scatter(litter, k, x, y, shkp);

View File

@@ -83,6 +83,7 @@ clear_fcorr(struct monst *grd, boolean forceshow)
if (lev->typ == CORR && cansee(fcx, fcy))
sawcorridor = TRUE;
lev->typ = egrd->fakecorr[fcbeg].ftyp;
lev->flags = egrd->fakecorr[fcbeg].flags;
if (IS_STWALL(lev->typ)) {
/* destroy any trap here (pit dug by you, hole dug via
wand while levitating or by monster, bear trap or land
@@ -506,25 +507,29 @@ invault(void)
EGD(guard)->fcbeg = 0;
EGD(guard)->fakecorr[0].fx = x;
EGD(guard)->fakecorr[0].fy = y;
if (IS_WALL(levl[x][y].typ)) {
EGD(guard)->fakecorr[0].ftyp = levl[x][y].typ;
} else { /* the initial guard location is a dug door */
EGD(guard)->fakecorr[0].ftyp = levl[x][y].typ;
EGD(guard)->fakecorr[0].flags = levl[x][y].flags;
if (!IS_WALL(levl[x][y].typ)) {
/* the initial guard location happens to be a dug door */
int vlt = EGD(guard)->vroom;
xchar lowx = g.rooms[vlt].lx, hix = g.rooms[vlt].hx;
xchar lowy = g.rooms[vlt].ly, hiy = g.rooms[vlt].hy;
int typ = levl[x][y].typ;
if (x == lowx - 1 && y == lowy - 1)
EGD(guard)->fakecorr[0].ftyp = TLCORNER;
typ = TLCORNER;
else if (x == hix + 1 && y == lowy - 1)
EGD(guard)->fakecorr[0].ftyp = TRCORNER;
typ = TRCORNER;
else if (x == lowx - 1 && y == hiy + 1)
EGD(guard)->fakecorr[0].ftyp = BLCORNER;
typ = BLCORNER;
else if (x == hix + 1 && y == hiy + 1)
EGD(guard)->fakecorr[0].ftyp = BRCORNER;
typ = BRCORNER;
else if (y == lowy - 1 || y == hiy + 1)
EGD(guard)->fakecorr[0].ftyp = HWALL;
typ = HWALL;
else if (x == lowx - 1 || x == hix + 1)
EGD(guard)->fakecorr[0].ftyp = VWALL;
typ = VWALL;
EGD(guard)->fakecorr[0].ftyp = typ;
}
levl[x][y].typ = DOOR;
levl[x][y].doormask = D_NODOOR;
@@ -581,14 +586,19 @@ wallify_vault(struct monst *grd)
if ((trap = t_at(x, y)) != 0)
deltrap(trap);
if (x == lox)
typ =
(y == loy) ? TLCORNER : (y == hiy) ? BLCORNER : VWALL;
typ = (y == loy) ? TLCORNER
: (y == hiy) ? BLCORNER
: VWALL;
else if (x == hix)
typ =
(y == loy) ? TRCORNER : (y == hiy) ? BRCORNER : VWALL;
typ = (y == loy) ? TRCORNER
: (y == hiy) ? BRCORNER
: VWALL;
else /* not left or right side, must be top or bottom */
typ = HWALL;
levl[x][y].typ = typ;
/* FIXME? both rm.doormask and rm.wall_info overlay
rm.flags, so clearing doormask clobbers wall_info
rather than reset it to the proper value. Do we care? */
levl[x][y].doormask = 0;
/*
* hack: player knows walls are restored because of the
@@ -789,6 +799,7 @@ gd_move(struct monst *grd)
verbalize("You've been warned, knave!");
mnexto(grd);
levl[m][n].typ = egrd->fakecorr[0].ftyp;
levl[m][n].flags = egrd->fakecorr[0].flags;
newsym(m, n);
grd->mpeaceful = 0;
return -1;
@@ -805,6 +816,7 @@ gd_move(struct monst *grd)
n = grd->my;
(void) rloc(grd, TRUE);
levl[m][n].typ = egrd->fakecorr[0].ftyp;
levl[m][n].flags = egrd->fakecorr[0].flags;
newsym(m, n);
grd->mpeaceful = 0;
letknow:
@@ -832,8 +844,8 @@ gd_move(struct monst *grd)
if (egrd->fcend > 1) {
if (egrd->fcend > 2 && in_fcorridor(grd, grd->mx, grd->my)
&& !egrd->gddone && !in_fcorridor(grd, u.ux, u.uy)
&& levl[egrd->fakecorr[0].fx][egrd->fakecorr[0].fy].typ
== egrd->fakecorr[0].ftyp) {
&& (levl[egrd->fakecorr[0].fx][egrd->fakecorr[0].fy].typ
== egrd->fakecorr[0].ftyp)) {
pline("%s, confused, disappears.", noit_Monnam(grd));
disappear_msg_seen = TRUE;
goto cleanup;
@@ -902,7 +914,8 @@ gd_move(struct monst *grd)
for (ny = y - 1; ny <= y + 1; ny++) {
if ((nx == x || ny == y) && (nx != x || ny != y)
&& isok(nx, ny)) {
typ = (crm = &levl[nx][ny])->typ;
crm = &levl[nx][ny];
typ = crm->typ;
if (!IS_STWALL(typ) && !IS_POOL(typ)) {
if (in_fcorridor(grd, nx, ny))
goto nextnxy;
@@ -914,16 +927,11 @@ gd_move(struct monst *grd)
egrd->gddone = 1;
if (ACCESSIBLE(typ))
goto newpos;
#ifdef STUPID
if (typ == SCORR)
crm->typ = CORR;
else
crm->typ = DOOR;
#else
crm->typ = (typ == SCORR) ? CORR : DOOR;
#endif
if (crm->typ == DOOR)
crm->doormask = D_NODOOR;
else
crm->flags = 0;
goto proceed;
}
}
@@ -971,6 +979,7 @@ gd_move(struct monst *grd)
break;
}
crm->typ = CORR;
crm->flags = 0;
proceed:
newspot = TRUE;
unblock_point(nx, ny); /* doesn't block light */
@@ -979,11 +988,15 @@ gd_move(struct monst *grd)
if ((nx != gx || ny != gy) || (grd->mx != gx || grd->my != gy)) {
fcp = &(egrd->fakecorr[egrd->fcend]);
/* fakecorr overflow does not occur because egrd->fakecorr[]
is too small, but it has occurred when the same <x,y> are
put into it repeatedly for some as yet unexplained reason */
if (egrd->fcend++ == FCSIZ)
panic("fakecorr overflow");
fcp->fx = nx;
fcp->fy = ny;
fcp->ftyp = typ;
fcp->flags = crm->flags;
} else if (!egrd->gddone) {
/* We're stuck, so try to find a new destination. */
if (!find_guard_dest(grd, &egrd->gdx, &egrd->gdy)