/** * Diablo II: Lord of Destruction v1.13c — Barbarian Milestone M20 Class Gate & End-to-End Suite * * Comprehensive cross-tree integration and architectural verification across all 30 Barbarian skills: * * Skill Trees Covered: * 1. Combat Skills: * - Bash (126), Leap (132), Double Swing (133), Stun (139), Double Throw (140), * Leap Attack (143), Concentrate (144), Frenzy (147), Whirlwind (151), Berserk (152). * 2. Combat Masteries: * - Sword Mastery (127), Axe Mastery (128), Mace Mastery (129), Polearm Mastery (134), * Throwing Mastery (135), Spear Mastery (136), Increased Stamina (141), Iron Skin (145), * Increased Speed (148), Natural Resistance (153). * 3. Warcries: * - Howl (130), Find Potion (131), Taunt (137), Shout (138), Find Item (142), * Battle Cry (146), Battle Orders (149), Grim Ward (150), War Cry (154), Battle Command (155). * * Core Verification Scenarios: * Scenario 1: Cross-Tree Buff & Warcry Duration Network (Shout, Battle Orders, Battle Command) * Scenario 2: Berserk 100% Magic Conversion & Zero-Defense Vulnerability Duration * Scenario 3: Frenzy Dual-Wield Stacking & Momentum Kinematics * Scenario 4: Whirlwind Uninterruptible Area Sweeping & 1.13c Damage Progression * Scenario 5: War Cry Radial Stun & 5-Band Physical Damage Synergies * Scenario 6: Weapon Masteries, Diminishing Returns Critical Strike & Stacking * Scenario 7: Corpse & Crowd Control Utilities (Find Potion, Find Item, Grim Ward, Taunt, Battle Cry) * Scenario 8: Complete 30-Skill Barbarian Tree Registry & Architectural Invariants * * ZERO MOCKS: vi.mock / vi.spyOn strictly forbidden. * ZERO CIRCULAR ORACLES: Precalculated independently from 1.13c ground truth constants. */ import { describe, expect, it } from 'vitest' import { BATCH1_SKILLS } from '../../../src/server/skills/skills.ts' import { calculateBashStats, calculateLeapStats, calculateDoubleSwingStats, calculateStunStats, calculateDoubleThrowStats, calculateLeapAttackStats, calculateConcentrateStats, calculateFrenzyStats, calculateWhirlwindStats, calculateBerserkStats, } from '../../../src/server/skills/barbarian-combat.ts' import { calculateSwordMasteryStats, calculateAxeMasteryStats, calculateMaceMasteryStats, calculatePolearmMasteryStats, calculateThrowingMasteryStats, calculateSpearMasteryStats, calculateIncreasedStaminaStats, calculateIronSkinStats, calculateIncreasedSpeedStats, calculateNaturalResistanceStats, isWeaponMatchingMastery, calculateEffectiveCriticalStrike, evaluateBarbarianMastery, } from '../../../src/server/skills/barbarian-masteries.ts' import { calculateHowlStats, calculateFindPotionStats, calculateTauntStats, calculateShoutStats, calculateFindItemStats, calculateBattleCryStats, calculateBattleOrdersStats, calculateGrimWardStats, calculateWarCryStats, calculateBattleCommandStats, evaluateBarbarianWarcry, } from '../../../src/server/skills/barbarian-warcries.ts' import { getSharedDataRegistry } from '../../../src/baker/mpq-archive-source.ts' describe('Milestone M20 — Barbarian Class Gate & Comprehensive E2E Integration Suite', () => { describe('Scenario 1: Cross-Tree Buff & Warcry Duration Network (Shout, Battle Orders, Battle Command)', () => { it('verifies mutual duration synergy (+125 frames / 5s per blvl)', () => { // Base durations const shoutBase = calculateShoutStats(20, 0, 0) const boBase = calculateBattleOrdersStats(20, 0, 0) const bcBase = calculateBattleCommandStats(20, 0, 0) expect(shoutBase.durationFrames).toBe(5250) // 210s expect(boBase.durationFrames).toBe(5500) // 220s expect(bcBase.durationFrames).toBe(4875) // 195s // Fully synergized party shout (20 BO, 20 BC): +5000 frames (200s) const shoutMax = calculateShoutStats(20, 20, 20) expect(shoutMax.durationFrames).toBe(10250) // 410s expect(shoutMax.durationSeconds).toBe(410.0) // Fully synergized Battle Orders (20 Shout, 20 BC): +5000 frames const boMax = calculateBattleOrdersStats(20, 20, 20) expect(boMax.durationFrames).toBe(10500) // 420s expect(boMax.durationSeconds).toBe(420.0) // Stat boosts: Shout defense (+290%), BO life/mana (+92%), BC all skills (+1) expect(shoutMax.defenseBonusPct).toBe(290) expect(boMax.maxLifeBonusPct).toBe(92) expect(boMax.maxManaBonusPct).toBe(92) expect(bcBase.allSkillsBonus).toBe(1) }) }) describe('Scenario 2: Berserk 100% Magic Conversion & Zero-Defense Vulnerability', () => { it('verifies 100% physical to magic damage conversion and vulnerability duration formula', () => { const zrk1 = calculateBerserkStats(1, 0, 0) expect(zrk1.magicConversionPct).toBe(100) expect(zrk1.zeroDefenseDurationFrames).toBe(68) // 75 - floor(110/7 * 0.5) = 75 - 7 = 68 const zrk10 = calculateBerserkStats(10, 0, 0) expect(zrk10.magicConversionPct).toBe(100) expect(zrk10.zeroDefenseDurationFrames).toBeLessThan(68) expect(zrk10.zeroDefenseDurationFrames).toBeGreaterThan(25) const zrk20 = calculateBerserkStats(20, 0, 0) expect(zrk20.zeroDefenseDurationFrames).toBeLessThan(zrk10.zeroDefenseDurationFrames) expect(zrk20.zeroDefenseDurationFrames).toBeGreaterThanOrEqual(25) // Synergies: Howl +10%/lvl, Shout +10%/lvl (150 base + 19 * 15 + 400 = 835%) const zrkMax = calculateBerserkStats(20, 20, 20) expect(zrkMax.damageBonusPct).toBe(150 + 19 * 15 + 400) expect(zrkMax.magicConversionPct).toBe(100) }) }) describe('Scenario 3: Frenzy Dual-Wield Stacking & Momentum Kinematics', () => { it('verifies dual-wield requirements, speed scaling, and 150-frame timer', () => { const fr1 = calculateFrenzyStats(1, 0, 0, 0) expect(fr1.requiresDualWield).toBe(true) expect(fr1.durationFrames).toBe(150) // 6.0s expect(fr1.minRunSpeedPct).toBe(20) const fr20 = calculateFrenzyStats(20, 20, 20, 20) expect(fr20.durationFrames).toBe(150) expect(fr20.damageBonusPct).toBe(90 + 19 * 5 + 20 * 8 + 20 * 8) // 90 base + 95 lvl + 160 DS + 160 Taunt expect(fr20.magicConversionPct).toBe(20) // 20% from Berserk }) }) describe('Scenario 4: Whirlwind Uninterruptible Area Sweeping & 1.13c Damage Progression', () => { it('verifies authentic negative scaling at slvl 1 and 4-frame hit checks', () => { const ww1 = calculateWhirlwindStats(1) expect(ww1.damageBonusPct).toBe(-50) expect(ww1.hitFrequencyFrames).toBe(4) const ww10 = calculateWhirlwindStats(10) expect(ww10.damageBonusPct).toBe(22) // -50 + 8 * 9 const ww20 = calculateWhirlwindStats(20) expect(ww20.damageBonusPct).toBe(102) // -50 + 8 * 19 expect(ww20.hitFrequencyFrames).toBe(4) }) }) describe('Scenario 5: War Cry Radial Stun & 5-Band Physical Damage Synergies', () => { it('verifies 5-band damage scaling, stun length, and synergies', () => { const wc1 = calculateWarCryStats(1, 0, 0, 0) expect(wc1.minPhysicalDamage).toBe(20) expect(wc1.maxPhysicalDamage).toBe(30) expect(wc1.stunDurationFrames).toBe(25) // 1.0s const wc10 = calculateWarCryStats(10, 0, 0, 0) expect(wc10.minPhysicalDamage).toBe(76) expect(wc10.maxPhysicalDamage).toBe(86) expect(wc10.stunDurationFrames).toBe(70) // 2.8s const wc20 = calculateWarCryStats(20, 0, 0, 0) expect(wc20.minPhysicalDamage).toBe(150) expect(wc20.maxPhysicalDamage).toBe(160) expect(wc20.stunDurationFrames).toBe(120) // 4.8s // Fully synergized (20 Howl, 20 Taunt, 20 Battle Cry): +360% dmg const wcMax = calculateWarCryStats(20, 20, 20, 20) expect(wcMax.synergyBonusPct).toBe(360) expect(wcMax.minPhysicalDamage).toBe(690) expect(wcMax.maxPhysicalDamage).toBe(736) }) }) describe('Scenario 6: Weapon Masteries, Diminishing Returns Critical Strike & Stacking', () => { it('verifies weapon category recognition and non-quadrupling critical strike rolls', () => { // Weapon matching expect(isWeaponMatchingMastery('legend sword', 127)).toBe(true) expect(isWeaponMatchingMastery('glorious axe', 128)).toBe(true) expect(isWeaponMatchingMastery('thunder maul', 129)).toBe(true) expect(isWeaponMatchingMastery('cryptic axe', 134)).toBe(true) expect(isWeaponMatchingMastery('winged harpoon', 135)).toBe(true) expect(isWeaponMatchingMastery('ghost spear', 136)).toBe(true) // Sequential CS + DS roll const combined = calculateEffectiveCriticalStrike(29, 50) // 29% CS, 50% DS -> 29 + 50 * (1 - 0.29) = 29 + 35.5 = 64.5% expect(combined).toBe(64.5) // Masteries evaluate cleanly const sw20 = calculateSwordMasteryStats(20) expect(sw20.criticalStrikeChancePct).toBe(29) expect(sw20.attackRatingBonusPct).toBe(180) expect(sw20.damageBonusPct).toBe(123) }) }) describe('Scenario 7: Corpse & Crowd Control Utilities (Find Potion, Find Item, Grim Ward, Taunt, Battle Cry)', () => { it('verifies corpse requirements and debuff percentages', () => { expect(calculateFindPotionStats(20).requiresCorpse).toBe(true) expect(calculateFindItemStats(20).requiresCorpse).toBe(true) expect(calculateGrimWardStats(20).requiresCorpse).toBe(true) expect(calculateGrimWardStats(20).totemDurationFrames).toBe(1000) expect(calculateGrimWardStats(20).fleeRadiusYards).toBe(22) const bc20 = calculateBattleCryStats(20) expect(bc20.targetDefenseReductionPct).toBe(88) expect(bc20.targetDamageReductionPct).toBe(44) const taunt20 = calculateTauntStats(20) expect(taunt20.targetAttackRatingReductionPct).toBe(43) expect(taunt20.targetDamageReductionPct).toBe(43) expect(taunt20.forcesMeleeAttack).toBe(true) }) }) describe('Scenario 8: Complete 30-Skill Barbarian Tree Registry & Architectural Invariants', () => { it('verifies all 30 Barbarian skills exist in the 1.13c Data Registry', async () => { const registry = await getSharedDataRegistry() // Barbarian skill IDs are 126..155 inclusive for (let id = 126; id <= 155; id++) { const skill = registry.getSkillById(id) expect(skill, `Skill ${id} must exist in registry`).toBeDefined() expect(skill?.charClass).toBe('bar') } }) it('enforces BATCH1_SKILLS length invariant = strictly 38', () => { expect(Object.keys(BATCH1_SKILLS).length).toBe(38) }) }) })