From 024e9e122576db664e37df0937cfb4c06c436e0c Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 8 Oct 2017 18:12:08 -0700 Subject: [PATCH] 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. --- doc/fixes36.1 | 1 + src/apply.c | 5 ++--- src/attrib.c | 19 +++++++++++++------ src/eat.c | 24 ++++++++++++++++++++---- src/potion.c | 4 ++-- src/pray.c | 2 +- 6 files changed, 39 insertions(+), 16 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index e6f3e7c78..19c92f722 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -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 " 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 diff --git a/src/apply.c b/src/apply.c index e5df1b904..c477a4fb8 100644 --- a/src/apply.c +++ b/src/apply.c @@ -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 */ diff --git a/src/attrib.c b/src/attrib.c index e0021632f..a3812d54b 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -366,20 +366,27 @@ set_moreluck() void restore_attrib() { - int i; + int i, equilibrium;; - for (i = 0; i < A_MAX; i++) { /* all temporary losses/gains */ + /* + * 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. + */ - if (ATEMP(i) && ATIME(i)) { + for (i = 0; i < A_MAX; i++) { /* all temporary losses/gains */ + 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 */ diff --git a/src/eat.c b/src/eat.c index 9bd6346b0..6bbc1836b 100644 --- a/src/eat.c +++ b/src/eat.c @@ -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) { diff --git a/src/potion.c b/src/potion.c index f167bf710..4925b27b0 100644 --- a/src/potion.c +++ b/src/potion.c @@ -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; diff --git a/src/pray.c b/src/pray.c index f9fe72083..51dfbb5bb 100644 --- a/src/pray.c +++ b/src/pray.c @@ -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)); -- 2.40.0