From fce66245cab5a4c5f91faac975b17ddb1f4df267 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Mon, 24 Nov 2025 02:07:23 +0000 Subject: [PATCH] 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. --- doc/fixes3-7-0.txt | 2 ++ include/extern.h | 2 +- src/allmain.c | 10 +++++++--- src/attrib.c | 10 +++++----- src/ball.c | 2 +- src/do.c | 6 +++--- src/do_wear.c | 2 +- src/dothrow.c | 8 ++++---- src/eat.c | 2 +- src/invent.c | 2 +- src/mkobj.c | 4 ++-- src/pickup.c | 8 +++----- src/polyself.c | 8 ++++---- src/pray.c | 4 ++-- src/steal.c | 6 +++--- src/steed.c | 2 +- src/trap.c | 10 +++++----- src/wield.c | 4 ++-- src/wizcmds.c | 2 +- src/zap.c | 2 +- 20 files changed, 50 insertions(+), 46 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 185f271ee..7ed444fe6 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -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 diff --git a/include/extern.h b/include/extern.h index 6fe005940..a5a7a76e7 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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; diff --git a/src/allmain.c b/src/allmain.c index f6d1ee503..cde778bfb 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -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) diff --git a/src/attrib.c b/src/attrib.c index fc6a7aebf..405cbf7da 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -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 */ diff --git a/src/ball.c b/src/ball.c index ebacb7e83..069a71ce4 100644 --- a/src/ball.c +++ b/src/ball.c @@ -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(); } } diff --git a/src/do.c b/src/do.c index ae26e7158..875d0c62f 100644 --- a/src/do.c +++ b/src/do.c @@ -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(); } } diff --git a/src/do_wear.c b/src/do_wear.c index 2cb86fa6d..2ee5b0785 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -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 diff --git a/src/dothrow.c b/src/dothrow.c index a7d4ffd68..e7bfd2789 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -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 */ } diff --git a/src/eat.c b/src/eat.c index b15f36ade..3074b9e10 100644 --- a/src/eat.c +++ b/src/eat.c @@ -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(); } } diff --git a/src/invent.c b/src/invent.c index bd8c689c9..869c97c3e 100644 --- a/src/invent.c +++ b/src/invent.c @@ -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; diff --git a/src/mkobj.c b/src/mkobj.c index a7687ca8a..77479c57b 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -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 { diff --git a/src/pickup.c b/src/pickup.c index b74119402..4a366f757 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -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) diff --git a/src/polyself.c b/src/polyself.c index 4ab183113..669f32da4 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -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)); diff --git a/src/pray.c b/src/pray.c index 5f00487d1..0564f3672 100644 --- a/src/pray.c +++ b/src/pray.c @@ -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(); diff --git a/src/steal.c b/src/steal.c index f90d66a55..a5f771291 100644 --- a/src/steal.c +++ b/src/steal.c @@ -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)) diff --git a/src/steed.c b/src/steed.c index 48d537b99..7b28794ae 100644 --- a/src/steed.c +++ b/src/steed.c @@ -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; diff --git a/src/trap.c b/src/trap.c index 46c0e1266..cfeec5ace 100644 --- a/src/trap.c +++ b/src/trap.c @@ -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 */ diff --git a/src/wield.c b/src/wield.c index 53b0aa94f..f24ae8e1e 100644 --- a/src/wield.c +++ b/src/wield.c @@ -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; } diff --git a/src/wizcmds.c b/src/wizcmds.c index 6dd756880..8c14b98f7 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -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; diff --git a/src/zap.c b/src/zap.c index 98b74b04a..16e18db34 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1214,7 +1214,7 @@ unturn_dead(struct monst *mon) } } if (is_u && res) - (void) encumber_msg(); + encumber_msg(); return res; }