mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 08:52:42 +00:00
WIP: in-flight combat/expedition/flavor changes
Bundle of uncommitted working-tree edits across combat engine, expedition cycle, flavor pools, and TwinBee/zone narration. Includes new files: combat_debug.go, dnd_boss_consumables.go, dnd_dex_floor.go, plus CHANGES_24H.md and REBALANCE_NOTES.md scratch notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
124
internal/plugin/dnd_boss_consumables.go
Normal file
124
internal/plugin/dnd_boss_consumables.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"sort"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// Auto-consumable rule (kept deliberately simple): the only pre-fight
|
||||
// auto-pick is the panic heal. Other consumables (Coal Bombs, Ink Vials,
|
||||
// Wards, etc.) sit in inventory until the player explicitly engages them
|
||||
// — no threat-assessor magic deciding to burn a Voidstone Shard on a
|
||||
// shadow.
|
||||
//
|
||||
// Heal triggers in the engine when player HP drops below 60% MaxHP. Each
|
||||
// trigger consumes one charge; charges = number of healing consumables
|
||||
// in the inventory. Post-combat, that many heal items are removed from
|
||||
// the inventory bag (cheapest tier first so players burn through low
|
||||
// stock before high-tier potions).
|
||||
//
|
||||
// The HealItem mod amount is the BEST heal value in inventory — every
|
||||
// fire heals at the highest-tier-available rate. Slight balance pad
|
||||
// (favors player), big simplification (engine doesn't need a per-charge
|
||||
// values slice).
|
||||
|
||||
// setupAutoHealFromInventory wires the heal-on-low-HP behavior from the
|
||||
// player's current consumable inventory. Returns the count of charges
|
||||
// it set up (0 if no healing items at all).
|
||||
func setupAutoHealFromInventory(consumables []ConsumableItem, mods *CombatModifiers) int {
|
||||
bestValue := 0
|
||||
count := 0
|
||||
for i := range consumables {
|
||||
c := &consumables[i]
|
||||
if c.Def == nil || c.Def.Effect != EffectHeal {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
if v := int(c.Def.Value); v > bestValue {
|
||||
bestValue = v
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
return 0
|
||||
}
|
||||
mods.HealItem = bestValue
|
||||
if mods.HealItemCharges < count {
|
||||
mods.HealItemCharges = count
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// isBossPhases compares by slice header (same backing array) — boss fights
|
||||
// are routed through bossCombatPhases (defined in combat_engine.go) and the
|
||||
// caller passes that exact reference.
|
||||
func isBossPhases(phases []CombatPhase) bool {
|
||||
if len(phases) != len(bossCombatPhases) {
|
||||
return false
|
||||
}
|
||||
if len(phases) == 0 {
|
||||
return false
|
||||
}
|
||||
return &phases[0] == &bossCombatPhases[0]
|
||||
}
|
||||
|
||||
// countHealEventsFired counts how many heal_item events the engine emitted
|
||||
// during this fight — equals the number of heal charges actually consumed
|
||||
// and therefore the number of inventory items to remove.
|
||||
func countHealEventsFired(result CombatResult) int {
|
||||
n := 0
|
||||
for _, e := range result.Events {
|
||||
if e.Action == "heal_item" {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// consumeFiredHealingItems removes `fired` healing consumables from the
|
||||
// player's inventory, cheapest-tier first so players burn cheap stock
|
||||
// before high-tier potions. No-op if fired <= 0.
|
||||
func consumeFiredHealingItems(userID id.UserID, fired int) {
|
||||
if fired <= 0 {
|
||||
return
|
||||
}
|
||||
items, err := loadAdvInventory(userID)
|
||||
if err != nil {
|
||||
slog.Error("auto-heal cleanup: load inventory failed", "user", userID, "err", err)
|
||||
return
|
||||
}
|
||||
// Filter to heal items, sort cheapest-tier-first.
|
||||
type entry struct {
|
||||
id int64
|
||||
tier int
|
||||
name string
|
||||
}
|
||||
var heals []entry
|
||||
for _, item := range items {
|
||||
if item.Type != "consumable" {
|
||||
continue
|
||||
}
|
||||
def := consumableDefByName(item.Name)
|
||||
if def == nil || def.Effect != EffectHeal {
|
||||
continue
|
||||
}
|
||||
heals = append(heals, entry{id: item.ID, tier: def.Tier, name: item.Name})
|
||||
}
|
||||
sort.SliceStable(heals, func(i, j int) bool { return heals[i].tier < heals[j].tier })
|
||||
removed := 0
|
||||
for _, h := range heals {
|
||||
if removed >= fired {
|
||||
break
|
||||
}
|
||||
if rerr := removeAdvInventoryItem(h.id); rerr != nil {
|
||||
slog.Error("auto-heal cleanup: remove failed", "user", userID, "item", h.name, "err", rerr)
|
||||
continue
|
||||
}
|
||||
removed++
|
||||
}
|
||||
if removed < fired {
|
||||
slog.Warn("auto-heal cleanup: short on inventory",
|
||||
"user", userID, "wanted", fired, "removed", removed)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user