From 56f0340f0cdbef154be121cc2dcb48ab4ae9ebf2 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 11 May 2023 11:51:57 -0700 Subject: [PATCH] fix #K3920 - migrating mimicker sanity Report says that Wizard of Yendor posing as some other monster triggers a sanity_check warning if on the migrating_mons list and hero has Protection_from_shape_changers attribute. My attmpts to reproduce that failed, but this updates the mon_sanity checking to explicitly allow a monster posing as another monster to be on the migrating monsters list. This also adds checks for whether a monster on the fmon list has MON_MIGRATING or MON_LIMBO or MON_DETACH bits set in monst->mstate and whether a monster on the migrating_mons list has MON_DETACH set or both MON_MIGRATING and MON_LIMBO clear. I won't be surprised if these new checks trigger sanity complaints. --- src/mon.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/mon.c b/src/mon.c index 2e038d348..4b96ff36e 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mon.c $NHDT-Date: 1681429657 2023/04/13 23:47:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.495 $ */ +/* NetHack 3.7 mon.c $NHDT-Date: 1683831104 2023/05/11 18:51:44 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.501 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -67,6 +67,7 @@ sanity_check_single_mon( but this would be too extreme to keep going */ panic("illegal mon data %s; mnum=%d (%s)", fmt_ptr((genericptr_t) mptr), mtmp->mnum, msg); + /*NOTREACHED*/ } else { int mndx = monsndx(mptr); @@ -174,10 +175,15 @@ sanity_check_single_mon( : (M_AP_TYPE(mtmp) == M_AP_OBJECT) ? "an object" : "something strange"; - if (Protection_from_shape_changers) + if (!strcmp(msg, "migr")) { + if (M_AP_TYPE(mtmp) != M_AP_MONSTER) + impossible("migrating %s mimicking %s %s", + is_mimic ? "mimic" : "monster", what, msg); + } else if (Protection_from_shape_changers) { impossible( "mimic%s concealed as %s despite Prot-from-shape-changers %s", is_mimic ? "" : "ker", what, msg); + } /* the Wizard's clone after "double trouble" starts out mimicking some other monster; pet's quickmimic effect can temporarily take on furniture, object, or monster shape, but only until the pet @@ -229,6 +235,12 @@ mon_sanity_check(void) fmt_ptr((genericptr_t) mtmp), x, y); } else if (mtmp->wormno) { sanity_check_worm(mtmp); + + /* TODO: figure out which other bits shouldn't be set for 'fmon' */ + } else if ((mtmp->mstate & (MON_DETACH | MON_MIGRATING | MON_LIMBO)) + != 0) { + impossible("floor mon (%s) with mstate set to 0x%08lx", + fmt_ptr((genericptr_t) mtmp), mtmp->mstate); } } @@ -253,6 +265,13 @@ mon_sanity_check(void) for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { sanity_check_single_mon(mtmp, FALSE, "migr"); + + /* TODO: figure out which other bits could or shouldn't be set + * when migrating */ + if ((mtmp->mstate & (MON_DETACH)) != 0 + || (mtmp->mstate & (MON_MIGRATING | MON_LIMBO)) == 0) + impossible("migrating mon (%s) with mstate set to 0x%08lx", + fmt_ptr((genericptr_t) mtmp), mtmp->mstate); } wormno_sanity_check(); /* test for bogus worm tail */ @@ -709,6 +728,8 @@ make_corpse(struct monst *mtmp, unsigned int corpseflags) return obj; } +#undef KEEPTRAITS + /* check mtmp and water/lava for compatibility, 0 (survived), 1 (died) */ int minliquid(struct monst* mtmp) @@ -1388,6 +1409,8 @@ meatobj(struct monst* mtmp) /* for gelatinous cubes */ return (count > 0 || ecount > 0) ? 1 : 0; } +#undef mstoning + /* Monster eats a corpse off the ground. * Return value is 0 = nothing eaten, 1 = ate a corpse, 2 = died */ int @@ -2778,6 +2801,8 @@ mondead(struct monst *mtmp) RESTORE_WARNING_FORMAT_NONLITERAL +#undef livelog_mon_nam + /* TRUE if corpse might be dropped, magr may die if mon was swallowed */ boolean corpse_chance( @@ -3215,7 +3240,7 @@ xkilled( } if (wasinside) { - /* spoteffects() can end up clearing the level of monsters; grab a copy */ + /* spoteffects() can end up clearing level of monsters; grab a copy */ museum = *mtmp; museum.nmon = 0; museum.minvent = 0; @@ -3301,6 +3326,8 @@ xkilled( return; } +#undef LEVEL_SPECIFIC_NOCORPSE + /* changes the monster into a stone monster of the same type this should only be called when poly_when_stoned() is true */ void @@ -4929,6 +4956,8 @@ egg_type_from_parent( return mnum; } +#undef BREEDER_EGG + /* decide whether an egg of the indicated monster type is viable; also used to determine whether an egg or tin can be created... */ boolean