]> granicus.if.org Git - nethack/commitdiff
max HP manipulation
authorPatR <rankin@nethack.org>
Mon, 21 Mar 2022 19:32:07 +0000 (12:32 -0700)
committerPatR <rankin@nethack.org>
Mon, 21 Mar 2022 19:32:07 +0000 (12:32 -0700)
Life-saving has been setting u.uhpmax to max(2 * u.ulevel, 10)
and if it took place during level drain that could make u.uhpmax
increase instead of decrease, confusing healing which gets applied
to a monster who has drained the hero with Stormbringer or the
Staff of Aesculapius.  Change the setting to be max(u.ulevel, 10)
(removing the times two part) and also have level drain force it
to be set back to previous value if/when it gets increased.

Max HP loss due to strength trying to drop below 3 or to fire trap
or to being hit by Death now uses a mininum max HP of u.ulevel
rather than 1.  They don't have the alternate minimum of 10; I'm
uneasy that there are still two different minimum values.

I changed adjattrib() to set the flag to request a status update
before it gave its optional message rather than after so that the
new characteristic value would be visible during the message.  That
resulted in not updating status when eating royal jelly changed HP
or max HP after boosting strength.  But the same missing update
would have occurred--or rather, failed to occur--without the change
in sequencing if the strength boost causes a change in encumbrance.

doc/fixes3-7-0.txt
include/extern.h
src/attrib.c
src/eat.c
src/end.c
src/exper.c
src/mhitu.c
src/trap.c

index 7f132707f71052e47d0f2d67f9d566ca8f94fe0b..74314087a6f0a6c4c71e21bf08cd433abbafbf88 100644 (file)
@@ -838,6 +838,10 @@ for #knownclass with menustyle=Tradtional, allow player to ask for `a even if
        no artifacts have been discovered yet, same as `<any-object-class>;
        likewise for `u to ask to see unique items
 reduce eucalyptus leaf nutrition to 1
+life-saving might increase max HP; if level drain triggers that, don't let max
+       HP go up because it confuses healing for monster wielding Stormbringer
+HP recovery and/or max HP boost from eating royal jelly didn't perform a
+       status update to show the change
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index 0e1d0d9b9e62a8a5d14d47ab78e1445c25694f47..6ec6fc16aebdae5c762bee0b129399a59d1827b5 100644 (file)
@@ -127,6 +127,8 @@ extern void init_attr(int);
 extern void redist_attr(void);
 extern void adjabil(int, int);
 extern int newhp(void);
+extern int minuhpmax(int);
+extern void setuhpmax(int);
 extern schar acurr(int);
 extern schar acurrstr(void);
 extern boolean extremeattr(int);
index b71ba46e0502f0ce5f5e5baedc25f6d13af41183..11004eb44d0034a50265df71dba0e77b0185a1ff 100644 (file)
@@ -111,8 +111,11 @@ static int innately(long *);
 
 /* adjust an attribute; return TRUE if change is made, FALSE otherwise */
 boolean
-adjattrib(int ndx, int incr, int msgflg) /* positive => no message, zero => message, and */
-{           /* negative => conditional (msg if change made) */
+adjattrib(
+    int ndx,    /* which characteristic */
+    int incr,   /* amount of change */
+    int msgflg) /* positive => no message, zero => message, and */
+{               /* negative => conditional (msg if change made) */
     int old_acurr, old_abase, old_amax, decr;
     boolean abonflg;
     const char *attrstr;
@@ -181,9 +184,9 @@ adjattrib(int ndx, int incr, int msgflg) /* positive => no message, zero => mess
         return FALSE;
     }
 
+    g.context.botl = TRUE;
     if (msgflg <= 0)
         You_feel("%s%s!", (incr > 1 || incr < -1) ? "very " : "", attrstr);
-    g.context.botl = TRUE;
     if (g.program_state.in_moveloop && (ndx == A_STR || ndx == A_CON))
         (void) encumber_msg();
     return TRUE;
@@ -210,6 +213,7 @@ gainstr(struct obj *otmp, int incr, boolean givemsg)
 void
 losestr(int num)
 {
+    int uhpmin = minuhpmax(1), olduhpmax = u.uhpmax;
     int ustr = ABASE(A_STR) - num;
 
     while (ustr < 3) {
@@ -220,8 +224,14 @@ losestr(int num)
             u.mhmax -= 6;
         } else {
             u.uhp -= 6;
-            u.uhpmax -= 6;
+            setuhpmax(u.uhpmax - 6);
         }
+        g.context.botl = TRUE;
+    }
+    if (u.uhpmax < uhpmin) {
+        setuhpmax(min(olduhpmax, uhpmin));
+        if (!Drain_resistance)
+            losexp(NULL); /* won't be fatal when no 'drainer' is supplied */
     }
     (void) adjattrib(A_STR, -num, 1);
 }
@@ -1038,6 +1048,30 @@ newhp(void)
     return hp;
 }
 
+/* minimum value for uhpmax is ulevel but for life-saving it is always at
+   least 10 if ulevel is less than that */
+int
+minuhpmax(int altmin)
+{
+    if (altmin < 1)
+        altmin = 1;
+    return max(u.ulevel, altmin);
+}
+
+/* update u.uhpmax and values of other things that depend upon it */
+void
+setuhpmax(int newmax)
+{
+    if (newmax != u.uhpmax) {
+        u.uhpmax = newmax;
+        if (u.uhpmax > u.uhppeak)
+            u.uhppeak = u.uhpmax;
+        g.context.botl = TRUE;
+    }
+    if (u.uhp > u.uhpmax)
+        u.uhp = u.uhpmax, g.context.botl = TRUE;
+}
+
 schar
 acurr(int x)
 {
index 05370d67e28888e3b930f17780636caa577ab5ee..ea77b7bbbffb7aa481b75b4d99a084cb26e637e0 100644 (file)
--- a/src/eat.c
+++ b/src/eat.c
@@ -2279,7 +2279,7 @@ fpostfx(struct obj *otmp)
         /* This stuff seems to be VERY healthy! */
         gainstr(otmp, 1, TRUE);
         if (Upolyd) {
-            u.mh += otmp->cursed ? -rnd(20) : rnd(20);
+            u.mh += otmp->cursed ? -rnd(20) : rnd(20), g.context.botl = TRUE;
             if (u.mh > u.mhmax) {
                 if (!rn2(17))
                     u.mhmax++;
@@ -2288,13 +2288,10 @@ fpostfx(struct obj *otmp)
                 rehumanize();
             }
         } else {
-            u.uhp += otmp->cursed ? -rnd(20) : rnd(20);
+            u.uhp += otmp->cursed ? -rnd(20) : rnd(20), g.context.botl = TRUE;
             if (u.uhp > u.uhpmax) {
-                if (!rn2(17)) {
-                    u.uhpmax++;
-                    if (u.uhpmax > u.uhppeak)
-                        u.uhppeak = u.uhpmax;
-                }
+                if (!rn2(17))
+                    setuhpmax(u.uhpmax + 1);
                 u.uhp = u.uhpmax;
             } else if (u.uhp <= 0) {
                 g.killer.format = KILLED_BY_AN;
index 0fcd8798d6d4de45bd9427d6acb50e6b971cbe47..6519a8729911cd4f5dc67b343a85d88ac1d716f6 100644 (file)
--- a/src/end.c
+++ b/src/end.c
@@ -908,16 +908,17 @@ savelife(int how)
        reducing ulevel below 1, but include this for bulletproofing */
     if (u.ulevel < 1)
         u.ulevel = 1;
-    uhpmin = max(2 * u.ulevel, 10);
+    uhpmin = minuhpmax(10);
     if (u.uhpmax < uhpmin)
-        u.uhpmax = uhpmin;
+        setuhpmax(uhpmin);
     u.uhp = u.uhpmax;
     if (Upolyd) /* Unchanging, or death which bypasses losing hit points */
         u.mh = u.mhmax;
     if (u.uhunger < 500 || how == CHOKING) {
         init_uhunger();
     }
-    /* cure impending doom of sickness hero won't have time to fix */
+    /* cure impending doom of sickness hero won't have time to fix
+       [shouldn't this also be applied to other fatal timeouts?] */
     if ((Sick & TIMEOUT) == 1L) {
         make_sick(0L, (char *) 0, FALSE, SICK_ALL);
     }
@@ -929,7 +930,7 @@ savelife(int how)
         g.multi = -1;
     if (u.utrap && u.utraptype == TT_LAVA)
         reset_utrap(FALSE);
-    g.context.botl = 1;
+    g.context.botl = TRUE;
     u.ugrave_arise = NON_PM;
     HUnchanging = 0L;
     curs_on_u();
index ce47763e125042fc80c8ad53dce4635cb36cf6f3..868e90c76dfe9702fc8309d217881fb5667353fa 100644 (file)
@@ -204,9 +204,10 @@ more_experienced(register int exper, register int rexp)
 
 /* e.g., hit by drain life attack */
 void
-losexp(const char *drainer) /* cause of death, if drain should be fatal */
+losexp(
+    const char *drainer) /* cause of death, if drain should be fatal */
 {
-    register int num;
+    int num, uhpmin, olduhpmax;
 
     /* override life-drain resistance when handling an explicit
        wizard mode request to reduce level; never fatal though */
@@ -237,10 +238,21 @@ losexp(const char *drainer) /* cause of death, if drain should be fatal */
         u.uexp = 0;
         livelog_printf(LL_MINORAC, "lost all experience");
     }
+
+    olduhpmax = u.uhpmax;
+    uhpmin = minuhpmax(10); /* same minimum as is used by life-saving */
     num = (int) u.uhpinc[u.ulevel];
     u.uhpmax -= num;
-    if (u.uhpmax < 1)
-        u.uhpmax = 1;
+    if (u.uhpmax < uhpmin)
+        setuhpmax(uhpmin);
+    /* uhpmax might try to go up if it has previously been reduced by
+       strength loss or by a fire trap or by an attack by Death which
+       all use a different minimum than life-saving or experience loss;
+       we don't allow it to go up because that contradicts assumptions
+       elsewhere (such as healing wielder who drains with Strombringer) */
+    if (u.uhpmax > olduhpmax)
+        setuhpmax(olduhpmax);
+
     u.uhp -= num;
     if (u.uhp < 1)
         u.uhp = 1;
@@ -302,9 +314,7 @@ pluslvl(
         u.mh += hpinc;
     }
     hpinc = newhp();
-    u.uhpmax += hpinc;
-    if (u.uhpmax > u.uhppeak)
-        u.uhppeak = u.uhpmax;
+    setuhpmax(u.uhpmax + hpinc);
     u.uhp += hpinc;
 
     /* increase spell power/energy points */
index 23c7243a5b0f62dfe765a910a46cd05e8d00135c..79c5435809fa4564f5cde40c3d2651ad3acbcb7d 100644 (file)
@@ -1082,7 +1082,7 @@ hitmu(register struct monst *mtmp, register struct attack *mattk)
                 lowerlimit = min((int) g.youmonst.data->mlevel, u.ulevel);
             } else {
                 hpmax_p = &u.uhpmax;
-                lowerlimit = u.ulevel;
+                lowerlimit = minuhpmax(1);
             }
             if (*hpmax_p - mhm.permdmg > lowerlimit)
                 *hpmax_p -= mhm.permdmg;
index b6bb0df39a88f5825e2cb3f34e9c9068e6c85629..406890a9a7ac968bde7f090615106dfc3b8d1e77 100644 (file)
@@ -3658,11 +3658,23 @@ dofiretrap(
         if (alt > num)
             num = alt;
         if (u.mhmax > mons[u.umonnum].mlevel)
-            u.mhmax -= rn2(min(u.mhmax, num + 1)), g.context.botl = 1;
+            u.mhmax -= rn2(min(u.mhmax, num + 1)), g.context.botl = TRUE;
+        if (u.mh > u.mhmax)
+            u.mh = u.mhmax, g.context.botl = TRUE;
     } else {
+        int uhpmin = minuhpmax(1), olduhpmax = u.uhpmax;
+
         num = d(2, 4);
-        if (u.uhpmax > u.ulevel)
-            u.uhpmax -= rn2(min(u.uhpmax, num + 1)), g.context.botl = 1;
+        if (u.uhpmax > uhpmin) {
+            u.uhpmax -= rn2(min(u.uhpmax, num + 1)), g.context.botl = TRUE;
+        } /* note: no 'else' here */
+        if (u.uhpmax < uhpmin) {
+            setuhpmax(min(olduhpmax, uhpmin)); /* sets g.context.botl */
+            if (!Drain_resistance)
+                losexp(NULL); /* never fatal when 'drainer' is Null */
+        }
+        if (u.uhp > u.uhpmax)
+            u.uhp = u.uhpmax, g.context.botl = TRUE;
     }
     if (!num)
         You("are uninjured.");