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:
prosolis
2026-05-12 21:59:19 -07:00
parent 2b86f3df16
commit 4e412219f3
49 changed files with 1381 additions and 444 deletions

View File

@@ -122,29 +122,28 @@ func assessThreat(player, enemy CombatStats) threatLevel {
// ── Selection Logic ──────────────────────────────────────────────────────────
// SelectConsumables picks up to 2 items (1 offensive + 1 defensive) from inventory.
// contentTier caps consumable tier to the content being fought (0 = no cap).
func SelectConsumables(inventory []ConsumableItem, playerStats, enemyStats CombatStats, arenaRound int, contentTier int) []ConsumableItem {
// contentTier is retained on the signature for callers but no longer caps
// what gets considered — if it's in your bag, the picker can use it.
// allowSkipTrivial=true lets the picker bail out of obvious wins to save
// items — appropriate for dungeon dives full of chump rooms, but wrong for
// arena/zone elite/boss encounters where the legacy threat assessor
// underestimates d20-mode lethality (one bad nat-20 streak ends the run).
//
// Resource-saving still happens via betterOffensive/betterDefensive, which
// prefer lower-tier items on non-Dangerous threats. Players with mixed
// inventories don't burn a Voidstone Shard on a goblin; players with only
// high-tier items now actually get to use them.
func SelectConsumables(inventory []ConsumableItem, playerStats, enemyStats CombatStats, arenaRound int, contentTier int, allowSkipTrivial bool) []ConsumableItem {
_ = contentTier // retained for caller compatibility
threat := assessThreat(playerStats, enemyStats)
// Arena losses cost real money + equipment durability — never skip there,
// even if the threat looks trivial. Adventure-side fights still skip
// trivial threats to avoid wasting items on chump enemies.
if threat == threatTrivial && arenaRound == 0 {
if threat == threatTrivial && allowSkipTrivial && arenaRound == 0 {
return nil
}
maxTier := maxConsumableTier(threat, arenaRound)
if contentTier > 0 && contentTier < maxTier {
maxTier = contentTier
}
var bestOffensive, bestDefensive *ConsumableItem
for i := range inventory {
item := &inventory[i]
if item.Def.Tier > maxTier {
continue
}
switch item.Def.Slot {
case "offensive":
if bestOffensive == nil || betterOffensive(item, bestOffensive, threat) {
@@ -167,23 +166,6 @@ func SelectConsumables(inventory []ConsumableItem, playerStats, enemyStats Comba
return selected
}
func maxConsumableTier(threat threatLevel, arenaRound int) int {
base := 1
switch threat {
case threatEasy:
base = 2
case threatCompetitive:
base = 3
case threatDangerous:
base = 5
}
// Arena: spend freely on later rounds
if arenaRound >= 3 {
base = min(5, base+1)
}
return base
}
// betterOffensive prefers lowest tier that's appropriate for the threat.
func betterOffensive(candidate, current *ConsumableItem, threat threatLevel) bool {
if threat == threatDangerous {