/** * Adversarial Stress & Correctness Verification Suite — Milestone 1: Three Sorceress Cold Armors * Skills Under Test: * - Frozen Armor (Skill #040, State 10, Group 1) * - Shiver Armor (Skill #050, State 88, Group 1) * - Chilling Armor (Skill #060, State 20, Group 1) * * Target Commit: e7d105d * Author: Challenger 1 (teamwork_preview_challenger) * Ground Truth Reference: Diablo II v1.13c (D2Game.dll, D2Common.dll, Skills.txt, Missiles.txt, Overlay.txt, States.txt) * * Verification Dimensions: * 1. Exhaustive Mathematical Boundary Sweeps (slvl 1..99 x synergies 0..20 / 0..40): * - Monotonicity of defense bonus, duration, cold damage, and freeze/chill lengths. * - Zero NaN, zero undefined, and positive non-zero outputs for all valid inputs. * - Exact formula validation against 1.13c ground truth analytical models. * 2. Degenerate, Extreme & Adversarial Inputs: * - Non-finite, negative, zero, and float slvl handling. * - Missing, malformed, negative, and mixed synergy structures. * 3. State Group 1 Mutual Exclusion Stress Testing: * - Rapid successive casting bursts & 100-step randomized permutations. * - Invariant: strictly 1 active Group 1 state at all times. * - Level-override rules (different armor overwrites regardless of slvl; same armor rejects lower slvl). * - Clean eviction of StatList defense modifiers upon armor transition. * 4. Reactive Trigger Adversarial Edge Cases: * - Frozen Armor: damagedinmelee with physical damage = 0 must NOT freeze. * - Chilling Armor: hitbymissile with returnFire = false must NOT reflect. * - Shiver Armor: attackedinmelee triggers proactively before attack connects regardless of damage. * - Event cross-trigger isolation (no cross-firing between melee and missile events). * 5. Full Invariant & System Integrity Checks: * - BATCH1_SKILLS length strictly 38. * - Mana costs: flat 7 (40), flat 11 (50), flat 17 (60). * - Cooldown ticks: 0 for all three armors. * - calculateSkillDamage routing parity. */ import { describe, expect, it } from 'vitest' import { calculateFrozenArmorDefense, calculateFrozenArmorDuration, calculateFrozenArmorFreezeLength, calculateShiverArmorDefense, calculateShiverArmorDuration, calculateShiverArmorDamage, calculateShiverArmorChillLength, calculateChillingArmorDefense, calculateChillingArmorDuration, calculateChillingArmorDamage, calculateChillingArmorChillLength, calculateColdArmorDuration, calculateSkillDamage, getSkillManaCost, getSkillCooldownTicks, getSkillCastOverlay, BATCH1_SKILLS, } from '../src/game/skills.ts' import { getSharedDataRegistry } from '../src/game/engine/data-registry.ts' import { UnitStatList, FIXED_ONE } from '../src/game/engine/stat-list.ts' import { StateBus } from '../src/game/engine/state-bus.ts' import { OVERLAY_METAS } from '../src/render/overlays-meta.ts' import { MISSILE_METAS } from '../src/render/missiles-meta.ts' describe('Challenger 1 Stress Suite — Three Sorceress Cold Armors (M1: #040, #050, #060)', () => { // ========================================================================= // Dimension 1: Exhaustive Mathematical Boundary Sweeps (slvl 1..99 x syn 0..40) // ========================================================================= describe('Dimension 1: Exhaustive Mathematical Boundary Sweeps & Monotonicity', () => { describe('Frozen Armor (Skill 40)', () => { it('evaluates % Enhanced Defense across slvl 1..99 with strict monotonicity and zero NaN', () => { let prevDef = 0 for (let slvl = 1; slvl <= 99; slvl++) { const def = calculateFrozenArmorDefense(slvl) expect(Number.isFinite(def)).toBe(true) expect(Number.isNaN(def)).toBe(false) expect(def).toBeGreaterThan(0) // 1.13c formula: 30 + (slvl - 1) * 5 expect(def).toBe(30 + (slvl - 1) * 5) expect(def).toBeGreaterThan(prevDef) prevDef = def } expect(calculateFrozenArmorDefense(1)).toBe(30) expect(calculateFrozenArmorDefense(10)).toBe(75) expect(calculateFrozenArmorDefense(20)).toBe(125) expect(calculateFrozenArmorDefense(99)).toBe(520) }) it('evaluates Duration across slvl 1..99 and syn 0..40 with strict monotonicity and zero NaN', () => { // Sweep 99 * 41 = 4059 test cases for (let syn = 0; syn <= 40; syn++) { let prevDur = 0 for (let slvl = 1; slvl <= 99; slvl++) { const dur = calculateFrozenArmorDuration(slvl, syn) expect(Number.isFinite(dur)).toBe(true) expect(Number.isNaN(dur)).toBe(false) expect(dur).toBeGreaterThan(0) // 1.13c formula: 3000 + (slvl - 1) * 300 + syn * 250 const expected = 3000 + (slvl - 1) * 300 + syn * 250 expect(dur).toBe(expected) expect(dur).toBeGreaterThan(prevDur) prevDur = dur } } // Synergy monotonicity at fixed slvl for (let slvl = 1; slvl <= 20; slvl++) { let prevDur = 0 for (let syn = 0; syn <= 40; syn++) { const dur = calculateFrozenArmorDuration(slvl, syn) expect(dur).toBeGreaterThan(prevDur) prevDur = dur } } // Spot-checks expect(calculateFrozenArmorDuration(1, 0)).toBe(3000) // 120s expect(calculateFrozenArmorDuration(10, 0)).toBe(5700) // 228s expect(calculateFrozenArmorDuration(20, 0)).toBe(8700) // 348s expect(calculateFrozenArmorDuration(20, 40)).toBe(18700) // 748s expect(calculateFrozenArmorDuration(99, 40)).toBe(42400) // 1696s }) it('evaluates Freeze Length across slvl 1..99 and syn 0..40 with monotonicity and zero NaN', () => { for (let syn = 0; syn <= 40; syn++) { let prevFreeze = 0 for (let slvl = 1; slvl <= 99; slvl++) { const frz = calculateFrozenArmorFreezeLength(slvl, syn) expect(Number.isFinite(frz)).toBe(true) expect(Number.isNaN(frz)).toBe(false) expect(frz).toBeGreaterThan(0) // 1.13c formula: Math.floor((30 + (slvl - 1) * 3) * (100 + syn * 5) / 100) const expected = Math.floor((30 + (slvl - 1) * 3) * (100 + syn * 5) / 100) expect(frz).toBe(expected) expect(frz).toBeGreaterThan(prevFreeze) prevFreeze = frz } } // Synergy monotonicity at fixed slvl for (let slvl = 1; slvl <= 20; slvl++) { let prevFreeze = 0 for (let syn = 0; syn <= 40; syn++) { const frz = calculateFrozenArmorFreezeLength(slvl, syn) expect(frz).toBeGreaterThanOrEqual(prevFreeze) prevFreeze = frz } } // Spot-checks expect(calculateFrozenArmorFreezeLength(1, 0)).toBe(30) // 1.20s expect(calculateFrozenArmorFreezeLength(10, 0)).toBe(57) // 2.28s expect(calculateFrozenArmorFreezeLength(20, 0)).toBe(87) // 3.48s expect(calculateFrozenArmorFreezeLength(20, 40)).toBe(261) // 10.44s expect(calculateFrozenArmorFreezeLength(99, 40)).toBe(972) // 38.88s }) it('asserts Frozen Armor deals strictly 0 damage across slvl 1..99', () => { for (let slvl = 1; slvl <= 99; slvl++) { const dmg = calculateSkillDamage(40, slvl) expect(dmg).toEqual({ min: 0, max: 0 }) } }) }) describe('Shiver Armor (Skill 50)', () => { it('evaluates % Enhanced Defense across slvl 1..99 with strict monotonicity and zero NaN', () => { let prevDef = 0 for (let slvl = 1; slvl <= 99; slvl++) { const def = calculateShiverArmorDefense(slvl) expect(Number.isFinite(def)).toBe(true) expect(Number.isNaN(def)).toBe(false) expect(def).toBeGreaterThan(0) // 1.13c formula: 45 + (slvl - 1) * 6 expect(def).toBe(45 + (slvl - 1) * 6) expect(def).toBeGreaterThan(prevDef) prevDef = def } expect(calculateShiverArmorDefense(1)).toBe(45) expect(calculateShiverArmorDefense(10)).toBe(99) expect(calculateShiverArmorDefense(20)).toBe(159) expect(calculateShiverArmorDefense(99)).toBe(633) }) it('evaluates Duration across slvl 1..99 and syn 0..40 with strict monotonicity and zero NaN', () => { for (let syn = 0; syn <= 40; syn++) { let prevDur = 0 for (let slvl = 1; slvl <= 99; slvl++) { const dur = calculateShiverArmorDuration(slvl, syn) expect(Number.isFinite(dur)).toBe(true) expect(Number.isNaN(dur)).toBe(false) expect(dur).toBeGreaterThan(0) const expected = 3000 + (slvl - 1) * 300 + syn * 250 expect(dur).toBe(expected) expect(dur).toBeGreaterThan(prevDur) prevDur = dur } } expect(calculateShiverArmorDuration(1, 0)).toBe(3000) expect(calculateShiverArmorDuration(20, 0)).toBe(8700) expect(calculateShiverArmorDuration(20, 40)).toBe(18700) }) it('evaluates Cold Damage across slvl 1..99 and syn 0..40 with strict monotonicity and zero NaN', () => { for (let syn = 0; syn <= 40; syn += 5) { let prevMin = 0 let prevMax = 0 for (let slvl = 1; slvl <= 99; slvl++) { const dmg = calculateShiverArmorDamage(slvl, { frozenArmor: syn, chillingArmor: 0 }) expect(Number.isFinite(dmg.min)).toBe(true) expect(Number.isFinite(dmg.max)).toBe(true) expect(Number.isNaN(dmg.min)).toBe(false) expect(Number.isNaN(dmg.max)).toBe(false) expect(dmg.min).toBeGreaterThan(0) expect(dmg.max).toBeGreaterThanOrEqual(dmg.min) expect(dmg.min).toBeGreaterThan(prevMin) expect(dmg.max).toBeGreaterThan(prevMax) prevMin = dmg.min prevMax = dmg.max } } // Synergy monotonicity at fixed slvl for (let slvl = 1; slvl <= 20; slvl += 5) { let prevMin = 0 for (let syn = 0; syn <= 40; syn++) { const dmg = calculateShiverArmorDamage(slvl, syn) expect(dmg.min).toBeGreaterThanOrEqual(prevMin) prevMin = dmg.min } } // Authentic 1.13c spot-checks (HitShift = 7 -> divide by 2) expect(calculateShiverArmorDamage(1, 0)).toEqual({ min: 6, max: 8 }) expect(calculateShiverArmorDamage(10, 0)).toEqual({ min: 26, max: 32.5 }) expect(calculateShiverArmorDamage(20, 0)).toEqual({ min: 60, max: 71.5 }) // +40 syn points via object (+360% -> multiplier 4.6x): 60 * 4.6 = 276.0, 71.5 * 4.6 = 328.9 expect(calculateShiverArmorDamage(20, { frozenArmor: 20, chillingArmor: 20 })).toEqual({ min: 276.0, max: 328.9 }) // +40 syn points via numeric synergy points (40 pts -> 1 + 40*0.09 = 4.6x) expect(calculateShiverArmorDamage(20, 40)).toEqual({ min: 276.0, max: 328.9 }) // +40 syn points via options.synMultiplier (4.6x) expect(calculateShiverArmorDamage(20, undefined, { synMultiplier: 4.6 })).toEqual({ min: 276.0, max: 328.9 }) }) it('evaluates Chill Length across slvl 1..99 and syn 0..40 with piecewise monotonicity and zero NaN', () => { for (let syn = 0; syn <= 40; syn += 10) { let prevChill = 0 for (let slvl = 1; slvl <= 99; slvl++) { const chill = calculateShiverArmorChillLength(slvl, syn) expect(Number.isFinite(chill)).toBe(true) expect(Number.isNaN(chill)).toBe(false) expect(chill).toBeGreaterThan(0) expect(chill).toBeGreaterThanOrEqual(prevChill) prevChill = chill } } // Level band step checks without synergy: // slvl 1-8: flat 100 frames for (let lvl = 1; lvl <= 8; lvl++) { expect(calculateShiverArmorChillLength(lvl, 0)).toBe(100) } // slvl 9-16: +25 frames / level expect(calculateShiverArmorChillLength(9, 0)).toBe(125) expect(calculateShiverArmorChillLength(10, 0)).toBe(150) expect(calculateShiverArmorChillLength(16, 0)).toBe(300) // slvl 17+: +50 frames / level expect(calculateShiverArmorChillLength(17, 0)).toBe(350) expect(calculateShiverArmorChillLength(20, 0)).toBe(500) expect(calculateShiverArmorChillLength(99, 0)).toBe(4450) // With +40 syn (+280% factor = 3.8x): 500 * 3.8 = 1900 expect(calculateShiverArmorChillLength(20, 40)).toBe(1900) expect(calculateShiverArmorChillLength(20, { frozenArmor: 20, chillingArmor: 20 })).toBe(1900) }) }) describe('Chilling Armor (Skill 60)', () => { it('evaluates % Enhanced Defense across slvl 1..99 with strict monotonicity and zero NaN', () => { let prevDef = 0 for (let slvl = 1; slvl <= 99; slvl++) { const def = calculateChillingArmorDefense(slvl) expect(Number.isFinite(def)).toBe(true) expect(Number.isNaN(def)).toBe(false) expect(def).toBeGreaterThan(0) expect(def).toBe(45 + (slvl - 1) * 5) expect(def).toBeGreaterThan(prevDef) prevDef = def } expect(calculateChillingArmorDefense(1)).toBe(45) expect(calculateChillingArmorDefense(10)).toBe(90) expect(calculateChillingArmorDefense(20)).toBe(140) expect(calculateChillingArmorDefense(99)).toBe(535) }) it('evaluates Duration across slvl 1..99 and syn 0..40 with strict monotonicity and zero NaN', () => { for (let syn = 0; syn <= 40; syn++) { let prevDur = 0 for (let slvl = 1; slvl <= 99; slvl++) { const dur = calculateChillingArmorDuration(slvl, syn) expect(Number.isFinite(dur)).toBe(true) expect(Number.isNaN(dur)).toBe(false) expect(dur).toBeGreaterThan(0) const expected = 3600 + (slvl - 1) * 150 + syn * 250 expect(dur).toBe(expected) expect(dur).toBeGreaterThan(prevDur) prevDur = dur } } expect(calculateChillingArmorDuration(1, 0)).toBe(3600) // 144s expect(calculateChillingArmorDuration(10, 0)).toBe(4950) // 198s expect(calculateChillingArmorDuration(20, 0)).toBe(6450) // 258s expect(calculateChillingArmorDuration(20, 40)).toBe(16450) // 658s expect(calculateChillingArmorDuration(99, 40)).toBe(28300) // 1132s }) it('evaluates Retaliatory Bolt Cold Damage across slvl 1..99 and syn 0..40 with monotonicity and zero NaN', () => { for (let syn = 0; syn <= 40; syn += 5) { let prevMin = 0 let prevMax = 0 for (let slvl = 1; slvl <= 99; slvl++) { const dmg = calculateChillingArmorDamage(slvl, { frozenArmor: syn, shiverArmor: 0 }) expect(Number.isFinite(dmg.min)).toBe(true) expect(Number.isFinite(dmg.max)).toBe(true) expect(Number.isNaN(dmg.min)).toBe(false) expect(Number.isNaN(dmg.max)).toBe(false) expect(dmg.min).toBeGreaterThan(0) expect(dmg.max).toBeGreaterThanOrEqual(dmg.min) expect(dmg.min).toBeGreaterThan(prevMin) expect(dmg.max).toBeGreaterThan(prevMax) prevMin = dmg.min prevMax = dmg.max } } // Spot-checks expect(calculateChillingArmorDamage(1, 0)).toEqual({ min: 4, max: 6 }) expect(calculateChillingArmorDamage(10, 0)).toEqual({ min: 15, max: 21.5 }) expect(calculateChillingArmorDamage(20, 0)).toEqual({ min: 39, max: 50.5 }) // +40 syn points via object (+280% -> multiplier 3.8x): 39 * 3.8 = 148.2, 50.5 * 3.8 = 191.9 expect(calculateChillingArmorDamage(20, { frozenArmor: 20, shiverArmor: 20 })).toEqual({ min: 148.2, max: 191.9 }) // +40 syn points via numeric synergy points (40 pts -> 1 + 40*0.07 = 3.8x) expect(calculateChillingArmorDamage(20, 40)).toEqual({ min: 148.2, max: 191.9 }) // +40 syn points via options.synMultiplier (3.8x) expect(calculateChillingArmorDamage(20, undefined, { synMultiplier: 3.8 })).toEqual({ min: 148.2, max: 191.9 }) }) it('evaluates Chill Length across slvl 1..99 and syn 0..40 with piecewise monotonicity and zero NaN', () => { for (let syn = 0; syn <= 40; syn += 10) { let prevChill = 0 for (let slvl = 1; slvl <= 99; slvl++) { const chill = calculateChillingArmorChillLength(slvl, syn) expect(Number.isFinite(chill)).toBe(true) expect(Number.isNaN(chill)).toBe(false) expect(chill).toBeGreaterThan(0) expect(chill).toBeGreaterThanOrEqual(prevChill) prevChill = chill } } // Band step checks without synergy: for (let lvl = 1; lvl <= 8; lvl++) { expect(calculateChillingArmorChillLength(lvl, 0)).toBe(100) } expect(calculateChillingArmorChillLength(9, 0)).toBe(125) expect(calculateChillingArmorChillLength(10, 0)).toBe(150) expect(calculateChillingArmorChillLength(16, 0)).toBe(300) // slvl 17+: +25 frames / level expect(calculateChillingArmorChillLength(17, 0)).toBe(325) expect(calculateChillingArmorChillLength(20, 0)).toBe(400) expect(calculateChillingArmorChillLength(99, 0)).toBe(2375) // With +40 syn (+400% factor = 5.0x): 400 * 5.0 = 2000 expect(calculateChillingArmorChillLength(20, 40)).toBe(2000) expect(calculateChillingArmorChillLength(20, { frozenArmor: 20, shiverArmor: 20 })).toBe(2000) }) }) }) // ========================================================================= // Dimension 2: Degenerate, Extreme & Adversarial Inputs // ========================================================================= describe('Dimension 2: Degenerate, Extreme & Adversarial Inputs', () => { it('clamps negative, zero, and non-integer slvl gracefully without throwing or returning NaN', () => { const dirtyLvls = [0, -1, -99, 0.5, 12.8, NaN, Infinity, -Infinity] for (const slvl of dirtyLvls) { // Frozen Armor const fDef = calculateFrozenArmorDefense(slvl) const fDur = calculateFrozenArmorDuration(slvl) const fFrz = calculateFrozenArmorFreezeLength(slvl) expect(Number.isFinite(fDef)).toBe(true) expect(Number.isFinite(fDur)).toBe(true) expect(Number.isFinite(fFrz)).toBe(true) // Shiver Armor const sDef = calculateShiverArmorDefense(slvl) const sDur = calculateShiverArmorDuration(slvl) const sDmg = calculateShiverArmorDamage(slvl) const sChl = calculateShiverArmorChillLength(slvl) expect(Number.isFinite(sDef)).toBe(true) expect(Number.isFinite(sDur)).toBe(true) expect(Number.isFinite(sDmg.min)).toBe(true) expect(Number.isFinite(sDmg.max)).toBe(true) expect(Number.isFinite(sChl)).toBe(true) // Chilling Armor const cDef = calculateChillingArmorDefense(slvl) const cDur = calculateChillingArmorDuration(slvl) const cDmg = calculateChillingArmorDamage(slvl) const cChl = calculateChillingArmorChillLength(slvl) expect(Number.isFinite(cDef)).toBe(true) expect(Number.isFinite(cDur)).toBe(true) expect(Number.isFinite(cDmg.min)).toBe(true) expect(Number.isFinite(cDmg.max)).toBe(true) expect(Number.isFinite(cChl)).toBe(true) } }) it('handles degenerate synergy input shapes (undefined, empty, invalid objects)', () => { const benignSynergies = [undefined, {}, { fake: 99 }, null as any] for (const syn of benignSynergies) { expect(() => calculateFrozenArmorDuration(20, syn)).not.toThrow() expect(() => calculateShiverArmorDamage(20, syn)).not.toThrow() expect(() => calculateChillingArmorDamage(20, syn)).not.toThrow() expect(calculateFrozenArmorDuration(20, syn)).toBe(8700) expect(calculateShiverArmorDamage(20, syn)).toEqual({ min: 60, max: 71.5 }) expect(calculateChillingArmorDamage(20, syn)).toEqual({ min: 39, max: 50.5 }) } // Damage functions safely default to multiplier 1.0 when negative or non-finite numbers are passed expect(calculateShiverArmorDamage(20, -10)).toEqual({ min: 60, max: 71.5 }) expect(calculateShiverArmorDamage(20, NaN)).toEqual({ min: 60, max: 71.5 }) expect(calculateShiverArmorDamage(20, Infinity)).toEqual({ min: 60, max: 71.5 }) expect(calculateChillingArmorDamage(20, -10)).toEqual({ min: 39, max: 50.5 }) expect(calculateChillingArmorDamage(20, NaN)).toEqual({ min: 39, max: 50.5 }) expect(calculateChillingArmorDamage(20, Infinity)).toEqual({ min: 39, max: 50.5 }) }) }) // ========================================================================= // Dimension 3: State Group 1 Mutual Exclusion Stress Testing // ========================================================================= describe('Dimension 3: State Group 1 Mutual Exclusion Stress Testing', () => { it('strictly maintains exactly 1 active Cold Armor during rapid alternating casts', async () => { const registry = await getSharedDataRegistry() const stats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const bus = new StateBus(stats, registry) const armors: { name: string; id: number }[] = [ { name: 'frozenarmor', id: 40 }, { name: 'shiverarmor', id: 50 }, { name: 'chillingarmor', id: 60 }, ] // 30 cycles of Frozen -> Shiver -> Chilling (90 casts total) for (let cycle = 0; cycle < 30; cycle++) { for (const armor of armors) { const res = bus.applyState({ stateNameOrId: armor.name, sourceSkillId: armor.id, slvl: 20, stats: { skill_armor_percent: 100 + armor.id }, }) expect(res.applied).toBe(true) // Invariant 1: Exactly 1 state with group === 1 const activeGroup1 = bus.getActiveEntries().filter(e => e.group === 1) expect(activeGroup1.length).toBe(1) expect(activeGroup1[0]!.stateName).toBe(armor.name) // Invariant 2: hasState confirms only current armor is active for (const other of armors) { if (other.name === armor.name) { expect(bus.hasState(other.name)).toBe(true) } else { expect(bus.hasState(other.name)).toBe(false) } } // Invariant 3: statList modifiers from previous cold armor are completely removed const activeMods = stats.getActiveModifiers() const armorMods = activeMods.filter(m => armors.some(a => a.name === m.stateName)) expect(armorMods.length).toBe(1) expect(armorMods[0]!.stateName).toBe(armor.name) expect(armorMods[0]!.stats['skill_armor_percent']).toBe(100 + armor.id) } } }) it('preserves single-state invariant across 100 randomized rapid casting bursts', async () => { const registry = await getSharedDataRegistry() const stats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const bus = new StateBus(stats, registry) const armors = ['frozenarmor', 'shiverarmor', 'chillingarmor'] let lastCast = '' for (let i = 0; i < 100; i++) { const pick = armors[Math.floor(Math.random() * armors.length)]! bus.applyState({ stateNameOrId: pick, slvl: 10 + (i % 20), }) lastCast = pick const group1Entries = bus.getActiveEntries().filter(e => e.group === 1) expect(group1Entries.length).toBe(1) expect(group1Entries[0]!.stateName).toBe(lastCast) } }) it('enforces cross-level eviction: lower-slvl different armor evicts higher-slvl armor', async () => { const registry = await getSharedDataRegistry() const stats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const bus = new StateBus(stats, registry) // Start with Chilling Armor slvl 50 bus.applyState({ stateNameOrId: 'chillingarmor', sourceSkillId: 60, slvl: 50 }) expect(bus.hasState('chillingarmor')).toBe(true) expect(bus.getState('chillingarmor')?.slvl).toBe(50) // Cast Frozen Armor slvl 1 -> must EVIC Chilling Armor bus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 1 }) expect(bus.hasState('chillingarmor')).toBe(false) expect(bus.hasState('frozenarmor')).toBe(true) expect(bus.getState('frozenarmor')?.slvl).toBe(1) // Cast Shiver Armor slvl 1 -> must EVICT Frozen Armor bus.applyState({ stateNameOrId: 'shiverarmor', sourceSkillId: 50, slvl: 1 }) expect(bus.hasState('frozenarmor')).toBe(false) expect(bus.hasState('shiverarmor')).toBe(true) expect(bus.getState('shiverarmor')?.slvl).toBe(1) }) it('enforces same-armor level rules: higher slvl overwrites; lower slvl is rejected', async () => { const registry = await getSharedDataRegistry() const stats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const bus = new StateBus(stats, registry) // Apply Frozen Armor slvl 20 with 3000 frames duration bus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 20, durationFrames: 3000 }) expect(bus.getState('frozenarmor')?.slvl).toBe(20) // Attempt to cast Frozen Armor slvl 5 while slvl 20 is active -> rejected const lowerRes = bus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 5, durationFrames: 3000, }) expect(lowerRes.applied).toBe(false) expect(bus.getState('frozenarmor')?.slvl).toBe(20) // Cast Frozen Armor slvl 30 -> accepted and upgraded const higherRes = bus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 30, durationFrames: 6000, }) expect(higherRes.applied).toBe(true) expect(bus.getState('frozenarmor')?.slvl).toBe(30) }) it('expires cold armor cleanly on clock tick and clears group 1 state', async () => { const registry = await getSharedDataRegistry() const stats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const bus = new StateBus(stats, registry) bus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 1, durationFrames: 3000, }) expect(bus.hasState('frozenarmor')).toBe(true) // Advance frame clock past 3000 const expired = bus.tick(3001) expect(expired).toContain('frozenarmor') expect(bus.hasState('frozenarmor')).toBe(false) expect(bus.getActiveEntries().filter(e => e.group === 1).length).toBe(0) }) }) // ========================================================================= // Dimension 4: Reactive Trigger Adversarial Edge Cases // ========================================================================= describe('Dimension 4: Reactive Trigger Adversarial Edge Cases', () => { describe('Frozen Armor (Skill 40) Edge Cases', () => { it('freezes attacker when damagedinmelee with physical damage > 0', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 20 }) const outcome = defenderBus.triggerReactiveEvents({ event: 'damagedinmelee', incomingPhys256: 100 * FIXED_ONE, attackerStateBus: attackerBus, }) expect(outcome.attackerStatesApplied).toContain('freeze') expect(attackerBus.hasState('freeze')).toBe(true) // Frozen armor deals 0 damage to attacker expect(outcome.attackerColdDamage256).toBe(0) expect(outcome.reflectedPhys256).toBe(0) }) it('does NOT freeze attacker if physical damage is 0 (e.g. 100% absorbed/miss/blocked)', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 20 }) const outcome = defenderBus.triggerReactiveEvents({ event: 'damagedinmelee', incomingPhys256: 0, attackerStateBus: attackerBus, }) expect(outcome.attackerStatesApplied).not.toContain('freeze') expect(attackerBus.hasState('freeze')).toBe(false) }) it('does NOT freeze attacker if melee damage is 100% elemental with 0 physical', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 20 }) const outcome = defenderBus.triggerReactiveEvents({ event: 'damagedinmelee', incomingPhys256: 0, incomingElem256: 500 * FIXED_ONE, attackerStateBus: attackerBus, }) expect(outcome.attackerStatesApplied).not.toContain('freeze') expect(attackerBus.hasState('freeze')).toBe(false) }) it('does NOT trigger Frozen Armor on hitbymissile', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'frozenarmor', sourceSkillId: 40, slvl: 20 }) const outcome = defenderBus.triggerReactiveEvents({ event: 'hitbymissile', incomingPhys256: 200 * FIXED_ONE, returnFire: true, attackerStateBus: attackerBus, }) expect(outcome.attackerStatesApplied).not.toContain('freeze') expect(attackerBus.hasState('freeze')).toBe(false) }) }) describe('Shiver Armor (Skill 50) Edge Cases', () => { it('proactively triggers on attackedinmelee BEFORE hit resolution, even if damage is 0', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'shiverarmor', sourceSkillId: 50, slvl: 20 }) // Attacker begins melee swing (attackedinmelee) with 0 incoming damage const outcome = defenderBus.triggerReactiveEvents({ event: 'attackedinmelee', incomingPhys256: 0, attackerStateBus: attackerBus, }) expect(outcome.attackerStatesApplied).toContain('cold') expect(attackerBus.hasState('cold')).toBe(true) expect(outcome.attackerColdDamage256).toBeGreaterThan(0) }) it('does NOT trigger Shiver Armor on hitbymissile or damagedbymissile', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'shiverarmor', sourceSkillId: 50, slvl: 20 }) const out1 = defenderBus.triggerReactiveEvents({ event: 'hitbymissile', incomingPhys256: 100 * FIXED_ONE, attackerStateBus: attackerBus, }) expect(out1.attackerStatesApplied).not.toContain('cold') expect(out1.attackerColdDamage256).toBe(0) const out2 = defenderBus.triggerReactiveEvents({ event: 'damagedbymissile', incomingPhys256: 100 * FIXED_ONE, attackerStateBus: attackerBus, }) expect(out2.attackerStatesApplied).not.toContain('cold') expect(out2.attackerColdDamage256).toBe(0) }) }) describe('Chilling Armor (Skill 60) Edge Cases', () => { it('reflects chillingarmorbolt when struck by missile with returnFire: true', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'chillingarmor', sourceSkillId: 60, slvl: 20 }) const outcome = defenderBus.triggerReactiveEvents({ event: 'hitbymissile', incomingPhys256: 100 * FIXED_ONE, returnFire: true, attackerStateBus: attackerBus, }) expect(outcome.spawnedMissiles).toContain('chillingarmorbolt') }) it('does NOT reflect chillingarmorbolt if incoming missile has returnFire: false', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const attackerStats = new UnitStatList(registry, { maxhp: 500 * FIXED_ONE, hitpoints: 500 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) const attackerBus = new StateBus(attackerStats, registry) defenderBus.applyState({ stateNameOrId: 'chillingarmor', sourceSkillId: 60, slvl: 20 }) const outcome = defenderBus.triggerReactiveEvents({ event: 'hitbymissile', incomingPhys256: 100 * FIXED_ONE, returnFire: false, attackerStateBus: attackerBus, }) expect(outcome.spawnedMissiles).not.toContain('chillingarmorbolt') }) it('does NOT reflect chillingarmorbolt if returnFire is 0', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) defenderBus.applyState({ stateNameOrId: 'chillingarmor', sourceSkillId: 60, slvl: 20 }) const outcome = defenderBus.triggerReactiveEvents({ event: 'hitbymissile', incomingPhys256: 100 * FIXED_ONE, returnFire: 0, }) expect(outcome.spawnedMissiles).not.toContain('chillingarmorbolt') }) it('does NOT trigger Chilling Armor on damagedinmelee or attackedinmelee', async () => { const registry = await getSharedDataRegistry() const defenderStats = new UnitStatList(registry, { maxhp: 1000 * FIXED_ONE, hitpoints: 1000 * FIXED_ONE }) const defenderBus = new StateBus(defenderStats, registry) defenderBus.applyState({ stateNameOrId: 'chillingarmor', sourceSkillId: 60, slvl: 20 }) const out1 = defenderBus.triggerReactiveEvents({ event: 'damagedinmelee', incomingPhys256: 100 * FIXED_ONE, }) expect(out1.spawnedMissiles).not.toContain('chillingarmorbolt') const out2 = defenderBus.triggerReactiveEvents({ event: 'attackedinmelee', incomingPhys256: 100 * FIXED_ONE, }) expect(out2.spawnedMissiles).not.toContain('chillingarmorbolt') }) }) }) // ========================================================================= // Dimension 5: Full Invariant & System Integrity Checks // ========================================================================= describe('Dimension 5: Full Invariant & System Integrity Checks', () => { it('enforces BATCH1_SKILLS length strictly 38', () => { expect(Object.keys(BATCH1_SKILLS).length).toBe(38) }) it('verifies constant mana costs across slvl 1..99 for all three armors', () => { for (let slvl = 1; slvl <= 99; slvl++) { expect(getSkillManaCost(40, slvl)).toBe(7) expect(getSkillManaCost(50, slvl)).toBe(11) expect(getSkillManaCost(60, slvl)).toBe(17) } }) it('verifies zero cooldown ticks across all three armors', () => { expect(getSkillCooldownTicks(40)).toBe(0) expect(getSkillCooldownTicks(50)).toBe(0) expect(getSkillCooldownTicks(60)).toBe(0) }) it('verifies calculateSkillDamage routing consistency', () => { for (let slvl = 1; slvl <= 30; slvl++) { // Frozen Armor: always 0 expect(calculateSkillDamage(40, slvl)).toEqual({ min: 0, max: 0 }) // Shiver Armor: matches calculateShiverArmorDamage const expectedShiver = calculateShiverArmorDamage(slvl) expect(calculateSkillDamage(50, slvl)).toEqual(expectedShiver) // Chilling Armor: matches calculateChillingArmorDamage const expectedChilling = calculateChillingArmorDamage(slvl) expect(calculateSkillDamage(60, slvl)).toEqual(expectedChilling) } }) it('verifies overlay and missile registrations', () => { expect(OVERLAY_METAS['frozenarmor']).toBeDefined() expect(OVERLAY_METAS['shiverarmor']).toBeDefined() expect(OVERLAY_METAS['chillingarmor']).toBeDefined() expect(OVERLAY_METAS['frozenarmor_hit']).toBeDefined() expect(OVERLAY_METAS['shiverarmor_hit']).toBeDefined() expect(OVERLAY_METAS['chillingarmor_hit']).toBeDefined() expect(MISSILE_METAS['chillingarmorbolt']).toBeDefined() expect(getSkillCastOverlay(40)).toBe('ice_cast_1') expect(getSkillCastOverlay(50)).toBe('ice_cast_2') expect(getSkillCastOverlay(60)).toBe('ice_cast_3') }) }) })