From 1b3219de3fac7866c4f1e823fa2bcd08b13264de Mon Sep 17 00:00:00 2001 From: alexpolo1 Date: Fri, 4 Sep 2026 09:17:47 +0200 Subject: [PATCH] fix(inventory): stash protected items instead of stranding them in the inventory A charm pickit rejects gets keep=False, but protect_charms_from_sell blocks both the sell and the drop, and the stash branch only transfers keep==True. The item could therefore leave the inventory by no route at all: the same 8 charms were re-hovered, re-OCR'd and re-rejected every game (~12s of town time each cycle), and every new junk charm picked off the ground joined the set permanently. _without_protected now flips a blocked item to keep=True so the stash step takes it, mirroring the recovery inspect_inventory already does per item at the vendor. bot.py recomputes keep_items after buy_consumables, since it is evaluated before that call and would otherwise stay stale and skip the stash entirely. The protection intent is preserved: a misread charm is still never sold or dropped, it just goes to the stash rather than clogging the inventory. Co-Authored-By: Claude Opus 5 (cherry picked from commit 914d4a911e7b9e9036c2a6bc33b96273181bfea6) --- src/bot.py | 4 ++++ src/inventory/personal.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/bot.py b/src/bot.py index 2a6d72b..a1e4c43 100644 --- a/src/bot.py +++ b/src/bot.py @@ -930,6 +930,10 @@ class Bot: if self._curr_loc: items = result_items sell_items = any([item.sell for item in items]) if items else None + # Recompute: a protected item (e.g. a charm) blocked at the vendor is + # flipped to keep=True so the stash step below picks it up. keep_items + # was computed before this call and would otherwise still be stale. + keep_items = any([item.keep for item in items]) if items else keep_items Logger.debug(f"Needs: {consumables.get_needs()}") _step("buy_consumables", "ok", f"at {self._curr_loc} | after: {consumables.get_needs()}") else: diff --git a/src/inventory/personal.py b/src/inventory/personal.py index 900cd88..b21e7c5 100644 --- a/src/inventory/personal.py +++ b/src/inventory/personal.py @@ -462,8 +462,14 @@ def transfer_items(items: list, action: str = "drop", img: np.ndarray = None) -> safe = [] for item in candidates: if _is_protected(item): - Logger.warning(f"Blocked {action_name} for protected item: {item.name} at {item.pos}") + Logger.warning(f"Blocked {action_name} for protected item: {item.name} at {item.pos} — marking for stash") item.sell = False + # Send it to the stash instead of leaving it in limbo. Without this a + # protected item pickit rejected (keep=False) can be neither sold, nor + # dropped, nor stashed (the stash branch only takes keep==True), so it + # sits in the inventory forever and is re-hovered every single game. + # This mirrors the per-item recovery already done by inspect_inventory. + item.keep = True continue safe.append(item) return safe