diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 7f132707f..74314087a 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -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 `; 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 diff --git a/include/extern.h b/include/extern.h index 0e1d0d9b9..6ec6fc16a 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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); diff --git a/src/attrib.c b/src/attrib.c index b71ba46e0..11004eb44 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -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) { diff --git a/src/eat.c b/src/eat.c index 05370d67e..ea77b7bbb 100644 --- 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; diff --git a/src/end.c b/src/end.c index 0fcd8798d..6519a8729 100644 --- 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(); diff --git a/src/exper.c b/src/exper.c index ce47763e1..868e90c76 100644 --- a/src/exper.c +++ b/src/exper.c @@ -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 */ diff --git a/src/mhitu.c b/src/mhitu.c index 23c7243a5..79c543580 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -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; diff --git a/src/trap.c b/src/trap.c index b6bb0df39..406890a9a 100644 --- a/src/trap.c +++ b/src/trap.c @@ -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.");