D&D: wire the Open5e magic-item registry into live gameplay

Magic items now reach players through three surfaces: zone loot drops,
Luigi's "Curios" shelf, and combat effects. Effects are formulaic
(Rarity scalar x Kind), mirroring the bestiary tuning pass, with an
empty magicItemEffectOverlay as the hand-authored refinement path.

- magic_items_gameplay.go: rarity index, magic_item_equipped persistence
  (new table, DnDSlot-keyed), codified effect formula, applyMagicItemEffects
  combat hook, potion/scroll -> ConsumableDef bridge, !adventure equip-magic
- dropZoneLoot: 15% magic-item substitution roll by tier rarity
- Luigi's Curios category: daily UTC-seeded 8-item rotation
- combat_bridge / combat_session_build: applyMagicItemEffects after passives
- consumableDefByName falls through so loot/shop potions auto-resolve
- renderDnDSheet: new Magic Items section

Equippable items live entirely in the DnDSlot scheme, separate from the
legacy tier-gear. Attunement items equip inert until attuned (3-slot cap).
This commit is contained in:
prosolis
2026-05-14 18:38:57 -07:00
parent 297ce3d786
commit 0d666beea3
12 changed files with 996 additions and 10 deletions

View File

@@ -447,6 +447,8 @@ func (p *AdventurePlugin) dispatchCommand(ctx MessageContext) error {
return p.handleBuyCmd(ctx, strings.TrimSpace(args[4:]))
case lower == "equip":
return p.handleEquipCmd(ctx)
case lower == "equip-magic" || lower == "equipmagic" || lower == "equip magic":
return p.handleEquipMagicCmd(ctx)
case lower == "inventory" || lower == "inv":
return p.handleInventoryCmd(ctx)
case lower == "leaderboard" || lower == "lb":
@@ -488,9 +490,10 @@ const advHelpText = `**Adventure Commands**
` + "`!adventure status`" + ` — View your character sheet
` + "`!expedition`" + ` — Adventure: pick a zone, advance through rooms, harvest as you go
` + "`!adventure shop`" + ` — Browse equipment categories
` + "`!adventure shop <category>`" + ` — View a category (weapon, armor, helmet, boots, tool)
` + "`!adventure shop <category>`" + ` — View a category (weapon, armor, helmet, boots, tool, supplies, curios)
` + "`!adventure buy <item>`" + ` — Buy equipment (e.g. ` + "`buy Enchanted Blade`" + ` or ` + "`buy 4 sword`" + `)
` + "`!adventure equip`" + ` — Equip Masterwork gear from inventory
` + "`!adventure equip-magic`" + ` — Equip magic items (curios) into your D&D slots
` + "`!adventure sell <item>`" + ` — Sell an inventory item (or ` + "`sell all`" + `)
` + "`!adventure inventory`" + ` — View your inventory
` + "`!adventure leaderboard`" + ` — View the leaderboard
@@ -824,6 +827,8 @@ func (p *AdventurePlugin) resolvePendingInteraction(ctx MessageContext, interact
return p.handleTreasureDiscard(ctx, interaction)
case "masterwork_equip":
return p.handleMasterworkEquipReply(ctx, interaction)
case "magic_equip":
return p.resolveMagicEquipReply(ctx, interaction)
case "masterwork_equip_confirm":
return p.handleMasterworkEquipConfirm(ctx, interaction)
case "rival_rps":
@@ -838,6 +843,8 @@ func (p *AdventurePlugin) resolvePendingInteraction(ctx MessageContext, interact
return p.resolveShopItemChoice(ctx, interaction)
case "shop_supply":
return p.resolveShopSupplyChoice(ctx, interaction)
case "shop_curio":
return p.resolveShopCurioChoice(ctx, interaction)
case "shop_confirm":
return p.resolveShopConfirm(ctx, interaction)
case "hospital_pay":