mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 08:52:42 +00:00
Adv 2.0 D&D Phase 10 SUB3c: Cleric L10/L15
Life/War/Trickery Domain L10/L15 capstones. L10 Divine Strike (all 3 subclasses): +4 flat per weapon hit, scaling to +9 at L14. New CombatModifiers.DivineStrikePerHit channel, gated on Weapon != nil since 5e specs "weapon hit". Damage type (radiant/weapon/ poison) varies by domain but isn't tracked by the engine. Life L15 Supreme Healing: heal-spell dice resolve at max instead of rolled. Wired through resolveHealOutOfCombat via lifeDomainSupremeHealing helper. War L15 Avatar of Battle: 5e physical resistance vs. non-magical weapons → flat 0.80 DamageReduct (softer than full 50% to account for elemental hits). Trickery L15 Improved Duplicity: 5e duplicates grant ally-flank advantage; no allies in 1v1, so proxied passively as +1 SporeCloud round and +5% damage (duplicates flicker around the foe). 10 new tests; cleric-suite green. Pre-existing rng flakes (TestOrcRageFiresOnLowHP, TestSimulateCombat_FirstAttackBonus...) are unrelated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,19 @@ func applySubclassPassives(stats *CombatStats, mods *CombatModifiers, c *DnDChar
|
||||
}
|
||||
mods.AssassinateBonusDmg = bonus
|
||||
}
|
||||
case SubclassLifeDomain:
|
||||
// Phase 10 SUB3c — Life Domain L10 Divine Strike. 5e: +1d8 radiant
|
||||
// damage on a weapon hit, once per turn (+2d8 at L14). We collapse
|
||||
// the once-per-turn cadence to "every weapon hit" since our 1v1 model
|
||||
// has no turn-boundary primitive; avg 1d8 = 4, 2d8 = 9 at L14+.
|
||||
// L5 Disciple of Life rides lifeDomainHealBonus and L15 Supreme
|
||||
// Healing rides lifeDomainSupremeHealing — both out-of-combat heal
|
||||
// hooks, so they don't surface here.
|
||||
if c.Level >= 14 {
|
||||
mods.DivineStrikePerHit += 9
|
||||
} else if c.Level >= 10 {
|
||||
mods.DivineStrikePerHit += 4
|
||||
}
|
||||
case SubclassWarDomain:
|
||||
// L5 War Priest: bonus-action weapon attack, usable WIS-mod times per
|
||||
// long rest. One-shot combat can't model an extra discrete attack, so
|
||||
@@ -165,6 +178,23 @@ func applySubclassPassives(stats *CombatStats, mods *CombatModifiers, c *DnDChar
|
||||
stats.AttackBonus++
|
||||
mods.DamageBonus += 0.15
|
||||
}
|
||||
// Phase 10 SUB3c — War Domain L10 Divine Strike: +1d8 weapon-type
|
||||
// damage on every weapon hit (+2d8 at L14). Same channel as Life
|
||||
// Domain's radiant version — engine doesn't track damage types.
|
||||
if c.Level >= 14 {
|
||||
mods.DivineStrikePerHit += 9
|
||||
} else if c.Level >= 10 {
|
||||
mods.DivineStrikePerHit += 4
|
||||
}
|
||||
// Phase 10 SUB3c — L15 Avatar of Battle: 5e gives resistance to
|
||||
// bludgeoning/piercing/slashing from non-magical weapons. Most enemy
|
||||
// damage in our model is non-magical physical, so resistance lands
|
||||
// on the bulk of incoming damage. We collapse to a flat 20% incoming
|
||||
// reduction — softer than full 50% physical resistance to account
|
||||
// for magical/elemental hits the resistance wouldn't catch.
|
||||
if c.Level >= 15 {
|
||||
mods.DamageReduct *= 0.80
|
||||
}
|
||||
case SubclassTrickeryDomain:
|
||||
// L7 Cloak of Shadows: turn invisible until you attack/cast/end of
|
||||
// next turn. We proxy the brief defensive window as 2 rounds of 15%
|
||||
@@ -174,6 +204,23 @@ func applySubclassPassives(stats *CombatStats, mods *CombatModifiers, c *DnDChar
|
||||
if c.Level >= 7 {
|
||||
mods.SporeCloud += 2
|
||||
}
|
||||
// Phase 10 SUB3c — Trickery Domain L10 Divine Strike: +1d8 poison
|
||||
// (+2d8 at L14). Same engine channel as the other two domains.
|
||||
if c.Level >= 14 {
|
||||
mods.DivineStrikePerHit += 9
|
||||
} else if c.Level >= 10 {
|
||||
mods.DivineStrikePerHit += 4
|
||||
}
|
||||
// Phase 10 SUB3c — L15 Improved Duplicity: 5e spawns up to 4
|
||||
// duplicates and grants advantage to allies adjacent to one. No
|
||||
// allies in 1v1, so the literal effect is inert. We proxy the
|
||||
// permanent-illusion fantasy passively: +1 round of SporeCloud
|
||||
// (duplicates flicker around the foe creating extra confusion on
|
||||
// top of Cloak of Shadows) and a small flanking damage bump.
|
||||
if c.Level >= 15 {
|
||||
mods.SporeCloud++
|
||||
mods.DamageBonus += 0.05
|
||||
}
|
||||
case SubclassHunter:
|
||||
// L5 Hunter's Prey: 5e gives a one-of-three pick (Colossus Slayer:
|
||||
// +1d8 vs. damaged foes once per turn / Giant Killer reaction /
|
||||
@@ -417,6 +464,15 @@ func lifeDomainHealBonus(c *DnDCharacter, spell SpellDefinition, slotLevel int)
|
||||
return 2 + lvl
|
||||
}
|
||||
|
||||
// lifeDomainSupremeHealing reports whether a Life Domain Cleric has reached
|
||||
// L15 Supreme Healing — when true, healing-spell dice should resolve at max
|
||||
// face value instead of being rolled. resolveHealOutOfCombat consults this to
|
||||
// decide whether to roll or take max for each die.
|
||||
func lifeDomainSupremeHealing(c *DnDCharacter) bool {
|
||||
return c != nil && c.Class == ClassCleric &&
|
||||
c.Subclass == SubclassLifeDomain && c.Level >= 15
|
||||
}
|
||||
|
||||
// grimHarvestHeal returns the HP to restore when a Necromancy Mage's queued
|
||||
// spell delivered the killing blow. 5e: heal 2× spell level on kill (3× if
|
||||
// the spell is necrotic). Slot=0 = nothing was stashed → no heal. Returns 0
|
||||
|
||||
Reference in New Issue
Block a user