From 671773abd56df875363704234179f382749648b9 Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Tue, 28 Apr 2026 23:13:09 -0700 Subject: [PATCH] Adventure: protect consumables from Robbie and 'sell all' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consumables (auto-crafted from foraged ingredients or dropped from T2+ activities) are a curated stockpile — selling them should be an explicit choice, not a side effect. Two surfaces tightened: - Robbie's qualifying-items filter now skips Type == "consumable" the same way it skips Arena gear and cards. - !adventure sell all leaves consumables in inventory and reports them in the kept-items tally. The merchant message points players at !adventure sell for explicit consumable sales. !adventure sell still works fine on consumables — that path is already explicit by definition. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/plugin/adventure_robbie.go | 6 ++++-- internal/plugin/adventure_shop.go | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/plugin/adventure_robbie.go b/internal/plugin/adventure_robbie.go index 50f9221..3221766 100644 --- a/internal/plugin/adventure_robbie.go +++ b/internal/plugin/adventure_robbie.go @@ -184,8 +184,10 @@ func (p *AdventurePlugin) robbieVisitPlayer(userID id.UserID, displayName string func robbieQualifyingItems(inv []AdvItem, equip map[EquipmentSlot]*AdvEquipment) []AdvItem { var result []AdvItem for _, item := range inv { - // Never touch Arena gear or cards - if item.Type == "ArenaGear" || item.Type == "card" { + // Never touch Arena gear, cards, or consumables. Consumables are a + // player-curated stockpile (crafted or dropped); selling them is an + // explicit decision the player must make themselves. + if item.Type == "ArenaGear" || item.Type == "card" || item.Type == "consumable" { continue } diff --git a/internal/plugin/adventure_shop.go b/internal/plugin/adventure_shop.go index 1fcaebd..4b15987 100644 --- a/internal/plugin/adventure_shop.go +++ b/internal/plugin/adventure_shop.go @@ -778,11 +778,16 @@ func (p *AdventurePlugin) advSellAll(userID id.UserID) string { var total float64 var sold int var keptSpecial int + var keptConsumable int for _, item := range items { if item.Type == "MasterworkGear" || item.Type == "ArenaGear" || item.Type == "card" { keptSpecial++ continue } + if item.Type == "consumable" { + keptConsumable++ + continue + } if err := removeAdvInventoryItem(item.ID); err != nil { continue } @@ -791,8 +796,13 @@ func (p *AdventurePlugin) advSellAll(userID id.UserID) string { } if sold == 0 { - if keptSpecial > 0 { + switch { + case keptSpecial > 0 && keptConsumable > 0: + return "Your inventory only contains special gear and consumables. The merchant won't touch any of it. Use `!adventure equip` for gear or `!adventure sell ` for individual consumables." + case keptSpecial > 0: return "Your inventory only contains Masterwork and Arena gear. The merchant doesn't deal in that. Use `!adventure equip` instead." + case keptConsumable > 0: + return "Your inventory only contains consumables. `sell all` won't touch them — use `!adventure sell ` to sell a specific consumable." } return "Your inventory is empty. There is nothing to sell. This is a metaphor for something but now is not the time." } @@ -811,6 +821,9 @@ func (p *AdventurePlugin) advSellAll(userID id.UserID) string { if keptSpecial > 0 { msg += fmt.Sprintf("\n\n(%d special gear items kept — the merchant knows better than to touch those.)", keptSpecial) } + if keptConsumable > 0 { + msg += fmt.Sprintf("\n(%d consumable(s) kept — `sell all` doesn't touch them. Sell explicitly with `!adventure sell `.)", keptConsumable) + } return msg }