mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-18 09:52:41 +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:
@@ -3,7 +3,6 @@ package plugin
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math/rand/v2"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
@@ -128,7 +127,7 @@ func applyMageSubclassSpellHooks(c *DnDCharacter, spell SpellDefinition, slotLev
|
||||
// applySpellDamageAttack — Fire Bolt, Inflict Wounds, Chill Touch, etc.
|
||||
// Roll d20 + spell attack vs enemy AC; nat 20 doubles dice damage.
|
||||
func applySpellDamageAttack(spell SpellDefinition, atk int, mods *CombatModifiers, enemy *CombatStats, slot, charLevel int) {
|
||||
roll := 1 + rand.IntN(20)
|
||||
roll := 1 + simIntN(20)
|
||||
isCrit := roll == 20
|
||||
isFumble := roll == 1
|
||||
if isFumble || (!isCrit && roll+atk < enemy.AC) {
|
||||
@@ -158,7 +157,7 @@ func applySpellDamageAttack(spell SpellDefinition, atk int, mods *CombatModifier
|
||||
// future multi-enemy combat (Phase 11+) but is not consulted here.
|
||||
func applySpellDamageSave(spell SpellDefinition, dc int, c *DnDCharacter, mods *CombatModifiers, enemy *CombatStats, slot int) {
|
||||
saveMod := enemySpellSaveMod(enemy)
|
||||
saveRoll := 1 + rand.IntN(20)
|
||||
saveRoll := 1 + simIntN(20)
|
||||
saved := saveRoll+saveMod >= dc
|
||||
dmg := rollSpellDamageDice(spell, slot, c.Level)
|
||||
if saved {
|
||||
@@ -182,7 +181,7 @@ func applySpellDamageAuto(spell SpellDefinition, mods *CombatModifiers, slot, ch
|
||||
}
|
||||
total := 0
|
||||
for i := 0; i < darts; i++ {
|
||||
total += 1 + rand.IntN(4) + 1
|
||||
total += 1 + simIntN(4) + 1
|
||||
}
|
||||
mods.SpellPreDamage += total
|
||||
mods.SpellPreDamageDesc = fmt.Sprintf("Magic Missile (%d darts, %d dmg)", darts, total)
|
||||
@@ -223,7 +222,7 @@ func enemySpellSaveMod(enemy *CombatStats) int {
|
||||
// double damage (5e: paralyzed creatures auto-crit on melee hits).
|
||||
func applySpellControl(spell SpellDefinition, dc int, mods *CombatModifiers, enemy *CombatStats, slot int) {
|
||||
saveMod := enemySpellSaveMod(enemy)
|
||||
saveRoll := 1 + rand.IntN(20)
|
||||
saveRoll := 1 + simIntN(20)
|
||||
if saveRoll+saveMod >= dc {
|
||||
mods.SpellPreDamageDesc = spell.Name + " — resisted"
|
||||
return
|
||||
@@ -382,7 +381,7 @@ func rollTurnSpellHeal(c *DnDCharacter, spell SpellDefinition, slotLevel int) in
|
||||
if supreme {
|
||||
heal += faces
|
||||
} else {
|
||||
heal += 1 + rand.IntN(faces)
|
||||
heal += 1 + simIntN(faces)
|
||||
}
|
||||
}
|
||||
heal += abilityModifier(c.WIS)
|
||||
@@ -418,7 +417,7 @@ func rollSpellDamageDice(spell SpellDefinition, slot, charLevel int) int {
|
||||
}
|
||||
total := flat
|
||||
for i := 0; i < dice; i++ {
|
||||
total += 1 + rand.IntN(faces)
|
||||
total += 1 + simIntN(faces)
|
||||
}
|
||||
if total < 1 {
|
||||
total = 1
|
||||
|
||||
Reference in New Issue
Block a user