fix #6144 - strength loss from severe hunger

It was possible to arbitrarily boost strength (up to its race-specific
limit) by wearing a ring of sustain ability, becoming weak from hunger
(but not actually losing strength due to Fixed_abil), removing the ring,
eating enough to stop being Weak, then repeat as desired.  I think you
could substitute polymorph for wearing ring, and rehumanize for removing
ring and get similar results, although that would be more tedious.

My first attempt to fix this was a lot more complicated.  This one puts
the temporary strength loss in ATEMP(A_STR) where it carries over from
normal form to polymophed form and back.  Fixed_abil doesn't prevent the
loss any more, nor its recovery.

One side-effect of the change is that the possibility of dying when
becoming weak from hunger (if Str gets down to 3, further attempts to
lower it take away HP instead of Str) no longer exists.  Using ATEMP()
instead of directly manipulating ABASE() means that current strength is
less but underlying base strength does not actually drop any more.
This commit is contained in:
PatR
2017-10-08 18:12:08 -07:00
parent 4659c55b5c
commit 024e9e1225
6 changed files with 39 additions and 16 deletions

View File

@@ -451,6 +451,7 @@ give feedback when released from a bear trap
depending upon how the dynamically inserted pattern-match phrase fit
#version output left out "pattern matching via <method>" if the basic NetHack
features entry was split across two lines
recovery of strength lost due to weakness from hunger was vulnerable to abuse
Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository

View File

@@ -1951,9 +1951,8 @@ struct obj *obj;
if (ABASE(idx) >= AMAX(idx))
continue;
val_limit = AMAX(idx);
/* don't recover strength lost from hunger */
if (idx == A_STR && u.uhs >= WEAK)
val_limit--;
/* this used to adjust 'val_limit' for A_STR when u.uhs was
WEAK or worse, but that's handled via ATEMP(A_STR) now */
if (Fixed_abil) {
/* potion/spell of restore ability override sustain ability
intrinsic but unicorn horn usage doesn't */

View File

@@ -366,20 +366,27 @@ set_moreluck()
void
restore_attrib()
{
int i;
int i, equilibrium;;
/*
* Note: this gets called on every turn but ATIME() is never set
* to non-zero anywhere, and ATEMP() is only used for strength loss
* from hunger, so it doesn't actually do anything.
*/
for (i = 0; i < A_MAX; i++) { /* all temporary losses/gains */
if (ATEMP(i) && ATIME(i)) {
equilibrium = (i == A_STR && u.uhs >= WEAK) ? -1 : 0;
if (ATEMP(i) != equilibrium && ATIME(i) != 0) {
if (!(--(ATIME(i)))) { /* countdown for change */
ATEMP(i) += ATEMP(i) > 0 ? -1 : 1;
ATEMP(i) += (ATEMP(i) > 0) ? -1 : 1;
context.botl = 1;
if (ATEMP(i)) /* reset timer */
ATIME(i) = 100 / ACURR(A_CON);
}
}
}
(void) encumber_msg();
if (context.botl)
(void) encumber_msg();
}
#define AVAL 50 /* tune value for exercise gains */

View File

@@ -112,8 +112,11 @@ register struct obj *obj;
void
init_uhunger()
{
context.botl = (u.uhs != NOT_HUNGRY || ATEMP(A_STR) < 0);
u.uhunger = 900;
u.uhs = NOT_HUNGRY;
if (ATEMP(A_STR) < 0)
ATEMP(A_STR) = 0;
}
/* tin types [SPINACH_TIN = -1, overrides corpsenm, nut==600] */
@@ -2961,10 +2964,23 @@ boolean incr;
}
if (newhs != u.uhs) {
if (newhs >= WEAK && u.uhs < WEAK)
losestr(1); /* this may kill you -- see below */
else if (newhs < WEAK && u.uhs >= WEAK)
losestr(-1);
if (newhs >= WEAK && u.uhs < WEAK) {
/* this used to be losestr(1) which had the potential to
be fatal (still handled below) by reducing HP if it
tried to take base strength below minimum of 3 */
ATEMP(A_STR) = -1; /* temporary loss overrides Fixed_abil */
/* defer context.botl status update until after hunger message */
} else if (newhs < WEAK && u.uhs >= WEAK) {
/* this used to be losestr(-1) which could be abused by
becoming weak while wearing ring of sustain ability,
removing ring, eating to 'restore' strength which boosted
strength by a point each time the cycle was performed;
substituting "while polymorphed" for sustain ability and
"rehumanize" for ring removal might have done that too */
ATEMP(A_STR) = 0; /* repair of loss also overrides Fixed_abil */
/* defer context.botl status update until after hunger message */
}
switch (newhs) {
case HUNGRY:
if (Hallucination) {

View File

@@ -565,8 +565,8 @@ register struct obj *otmp;
i = rn2(A_MAX); /* start at a random point */
for (ii = 0; ii < A_MAX; ii++) {
lim = AMAX(i);
if (i == A_STR && u.uhs >= 3)
--lim; /* WEAK */
/* this used to adjust 'lim' for A_STR when u.uhs was
WEAK or worse, but that's handled via ATEMP(A_STR) now */
if (ABASE(i) < lim) {
ABASE(i) = lim;
context.botl = 1;

View File

@@ -343,7 +343,7 @@ int trouble;
u.utrap = 0;
break;
case TROUBLE_STARVING:
losestr(-1);
/* temporarily lost strength recovery now handled by init_uhunger() */
/*FALLTHRU*/
case TROUBLE_HUNGRY:
Your("%s feels content.", body_part(STOMACH));