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:
@@ -224,7 +224,14 @@ func TestApplyClassPassives(t *testing.T) {
|
||||
wantFlatStart int
|
||||
wantInitBias float64
|
||||
}{
|
||||
{ClassFighter, 0.05, 0, false, 0, 1.0, 0, 0},
|
||||
// Fighter-ceiling rebaseline (2026-07-16): Fighter picked up a -2 to-hit
|
||||
// sub-swing trim; Rogue's steady rider flipped +0.05→-0.10 and it took a
|
||||
// -3 to-hit trim (both offset a new 2nd swing gated at L5, not seen here);
|
||||
// Druid took a survival trim (DR 0.95→0.19) + -0.20 damage; Bard a DR 0.4
|
||||
// survival trim; Sorcerer a +1 to-hit to match the blasters; Paladin a
|
||||
// 0.9 DR survival trim. Caster CantripPerRound / MaxHP / Defense adds are
|
||||
// not asserted here.
|
||||
{ClassFighter, 0.05, -2, false, 0, 1.0, 0, 0},
|
||||
// Phase 2 class-balance rebalance: rogue picked up +5% damage,
|
||||
// Mage/Bard/Warlock gained a level-scaled FlatDmgStart burst, Sorcerer's
|
||||
// burst now also scales with level, and Warlock picked up +1 attack.
|
||||
@@ -233,7 +240,7 @@ func TestApplyClassPassives(t *testing.T) {
|
||||
// Phase 3 class-balance: Druid picked up a WIS-scaled FlatDmgStart burst
|
||||
// (lvl 1 + clamp(mod(WIS=0)) = 1), and Sorcerer's burst base went 3→5
|
||||
// (5 + 1 + clamp(mod(CHA=10)=0) = 6).
|
||||
{ClassRogue, 0.05, 0, true, 0, 1.0, 0, 0},
|
||||
{ClassRogue, -0.10, -3, true, 0, 1.0, 0, 0},
|
||||
{ClassMage, 0.05, 1, false, 0, 1.0, 1, 0},
|
||||
{ClassCleric, 0, 0, false, 5, 1.0, 0, 0},
|
||||
// Class-identity audit (2026-05-16): Ranger Hunter's Mark is now
|
||||
@@ -243,11 +250,11 @@ func TestApplyClassPassives(t *testing.T) {
|
||||
// FlatDmgStart compensation riders are gone; +1 to-hit stays on
|
||||
// Ranger as the "read prey tells" half.
|
||||
{ClassRanger, 0, 1, false, 0, 1.0, 0, 0},
|
||||
{ClassDruid, 0, 0, false, 0, 0.95, 1, 0},
|
||||
{ClassBard, 0.05, 1, false, 0, 1.0, 1, 1},
|
||||
{ClassSorcerer, 0.05, 0, false, 0, 1.0, 6, 0},
|
||||
{ClassDruid, -0.20, 0, false, 0, 0.95 * 0.2, 1, 0},
|
||||
{ClassBard, 0.05, 1, false, 0, 0.4, 1, 1},
|
||||
{ClassSorcerer, 0.05, 1, false, 0, 1.0, 6, 0},
|
||||
{ClassWarlock, 0.12, 1, false, 0, 1.0, 1, 0},
|
||||
{ClassPaladin, 0, 0, false, 0, 1.0, 0, 0},
|
||||
{ClassPaladin, 0, 0, false, 0, 0.9, 0, 0},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
stats := CombatStats{AttackBonus: 5}
|
||||
|
||||
Reference in New Issue
Block a user