mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-18 01:42:42 +00:00
combat: revive the caster sustained-cantrip floor in the turn engine
The arcane-blaster "sustained floor" passives (CantripPerRound, DamageBonus, FlatDmgStart from casterBlasterFloor) were built for the swing-based engine (SimulateCombat, combat_engine.go:590). But every live expedition auto-resolves through the turn engine (autoDriveCombat -> session -> combat_turn_engine), where casters autocast every turn and never weapon-swing -- so CantripPerRound never fired and DamageBonus was inert. Casters fought at bare cantrip dice (~4d10~=22 at L20) instead of their intended floor, in sim AND in prod. This is why every caster damage dial read as a dead lever across the whole rebaseline. Fix (combat_cmd.go): bridge the already-computed CantripPerRound into the turn-engine damage-cantrip cast, hit-gated (only lift a cast that already connected, so the ~35% miss variance survives and the floor isn't a guaranteed flat hammer). Self-targeting: only Mage/Sorcerer/Warlock carry a nonzero CantripPerRound -- martials swing (untouched), cleric/bard/druid have floor 0. Tuning (dnd_passives.go): casterCantripBase 9 -> 3, now a live, class-specific lever. Mage/Sorcerer take base 3; Warlock passes 0 (its bare-dice cantrip plus a structural edge already lands it mid-band, so an added floor overshoots). Removed the dead casterHPPerLevel rider (it inflated the truncation-fraction denominator without adding startable HP -- a bug). Also lands the deterministic-seeding infra (sim_seed.go + simIntN/simFloat64 threading) used to read these deltas out of the process-seed noise; prod is byte-identical (unseeded -> package rand). Confirmation (expedition-sim, L20 T5 dragons_lair+abyss_portal, n=250): casters now in the 35-45 floor -- sorcerer 39, mage 38, warlock 36; martial leaders undisturbed (rogue 68, druid 66, ranger 65, fighter 64, ... paladin 55).
This commit is contained in:
@@ -707,6 +707,24 @@ func (p *AdventurePlugin) castActionForSeat(ct *combatTurn, seat int, args strin
|
||||
PlayerHeal: out.PlayerHeal,
|
||||
EnemySkip: out.EnemySkip,
|
||||
}
|
||||
// Revive the arcane-blaster sustained cantrip floor in the turn engine.
|
||||
// combat_engine.go:590 deals CantripPerRound flat every round, but that
|
||||
// path is the swing-based engine (SimulateCombat). Auto-resolve — the
|
||||
// live path for every expedition — runs the turn engine, where a caster
|
||||
// autocasts and never weapon-swings, so the floor was dead code: casters
|
||||
// fought at bare cantrip dice (~4d10≈22 at L20). Lift LANDED cantrip
|
||||
// damage to the floor, but only when the cast already connected
|
||||
// (eff.EnemyDamage > 0) — a whiffed Fire Bolt still whiffs, so the ~35%
|
||||
// miss variance survives and the floor doesn't become a guaranteed flat
|
||||
// hammer (that overshot to ~99%). Damage cantrips only; slot spells keep
|
||||
// their rolled damage. Only Mage/Sorcerer/Warlock carry a nonzero
|
||||
// CantripPerRound, so this is self-targeting.
|
||||
if spell.Level == 0 && eff.EnemyDamage > 0 &&
|
||||
(spell.Effect == EffectDamageAttack || spell.Effect == EffectDamageSave || spell.Effect == EffectDamageAuto) {
|
||||
if floor := ct.players[seat].Mods.CantripPerRound; floor > eff.EnemyDamage {
|
||||
eff.EnemyDamage = floor
|
||||
}
|
||||
}
|
||||
// §1 — redirect the heal onto the named ally. The roll is the same; only
|
||||
// the body it lands on changes. This is the line that makes a cleric a
|
||||
// cleric: until it existed, every heal in the engine was a self-heal, and
|
||||
|
||||
Reference in New Issue
Block a user