mhpmax of life-drained monsters

The report about problems after stone-to-flesh on a petrified
long worm included stethoscope feedback of 0(-1) hit points, after
life-draining.  I was unable to reproduce a maximum hp of -1 and hope
that it was a side-effect of the [already fixed] stale mon->wormno
value used when resurrecting the long worm.  Anyway, this changes
life-draining to never take mon->hpmax below mon->m_lev + 1 (the +1
is needed to cope with m_lev==0 monsters).  The same limit is also
applied to monster life-saving but more to avoid replicating the
arbitrary minimum of 10 (four instances) then because it might be
less than m_lev+1 somehow.

Sanity checking now tests whether a monster's max HP is less than
its level + 1 so if there are ways other than life-drain attacks for
it to drop that low, the fuzzer will choke.  The new check also tests
whether a monster's current HP is greater than max HP.

Polymophred hero killing a golem or vortex by vampire bite reported
"<Mon> dies."  Give an alternate message since those aren't alive.
This commit is contained in:
PatR
2020-06-27 18:15:19 -07:00
parent 30b19a3891
commit 7d7b98f0ae
7 changed files with 134 additions and 48 deletions

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 mhitm.c $NHDT-Date: 1583608838 2020/03/07 19:20:38 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.132 $ */
/* NetHack 3.6 mhitm.c $NHDT-Date: 1593306906 2020/06/28 01:15:06 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.137 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1286,22 +1286,32 @@ int dieroll;
}
if (!tele_restrict(magr)) {
boolean couldspot = canspotmon(magr);
(void) rloc(magr, TRUE);
if (g.vis && couldspot && !canspotmon(magr))
pline("%s suddenly disappears!", buf);
}
break;
case AD_DRLI:
case AD_DRLI: /* drain life */
if (!cancelled && !rn2(3) && !resists_drli(mdef)) {
tmp = d(2, 6);
tmp = d(2, 6); /* Stormbringer uses monhp_per_lvl(usually 1d8) */
if (g.vis && canspotmon(mdef))
pline("%s suddenly seems weaker!", Monnam(mdef));
mdef->mhpmax -= tmp;
if (mdef->m_lev == 0)
pline("%s becomes weaker!", Monnam(mdef));
if (mdef->mhpmax - tmp > (int) mdef->m_lev) {
mdef->mhpmax -= tmp;
} else {
/* limit floor of mhpmax reduction to current m_lev + 1;
avoid increasing it if somehow already less than that */
if (mdef->mhpmax > (int) mdef->m_lev)
mdef->mhpmax = (int) mdef->m_lev + 1;
}
if (mdef->m_lev == 0) /* automatic kill if drained past level 0 */
tmp = mdef->mhp;
else
mdef->m_lev--;
/* Automatic kill if drained past level 0 */
/* unlike hitting with Stormbringer, wounded attacker doesn't
heal any from the drained life */
}
break;
case AD_SSEX: