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
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 */
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 */
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] */
}
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) {
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;
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));