From: PatR Date: Fri, 18 Dec 2020 23:05:54 +0000 (-0800) Subject: concealed monster sanity checks X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c709c45780141a5e595729abf7bc9d7552d128cb;p=nethack concealed monster sanity checks Add some more checks to sanity_check_single_mon(). If mon->data is discovered to be bad, panic instead of just issuing a warning since a subsequent crash would be inevitable. Make sure hidden ceiling hiders have a ceiling to hide at (so not on the planes of air or water; some quest levels should probably be classified as "no ceiling" but currently aren't). Perform a few mimic checks. Protection from shape changers had a couple of minor bugs. A mimic hidden at a spot the hero couldn't see would be allowed to remain hidden (and stay that way once within view because protection from shape changers isn't re-checked during ordinary activity). Also, if a pet was shape-changed while eating a mimic corpse at the time protection from shape changers started, it would fall into untimed sleep as part of being forced back to normal shape [rescham()] if its location could be seen. --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index b873c0833..2b498f61e 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1,4 +1,4 @@ -NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.394 $ $NHDT-Date: 1608236443 2020/12/17 20:20:43 $ +NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.395 $ $NHDT-Date: 1608332750 2020/12/18 23:05:50 $ General Fixes and Modified Features ----------------------------------- @@ -337,6 +337,10 @@ when water damage wet a towel, the new wetness might randomly become less when the wetness of a towel in inventory changed, persistent inventory wasn't updated to show that make Death revive earlier, and all the Riders after 67 turns at latest +when protection from shape changers begins, force mimic out of concealment + even if hero can't see its location; for locations that can be seen, + don't make double-trouble Wizard concealed as another monster--or pet + temporarily mimicking something while eating mimic corpse--fall asleep Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/src/mon.c b/src/mon.c index ad126c6c5..1676dc072 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 mon.c $NHDT-Date: 1607901923 2020/12/13 23:25:23 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.360 $ */ +/* NetHack 3.7 mon.c $NHDT-Date: 1608332750 2020/12/18 23:05:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.361 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -46,11 +46,16 @@ struct monst *mtmp; boolean chk_geno; const char *msg; { - if (mtmp->data < &mons[LOW_PM] || mtmp->data >= &mons[NUMMONS]) { - impossible("illegal mon data %s; mnum=%d (%s)", - fmt_ptr((genericptr_t) mtmp->data), mtmp->mnum, msg); + struct permonst *mptr = mtmp->data; + int mx = mtmp->mx, my = mtmp->my; + + if (!mptr || mptr < &mons[LOW_PM] || mptr >= &mons[NUMMONS]) { + /* most sanity checks issue warnings if they detect a problem, + 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); } else { - int mndx = monsndx(mtmp->data); + int mndx = monsndx(mptr); if (mtmp->mnum != mndx) { impossible("monster mnum=%d, monsndx=%d (%s)", @@ -79,7 +84,7 @@ const char *msg; /* bad if not fmons list or if not vault guard */ if (strcmp(msg, "fmon") || !mtmp->isgd) impossible("dead monster on %s; %s at <%d,%d>", - msg, mons[mndx].mname, mtmp->mx, mtmp->my); + msg, mons[mndx].mname, mx, my); #endif return; } @@ -103,7 +108,7 @@ const char *msg; if (mtmp->mtrapped) { if (mtmp->wormno) { /* TODO: how to check worm in trap? */ - } else if (!t_at(mtmp->mx, mtmp->my)) + } else if (!t_at(mx, my)) impossible("trapped without a trap (%s)", msg); } @@ -111,17 +116,54 @@ const char *msg; if (mtmp->mundetected) { struct trap *t; + if (!isok(mx, my)) /* caller will have checked this but not fixed it */ + mx = my = 0; if (mtmp == u.ustuck) impossible("hiding monster stuck to you (%s)", msg); - if (m_at(mtmp->mx, mtmp->my) == mtmp && hides_under(mtmp->data) && !OBJ_AT(mtmp->mx, mtmp->my)) + if (m_at(mx, my) == mtmp && hides_under(mptr) && !OBJ_AT(mx, my)) impossible("mon hiding under nonexistent obj (%s)", msg); - if (mtmp->data->mlet == S_EEL && !is_pool(mtmp->mx, mtmp->my) && !Is_waterlevel(&u.uz)) + if (mptr->mlet == S_EEL + && !is_pool(mx, my) && !Is_waterlevel(&u.uz)) impossible("eel hiding out of water (%s)", msg); - if (mtmp->mtrapped && (t = t_at(mtmp->mx, mtmp->my)) != 0 + if (ceiling_hider(mptr) + /* normally !accessible would be overridable with passes_walls, + but not for hiding on the ceiling */ + && (!has_ceiling(&u.uz) || !accessible(mx, my))) + impossible("ceiling hider hiding %s (%s)", + !has_ceiling(&u.uz) ? "without ceiling" + : "in solid stone", + msg); + if (mtmp->mtrapped && (t = t_at(mx, my)) != 0 && !(t->ttyp == PIT || t->ttyp == SPIKED_PIT)) impossible("hiding while trapped in a non-pit (%s)", msg); - } + } else if (M_AP_TYPE(mtmp) != M_AP_NOTHING) { + boolean is_mimic = (mptr->mlet == S_MIMIC); + const char *what = (M_AP_TYPE(mtmp) == M_AP_FURNITURE) ? "furniture" + : (M_AP_TYPE(mtmp) == M_AP_MONSTER) ? "a monster" + : (M_AP_TYPE(mtmp) == M_AP_OBJECT) ? "an object" + : "something strange"; + + if (Protection_from_shape_changers) + impossible( + "mimic%s concealed as %s despite Prot-from-shape-changers %s", + is_mimic ? "" : "ker", what, msg); + /* pet's quickmimic can take on furniture and object shapes, + but only until the pet finishes eating a mimic corpse */ + if (!(is_mimic || mtmp->meating)) + impossible("non-mimic (%s) posing as %s (%s)", + mptr->mname, what, msg); + if (!(accessible(mx, my) || passes_walls(mptr))) { + char buf[BUFSZ]; + const char *typnam = levltyp_to_name(levl[mx][my].typ); + if (!typnam) { + Sprintf(buf, "[%d]", levl[mx][my].typ); + typnam = buf; + } + impossible("mimic%s concealed in inaccessible location: %s (%s)", + is_mimic ? "" : "ker", typnam, msg); + } + } } void @@ -3569,11 +3611,19 @@ rescham() } if (is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN) new_were(mtmp); - if (M_AP_TYPE(mtmp) && cansee(mtmp->mx, mtmp->my)) { - seemimic(mtmp); - /* we pretend that the mimic doesn't - know that it has been unmasked */ - mtmp->msleeping = 1; + if (M_AP_TYPE(mtmp) != M_AP_NOTHING) { + /* this used to include a cansee() check but Protection_from_ + _shape_changers shouldn't be trumped by being unseen */ + if (!mtmp->meating) { + /* make revealed mimic fall asleep in lieu of shape change */ + if (M_AP_TYPE(mtmp) != M_AP_MONSTER) + mtmp->msleeping = 1; + seemimic(mtmp); + } else { + /* quickmimic: pet is midst of eating a mimic corpse; + this terminates the meal early */ + finish_meating(mtmp); + } } } }