Don't attempt to cache encumber_msg result

There was only one point in the code at which this caching was
being done, and it was incorrect: it's possible for the result of
near_capacity to change during a monster turn because monster
actions can change either inventory weight or carry capacity.

The bug was particularly relevant in cases where a character
polymorphed into a slow weak monster gets attacked by a monster
that moves at normal speed: due to the polyform being slow, the
normal-speed monster gets in a lot of attacks and causes a
rehumanization, but due to the polyform being weak, it was
burdened at the start of the monster turn, and so when that
penalty is (due to the bug) applied to the next turn it can
mean that the character misses the next turn too, and may end up
dying as a result.
This commit is contained in:
Alex Smith
2025-11-24 02:07:23 +00:00
parent ae516ddc67
commit fce66245ca
20 changed files with 50 additions and 46 deletions

View File

@@ -1535,6 +1535,8 @@ some messages by amorous demons and mail daemon were delivered as verbal ones
travel couldn't find the vibrating square if it was covered by an object or
a monster; it isn't really a trap so treat it as special terrain
travel would stop one step in front of known vibrating square like other traps
fix bug which delayed burden changes due to monster actions (e.g. reverting to
natural form due to damage, changing carry capacity) for one turn
Fixes to 3.7.0-x General Problems Exposed Via git Repository

View File

@@ -2405,7 +2405,7 @@ extern int query_category(const char *, struct obj *, int, menu_item **, int) NO
extern int query_objlist(const char *, struct obj **, int, menu_item **, int,
boolean(*)(struct obj *)) NONNULLARG24;
extern struct obj *pick_obj(struct obj *) NONNULLARG1;
extern int encumber_msg(void);
extern void encumber_msg(void);
extern int container_at(coordxy, coordxy, boolean);
extern int doloot(void);
extern void observe_quantum_cat(struct obj *, boolean, boolean) NONNULLARG1;

View File

@@ -90,7 +90,7 @@ moveloop_preamble(boolean resuming)
fix_shop_damage();
}
(void) encumber_msg(); /* in case they auto-picked up something */
encumber_msg(); /* in case they auto-picked up something */
if (gd.defer_see_monsters) {
gd.defer_see_monsters = FALSE;
see_monsters();
@@ -197,7 +197,7 @@ moveloop_core(void)
u.umovement -= NORMAL_SPEED;
do { /* hero can't move this turn loop */
mvl_wtcap = encumber_msg();
encumber_msg();
svc.context.mon_moving = TRUE;
do {
@@ -207,6 +207,10 @@ moveloop_core(void)
} while (monscanmove);
svc.context.mon_moving = FALSE;
/* this needs to be after the monster movement loop in
case monster actions affected burden, e.g. rehumanize */
mvl_wtcap = near_capacity();
if (!monscanmove && u.umovement < NORMAL_SPEED) {
/* both hero and monsters are out of steam this round */
struct monst *mtmp;
@@ -392,7 +396,7 @@ moveloop_core(void)
inventory may have changed in, e.g., nh_timeout(); we do
need two checks here so that the player gets feedback
immediately if their own action encumbered them */
(void) encumber_msg();
encumber_msg();
#ifdef STATUS_HILITES
if (iflags.hilite_delta)

View File

@@ -191,7 +191,7 @@ adjattrib(
if (msgflg <= 0)
You_feel("%s%s!", (incr > 1 || incr < -1) ? "very " : "", attrstr);
if (program_state.in_moveloop && (ndx == A_STR || ndx == A_CON))
(void) encumber_msg();
encumber_msg();
return TRUE;
}
@@ -401,7 +401,7 @@ poisoned(
/* "Poisoned by a poisoned ___" is redundant */
done(strstri(pkiller, "poison") ? DIED : POISONING);
}
(void) encumber_msg();
encumber_msg();
}
void
@@ -477,7 +477,7 @@ restore_attrib(void)
}
}
if (disp.botl)
(void) encumber_msg();
encumber_msg();
}
#define AVAL 50 /* tune value for exercise gains */
@@ -511,7 +511,7 @@ exercise(int i, boolean inc_or_dec)
(inc_or_dec) ? "inc" : "dec", AEXE(i));
}
if (svm.moves > 0 && (i == A_STR || i == A_CON))
(void) encumber_msg();
encumber_msg();
}
staticfn void
@@ -753,7 +753,7 @@ redist_attr(void)
if (ABASE(i) < ATTRMIN(i))
ABASE(i) = ATTRMIN(i);
}
/* (void) encumber_msg(); -- caller needs to do this */
/* encumber_msg(); -- caller needs to do this */
}
/* apply minor variation to attributes */

View File

@@ -34,7 +34,7 @@ ballrelease(boolean showmsg)
/* [this used to test 'if (uwep != uball)' but that always passes
after the setuwep() above] */
freeinv(uball); /* remove from inventory but don't place on floor */
(void) encumber_msg();
encumber_msg();
}
}

View File

@@ -839,7 +839,7 @@ dropz(struct obj *obj, boolean with_impact)
map_object(obj, 0);
newsym(u.ux, u.uy); /* remap location under self */
}
(void) encumber_msg();
encumber_msg();
}
/* when swallowed, move dropped object from OBJ_FREE to u.ustuck's inventory;
@@ -2431,7 +2431,7 @@ set_wounded_legs(long side, int timex)
direct assignment instead of bitwise-OR so getting wounded in
one leg mysteriously healed the other */
EWounded_legs |= side;
(void) encumber_msg();
encumber_msg();
}
void
@@ -2470,7 +2470,7 @@ heal_legs(
more when steed becomes healthy, then possible floor
feedback, then able to carry less when back on foot]. */
if (how == 0)
(void) encumber_msg();
encumber_msg();
}
}

View File

@@ -671,7 +671,7 @@ Gloves_off(void)
}
setworn((struct obj *) 0, W_ARMG);
svc.context.takeoff.cancelled_don = FALSE;
(void) encumber_msg(); /* immediate feedback for GoP */
encumber_msg(); /* immediate feedback for GoP */
/* usually can't remove gloves when they're slippery but it can
be done by having them fall off (polymorph), stolen, or

View File

@@ -268,7 +268,7 @@ throw_obj(struct obj *obj, int shotlimit)
}
freeinv(otmp);
throwit(otmp, wep_mask, twoweap, oldslot);
(void) encumber_msg();
encumber_msg();
}
gm.m_shot.n = gm.m_shot.i = 0;
gm.m_shot.o = STRANGE_OBJECT;
@@ -1699,7 +1699,7 @@ throwit(
if (!impaired && rn2(100)) {
pline("%s to your hand!", Tobjnam(obj, "return"));
obj = addinv_before(obj, oldslot);
(void) encumber_msg();
encumber_msg();
/* addinv autoquivers an aklys if quiver is empty;
if obj is quivered, remove it before wielding */
if (obj->owornmask & W_QUIVER)
@@ -1886,7 +1886,7 @@ return_throw_to_inv(
set_twoweap(TRUE); /* u.twoweap = TRUE */
}
(void) encumber_msg();
encumber_msg();
return obj;
}
@@ -2124,7 +2124,7 @@ thitmonst(
sho_obj_return_to_u(obj);
obj = addinv(obj); /* back into your inventory */
nhUse(obj);
(void) encumber_msg();
encumber_msg();
}
return 1; /* caller doesn't need to place it */
}

View File

@@ -130,7 +130,7 @@ init_uhunger(void)
u.uhs = NOT_HUNGRY;
if (ATEMP(A_STR) < 0) {
ATEMP(A_STR) = 0;
(void) encumber_msg();
encumber_msg();
}
}

View File

@@ -1269,7 +1269,7 @@ hold_another_object(
prinv(hold_msg, obj, oquan);
/* obj made it into inventory and is staying there */
update_inventory();
(void) encumber_msg();
encumber_msg();
}
}
return obj;

View File

@@ -1657,7 +1657,7 @@ shrink_glob(
}
if (updinv) {
update_inventory();
(void) encumber_msg();
encumber_msg();
}
}
@@ -2892,7 +2892,7 @@ hornoplenty(
/* item still in magic horn was weightless; when it's now in
a carried container, hero's encumbrance could change */
if (carried(targetbox)) {
(void) encumber_msg();
encumber_msg();
update_inventory(); /* for contents count or wizweight */
}
} else {

View File

@@ -1972,10 +1972,9 @@ pickup_prinv(
}
/*
* prints a message if encumbrance changed since the last check and
* returns the new encumbrance value (from near_capacity()).
* prints a message if encumbrance changed since the last check
*/
int
void
encumber_msg(void)
{
int newcap = near_capacity();
@@ -2018,7 +2017,6 @@ encumber_msg(void)
}
go.oldcap = newcap;
return newcap;
}
/* Is there a container at x,y. Optional: return count of containers at x,y */
@@ -3821,7 +3819,7 @@ tipcontainer(struct obj *box) /* or bag */
if (targetbox)
targetbox->owt = weight(targetbox);
if (srcheld || dstheld)
(void) encumber_msg();
encumber_msg();
}
if (srcheld || dstheld)

View File

@@ -425,7 +425,7 @@ newman(void)
done(DIED);
/* must have been life-saved to get here */
newuhs(FALSE);
(void) encumber_msg(); /* used to be done by redist_attr() */
encumber_msg(); /* used to be done by redist_attr() */
return; /* lifesaved */
}
}
@@ -454,7 +454,7 @@ newman(void)
disp.botl = TRUE;
see_monsters();
(void) encumber_msg();
encumber_msg();
retouch_equipment(2);
if (!uarmg)
@@ -1012,7 +1012,7 @@ polymon(int mntmp)
disp.botl = TRUE;
gv.vision_full_recalc = 1;
see_monsters();
(void) encumber_msg();
encumber_msg();
retouch_equipment(2);
/* this might trigger a recursive call to polymon() [stone golem
@@ -1398,7 +1398,7 @@ rehumanize(void)
disp.botl = TRUE;
gv.vision_full_recalc = 1;
(void) encumber_msg();
encumber_msg();
if (was_flying && !Flying && u.usteed)
You("and %s return gently to the %s.",
mon_nam(u.usteed), surface(u.ux, u.uy));

View File

@@ -550,7 +550,7 @@ fix_worst_trouble(int trouble)
disp.botl = TRUE;
}
}
(void) encumber_msg();
encumber_msg();
break;
case TROUBLE_BLIND: { /* handles deafness as well as blindness */
char msgbuf[BUFSZ];
@@ -1263,7 +1263,7 @@ pleased(aligntyp g_align)
if (ABASE(A_STR) < AMAX(A_STR)) {
ABASE(A_STR) = AMAX(A_STR);
disp.botl = TRUE; /* before potential message */
(void) encumber_msg();
encumber_msg();
}
if (u.uhunger < 900)
init_uhunger();

View File

@@ -601,7 +601,7 @@ steal(struct monst *mtmp, char *objnambuf)
&& mtmp->data->mlet == S_NYMPH)
++named;
urgent_pline("%s stole %s.", named ? "She" : Monnambuf, doname(otmp));
(void) encumber_msg();
encumber_msg();
could_petrify = (otmp->otyp == CORPSE
&& touch_petrifies(&mons[otmp->corpsenm]));
otmp->how_lost = LOST_STOLEN;
@@ -762,7 +762,7 @@ stealamulet(struct monst *mtmp)
pline("%s steals %s!", Some_Monnam(mtmp), buf);
if (can_teleport(mtmp->data) && !tele_restrict(mtmp))
(void) rloc(mtmp, RLOC_MSG);
(void) encumber_msg();
encumber_msg();
}
}
@@ -799,7 +799,7 @@ maybe_absorb_item(
otense(obj, "are"), hand_s);
}
freeinv(obj);
(void) encumber_msg();
encumber_msg();
} else {
/* not carried; presumably thrown or kicked */
if (canspotmon(mon))

View File

@@ -811,7 +811,7 @@ dismount_steed(
(void) float_down(0L, W_SADDLE);
gi.in_steed_dismounting = FALSE;
disp.botl = TRUE;
(void) encumber_msg();
encumber_msg();
gv.vision_full_recalc = 1;
} else
disp.botl = TRUE;

View File

@@ -3907,7 +3907,7 @@ float_up(void)
float_vs_flight(); /* set BFlying, also BLevitation if still trapped */
/* levitation gives maximum carrying capacity, so encumbrance
state might be reduced */
(void) encumber_msg();
encumber_msg();
return;
}
@@ -3954,7 +3954,7 @@ float_down(
: (u.utraptype == TT_BURIEDBALL) ? "chain"
: (u.utraptype == TT_LAVA) ? "lava"
: "ground"); /* TT_INFLOOR */
(void) encumber_msg(); /* carrying capacity might have changed */
encumber_msg(); /* carrying capacity might have changed */
return 0;
}
disp.botl = TRUE;
@@ -3965,14 +3965,14 @@ float_down(
* unless hero is stuck in floor */
if (Flying) {
You("have stopped levitating and are now flying.");
(void) encumber_msg(); /* carrying capacity might have changed */
encumber_msg(); /* carrying capacity might have changed */
return 1;
}
}
if (u.uswallow) {
You("float down, but you are still %s.",
digests(u.ustuck->data) ? "swallowed" : "engulfed");
(void) encumber_msg();
encumber_msg();
return 1;
}
@@ -4056,7 +4056,7 @@ float_down(
/* levitation gives maximum carrying capacity, so having it end
potentially triggers greater encumbrance; do this after
'come down' messages, before trap activation or autopickup */
(void) encumber_msg();
encumber_msg();
/* can't rely on u.uz0 for detecting trap door-induced level change;
it gets changed to reflect the new level before we can check it */

View File

@@ -958,7 +958,7 @@ chwepon(struct obj *otmp, int amount)
if (otyp != STRANGE_OBJECT)
makeknown(otyp);
if (multiple)
(void) encumber_msg();
encumber_msg();
return 1;
} else if (uwep->otyp == CRYSKNIFE && amount < 0) {
multiple = (uwep->quan > 1L);
@@ -975,7 +975,7 @@ chwepon(struct obj *otmp, int amount)
if (otyp != STRANGE_OBJECT && otmp->bknown)
makeknown(otyp);
if (multiple)
(void) encumber_msg();
encumber_msg();
return 1;
}

View File

@@ -37,7 +37,7 @@ wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */
flags.verbose = FALSE;
makewish();
flags.verbose = save_verbose;
(void) encumber_msg();
encumber_msg();
} else
pline(unavailcmd, ecname_from_fn(wiz_wish));
return ECMD_OK;

View File

@@ -1214,7 +1214,7 @@ unturn_dead(struct monst *mon)
}
}
if (is_u && res)
(void) encumber_msg();
encumber_msg();
return res;
}