fix #K3422 - mimic remains as 'strange object'

even when protection from shape changers is in effect.  I'm not sure
why mimicking other things doesn't trigger the same sanity check
warning.  This fix works for the strange object case and I assume
that it doesn't break the more general case.

When investigating, I noticed that save and restore (even leaving
the level and then returning) causes cancelled shape changers to be
uncancelled.  Treat being cancelled similarly to having to having
protection from shape changers in effect:  shape changer is forced
to revert to its innate form and not allowed to change shape.
This commit is contained in:
PatR
2021-08-24 08:09:30 -07:00
parent a1b927a278
commit e67f45fc5c
4 changed files with 63 additions and 59 deletions

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 mon.c $NHDT-Date: 1627413528 2021/07/27 19:18:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.382 $ */
/* NetHack 3.7 mon.c $NHDT-Date: 1629817677 2021/08/24 15:07:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.384 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Derek S. Ray, 2015. */
/* NetHack may be freely redistributed. See license for details. */
@@ -3644,41 +3644,58 @@ seemimic(register struct monst* mtmp)
newsym(mtmp->mx, mtmp->my);
}
/* force all chameleons to become normal */
/* [taken out of rescham() in order to be shared by restore_cham()] */
void
rescham(void)
normal_shape(struct monst *mon)
{
register struct monst *mtmp;
int mcham;
int mcham = (int) mon->cham;
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (DEADMONSTER(mtmp))
continue;
mcham = (int) mtmp->cham;
if (mcham >= LOW_PM) {
(void) newcham(mtmp, &mons[mcham], FALSE, FALSE);
mtmp->cham = NON_PM;
}
if (is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN)
new_were(mtmp);
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);
}
if (mcham >= LOW_PM) {
unsigned mcan = mon->mcan;
(void) newcham(mon, &mons[mcham], FALSE, FALSE);
mon->cham = NON_PM;
/* newcham() may uncancel a polymorphing monster; override that */
if (mcan)
mon->mcan = 1;
newsym(mon->mx, mon->my);
}
if (is_were(mon->data) && mon->data->mlet != S_HUMAN) {
new_were(mon);
}
if (M_AP_TYPE(mon) != M_AP_NOTHING) {
/* this used to include a cansee() check but Protection_from_
_shape_changers shouldn't be trumped by being unseen */
if (!mon->meating) {
/* make revealed mimic fall asleep in lieu of shape change */
if (M_AP_TYPE(mon) != M_AP_MONSTER)
mon->msleeping = 1;
seemimic(mon);
} else {
/* quickmimic: pet is midst of eating a mimic corpse;
this terminates the meal early */
finish_meating(mon);
}
}
}
/* Let the chameleons change again -dgk */
/* force all chameleons and mimics to become themselves and werecreatures
to revert to human form; called when Protection_from_shape_changers gets
activated via wearing or eating ring */
void
rescham(void)
{
register struct monst *mtmp;
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (DEADMONSTER(mtmp))
continue;
normal_shape(mtmp);
}
}
/* let chameleons change and mimics hide again; called when taking off
ring of protection from shape changers */
void
restartcham(void)
{
@@ -3689,8 +3706,7 @@ restartcham(void)
continue;
if (!mtmp->mcan)
mtmp->cham = pm_to_cham(monsndx(mtmp->data));
if (mtmp->data->mlet == S_MIMIC && mtmp->msleeping
&& cansee(mtmp->mx, mtmp->my)) {
if (mtmp->data->mlet == S_MIMIC && mtmp->msleeping) {
set_mimic_sym(mtmp);
newsym(mtmp->mx, mtmp->my);
}
@@ -3701,26 +3717,20 @@ restartcham(void)
against shape-changing might be different now than it was at the
time the level was saved. */
void
restore_cham(struct monst* mon)
restore_cham(struct monst *mon)
{
int mcham;
if (Protection_from_shape_changers) {
mcham = (int) mon->cham;
if (mcham >= LOW_PM) {
mon->cham = NON_PM;
(void) newcham(mon, &mons[mcham], FALSE, FALSE);
} else if (is_were(mon->data) && !is_human(mon->data)) {
new_were(mon);
}
if (Protection_from_shape_changers || mon->mcan) {
/* force chameleon or mimic to revert to its natural shape */
normal_shape(mon);
} else if (mon->cham == NON_PM) {
/* chameleon doesn't change shape here, just gets allowed to do so */
mon->cham = pm_to_cham(monsndx(mon->data));
}
}
/* unwatched hiders may hide again; if so, returns True */
static boolean
restrap(struct monst* mtmp)
restrap(struct monst *mtmp)
{
struct trap *t;

View File

@@ -1,4 +1,4 @@
/* NetHack 3.7 zap.c $NHDT-Date: 1626390628 2021/07/15 23:10:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.369 $ */
/* NetHack 3.7 zap.c $NHDT-Date: 1629817679 2021/08/24 15:07:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.372 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -2898,19 +2898,8 @@ cancel_monst(struct monst *mdef, struct obj *obj, boolean youattack,
}
} else {
mdef->mcan = 1;
/* force shapeshifter into its base form */
if (M_AP_TYPE(mdef) != M_AP_NOTHING)
seemimic(mdef);
/* [not 'else if'; chameleon might have been hiding as a mimic] */
if (mdef->cham >= LOW_PM) {
/* note: newcham() uncancels shapechangers (resets m->mcan
to 0), but only for shapechangers whose m->cham is already
NON_PM and we just verified that it's LOW_PM or higher */
newcham(mdef, &mons[mdef->cham], FALSE, FALSE);
mdef->cham = NON_PM; /* cancelled shapeshifter can't shift */
}
if (is_were(mdef->data) && !is_human(mdef->data))
were_change(mdef);
/* force shapeshifter into its base form or mimic to unhide */
normal_shape(mdef);
if (mdef->data == &mons[PM_CLAY_GOLEM]) {
if (canseemon(mdef))