From d13eceb28bc84a36d09254a7e1d8b939115afab6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 14 Jun 2026 14:19:34 -0400 Subject: [PATCH] eliminate one more source of vault guard newsym msgs see_monsters() was producing spurious vault guard at 0,0 messages Reproduce issue by: 1. Entering vault via teleport. 2. Wait for guard to enter. 3. Drop gold (if necessary) and follow guard. 4. Right after the guard disappears, but before the corridor does, the following can lead to the messages: a) control-R to refresh the display. or b) save the game and restore. In both cases, see_monsters() will get called and lead to the spurious messages for the vault guard that is parked. Also, add a macro PARKEDMONSTER(mon) instead of checking the the isgd bit and the value of mon->mx being zero in multiple places Also, adds MON_PARKED bit to mstate. Currently the PARKEDMONSTER(mon) macro mentioned above, does not use the new bit. --- include/monst.h | 25 +++++++++++++++---------- src/detect.c | 22 +++++++++++----------- src/display.c | 2 +- src/light.c | 4 ++-- src/minion.c | 4 +--- src/mon.c | 2 +- src/monmove.c | 2 +- src/restore.c | 7 +++++++ src/steed.c | 20 ++++++++++++++++---- src/vault.c | 5 ++++- src/wizcmds.c | 1 + 11 files changed, 60 insertions(+), 34 deletions(-) diff --git a/include/monst.h b/include/monst.h index a94bf2bc7..3a9bf151a 100644 --- a/include/monst.h +++ b/include/monst.h @@ -55,16 +55,17 @@ enum m_ap_types { M_AP_MONSTER = 3 /* a monster; mostly used for cloned Wizard */ }; -#define MON_FLOOR 0x00 -#define MON_OFFMAP 0x01 -#define MON_DETACH 0x02 -#define MON_MIGRATING 0x04 -#define MON_LIMBO 0x08 -#define MON_BUBBLEMOVE 0x10 -#define MON_ENDGAME_FREE 0x20 -#define MON_ENDGAME_MIGR 0x40 -#define MON_OBLITERATE 0x80 -#define MON_STILL_ARRIVING 0x100 +#define MON_FLOOR 0x0000 +#define MON_OFFMAP 0x0001 +#define MON_DETACH 0x0002 +#define MON_MIGRATING 0x0004 +#define MON_LIMBO 0x0008 +#define MON_BUBBLEMOVE 0x0010 +#define MON_ENDGAME_FREE 0x0020 +#define MON_ENDGAME_MIGR 0x0040 +#define MON_OBLITERATE 0x0080 +#define MON_STILL_ARRIVING 0x0100 +#define MON_PARKED 0x0200 #define M_AP_TYPMASK 0x7 #define M_AP_F_DKNOWN 0x8 @@ -212,6 +213,10 @@ struct monst { /* dead monsters stay on the fmon list until dmonsfree() at end of turn */ #define DEADMONSTER(mon) ((mon)->mhp < 1) +/* vault guards intentionally remain on the fmon list at 0,0 until + the temporary corridor is dealt with */ +#define PARKEDMONSTER(mon) ((mon)->isgd && (mon)->mx == 0) +/* eventually, we'll be able to use (((mon)->mstate & MON_PARKED) != 0) */ #define is_starting_pet(mon) ((mon)->m_id == svc.context.startingpet_mid) #define is_vampshifter(mon) \ diff --git a/src/detect.c b/src/detect.c index 11c370a05..ef9348001 100644 --- a/src/detect.c +++ b/src/detect.c @@ -345,7 +345,7 @@ gold_detect(struct obj *sobj) /* look for gold carried by monsters (might be in a container) */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { if (mtmp == u.usteed) { @@ -433,7 +433,7 @@ gold_detect(struct obj *sobj) ugold = TRUE; } for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; temp = 0; if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { @@ -497,7 +497,7 @@ food_detect(struct obj *sobj) ct++; } for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; for (obj = mtmp->minvent; obj; obj = obj->nobj) if (o_in(obj, oclass)) { @@ -561,7 +561,7 @@ food_detect(struct obj *sobj) map_object(temp, 1); } for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; for (obj = mtmp->minvent; obj; obj = obj->nobj) if ((temp = o_in(obj, oclass)) != 0) { @@ -666,7 +666,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ u.usteed->mx = u.ux, u.usteed->my = u.uy; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; for (obj = mtmp->minvent; obj; obj = obj->nobj) { if ((!class && !boulder) || o_in(obj, class) @@ -736,7 +736,7 @@ object_detect(struct obj *detector, /* object doing the detecting */ /* Objects in the monster's inventory override floor objects. */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; for (obj = mtmp->minvent; obj; obj = obj->nobj) if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0 @@ -807,7 +807,7 @@ monster_detect(struct obj *otmp, /* detecting object (if any) */ * with positive hit-points to know for sure. */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; ++mcnt; break; /* no need for full count, just 1 or more vs 0 */ @@ -826,7 +826,7 @@ monster_detect(struct obj *otmp, /* detecting object (if any) */ cls(); unconstrained = unconstrain_map(); for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; if (!mclass || mtmp->data->mlet == mclass || (mtmp->data == &mons[PM_LONG_WORM] @@ -969,7 +969,7 @@ display_trap_map(int cursed_src) (void) detect_obj_traps(svl.level.buriedobjlist, TRUE, cursed_src, NULL); (void) detect_obj_traps(fobj, TRUE, cursed_src, NULL); for (mon = fmon; mon; mon = mon->nmon) { - if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) + if (DEADMONSTER(mon) || PARKEDMONSTER(mon)) continue; (void) detect_obj_traps(mon->minvent, TRUE, cursed_src, NULL); } @@ -1046,7 +1046,7 @@ trap_detect( found = TRUE; } for (mon = fmon; mon; mon = mon->nmon) { - if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) + if (DEADMONSTER(mon) || PARKEDMONSTER(mon)) continue; if ((tr = detect_obj_traps(mon->minvent, FALSE, 0, NULL)) != OTRAP_NONE) { @@ -1643,7 +1643,7 @@ findone(coordxy zx, coordxy zy, genericptr_t whatfound) struct monst *mtmp = m_at(zx, zy); struct found_things *found_p = (struct found_things *) whatfound; - if (mtmp && (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx))) + if (mtmp && (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp))) mtmp = (struct monst *) NULL; found_p->ft_cc.x = zx; /* needed by detect_obj_traps() */ found_p->ft_cc.y = zy; diff --git a/src/display.c b/src/display.c index ffb9b2cbc..76daaa30b 100644 --- a/src/display.c +++ b/src/display.c @@ -1544,7 +1544,7 @@ see_monsters(void) /* loop through level.monsters (aka fmon) */ for (mon = fmon; mon; mon = mon->nmon) { - if (DEADMONSTER(mon)) + if (DEADMONSTER(mon) || PARKEDMONSTER(mon)) continue; if ((mon->mstate & MON_STILL_ARRIVING) != 0) continue; diff --git a/src/light.c b/src/light.c index fd41acd73..48f3671b1 100644 --- a/src/light.c +++ b/src/light.c @@ -303,7 +303,7 @@ show_transient_light(struct obj *obj, coordxy x, coordxy y) radius_squared = ls->range * ls->range; for (mon = fmon; mon; mon = mon->nmon) { - if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) + if (DEADMONSTER(mon) || PARKEDMONSTER(mon)) continue; /* light range is the radius of a circle and we're limiting canseemon() to a square enclosing that circle, but setting @@ -343,7 +343,7 @@ transient_light_cleanup(void) so need to be replaced by "remembered, unseen monster" glyph */ mtempcount = 0; for (mon = fmon; mon; mon = mon->nmon) { - if (DEADMONSTER(mon)) + if (DEADMONSTER(mon) || PARKEDMONSTER(mon)) continue; if (mon->mtemplit) { mon->mtemplit = 0; diff --git a/src/minion.c b/src/minion.c index b161f928d..03a782727 100644 --- a/src/minion.c +++ b/src/minion.c @@ -43,9 +43,7 @@ monster_census(boolean spotted) /* seen|sensed vs all */ int count = 0; for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - if (DEADMONSTER(mtmp)) - continue; - if (mtmp->isgd && mtmp->mx == 0) + if (DEADMONSTER(mtmp) || PARKEDMONSTER(mtmp)) continue; if (spotted && !canspotmon(mtmp)) continue; diff --git a/src/mon.c b/src/mon.c index a69a1aedf..3e86cb730 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1230,7 +1230,7 @@ movemon_singlemon(struct monst *mtmp) off the map too; gd_move() decides whether the temporary corridor can be removed and guard discarded (via clearing mon->isgd flag so that dmonsfree() will get rid of mon) */ - if (mtmp->isgd && !mtmp->mx && !(mtmp->mstate & MON_MIGRATING)) { + if (PARKEDMONSTER(mtmp) && !(mtmp->mstate & MON_MIGRATING)) { /* parked at <0,0>; eventually isgd should get set to false */ if (svm.moves > mtmp->mlstmv) { (void) gd_move(mtmp); diff --git a/src/monmove.c b/src/monmove.c index 340c2f628..10a97675a 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -922,7 +922,7 @@ dochug(struct monst *mtmp) /*FALLTHRU*/ case MMOVE_NOTHING: /* no movement, but it can still attack you */ case MMOVE_DONE: /* absolutely no movement */ - /* vault guard might have vanished */ + /* vault guard might have vanished; PARKEDMONSTER(mtmp) */ if (mtmp->isgd && (DEADMONSTER(mtmp) || mtmp->mx == 0)) return 1; /* behave as if it died */ /* During hallucination, monster appearance should diff --git a/src/restore.c b/src/restore.c index 0cc82f30f..2c9a01a75 100644 --- a/src/restore.c +++ b/src/restore.c @@ -448,6 +448,13 @@ restmonchn(NHFILE *nhfp) restshk(mtmp, ghostly); if (mtmp->ispriest) restpriest(mtmp, ghostly); + if (mtmp->isgd) { + /* fixup for new bit MON_PARKED added post 5.0.0 */ + if (!mtmp->mx + && (mtmp->mstate & MON_PARKED) == 0L + && (mtmp->mstate & MON_MIGRATING) == 0L) + mtmp->mstate |= MON_PARKED; + } if (!ghostly) { if (mtmp->m_id == svc.context.polearm.m_id) diff --git a/src/steed.c b/src/steed.c index a9c1ad7d0..536feec6f 100644 --- a/src/steed.c +++ b/src/steed.c @@ -903,8 +903,10 @@ place_monster(struct monst *mon, coordxy x, coordxy y) buf[0] = '\0'; /* normal map bounds are <1..COLNO-1,0..ROWNO-1> but sometimes - vault guards (either living or dead) are parked at <0,0> */ - if (!isok(x, y) && (x != 0 || y != 0 || !mon->isgd)) { + vault guards (either living or dead) are parked at <0,0>; + their mstate should have the MON_PARKED bit set (post-5.0.0) */ + if (!isok(x, y) + && !(((mon->mstate & MON_PARKED) != 0) || PARKEDMONSTER(mon))) { describe_level(buf, 0); impossible("trying to place %s at <%d,%d> mstate:%lx on %s", minimal_monnam(mon, TRUE), x, y, mon->mstate, buf); @@ -912,7 +914,7 @@ place_monster(struct monst *mon, coordxy x, coordxy y) } if ((mon == u.usteed && !gi.in_steed_dismounting) /* special case is for convoluted vault guard handling */ - || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) { + || (DEADMONSTER(mon) && !PARKEDMONSTER(mon))) { describe_level(buf, 0); impossible("placing %s onto map, mstate:%lx, on %s?", (mon == u.usteed) ? "steed" : "defunct monster", @@ -928,7 +930,17 @@ place_monster(struct monst *mon, coordxy x, coordxy y) } mon->mx = x, mon->my = y; svl.level.monsters[x][y] = mon; - mon->mstate = MON_FLOOR; + /* even though MON_FLOOR is not actually a bit currently + (MON_FLOOR == 0) we want to preserve some of the other + bits that may be set. We'll probably make MON_FLOOR an + actual bit one day */ + + mon->mstate &= ~(MON_OFFMAP | MON_DETACH | MON_LIMBO | MON_MIGRATING); + + /* We don't mess with these bits above: + MON_BUBBLEMOVE | MON_ENDGAME_FREE | MON_ENDGAME_MIGR + | MON_OBLITERATE | MON_STILL_ARRIVING | MON_PARKED + */ } /*steed.c*/ diff --git a/src/vault.c b/src/vault.c index 32d9e84b0..94f50d909 100644 --- a/src/vault.c +++ b/src/vault.c @@ -146,6 +146,7 @@ restfakecorr(struct monst *grd) /* it seems you left the corridor - let the guard disappear */ if (clear_fcorr(grd, FALSE)) { grd->isgd = 0; /* dmonsfree() should delete this mon */ + grd->mstate &= ~MON_PARKED; mongone(grd); } } @@ -162,8 +163,10 @@ parkguard(struct monst *grd) remove_monster(grd->mx, grd->my); newsym(grd->mx, grd->my); } - if (m_at(0, 0) != grd) + if (m_at(0, 0) != grd) { + grd->mstate |= MON_PARKED; place_monster(grd, 0, 0); + } /* [grd->mx,my just got set to 0,0 by place_monster(), so this just sets EGD(grd)->ogx,ogy to 0,0 too; is that what we want?] */ EGD(grd)->ogx = grd->mx; diff --git a/src/wizcmds.c b/src/wizcmds.c index 60b991db9..e7fda43ea 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -98,6 +98,7 @@ makemap_unmakemon(struct monst *mtmp, boolean migratory) mongone() -> m_detach() -> mon_leaving_level() copes with that */ mtmp->mstate |= MON_OFFMAP; mtmp->mstate &= ~(MON_MIGRATING | MON_LIMBO | MON_ENDGAME_MIGR); + /* FIXME: will post-5.0.0 MON_PARKED need to be dealt with here? */ mtmp->nmon = fmon; fmon = mtmp; }