From 593a93d2545731629b74fd5ca64dad54cf88ba53 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 21 Jan 2024 01:17:18 -0800 Subject: [PATCH] obj->how_lost fix PR #1140 added checking the thrown, stolen, and dropped flags of an item when testing whether it would merge (at my suggestion...) with a stack in the target list (hero's invent). That interferred with picking it back up--whether via autopickup or explicit pickup--while inventory was full even when the item would otherwise be mergable. There was some trial and error involved when trying to figure where to put the fix but things seem to be working. This replaces a static analyzer workaround and could possibly bring its unwarranted complaint back. --- doc/fixes3-7-0.txt | 2 ++ src/invent.c | 38 ++++++++++++++++++-------------------- src/pickup.c | 16 ++++++++++++++-- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 6e561aea0..95eead967 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1833,6 +1833,8 @@ duration of temporary stoning resistance would be extended while eating a meal safely farlook at something that was on top of an engraving or grave would report the engraving text or headstone epitaph along with the 'something' +when carrying 52 items (excluding gold), hero couldn't pick up a thrown, + stolen, or dropped item which was split off from a still-carried stack Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/invent.c b/src/invent.c index d1da6bf9b..0b985fb06 100644 --- a/src/invent.c +++ b/src/invent.c @@ -15,7 +15,8 @@ static char *loot_xname(struct obj *); static int invletter_value(char); static int QSORTCALLBACK sortloot_cmp(const genericptr, const genericptr); static void reorder_invent(void); -static struct obj *addinv_core0(struct obj *, struct obj *, boolean) NONNULLARG1; +static struct obj *addinv_core0(struct obj *, struct obj *, + boolean) NONNULLARG1; static void noarmor(boolean); static void invdisp_nothing(const char *, const char *); static boolean worn_wield_only(struct obj *); @@ -782,8 +783,7 @@ struct obj * merge_choice(struct obj *objlist, struct obj *obj) { struct monst *shkp; - int save_nocharge; - struct obj *objlist2; + unsigned save_nocharge; if (obj->otyp == SCR_SCARE_MONSTER) /* punt on these */ return (struct obj *) 0; @@ -805,14 +805,14 @@ merge_choice(struct obj *objlist, struct obj *obj) else if (inhishop(shkp)) return (struct obj *) 0; } - objlist2 = objlist; /* allow objlist arg to be nonnull w/o a warning */ - while (objlist2) { - if (mergable(objlist2, obj)) + do { + /*assert(objlist != NULL);*/ + if (mergable(objlist, obj)) break; - objlist2 = objlist2->nobj; - } + objlist = objlist->nobj; + } while (objlist); obj->no_charge = save_nocharge; - return objlist2; + return objlist; } /* merge obj with otmp and delete obj if types agree */ @@ -866,20 +866,18 @@ merged(struct obj **potmp, struct obj **pobj) identification states don't match, one of them must have previously been identified */ if (obj->known != otmp->known) { - otmp->known = TRUE; + otmp->known = 1; discovered = TRUE; } if (obj->rknown != otmp->rknown) { - otmp->rknown = TRUE; - if (otmp->oerodeproof) { + otmp->rknown = 1; + if (otmp->oerodeproof) discovered = TRUE; - } } if (obj->bknown != otmp->bknown) { - otmp->bknown = TRUE; - if (!Role_if(PM_CLERIC)) { + otmp->bknown = 1; + if (!Role_if(PM_CLERIC)) discovered = TRUE; - } } /* fixup for `#adjust' merging wielded darts, daggers, &c */ @@ -894,13 +892,13 @@ merged(struct obj **potmp, struct obj **pobj) (Prior to 3.3.0, it was not possible for the two stacks to be worn in different slots and `obj' didn't need to be unworn when merging.) */ - if (wmask & W_WEP) + if ((wmask & W_WEP) != 0L) { wmask = W_WEP; - else if (wmask & W_SWAPWEP) + } else if ((wmask & W_SWAPWEP) != 0L) { wmask = W_SWAPWEP; - else if (wmask & W_QUIVER) + } else if ((wmask & W_QUIVER) != 0L) { wmask = W_QUIVER; - else { + } else { impossible("merging strangely worn items (%lx)", wmask); wmask = otmp->owornmask; } diff --git a/src/pickup.c b/src/pickup.c index 65081f2b8..af460e876 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1716,7 +1716,7 @@ lift_object( /* [exception for gold coins will have to change if silver/copper ones ever get implemented] */ && inv_cnt(FALSE) >= invlet_basic - && !merge_choice(gi.invent, obj)) { + && !merge_choice(gi.invent, obj)) { /* if there is some gold here (and we haven't already skipped it), we aren't limited by the 52 item limit for it, but caller and "grandcaller" aren't prepared to skip stuff and then pickup @@ -1780,6 +1780,7 @@ pickup_object( long count, /* if non-zero, pick up a subset of this amount */ boolean telekinesis) /* not picking it up directly by hand */ { + unsigned save_how_lost; int res; if (obj->quan < count) { @@ -1836,7 +1837,17 @@ pickup_object( } } - if ((res = lift_object(obj, (struct obj *) 0, &count, telekinesis)) <= 0) + save_how_lost = obj->how_lost; + /* obj has either already passed autopick_testobj or we are explicitly + picking it off the floor, so override obj->how_lost; otherwise we + couldn't pick up a thrown, stolen, or dropped item that was split + off from a carried stack even while still carrying the rest of the + stack unless we have at least one free slot available */ + obj->how_lost = LOST_NONE; /* affects merge_choice() */ + res = lift_object(obj, (struct obj *) 0, &count, telekinesis); + obj->how_lost = save_how_lost; /* even when res > 0, + * in case we call splitobj() below */ + if (res <= 0) return res; /* Whats left of the special case for gold :-) */ @@ -1845,6 +1856,7 @@ pickup_object( if (obj->quan != count && obj->otyp != LOADSTONE) obj = splitobj(obj, count); + obj->how_lost = LOST_NONE; obj = pick_obj(obj); if (uwep && uwep == obj)