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).
This commit is contained in:
prosolis
2026-05-28 21:55:34 -07:00
parent d80b437525
commit 1b8d13e0dd
3 changed files with 32 additions and 10 deletions

View File

@@ -88,14 +88,14 @@ var srdProfiles = map[string]SRDProfile{
{Name: "Thorned Lash", AttackBonus: 9, Damage: 12}, {Name: "Thorned Lash", AttackBonus: 9, Damage: 12},
{Name: "Thorned Lash", AttackBonus: 9, Damage: 11}, {Name: "Thorned Lash", AttackBonus: 9, Damage: 11},
}}, }},
"boss_infernax": {Attacks: []SRDAttack{ // Attack 38 → ~49 "boss_infernax": {Attacks: []SRDAttack{ // D11 T5 lift: 49 → ~42 (nerf; was an impossible wall at L15-16)
{Name: "Bite", AttackBonus: 11, Damage: 19}, {Name: "Bite", AttackBonus: 11, Damage: 16},
{Name: "Claw", AttackBonus: 11, Damage: 15}, {Name: "Claw", AttackBonus: 11, Damage: 13},
{Name: "Claw", AttackBonus: 11, Damage: 15}, {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: "Longsword", AttackBonus: 11, Damage: 24},
{Name: "Whip", AttackBonus: 11, Damage: 16}, {Name: "Whip", AttackBonus: 11, Damage: 17},
}}, }},
// ── Multiattack elites ─────────────────────────────────────────────── // ── Multiattack elites ───────────────────────────────────────────────

View File

@@ -80,7 +80,12 @@ func (p *AdventurePlugin) handleFightCmd(ctx MessageContext) error {
} else { } else {
monster, ok = pickZoneEnemy(zone, run.RunID, run.CurrentRoom, true) 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.)_") return p.replyDM(ctx, "_(No bestiary entry for this encounter — file a bug. `!zone abandon` to bail.)_")
} }

View File

@@ -602,9 +602,18 @@ var _ = func() bool {
}, },
"boss_infernax": { "boss_infernax": {
ID: "boss_infernax", Name: "Infernax the Undying", 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, 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, XPValue: 62000,
Notes: "Dragon's Lair boss. Ancient Red Dragon. Legendary Actions; Lair Actions; phase 2 below 50% HP — fire ignores resistance.", Notes: "Dragon's Lair boss. Ancient Red Dragon. Legendary Actions; Lair Actions; phase 2 below 50% HP — fire ignores resistance.",
FireAttacker: true, FireAttacker: true,
@@ -652,7 +661,15 @@ var _ = func() bool {
}, },
"boss_belaxath": { "boss_belaxath": {
ID: "boss_belaxath", Name: "Belaxath the Undivided", 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, BlockRate: 0.20,
Ability: &MonsterAbility{Name: "Lightning Discharge", Phase: "decisive", ProcChance: 0.40, Effect: "aoe"}, Ability: &MonsterAbility{Name: "Lightning Discharge", Phase: "decisive", ProcChance: 0.40, Effect: "aoe"},
XPValue: 22000, XPValue: 22000,