fix #K4061 - wand with "interesting effect"
Report was for an impossible "What an interesting effect (%d)" which the fuzzer turned into a panic. Monster on the drawbridge zapped toward the open portcullis, destroying the bridge and killing itself. Wand was made of wood and burned up in lava under the fallen span. Freeing the object zeroed it rather than leaving stale data, and the zap continued while referencing freed memory that looked like it had type STRANGE_OBJECT, triggering the impossible. This will make a monster-induced zap stop early if a drawbridge incident destroys the wand. That isn't the best possible fix because the zap should continue despite the wand's destruction, but at least it will now avoid triggering "intestsing effect".
This commit is contained in:
71
src/muse.c
71
src/muse.c
@@ -1,4 +1,4 @@
|
|||||||
/* NetHack 3.7 muse.c $NHDT-Date: 1701557157 2023/12/02 22:45:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.202 $ */
|
/* NetHack 3.7 muse.c $NHDT-Date: 1702356860 2023/12/12 04:54:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.203 $ */
|
||||||
/* Copyright (C) 1990 by Ken Arromdee */
|
/* Copyright (C) 1990 by Ken Arromdee */
|
||||||
/* NetHack may be freely redistributed. See license for details. */
|
/* NetHack may be freely redistributed. See license for details. */
|
||||||
|
|
||||||
@@ -1598,16 +1598,16 @@ mbhitm(struct monst *mtmp, struct obj *otmp)
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
mbhit(
|
mbhit(
|
||||||
struct monst *mon, /* monster shooting the wand */
|
struct monst *mon, /* monster shooting the wand */
|
||||||
register int range, /* direction and range */
|
int range, /* direction and range */
|
||||||
int (*fhitm)(MONST_P, OBJ_P),
|
int (*fhitm)(MONST_P, OBJ_P), /* must be non-Null */
|
||||||
int (*fhito)(OBJ_P, OBJ_P), /* fns called when mon/obj hit */
|
int (*fhito)(OBJ_P, OBJ_P), /* fns called when mon/obj hit */
|
||||||
struct obj *obj) /* 2nd arg to fhitm/fhito */
|
struct obj *obj) /* 2nd arg to fhitm/fhito */
|
||||||
{
|
{
|
||||||
register struct monst *mtmp;
|
struct monst *mtmp;
|
||||||
register struct obj *otmp;
|
struct obj *otmp;
|
||||||
register uchar typ;
|
uchar ltyp;
|
||||||
int ddx, ddy;
|
int ddx, ddy, otyp = obj->otyp;
|
||||||
|
|
||||||
gb.bhitpos.x = mon->mx;
|
gb.bhitpos.x = mon->mx;
|
||||||
gb.bhitpos.y = mon->my;
|
gb.bhitpos.y = mon->my;
|
||||||
@@ -1615,7 +1615,7 @@ mbhit(
|
|||||||
ddy = sgn(mon->muy - mon->my);
|
ddy = sgn(mon->muy - mon->my);
|
||||||
|
|
||||||
while (range-- > 0) {
|
while (range-- > 0) {
|
||||||
coordxy x, y;
|
coordxy x, y, dbx, dby;
|
||||||
|
|
||||||
gb.bhitpos.x += ddx;
|
gb.bhitpos.x += ddx;
|
||||||
gb.bhitpos.y += ddy;
|
gb.bhitpos.y += ddy;
|
||||||
@@ -1627,11 +1627,6 @@ mbhit(
|
|||||||
gb.bhitpos.y -= ddy;
|
gb.bhitpos.y -= ddy;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (find_drawbridge(&x, &y))
|
|
||||||
switch (obj->otyp) {
|
|
||||||
case WAN_STRIKING:
|
|
||||||
destroy_drawbridge(x, y);
|
|
||||||
}
|
|
||||||
if (u_at(gb.bhitpos.x, gb.bhitpos.y)) {
|
if (u_at(gb.bhitpos.x, gb.bhitpos.y)) {
|
||||||
(*fhitm)(&gy.youmonst, obj);
|
(*fhitm)(&gy.youmonst, obj);
|
||||||
range -= 3;
|
range -= 3;
|
||||||
@@ -1644,7 +1639,7 @@ mbhit(
|
|||||||
/* modified by GAN to hit all objects */
|
/* modified by GAN to hit all objects */
|
||||||
if (fhito) {
|
if (fhito) {
|
||||||
int hitanything = 0;
|
int hitanything = 0;
|
||||||
register struct obj *next_obj;
|
struct obj *next_obj;
|
||||||
|
|
||||||
for (otmp = gl.level.objects[gb.bhitpos.x][gb.bhitpos.y]; otmp;
|
for (otmp = gl.level.objects[gb.bhitpos.x][gb.bhitpos.y]; otmp;
|
||||||
otmp = next_obj) {
|
otmp = next_obj) {
|
||||||
@@ -1655,9 +1650,32 @@ mbhit(
|
|||||||
if (hitanything)
|
if (hitanything)
|
||||||
range--;
|
range--;
|
||||||
}
|
}
|
||||||
typ = levl[gb.bhitpos.x][gb.bhitpos.y].typ;
|
ltyp = levl[gb.bhitpos.x][gb.bhitpos.y].typ;
|
||||||
if (IS_DOOR(typ) || typ == SDOOR) {
|
dbx = x, dby = y;
|
||||||
switch (obj->otyp) {
|
if (otyp == WAN_STRIKING && find_drawbridge(&dbx, &dby)) {
|
||||||
|
/* this might kill mon and destroy obj; mon will remain
|
||||||
|
accessible even if dead but obj could be deleted */
|
||||||
|
destroy_drawbridge(dbx, dby);
|
||||||
|
for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
|
||||||
|
if (otmp == obj)
|
||||||
|
break;
|
||||||
|
if (!otmp) {
|
||||||
|
for (otmp = gl.level.objects[x][y]; otmp;
|
||||||
|
otmp = otmp->nexthere)
|
||||||
|
if (otmp == obj)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* if otmp is Null, mon isn't carrying obj anymore and didn't
|
||||||
|
just drop it here; assume that it was destroyed and stop
|
||||||
|
the zap; not really correct behavior but we can't continue
|
||||||
|
without the responsible object because fhitm (mbhitm) and
|
||||||
|
fhito (bhito) will want it at forthcoming spots in zap path */
|
||||||
|
if (!otmp) {
|
||||||
|
obj = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (IS_DOOR(ltyp) || ltyp == SDOOR) {
|
||||||
|
switch (otyp) {
|
||||||
/* note: monsters don't use opening or locking magic
|
/* note: monsters don't use opening or locking magic
|
||||||
at present, but keep these as placeholders */
|
at present, but keep these as placeholders */
|
||||||
case WAN_OPENING:
|
case WAN_OPENING:
|
||||||
@@ -1665,7 +1683,7 @@ mbhit(
|
|||||||
case WAN_STRIKING:
|
case WAN_STRIKING:
|
||||||
if (doorlock(obj, gb.bhitpos.x, gb.bhitpos.y)) {
|
if (doorlock(obj, gb.bhitpos.x, gb.bhitpos.y)) {
|
||||||
if (gz.zap_oseen)
|
if (gz.zap_oseen)
|
||||||
makeknown(obj->otyp);
|
makeknown(otyp);
|
||||||
/* if a shop door gets broken, add it to
|
/* if a shop door gets broken, add it to
|
||||||
the shk's fix list (no cost to player) */
|
the shk's fix list (no cost to player) */
|
||||||
if (levl[gb.bhitpos.x][gb.bhitpos.y].doormask == D_BROKEN
|
if (levl[gb.bhitpos.x][gb.bhitpos.y].doormask == D_BROKEN
|
||||||
@@ -1675,14 +1693,18 @@ mbhit(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ZAP_POS(typ)
|
if (!ZAP_POS(ltyp)
|
||||||
|| (IS_DOOR(typ) && (levl[gb.bhitpos.x][gb.bhitpos.y].doormask
|
|| (IS_DOOR(ltyp) && (levl[gb.bhitpos.x][gb.bhitpos.y].doormask
|
||||||
& (D_LOCKED | D_CLOSED)))) {
|
& (D_LOCKED | D_CLOSED)))) {
|
||||||
gb.bhitpos.x -= ddx;
|
gb.bhitpos.x -= ddx;
|
||||||
gb.bhitpos.y -= ddy;
|
gb.bhitpos.y -= ddy;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* 'obj' was set of Null when it couldn't be found, but isn't used again;
|
||||||
|
however, someday that might change, so we want it set to Null; give it
|
||||||
|
a fake use to pacify potential "set but not used"-type warnings */
|
||||||
|
nhUse(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform an offensive action for a monster. Must be called immediately
|
/* Perform an offensive action for a monster. Must be called immediately
|
||||||
@@ -1741,6 +1763,7 @@ use_offensive(struct monst *mtmp)
|
|||||||
mzapwand(mtmp, otmp, FALSE);
|
mzapwand(mtmp, otmp, FALSE);
|
||||||
gm.m_using = TRUE;
|
gm.m_using = TRUE;
|
||||||
mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp);
|
mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp);
|
||||||
|
/* note: 'otmp' might have been destroyed (drawbridge destruction) */
|
||||||
gm.m_using = FALSE;
|
gm.m_using = FALSE;
|
||||||
return 2;
|
return 2;
|
||||||
case MUSE_SCR_EARTH: {
|
case MUSE_SCR_EARTH: {
|
||||||
|
|||||||
Reference in New Issue
Block a user