diff --git a/include/extern.h b/include/extern.h index f6063cef9..27168a3c3 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1756,6 +1756,7 @@ extern struct monst *get_iter_mons(boolean (*)(struct monst *)); extern struct monst *get_iter_mons_xy(boolean (*)(struct monst *, coordxy, coordxy), coordxy, coordxy); +extern int healmon(struct monst *, int, int) NONNULLARG1; extern void rescham(void); extern void restartcham(void); extern void restore_cham(struct monst *) NONNULLARG1; diff --git a/src/artifact.c b/src/artifact.c index 446e4ab95..7b9c3864f 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -1636,9 +1636,7 @@ artifact_hit( healup(drain, 0, FALSE, FALSE); } else { assert(magr != 0); - magr->mhp += drain; - if (magr->mhp > magr->mhpmax) - magr->mhp = magr->mhpmax; + healmon(magr, drain, 0); } } return vis; @@ -1664,9 +1662,7 @@ artifact_hit( } losexp("life drainage"); if (magr && magr->mhp < magr->mhpmax) { - magr->mhp += (abs(oldhpmax - u.uhpmax) + 1) / 2; - if (magr->mhp > magr->mhpmax) - magr->mhp = magr->mhpmax; + healmon(magr, (abs(oldhpmax - u.uhpmax) + 1) / 2, 0); } return TRUE; } diff --git a/src/do.c b/src/do.c index e9be24ca0..4f633979a 100644 --- a/src/do.c +++ b/src/do.c @@ -876,7 +876,7 @@ engulfer_digests_food(struct obj *obj) } else if (could_grow) { (void) grow_up(u.ustuck, (struct monst *) 0); } else if (could_heal) { - u.ustuck->mhp = u.ustuck->mhpmax; + healmon(u.ustuck, u.ustuck->mhpmax, 0); /* False: don't realize that sight is cured from inside */ mcureblindness(u.ustuck, FALSE); } diff --git a/src/dog.c b/src/dog.c index 53d3aea66..4b523db7c 100644 --- a/src/dog.c +++ b/src/dog.c @@ -675,10 +675,7 @@ mon_catchup_elapsed_time( /* recover lost hit points */ if (!regenerates(mtmp->data)) imv /= 20; - if (mtmp->mhp + imv >= mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; - else - mtmp->mhp += imv; + healmon(mtmp, imv, 0); set_mon_lastmove(mtmp); } diff --git a/src/mcastu.c b/src/mcastu.c index 4ac28f956..95e9a1c65 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -363,8 +363,7 @@ m_cure_self(struct monst *mtmp, int dmg) if (canseemon(mtmp)) pline_mon(mtmp, "%s looks better.", Monnam(mtmp)); /* note: player healing does 6d4; this used to do 1d8 */ - if ((mtmp->mhp += d(3, 6)) > mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, d(3, 6), 0); dmg = 0; } return dmg; diff --git a/src/mhitm.c b/src/mhitm.c index eb3bcccda..1380148ed 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1093,7 +1093,7 @@ mdamagem( return (M_ATTK_DEF_DIED | (!DEADMONSTER(magr) ? 0 : M_ATTK_AGR_DIED)); } else if (pd == &mons[PM_NURSE]) { - magr->mhp = magr->mhpmax; + healmon(magr, magr->mhpmax, 0); } mon_givit(magr, pd); } @@ -1390,9 +1390,7 @@ passivemm( } if (canseemon(magr)) pline_mon(magr, "%s is suddenly very cold!", Monnam(magr)); - mdef->mhp += tmp / 2; - if (mdef->mhpmax < mdef->mhp) - mdef->mhpmax = mdef->mhp; + healmon(mdef, tmp/2, tmp/2); if (mdef->mhpmax > ((int) (mdef->m_lev + 1) * 8)) (void) split_mon(mdef, magr); break; diff --git a/src/mon.c b/src/mon.c index 545283c8d..a59b1a13a 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1351,9 +1351,7 @@ m_consume_obj(struct monst *mtmp, struct obj *otmp) /* non-pet: Heal up to the object's weight in hp */ if (!ispet && mtmp->mhp < mtmp->mhpmax) { - mtmp->mhp += objects[otmp->otyp].oc_weight; - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, objects[otmp->otyp].oc_weight, 0); } if (Has_contents(otmp)) meatbox(mtmp, otmp); @@ -1398,7 +1396,7 @@ m_consume_obj(struct monst *mtmp, struct obj *otmp) } } if (heal) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, mtmp->mhpmax, 0); if ((eyes || heal) && !mtmp->mcansee) mcureblindness(mtmp, canseemon(mtmp)); if (ispet && deadmimic) @@ -4462,6 +4460,44 @@ get_iter_mons_xy( } +/* Heal the given monster by amt hitpoints, unless it is somehow prevented + from healing. "overheal" is the maximum amount by which the max HP will + increase to allow for the healing (the resulting HP caps at max HP + + overheal, and the max HP stays the some unless it needs to increase to + accommodate the new HP). Overhealing the player is not currently + implemented by this method. + + This function should only be used for situations which are conceptually + heals, rather than other situations where a monster's HP is set, so that + "prevent healing" effects work correctly. In particular, it should not + be used for cases where a monster's HP is restored upon revival, or when + a monster is created. It also shouldn't be used for lifesaving, which + overrides "cannot heal" effects. + + amt and overheal must not be negative (0 is allowed, and a very common + amount for overheal). Returns the number of hitpoints healed. */ +int +healmon(struct monst *mtmp, int amt, int overheal) +{ + if (mtmp == &gy.youmonst) { + int oldhp = Upolyd ? u.mh : u.uhp; + healup(amt, 0, 0, 0); + return (Upolyd ? u.mh : u.uhp) - oldhp; + } else { + int oldhp = mtmp->mhp; + if (mtmp->mhp + amt > mtmp->mhpmax + overheal) { + mtmp->mhpmax += overheal; + mtmp->mhp = mtmp->mhpmax; + } else { + mtmp->mhp += amt; + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhpmax = mtmp->mhp; + } + return mtmp->mhp - oldhp; + } +} + + /* 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 or via #wizintrinsic */ @@ -5535,10 +5571,7 @@ golemeffects(struct monst *mon, int damtype, int dam) mon_adjust_speed(mon, -1, (struct obj *) 0); } if (heal) { - if (mon->mhp < mon->mhpmax) { - mon->mhp += heal; - if (mon->mhp > mon->mhpmax) - mon->mhp = mon->mhpmax; + if (healmon(mon, heal, 0)) { if (cansee(mon->mx, mon->my)) pline_mon(mon, "%s seems healthier.", Monnam(mon)); } diff --git a/src/muse.c b/src/muse.c index 264d6b9ab..54c92f4d0 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1139,9 +1139,7 @@ use_defensive(struct monst *mtmp) case MUSE_POT_HEALING: mquaffmsg(mtmp, otmp); i = d(6 + 2 * bcsign(otmp), 4); - mtmp->mhp += i; - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = ++mtmp->mhpmax; + healmon(mtmp, i, 1); if (!otmp->cursed && !mtmp->mcansee) mcureblindness(mtmp, vismon); if (vismon) @@ -1153,9 +1151,7 @@ use_defensive(struct monst *mtmp) case MUSE_POT_EXTRA_HEALING: mquaffmsg(mtmp, otmp); i = d(6 + 2 * bcsign(otmp), 8); - mtmp->mhp += i; - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 5 : 2)); + healmon(mtmp, i, otmp->blessed ? 5 : 2); if (!mtmp->mcansee) mcureblindness(mtmp, vismon); if (vismon) @@ -1168,7 +1164,7 @@ use_defensive(struct monst *mtmp) mquaffmsg(mtmp, otmp); if (otmp->otyp == POT_SICKNESS) unbless(otmp); /* Pestilence */ - mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 8 : 4)); + healmon(mtmp, mtmp->mhpmax, otmp->blessed ? 8 : 4); if (!mtmp->mcansee && otmp->otyp != POT_SICKNESS) mcureblindness(mtmp, vismon); if (vismon) diff --git a/src/potion.c b/src/potion.c index 9d5a89fc8..7bec2fcea 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1740,7 +1740,7 @@ potionhit(struct monst *mon, struct obj *obj, int how) do_healing: angermon = FALSE; if (mon->mhp < mon->mhpmax) { - mon->mhp = mon->mhpmax; + healmon(mon, mon->mhpmax, 0); if (canseemon(mon)) pline("%s looks sound and hale again.", Monnam(mon)); } @@ -1827,9 +1827,7 @@ potionhit(struct monst *mon, struct obj *obj, int how) angermon = FALSE; if (canseemon(mon)) pline("%s looks healthier.", Monnam(mon)); - mon->mhp += d(2, 6); - if (mon->mhp > mon->mhpmax) - mon->mhp = mon->mhpmax; + healmon(mon, d(2, 6), 0); if (is_were(mon->data) && is_human(mon->data) && !Protection_from_shape_changers) new_were(mon); /* transform into beast */ diff --git a/src/uhitm.c b/src/uhitm.c index 3b233c152..b3b470c00 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -5931,9 +5931,7 @@ passive( You("are suddenly very cold!"); mdamageu(mon, tmp); /* monster gets stronger with your heat! */ - mon->mhp += (tmp + rn2(2)) / 2; - if (mon->mhpmax < mon->mhp) - mon->mhpmax = mon->mhp; + healmon(mon, (tmp + rn2(2)) / 2, (tmp + 1) / 2); /* at a certain point, the monster will reproduce! */ if (mon->mhpmax > (((int) mon->m_lev) + 1) * 8) (void) split_mon(mon, &gy.youmonst); diff --git a/src/were.c b/src/were.c index 0832555f1..9baa4155e 100644 --- a/src/were.c +++ b/src/were.c @@ -123,7 +123,7 @@ new_were(struct monst *mon) mon->mcanmove = 1; } /* regenerate by 1/4 of the lost hit points */ - mon->mhp += (mon->mhpmax - mon->mhp) / 4; + healmon(mon, (mon->mhpmax - mon->mhp) / 4, 0); newsym(mon->mx, mon->my); mon_break_armor(mon, FALSE); possibly_unwield(mon, FALSE); diff --git a/src/wizard.c b/src/wizard.c index ce064615c..3e28a6360 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -396,7 +396,7 @@ tactics(struct monst *mtmp) /* if you're not around, cast healing spells */ if (distu(mx, my) > (BOLT_LIM * BOLT_LIM)) if (mtmp->mhp <= mtmp->mhpmax - 8) { - mtmp->mhp += rnd(8); + healmon(mtmp, rnd(8), 0); return 1; } FALLTHROUGH; diff --git a/src/zap.c b/src/zap.c index 7fc54146e..f83604778 100644 --- a/src/zap.c +++ b/src/zap.c @@ -434,9 +434,7 @@ bhitm(struct monst *mtmp, struct obj *otmp) int delta = mtmp->mhpmax - mtmp->mhp; wake = FALSE; /* wakeup() makes the target angry */ - mtmp->mhp += healamt; - if (mtmp->mhp > mtmp->mhpmax) - mtmp->mhp = mtmp->mhpmax; + healmon(mtmp, healamt, 0); /* plain healing must be blessed to cure blindness; extra healing only needs to not be cursed, so spell always cures [potions quaffed by monsters behave slightly differently; @@ -4250,10 +4248,9 @@ zhitm( case ZT_DEATH: /* death/disintegration */ if (abs(type) != ZT_BREATH(ZT_DEATH)) { /* death */ if (mon->data == &mons[PM_DEATH]) { - mon->mhpmax += mon->mhpmax / 2; + healmon(mon, mon->mhpmax * 3 / 2, mon->mhpmax / 2); if (mon->mhpmax >= MAGIC_COOKIE) mon->mhpmax = MAGIC_COOKIE - 1; - mon->mhp = mon->mhpmax; tmp = 0; break; }