mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 08:52:42 +00:00
Adv 2.0 D&D Phase 10 SUB3a-iii: Thief L10/L15
L10 Use Magic Device: no scroll/wand item system to gate, so we proxy the "now allowed to use magic items" fantasy as a small per-fight wand zap (FlatDmgStart += 6) plus a +5% DamageBonus representing better tactical use of magical implements. L15 Thief's Reflexes: engine has no "extra turn round 1" primitive, so we ride the AssassinateBonusDmg + AssassinateAdvantage channel (same one Gloom Stalker's Dread Ambusher uses for bonus opener damage). Flat +8 on first hit approximates one extra turn's hit; advantage on first attack covers the "you swing first" piece. 3 new tests cover the L10 thresholds, L9 gate, and the L15 layer stacking on top of L10. Full suite green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,28 @@ func applySubclassPassives(stats *CombatStats, mods *CombatModifiers, c *DnDChar
|
|||||||
if c.Level >= 7 {
|
if c.Level >= 7 {
|
||||||
stats.AttackBonus++
|
stats.AttackBonus++
|
||||||
}
|
}
|
||||||
|
case SubclassThief:
|
||||||
|
// Phase 10 SUB3a-iii — Thief L10/L15.
|
||||||
|
//
|
||||||
|
// L10 Use Magic Device (5e L13: ignore class/race/level requirements
|
||||||
|
// on magic items, including spell scrolls and wands). We don't have a
|
||||||
|
// scroll/wand item system, so we proxy the fantasy as a small free
|
||||||
|
// "magical implement" effect every fight: a pre-combat wand-zap and a
|
||||||
|
// flat damage bump representing better tactical use of magic items
|
||||||
|
// the Thief is now allowed to wield.
|
||||||
|
// L15 Thief's Reflexes (5e L17: take two turns during the first round
|
||||||
|
// of combat). Engine has no "extra turn" primitive, so we ride the
|
||||||
|
// AssassinateBonusDmg + AssassinateAdvantage channel — the same one
|
||||||
|
// Gloom Stalker's Dread Ambusher uses for bonus-opener damage. The
|
||||||
|
// flat-damage proxy approximates the value of one extra turn's hit.
|
||||||
|
if c.Level >= 10 {
|
||||||
|
mods.FlatDmgStart += 6
|
||||||
|
mods.DamageBonus += 0.05
|
||||||
|
}
|
||||||
|
if c.Level >= 15 {
|
||||||
|
mods.AssassinateAdvantage = true
|
||||||
|
mods.AssassinateBonusDmg += 8
|
||||||
|
}
|
||||||
case SubclassAbjuration:
|
case SubclassAbjuration:
|
||||||
// L5 Arcane Ward: HP buffer = 2× Mage level. 5e fluffs this as
|
// L5 Arcane Ward: HP buffer = 2× Mage level. 5e fluffs this as
|
||||||
// recharged when casting an abjuration spell of L1+; we approximate
|
// recharged when casting an abjuration spell of L1+; we approximate
|
||||||
|
|||||||
@@ -1020,6 +1020,55 @@ func TestInitSubclassResources_BattleMasterL15GrowsPool(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── SUB3a-iii — Thief L10/L15 ───────────────────────────────────────────
|
||||||
|
|
||||||
|
func TestApplySubclassPassives_ThiefL10UseMagicDevice(t *testing.T) {
|
||||||
|
c := &DnDCharacter{Class: ClassRogue, Subclass: SubclassThief, Level: 10}
|
||||||
|
stats := &CombatStats{}
|
||||||
|
mods := &CombatModifiers{DamageReduct: 1.0}
|
||||||
|
applySubclassPassives(stats, mods, c)
|
||||||
|
if mods.FlatDmgStart != 6 {
|
||||||
|
t.Errorf("L10 Thief FlatDmgStart = %d, want 6 (UMD wand zap)", mods.FlatDmgStart)
|
||||||
|
}
|
||||||
|
if mods.DamageBonus < 0.049 || mods.DamageBonus > 0.051 {
|
||||||
|
t.Errorf("L10 Thief DamageBonus = %v, want ~0.05 (UMD)", mods.DamageBonus)
|
||||||
|
}
|
||||||
|
if mods.AssassinateAdvantage {
|
||||||
|
t.Error("L10 Thief should not have AssassinateAdvantage (L15 only)")
|
||||||
|
}
|
||||||
|
if mods.AssassinateBonusDmg != 0 {
|
||||||
|
t.Errorf("L10 Thief AssassinateBonusDmg = %d, want 0 (L15 only)", mods.AssassinateBonusDmg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplySubclassPassives_ThiefL9NoUseMagicDevice(t *testing.T) {
|
||||||
|
c := &DnDCharacter{Class: ClassRogue, Subclass: SubclassThief, Level: 9}
|
||||||
|
mods := &CombatModifiers{DamageReduct: 1.0}
|
||||||
|
applySubclassPassives(&CombatStats{}, mods, c)
|
||||||
|
if mods.FlatDmgStart != 0 {
|
||||||
|
t.Errorf("L9 Thief FlatDmgStart = %d, want 0 (UMD gates at L10)", mods.FlatDmgStart)
|
||||||
|
}
|
||||||
|
if mods.DamageBonus != 0 {
|
||||||
|
t.Errorf("L9 Thief DamageBonus = %v, want 0", mods.DamageBonus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApplySubclassPassives_ThiefL15ThiefsReflexes(t *testing.T) {
|
||||||
|
c := &DnDCharacter{Class: ClassRogue, Subclass: SubclassThief, Level: 15}
|
||||||
|
mods := &CombatModifiers{DamageReduct: 1.0}
|
||||||
|
applySubclassPassives(&CombatStats{}, mods, c)
|
||||||
|
if !mods.AssassinateAdvantage {
|
||||||
|
t.Error("L15 Thief should have AssassinateAdvantage (Thief's Reflexes)")
|
||||||
|
}
|
||||||
|
if mods.AssassinateBonusDmg != 8 {
|
||||||
|
t.Errorf("L15 Thief AssassinateBonusDmg = %d, want 8 (extra turn proxy)", mods.AssassinateBonusDmg)
|
||||||
|
}
|
||||||
|
// L10 layer must still apply.
|
||||||
|
if mods.FlatDmgStart != 6 {
|
||||||
|
t.Errorf("L15 Thief FlatDmgStart = %d, want 6 (L10 UMD still active)", mods.FlatDmgStart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInitSubclassResources_BattleMasterL15PreservesSpentDice(t *testing.T) {
|
func TestInitSubclassResources_BattleMasterL15PreservesSpentDice(t *testing.T) {
|
||||||
// Player burned through 3 dice (current=1, max=4), then levels up to 15.
|
// Player burned through 3 dice (current=1, max=4), then levels up to 15.
|
||||||
// The growth delta (+1) should be added to current → cur=2, max=5. We
|
// The growth delta (+1) should be added to current → cur=2, max=5. We
|
||||||
|
|||||||
Reference in New Issue
Block a user