From 1b8d13e0dd0e223c3d248e1a1d1f560efea0d558 Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Thu, 28 May 2026 21:55:34 -0700 Subject: [PATCH] J3 D11: T5 difficulty lift (leaders-define-band) + empty-EnemyID combat guard Boss-only tuning at the L15/L16 mid-range (D8-f #2 had tested T5 at the L12 floor where everything walls). The two T5 zones needed opposite treatment: - dragons_lair (infernax): impossible wall, leaders 0-2% -> HP 546->405, AC 22->20, frightful-presence stun 0.80->0.40, multiattack 49->42. - abyss_portal (belaxath): leader faceroll 88-92% -> HP 262->300, AC 19->20, multiattack 40->41. Final n=50: leaders 61% at L15 (both zones), 72-79% at L16 -- same leaders-define-band shape as T4; casters ~0% (J3 caster-track gap, not monster-tunable). Bosses are the sole binding lever (every run decided at the boss; standard/elite pools already survivable). Also harden handleFightCmd: a malformed bestiary entry with an empty ID was silently persisting an enemy-less combat session that spun to autoDriveCombat's 200-round cap. Now treated as a bestiary miss (fail loud). Writeup: sim_results/t5_findings.md (gitignored). --- internal/plugin/bestiary_srd.go | 12 ++++++------ internal/plugin/combat_cmd.go | 7 ++++++- internal/plugin/dnd_bestiary.go | 23 ++++++++++++++++++++--- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/internal/plugin/bestiary_srd.go b/internal/plugin/bestiary_srd.go index 95e966c..c7d48c0 100644 --- a/internal/plugin/bestiary_srd.go +++ b/internal/plugin/bestiary_srd.go @@ -88,14 +88,14 @@ var srdProfiles = map[string]SRDProfile{ {Name: "Thorned Lash", AttackBonus: 9, Damage: 12}, {Name: "Thorned Lash", AttackBonus: 9, Damage: 11}, }}, - "boss_infernax": {Attacks: []SRDAttack{ // Attack 38 → ~49 - {Name: "Bite", AttackBonus: 11, Damage: 19}, - {Name: "Claw", AttackBonus: 11, Damage: 15}, - {Name: "Claw", AttackBonus: 11, Damage: 15}, + "boss_infernax": {Attacks: []SRDAttack{ // D11 T5 lift: 49 → ~42 (nerf; was an impossible wall at L15-16) + {Name: "Bite", AttackBonus: 11, Damage: 16}, + {Name: "Claw", AttackBonus: 11, Damage: 13}, + {Name: "Claw", AttackBonus: 11, Damage: 13}, }}, - "boss_belaxath": {Attacks: []SRDAttack{ // Attack 31 → ~40 + "boss_belaxath": {Attacks: []SRDAttack{ // D11 T5 lift: 40 → ~41 (buff; was a leader faceroll) {Name: "Longsword", AttackBonus: 11, Damage: 24}, - {Name: "Whip", AttackBonus: 11, Damage: 16}, + {Name: "Whip", AttackBonus: 11, Damage: 17}, }}, // ── Multiattack elites ─────────────────────────────────────────────── diff --git a/internal/plugin/combat_cmd.go b/internal/plugin/combat_cmd.go index acdfec2..9186e72 100644 --- a/internal/plugin/combat_cmd.go +++ b/internal/plugin/combat_cmd.go @@ -80,7 +80,12 @@ func (p *AdventurePlugin) handleFightCmd(ctx MessageContext) error { } else { monster, ok = pickZoneEnemy(zone, run.RunID, run.CurrentRoom, true) } - if !ok { + if !ok || monster.ID == "" { + // monster.ID == "" guards a malformed bestiary entry (e.g. one whose + // ID field was dropped): startCombatSession would otherwise persist a + // session with an empty EnemyID, and the turn engine — having no enemy + // to resolve — spins inertly until autoDriveCombat's round cap. Fail + // loudly instead of stalling. return p.replyDM(ctx, "_(No bestiary entry for this encounter — file a bug. `!zone abandon` to bail.)_") } diff --git a/internal/plugin/dnd_bestiary.go b/internal/plugin/dnd_bestiary.go index 2bb36de..cd3cf3b 100644 --- a/internal/plugin/dnd_bestiary.go +++ b/internal/plugin/dnd_bestiary.go @@ -602,9 +602,18 @@ var _ = func() bool { }, "boss_infernax": { ID: "boss_infernax", Name: "Infernax the Undying", - CR: 24, HP: 546, AC: 22, Attack: 38, AttackBonus: 11, Speed: 18, + // T5 lift (D11): dragons_lair was an impossible wall — at L15-16 + // (mid-range) leaders cleared 0-2%, 100% TPK at the boss (it has + // 546 HP / AC22 / 49-dmg multiattack / near-guaranteed stun, a + // CR24 block fought ~9 levels under its CR). Nerfed to land + // martial leaders in the 60-75% band: HP 546→405, AC 22→20, + // frightful-presence stun 0.80→0.40, multiattack 49→42 (srd). + // (Pass 1's 360/AC20/36 over-nerfed → 98% leaders; Pass 2's + // 460/AC21/42 over-corrected → ~40%; this Pass 3 405/AC20/42 + // lands leaders in band.) + CR: 24, HP: 405, AC: 20, Attack: 38, AttackBonus: 11, Speed: 18, BlockRate: 0.15, - Ability: &MonsterAbility{Name: "Frightful Presence", Phase: "opening", ProcChance: 0.80, Effect: "stun"}, + Ability: &MonsterAbility{Name: "Frightful Presence", Phase: "opening", ProcChance: 0.40, Effect: "stun"}, XPValue: 62000, Notes: "Dragon's Lair boss. Ancient Red Dragon. Legendary Actions; Lair Actions; phase 2 below 50% HP — fire ignores resistance.", FireAttacker: true, @@ -652,7 +661,15 @@ var _ = func() bool { }, "boss_belaxath": { ID: "boss_belaxath", Name: "Belaxath the Undivided", - CR: 19, HP: 262, AC: 19, Attack: 31, AttackBonus: 11, Speed: 14, + // T5 lift (D11): abyss_portal was a faceroll for leaders — at its + // own floor (L15) fighter/ranger cleared 88-92% (above the 60-75% + // band), killing the 262-HP boss in 6-13 rounds with HP to spare. + // Buffed to bring leaders into band: HP 262→300, AC 19→20, + // multiattack 40→41 (srd). Elites (nalfeshnee/marilith) already + // wall the trailers, so this only moves the leaders. (Pass 1's + // 360/AC20/45 over-buffed → 19-40% leaders; this is the Pass 2 + // correction back down toward band.) + CR: 19, HP: 300, AC: 20, Attack: 31, AttackBonus: 11, Speed: 14, BlockRate: 0.20, Ability: &MonsterAbility{Name: "Lightning Discharge", Phase: "decisive", ProcChance: 0.40, Effect: "aoe"}, XPValue: 22000,