D&D: class passives + subclasses for the five caster classes

Adds signature class passives for Druid/Bard/Sorcerer/Warlock/Paladin in
dnd_passives.go, each riding an existing CombatModifiers channel so a
playable caster is no longer mechanically empty: Druid Wild Resilience
(damage reduction), Bard Bardic Inspiration (initiative), Sorcerer Innate
Sorcery (CHA-scaled pre-combat burst), Warlock Agonizing Blast (+10%
damage), Paladin Divine Smite (level-scaled burst).

Adds 15 subclasses (3 per caster, registry now 30) using canonical 5e
archetype names, with passive-only L5/7/10/15 effects in
applySubclassPassives — consistent with the majority of the original
fifteen, no new resource pools or active abilities. subclassesForClass is
now non-empty for casters, so the L5 !subclass prompt and sheet display
light up through the existing generic plumbing.

Tests: TestApplyClassPassives extended to all ten classes and the new
channels; TestSubclassRegistry_* updated to expect 30 entries across all
ten classes.
This commit is contained in:
prosolis
2026-05-14 15:18:29 -07:00
parent 6ef8b9fd0a
commit d6ea08bba6
5 changed files with 480 additions and 14 deletions

View File

@@ -340,6 +340,331 @@ func applySubclassPassives(stats *CombatStats, mods *CombatModifiers, c *DnDChar
if c.Level >= 15 {
mods.DamageReduct *= 0.90
}
// ── Open5e caster scaffold — Druid / Bard / Sorcerer / Warlock /
// Paladin subclasses. Passive-only, consistent with the majority of the
// original fifteen (Champion, Thief, the Mage and Ranger subclasses):
// no new resource pools or active abilities. Each L5/7/10/15 tier rides
// an existing CombatModifiers channel — per-case notes name the 5e
// ability each proxy stands in for. DamageReduct is initialized to 1.0
// by DerivePlayerStats, so the `*=` reductions compose cleanly.
case SubclassCircleLand:
// L5 Natural Recovery: a measured self-heal — rides HealItem (fires
// once at <50% HP). L7 Land's Stride: +2 Speed (initiative). L10
// Nature's Ward: resistance to a slice of elemental/poison damage,
// collapsed to a flat 8% incoming reduction. L15 Nature's Sanctuary:
// foes hesitate to close — 1 round of SporeCloud — plus a further 10%.
if c.Level >= 5 {
mods.HealItem += 5
}
if c.Level >= 7 {
stats.Speed += 2
}
if c.Level >= 10 {
mods.DamageReduct *= 0.92
}
if c.Level >= 15 {
mods.SporeCloud++
mods.DamageReduct *= 0.90
}
case SubclassCircleMoon:
// L5 Combat Wild Shape: a bestial form's HP pool — an ArcaneWardHP
// buffer scaling with level — plus the form's heavier hits (+10%
// damage). L7 Primal Strike: beast attacks bite harder (+5%). L10
// Elemental Wild Shape: an elemental form's opening slam — a
// FlatDmgStart burst. L15 Archdruid: the form barely tires — a flat
// 15% incoming reduction.
if c.Level >= 5 {
if buf := 3 * c.Level; mods.ArcaneWardHP < buf {
mods.ArcaneWardHP = buf
}
mods.DamageBonus += 0.10
}
if c.Level >= 7 {
mods.DamageBonus += 0.05
}
if c.Level >= 10 {
mods.FlatDmgStart += 8
}
if c.Level >= 15 {
mods.DamageReduct *= 0.85
}
case SubclassCircleShepherd:
// L5 Spirit Totem (Bear): a warding spirit grants a temp-HP buffer.
// L7 Mighty Summoner: a conjured beast joins the fight — rides the
// pet channel. L10 Guardian Spirit: the spirits mend you — HealItem.
// L15 Faithful Summons: the conjured ally is tougher and strikes
// more often.
if c.Level >= 5 {
if buf := 2 * c.Level; mods.ArcaneWardHP < buf {
mods.ArcaneWardHP = buf
}
}
if c.Level >= 7 {
if mods.PetAttackProc < 0.25 {
mods.PetAttackProc = 0.25
}
if dmg := 3 + c.Level/2; mods.PetAttackDmg < dmg {
mods.PetAttackDmg = dmg
}
}
if c.Level >= 10 {
mods.HealItem += c.Level / 2
}
if c.Level >= 15 {
if mods.PetAttackProc < 0.45 {
mods.PetAttackProc = 0.45
}
if dmg := 8 + c.Level/2; mods.PetAttackDmg < dmg {
mods.PetAttackDmg = dmg
}
}
case SubclassCollegeLore:
// L5 Cutting Words: a barbed quip throws the foe off — 2 rounds of
// SporeCloud miss chance. L7 Additional Magical Secrets: borrowed
// versatility, a flat +1 to attack rolls. L10 Peerless Skill: a
// self-aimed Bardic Inspiration die on the opener (FirstAttackBonus).
// L15: deeper Magical Secrets keep paying out — +10% damage.
if c.Level >= 5 {
mods.SporeCloud += 2
}
if c.Level >= 7 {
stats.AttackBonus++
}
if c.Level >= 10 {
mods.FirstAttackBonus += 5
}
if c.Level >= 15 {
mods.DamageBonus += 0.10
}
case SubclassCollegeValor:
// L5 Combat Inspiration: songs that sharpen the blade (+10% damage).
// L7 Extra Attack: throughput — +1 attack and a further +10% damage
// proxy for the second swing. L10 Battle Magic: a bonus-action
// strike after a spell — a FlatDmgStart burst. L15: +1 AC, the
// late-game shield half of Combat Inspiration.
if c.Level >= 5 {
mods.DamageBonus += 0.10
}
if c.Level >= 7 {
stats.AttackBonus++
mods.DamageBonus += 0.10
}
if c.Level >= 10 {
mods.FlatDmgStart += 6
}
if c.Level >= 15 {
stats.AC++
}
case SubclassCollegeGlamour:
// L5 Mantle of Inspiration: a fey-glamour temp-HP buffer. L7 Mantle
// of Majesty: the foe is briefly enthralled — skips its first
// attack. L10 Enthralling Performance: lingering fascination — 2
// rounds of SporeCloud miss chance. L15 Unbreakable Majesty: foes
// struggle to bring themselves to strike you — a flat 15% reduction.
if c.Level >= 5 {
if buf := 2 * c.Level; mods.ArcaneWardHP < buf {
mods.ArcaneWardHP = buf
}
}
if c.Level >= 7 {
mods.SpellEnemySkipFirst = true
}
if c.Level >= 10 {
mods.SporeCloud += 2
}
if c.Level >= 15 {
mods.DamageReduct *= 0.85
}
case SubclassDraconicBloodline:
// L5 Draconic Resilience: scaled hide — +1 AC and an HP buffer. L7
// Elemental Affinity: CHA folded into spell damage — +10%. L10
// Dragon Wings: mobility and a tougher frame — an 8% reduction and a
// small damage bump. L15 Draconic Presence: a frightful aura — 2
// rounds of SporeCloud miss chance.
if c.Level >= 5 {
stats.AC++
if mods.ArcaneWardHP < c.Level {
mods.ArcaneWardHP = c.Level
}
}
if c.Level >= 7 {
mods.DamageBonus += 0.10
}
if c.Level >= 10 {
mods.DamageReduct *= 0.92
mods.DamageBonus += 0.05
}
if c.Level >= 15 {
mods.SporeCloud += 2
}
case SubclassWildMagic:
// L5 Tides of Chaos: fortune favors the opener — advantage on the
// first attack. L7 Bend Luck: nudge a roll your way —
// FirstAttackBonus. L10 Controlled Chaos: a surge you actually aimed
// — a FlatDmgStart burst. L15 Spell Bombardment: dice that keep
// exploding — +15% damage.
if c.Level >= 5 {
mods.AssassinateAdvantage = true
}
if c.Level >= 7 {
mods.FirstAttackBonus += 3
}
if c.Level >= 10 {
mods.FlatDmgStart += 10
}
if c.Level >= 15 {
mods.DamageBonus += 0.15
}
case SubclassStormSorcery:
// L5 Tempestuous Magic: the storm moves first — +1 initiative and
// +2 Speed. L7 Heart of the Storm: a thunderclap as you cast — a
// level-scaled FlatDmgStart burst. L10 Storm Guide: you ride the
// weather — a small 5% reduction and 1 round of SporeCloud. L15
// Storm's Fury: incoming blows are answered with lightning —
// ReflectNext on the first hit.
if c.Level >= 5 {
mods.InitiativeBias += 1
stats.Speed += 2
}
if c.Level >= 7 {
mods.FlatDmgStart += 5 + c.Level/2
}
if c.Level >= 10 {
mods.DamageReduct *= 0.95
mods.SporeCloud++
}
if c.Level >= 15 {
mods.ReflectNext += 0.30
}
case SubclassFiendPatron:
// L5 Dark One's Blessing: temp HP as foes fall — modeled as an
// up-front CHA-scaled buffer. L7 Dark One's Own Luck: a nudged roll
// — FirstAttackBonus. L10 Fiendish Resilience: chosen resistance — a
// flat 10% incoming reduction. L15 Hurl Through Hell: a foe sent
// briefly to the lower planes — a heavy FlatDmgStart burst.
if c.Level >= 5 {
chaMod := abilityModifier(c.CHA)
if chaMod < 0 {
chaMod = 0
}
if buf := c.Level + chaMod; mods.ArcaneWardHP < buf {
mods.ArcaneWardHP = buf
}
}
if c.Level >= 7 {
mods.FirstAttackBonus += 3
}
if c.Level >= 10 {
mods.DamageReduct *= 0.90
}
if c.Level >= 15 {
mods.FlatDmgStart += 12
}
case SubclassArchfeyPatron:
// L5 Fey Presence: the foe is briefly charmed/frightened — skips its
// first attack. L7 Misty Escape: you blink out of harm's way — 2
// rounds of SporeCloud miss chance. L10 Beguiling Defenses: hostile
// magic rebounds — ReflectNext on the first hit. L15 Dark Delirium:
// the foe is lost in illusion — more SporeCloud and a small edge.
if c.Level >= 5 {
mods.SpellEnemySkipFirst = true
}
if c.Level >= 7 {
mods.SporeCloud += 2
}
if c.Level >= 10 {
mods.ReflectNext += 0.25
}
if c.Level >= 15 {
mods.SporeCloud += 2
mods.DamageBonus += 0.05
}
case SubclassGreatOldOne:
// L5 Entropic Ward: a missed attack tilts the next exchange — a
// small reduction and 1 round of SporeCloud. L7 Thought Shield:
// psychic resilience — a flat 8% incoming reduction. L10 Create
// Thrall: a dominated mind fights beside you — the pet channel. L15:
// deeper psychic dominion keeps the thrall in the fight and sharpens
// your own assault (+10% damage).
if c.Level >= 5 {
mods.DamageReduct *= 0.95
mods.SporeCloud++
}
if c.Level >= 7 {
mods.DamageReduct *= 0.92
}
if c.Level >= 10 {
if mods.PetAttackProc < 0.30 {
mods.PetAttackProc = 0.30
}
if dmg := 4 + c.Level/2; mods.PetAttackDmg < dmg {
mods.PetAttackDmg = dmg
}
}
if c.Level >= 15 {
mods.DamageBonus += 0.10
}
case SubclassOathDevotion:
// L5 Sacred Weapon: a blade lit with the oath — +1 attack and
// radiant damage on every hit (DivineStrikePerHit, the channel the
// Cleric domains use). L7 Aura of Devotion: a small warding aura —
// 5% reduction. L10 Aura of Courage/Purity: the aura hardens — a
// further 8% reduction. L15 Holy Nimbus: a radiant aura that sears
// the foe on engagement — a FlatDmgStart burst.
if c.Level >= 5 {
stats.AttackBonus++
mods.DivineStrikePerHit += 4
}
if c.Level >= 7 {
mods.DamageReduct *= 0.95
}
if c.Level >= 10 {
mods.DamageReduct *= 0.92
}
if c.Level >= 15 {
mods.FlatDmgStart += 10
}
case SubclassOathVengeance:
// L5 Vow of Enmity: advantage against the marked foe plus the focus
// to press it — advantage on the opener and +10% damage. L7
// Relentless Avenger: you keep pace with a fleeing target — +2 Speed
// and a small damage bump. L10 Soul of Vengeance: an extra strike at
// the marked foe — +10% damage. L15: the hunt never lets up —
// a FlatDmgStart opener.
if c.Level >= 5 {
mods.AssassinateAdvantage = true
mods.DamageBonus += 0.10
}
if c.Level >= 7 {
stats.Speed += 2
mods.DamageBonus += 0.05
}
if c.Level >= 10 {
mods.DamageBonus += 0.10
}
if c.Level >= 15 {
mods.FlatDmgStart += 6
}
case SubclassOathAncients:
// L5 Nature's Wrath: the green and growing things slow the foe — a
// small reduction. L7 Aura of Warding: resistance to spell damage —
// a further 8% reduction. L10 Undying Sentinel: you refuse to fall —
// survive one otherwise-lethal hit (DeathSave). L15 Elder Champion:
// the old light blazes up — +1 attack and +10% damage.
if c.Level >= 5 {
mods.DamageReduct *= 0.95
}
if c.Level >= 7 {
mods.DamageReduct *= 0.92
}
if c.Level >= 10 {
mods.DeathSave = true
}
if c.Level >= 15 {
stats.AttackBonus++
mods.DamageBonus += 0.10
}
}
}