/** * Diablo II: Lord of Destruction v1.13c — Druid Skills Adversarial Stress Testing Harness * * Dedicated Challenger Suite for Cohort 6 Iteration 2: * 1. Cyclone Armor Poison Leak & Multi-Elemental Stress Testing * 2. Shapeshifting Form Exclusivity & Pre-cast Guards Exhaustive Matrix * 3. Armageddon & Hurricane Persistent Aura States & Duration Scaling * 4. Melee Charge Progression & Contagion Mechanics */ import { describe, expect, it } from 'vitest' import { getSharedDataRegistry } from '../../../src/baker/mpq-archive-source.ts' import { UnitStatList } from '../../../src/server/engine/stat-list.ts' import { StateBus } from '../../../src/server/engine/state-bus.ts' import { MissileEngine } from '../../../src/server/engine/missile-engine.ts' import { SummonManager } from '../../../src/server/ai/summon-manager.ts' import { AuraScanner } from '../../../src/server/engine/aura-scanner.ts' import { executeSUnitDmg, type CombatUnitContext } from '../../../src/server/engine/combat-pipeline.ts' import { executeSkill113c, evaluateSkill113c } from '../../../src/server/skills/registry.ts' import { canCastDruidSkill, calculateFeralRageStats, calculateMaulStats, calculateRabiesStats, advanceChargeOrb, simulateRabiesContagion, DRUID_SHAPESHIFT_SKILL_IDS, } from '../../../src/server/skills/druid-shapeshift.ts' import { calculateArmageddonStats, calculateHurricaneStats, } from '../../../src/server/skills/druid-elemental.ts' function createTestUnit( id: string, name: string, unitType: 'player' | 'monster', maxHp = 2000, ): CombatUnitContext { const statList = new UnitStatList() statList.setBaseStat('maxhp', maxHp << 8) statList.setHp256(maxHp << 8) const stateBus = new StateBus(statList) return { id, name, unitType, statList, stateBus, } } describe('Challenger Adversarial Stress Testing — Druid Elemental & Shapeshifting Invariants', () => { // ========================================================================= // Dimension 1: Cyclone Armor Poison Leak & Multi-Elemental Stress Testing // ========================================================================= describe('Dimension 1: Cyclone Armor Invariants & Differential Fuzzing', () => { it('1.1 Cyclone Armor Poison Leak: Poison completely bypasses pool across 50 random damage/pool configurations', () => { // Differential property test: // For any incoming poison damage and any shield pool size: // - Shield pool MUST remain completely untouched. // - Defender HP MUST decrease by the exact poison damage amount (net of resistance). for (let seed = 1; seed <= 50; seed++) { const initialHp = 5000 const defender = createTestUnit(`def_${seed}`, 'DruidDefender', 'player', initialHp) const attacker = createTestUnit(`att_${seed}`, 'Attacker', 'monster') const poolSize = 50 + ((seed * 37) % 500) // 50 .. 549 const poisonDmg = 10 + ((seed * 61) % 400) // 10 .. 409 defender.stateBus.applyState({ stateNameOrId: 'cyclonearmor', slvl: 10, stats: { magicarmor: poolSize }, }) const outcome = executeSUnitDmg(attacker, defender, { skillId: 92, // Poison Nova elemType: 'pois', elemMin256: poisonDmg << 8, elemMax256: poisonDmg << 8, isPoison: true, autoHit: true, }) const armorState = defender.stateBus.getState('cyclonearmor') expect(armorState, `Cyclone Armor should still be active for seed ${seed}`).toBeDefined() expect( armorState?.stats['magicarmor'], `Cyclone Armor pool must NOT be depleted by poison damage (seed ${seed})`, ).toBe(poolSize) const hpLost = initialHp - (defender.statList.getHp256() >> 8) expect(hpLost, `Defender HP must take direct poison damage (seed ${seed})`).toBe(poisonDmg) expect(outcome.totalDamage).toBe(poisonDmg) } }) it('1.2 Elemental Absorption: Fire, Cold, Lightning damage are absorbed; Physical & Magic bypass', () => { const elements: Array<{ elem: 'fire' | 'cold' | 'ltng' | 'lightning' | 'phys' | 'mag' isPoison?: boolean shouldAbsorb: boolean }> = [ { elem: 'fire', shouldAbsorb: true }, { elem: 'cold', shouldAbsorb: true }, { elem: 'ltng', shouldAbsorb: true }, { elem: 'lightning', shouldAbsorb: true }, { elem: 'phys', shouldAbsorb: false }, { elem: 'mag', shouldAbsorb: false }, { elem: 'fire', isPoison: true, shouldAbsorb: false }, // if flagged poison, bypass ] for (const tc of elements) { const initialHp = 2000 const defender = createTestUnit('def_el', 'DruidDefender', 'player', initialHp) const attacker = createTestUnit('att_el', 'Attacker', 'monster') const initialPool = 300 const incomingDmg = 120 defender.stateBus.applyState({ stateNameOrId: 'cyclonearmor', slvl: 10, stats: { magicarmor: initialPool }, }) const outcome = executeSUnitDmg(attacker, defender, { skillId: tc.elem === 'phys' ? 0 : 50, ...(tc.elem === 'phys' ? { flatPhysMin256: incomingDmg << 8, flatPhysMax256: incomingDmg << 8 } : { elemType: tc.elem, elemMin256: incomingDmg << 8, elemMax256: incomingDmg << 8, ...(tc.isPoison ? { isPoison: true } : {}), }), autoHit: true, }) const armorState = defender.stateBus.getState('cyclonearmor') if (tc.shouldAbsorb) { expect( armorState?.stats['magicarmor'], `${tc.elem} must reduce Cyclone Armor pool`, ).toBe(initialPool - incomingDmg) expect(defender.statList.getHp256() >> 8, `${tc.elem} should NOT reduce HP when fully absorbed`).toBe(initialHp) expect(outcome.totalDamage).toBe(0) } else { expect( armorState?.stats['magicarmor'], `${tc.elem} must NOT reduce Cyclone Armor pool`, ).toBe(initialPool) expect(defender.statList.getHp256() >> 8, `${tc.elem} must deal direct damage to defender HP`).toBe(initialHp - incomingDmg) expect(outcome.totalDamage).toBe(incomingDmg) } } }) it('1.3 Overflow Absorption: Elemental damage exceeding pool dissipates shield and leaks overflow to HP', () => { const initialHp = 1000 const defender = createTestUnit('def_ov', 'DruidDefender', 'player', initialHp) const attacker = createTestUnit('att_ov', 'Attacker', 'monster') const initialPool = 100 const incomingFireDmg = 250 // 100 absorbed, 150 overflow defender.stateBus.applyState({ stateNameOrId: 'cyclonearmor', slvl: 10, stats: { magicarmor: initialPool }, }) const outcome = executeSUnitDmg(attacker, defender, { skillId: 50, elemType: 'fire', elemMin256: incomingFireDmg << 8, elemMax256: incomingFireDmg << 8, autoHit: true, }) // Armor pool depleted to 0 -> state removed! expect(defender.stateBus.hasState('cyclonearmor')).toBe(false) // Overflow of 150 damage deals HP damage (1000 - 150 = 850) expect(defender.statList.getHp256() >> 8).toBe(850) expect(outcome.totalDamage).toBe(150) }) }) // ========================================================================= // Dimension 2: Shapeshifting Form Exclusivity & Pre-cast Guards // ========================================================================= describe('Dimension 2: Shapeshifting Form Exclusivity & Pre-cast Guards Matrix', () => { it('2.1 Exhaustive Matrix: all 18 human-only Druid skills fail-fast in Wolf and Bear forms with invalid_form', async () => { const reg = await getSharedDataRegistry() const missileEngine = new MissileEngine(reg) const summonManager = new SummonManager(reg) const auraScanner = new AuraScanner(reg) const humanOnlySkills = [ // 10 Summons 221, 222, 226, 227, 231, 236, 237, 241, 246, 247, // 8 Elemental Spells 225, 229, 230, 234, 235, 240, 244, 245, ] for (const skillId of humanOnlySkills) { const skill = reg.getSkillById(skillId)! // Wolf form test const casterWolf = createTestUnit(`wolf_${skillId}`, 'WolfDruid', 'player') casterWolf.stateBus.applyState({ stateNameOrId: 'wolf', slvl: 10, stats: {} }) const evalWolf = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: casterWolf.statList, stateBus: casterWolf.stateBus, }) const outWolf = executeSkill113c({ registry: reg, skill, evalResult: evalWolf, caster: casterWolf, targets: [], targetPositions: new Map(), corpses: [], missileEngine, auraScanner, summonManager, currentTick: 0, targetX: 10, targetY: 10, }) expect(outWolf.executed, `Skill ${skillId} (${skill.name}) must NOT execute in wolf form`).toBe(false) expect(outWolf.error, `Skill ${skillId} (${skill.name}) must return error: 'invalid_form' in wolf form`).toBe('invalid_form') // Bear form test const casterBear = createTestUnit(`bear_${skillId}`, 'BearDruid', 'player') casterBear.stateBus.applyState({ stateNameOrId: 'bear', slvl: 10, stats: {} }) const evalBear = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: casterBear.statList, stateBus: casterBear.stateBus, }) const outBear = executeSkill113c({ registry: reg, skill, evalResult: evalBear, caster: casterBear, targets: [], targetPositions: new Map(), corpses: [], missileEngine, auraScanner, summonManager, currentTick: 0, targetX: 10, targetY: 10, }) expect(outBear.executed, `Skill ${skillId} (${skill.name}) must NOT execute in bear form`).toBe(false) expect(outBear.error, `Skill ${skillId} (${skill.name}) must return error: 'invalid_form' in bear form`).toBe('invalid_form') // Human form test: should execute without invalid_form error const casterHuman = createTestUnit(`hum_${skillId}`, 'HumanDruid', 'player') const evalHuman = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: casterHuman.statList, stateBus: casterHuman.stateBus, }) const outHuman = executeSkill113c({ registry: reg, skill, evalResult: evalHuman, caster: casterHuman, targets: [], targetPositions: new Map(), corpses: [], missileEngine, auraScanner, summonManager, currentTick: 0, targetX: 10, targetY: 10, }) expect(outHuman.error, `Skill ${skillId} (${skill.name}) in human form must NOT return invalid_form`).toBeUndefined() } }) it('2.2 Armageddon (#249) and Hurricane (#250) execute successfully in Wolf, Bear, and Human forms', async () => { const reg = await getSharedDataRegistry() const missileEngine = new MissileEngine(reg) const auraScanner = new AuraScanner(reg) const stormSkills = [249, 250] const forms: Array<'human' | 'wolf' | 'bear'> = ['human', 'wolf', 'bear'] for (const skillId of stormSkills) { const skill = reg.getSkillById(skillId)! for (const form of forms) { const caster = createTestUnit(`caster_${skillId}_${form}`, `Druid_${form}`, 'player') if (form !== 'human') { caster.stateBus.applyState({ stateNameOrId: form, slvl: 10, stats: {} }) } const evalRes = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: caster.statList, stateBus: caster.stateBus, }) const out = executeSkill113c({ registry: reg, skill, evalResult: evalRes, caster, targets: [], targetPositions: new Map(), corpses: [], missileEngine, auraScanner, summonManager: null as any, currentTick: 0, targetX: 0, targetY: 0, }) expect(out.executed, `Skill ${skillId} must execute in ${form} form`).toBe(true) expect(out.error).toBeUndefined() const expectedState = skillId === 249 ? 'armageddon' : 'hurricane' expect(caster.stateBus.hasState(expectedState)).toBe(true) } } }) it('2.3 Form-exclusive attacks: Werewolf attacks blocked in Bear/Human; Werebear attacks blocked in Wolf/Human', async () => { const reg = await getSharedDataRegistry() const missileEngine = new MissileEngine(reg) const target = createTestUnit('target', 'Enemy', 'monster') const wolfOnlySkills = [ DRUID_SHAPESHIFT_SKILL_IDS.FERAL_RAGE, // 232 DRUID_SHAPESHIFT_SKILL_IDS.RABIES, // 238 DRUID_SHAPESHIFT_SKILL_IDS.FURY, // 248 ] const bearOnlySkills = [ DRUID_SHAPESHIFT_SKILL_IDS.MAUL, // 233 DRUID_SHAPESHIFT_SKILL_IDS.SHOCK_WAVE, // 243 ] // Test wolf-only skills for (const skillId of wolfOnlySkills) { const skill = reg.getSkillById(skillId)! // Blocked in Human const humanCaster = createTestUnit('h', 'Human', 'player') const evalH = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: humanCaster.statList, stateBus: humanCaster.stateBus }) const outH = executeSkill113c({ registry: reg, skill, evalResult: evalH, caster: humanCaster, targets: [target], targetPositions: new Map(), corpses: [], missileEngine, auraScanner: null as any, summonManager: null as any, currentTick: 0, targetX: 0, targetY: 0, }) expect(outH.executed).toBe(false) expect(outH.error).toBe('invalid_form') // Blocked in Bear const bearCaster = createTestUnit('b', 'Bear', 'player') bearCaster.stateBus.applyState({ stateNameOrId: 'bear', slvl: 10, stats: {} }) const evalB = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: bearCaster.statList, stateBus: bearCaster.stateBus }) const outB = executeSkill113c({ registry: reg, skill, evalResult: evalB, caster: bearCaster, targets: [target], targetPositions: new Map(), corpses: [], missileEngine, auraScanner: null as any, summonManager: null as any, currentTick: 0, targetX: 0, targetY: 0, }) expect(outB.executed).toBe(false) expect(outB.error).toBe('invalid_form') } // Test bear-only skills for (const skillId of bearOnlySkills) { const skill = reg.getSkillById(skillId)! // Blocked in Human const humanCaster = createTestUnit('h', 'Human', 'player') const evalH = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: humanCaster.statList, stateBus: humanCaster.stateBus }) const outH = executeSkill113c({ registry: reg, skill, evalResult: evalH, caster: humanCaster, targets: [target], targetPositions: new Map(), corpses: [], missileEngine, auraScanner: null as any, summonManager: null as any, currentTick: 0, targetX: 0, targetY: 0, }) expect(outH.executed).toBe(false) expect(outH.error).toBe('invalid_form') // Blocked in Wolf const wolfCaster = createTestUnit('w', 'Wolf', 'player') wolfCaster.stateBus.applyState({ stateNameOrId: 'wolf', slvl: 10, stats: {} }) const evalW = evaluateSkill113c({ registry: reg, skill, slvl: 10, blvl: 10, statList: wolfCaster.statList, stateBus: wolfCaster.stateBus }) const outW = executeSkill113c({ registry: reg, skill, evalResult: evalW, caster: wolfCaster, targets: [target], targetPositions: new Map(), corpses: [], missileEngine, auraScanner: null as any, summonManager: null as any, currentTick: 0, targetX: 0, targetY: 0, }) expect(outW.executed).toBe(false) expect(outW.error).toBe('invalid_form') } }) it('2.4 Cross-morphing: Werewolf to Werebear cleanly evicts wolf and applies bear state with bear buffs', async () => { const reg = await getSharedDataRegistry() const caster = createTestUnit('dru_morph', 'MorphDruid', 'player') // Start in Werewolf const skillWolf = reg.getSkillById(223)! const evalWolf = evaluateSkill113c({ registry: reg, skill: skillWolf, slvl: 5, blvl: 5, statList: caster.statList, stateBus: caster.stateBus }) executeSkill113c({ registry: reg, skill: skillWolf, evalResult: evalWolf, caster, targets: [], targetPositions: new Map(), corpses: [], missileEngine: null as any, auraScanner: null as any, summonManager: null as any, currentTick: 0, targetX: 0, targetY: 0, }) expect(caster.stateBus.hasState('wolf')).toBe(true) expect(caster.stateBus.hasState('bear')).toBe(false) // Cross-morph directly to Werebear (#228) const skillBear = reg.getSkillById(228)! const evalBear = evaluateSkill113c({ registry: reg, skill: skillBear, slvl: 10, blvl: 10, statList: caster.statList, stateBus: caster.stateBus }) const outBear = executeSkill113c({ registry: reg, skill: skillBear, evalResult: evalBear, caster, targets: [], targetPositions: new Map(), corpses: [], missileEngine: null as any, auraScanner: null as any, summonManager: null as any, currentTick: 100, targetX: 0, targetY: 0, }) expect(outBear.executed).toBe(true) expect(caster.stateBus.hasState('wolf'), 'Wolf state must be evicted').toBe(false) expect(caster.stateBus.hasState('bear'), 'Bear state must be active').toBe(true) const bearState = caster.stateBus.getState('bear') expect(bearState?.stats['damagepercent']).toBe(55 + (10 - 1) * 8) expect(bearState?.stats['item_armor_percent']).toBe(25 + (10 - 1) * 6) // Morph back to Werewolf (#223) const outWolf2 = executeSkill113c({ registry: reg, skill: skillWolf, evalResult: evalWolf, caster, targets: [], targetPositions: new Map(), corpses: [], missileEngine: null as any, auraScanner: null as any, summonManager: null as any, currentTick: 200, targetX: 0, targetY: 0, }) expect(outWolf2.executed).toBe(true) expect(caster.stateBus.hasState('bear'), 'Bear state must be evicted').toBe(false) expect(caster.stateBus.hasState('wolf'), 'Wolf state must be active').toBe(true) }) }) // ========================================================================= // Dimension 3: Armageddon & Hurricane Persistent Aura States // ========================================================================= describe('Dimension 3: Armageddon & Hurricane Persistent Aura States', () => { it('3.1 Armageddon (#249) attaches armageddon state with scaling duration and records in statesApplied', async () => { const reg = await getSharedDataRegistry() const missileEngine = new MissileEngine(reg) const auraScanner = new AuraScanner(reg) const caster = createTestUnit('dru_arma', 'ArmaDruid', 'player') const skill249 = reg.getSkillById(249)! // Test at slvl 15 const eval249 = evaluateSkill113c({ registry: reg, skill: skill249, slvl: 15, blvl: 15, statList: caster.statList, stateBus: caster.stateBus, }) const out = executeSkill113c({ registry: reg, skill: skill249, evalResult: eval249, caster, targets: [], targetPositions: new Map(), corpses: [], missileEngine, auraScanner, summonManager: null as any, currentTick: 50, targetX: 0, targetY: 0, }) expect(out.executed).toBe(true) expect(out.statesApplied).toContain('armageddon') expect(caster.stateBus.hasState('armageddon')).toBe(true) const armaState = caster.stateBus.getState('armageddon')! expect(armaState.sourceSkillId).toBe(249) expect(armaState.durationFrames).toBeGreaterThanOrEqual(250) }) it('3.2 Hurricane (#250) attaches hurricane state with scaling duration and records in statesApplied', async () => { const reg = await getSharedDataRegistry() const missileEngine = new MissileEngine(reg) const auraScanner = new AuraScanner(reg) const caster = createTestUnit('dru_hurr', 'HurrDruid', 'player') const skill250 = reg.getSkillById(250)! const eval250 = evaluateSkill113c({ registry: reg, skill: skill250, slvl: 20, blvl: 20, statList: caster.statList, stateBus: caster.stateBus, }) const out = executeSkill113c({ registry: reg, skill: skill250, evalResult: eval250, caster, targets: [], targetPositions: new Map(), corpses: [], missileEngine, auraScanner, summonManager: null as any, currentTick: 100, targetX: 0, targetY: 0, }) expect(out.executed).toBe(true) expect(out.statesApplied).toContain('hurricane') expect(caster.stateBus.hasState('hurricane')).toBe(true) const hurrState = caster.stateBus.getState('hurricane')! expect(hurrState.sourceSkillId).toBe(250) expect(hurrState.durationFrames).toBeGreaterThanOrEqual(250) }) }) // ========================================================================= // Dimension 4: Melee Charge Progression & Contagion // ========================================================================= describe('Dimension 4: Melee Charge Progression & Contagion Mechanics', () => { it('4.1 Feral Rage (#232): successive hits advance charges 1..3 with stats and 20.0s decay timer; capped at 3', async () => { const reg = await getSharedDataRegistry() const caster = createTestUnit('dru_fr', 'WolfDruid', 'player') caster.stateBus.applyState({ stateNameOrId: 'wolf', slvl: 10, stats: {} }) const target = createTestUnit('target_fr', 'TargetMonster', 'monster', 5000) const skill232 = reg.getSkillById(232)! const slvl = 10 const eval232 = evaluateSkill113c({ registry: reg, skill: skill232, slvl, blvl: 10, statList: caster.statList, stateBus: caster.stateBus }) const frStats = calculateFeralRageStats(slvl) // Expected progression: // Hit 1 -> stage 1 // Hit 2 -> stage 2 // Hit 3 -> stage 3 // Hit 4 -> stage 3 (capped) for (let hitIndex = 1; hitIndex <= 4; hitIndex++) { const tick = hitIndex * 25 const out = executeSkill113c({ registry: reg, skill: skill232, evalResult: eval232, caster, targets: [target], targetPositions: new Map(), corpses: [], missileEngine: null as any, auraScanner: null as any, summonManager: null as any, currentTick: tick, targetX: 0, targetY: 0, }) expect(out.executed).toBe(true) expect(out.statesApplied).toContain('feralrage') const expectedCharge = Math.min(hitIndex, 3) const expectedLeech = Math.round((frStats.lifeLeechPct * expectedCharge) / 3) const expectedVelocity = Math.round((frStats.velocityBonusPct * expectedCharge) / 3) const state = caster.stateBus.getState('feralrage') expect(state, `Feral rage state must exist after hit ${hitIndex}`).toBeDefined() expect(state?.stats['charge']).toBe(expectedCharge) expect(state?.stats['progressive_charges']).toBe(expectedCharge) expect(state?.stats['life_leech']).toBe(expectedLeech) expect(state?.stats['life_leech_pct']).toBe(expectedLeech) expect(state?.stats['velocitypercent']).toBe(expectedVelocity) expect(state?.durationFrames).toBe(500) // 20.0 seconds expect(state?.expireFrame).toBe(tick + 500) // decay timer refreshed } }) it('4.2 Maul (#233): successive hits advance charges 1..5 with damage % and stun duration; capped at 5', async () => { const reg = await getSharedDataRegistry() const caster = createTestUnit('dru_maul', 'BearDruid', 'player') caster.stateBus.applyState({ stateNameOrId: 'bear', slvl: 10, stats: {} }) const target = createTestUnit('target_maul', 'TargetMonster', 'monster', 10000) const skill233 = reg.getSkillById(233)! const slvl = 10 const eval233 = evaluateSkill113c({ registry: reg, skill: skill233, slvl, blvl: 10, statList: caster.statList, stateBus: caster.stateBus }) const maulStats = calculateMaulStats(slvl) for (let hitIndex = 1; hitIndex <= 6; hitIndex++) { const tick = hitIndex * 25 const out = executeSkill113c({ registry: reg, skill: skill233, evalResult: eval233, caster, targets: [target], targetPositions: new Map(), corpses: [], missileEngine: null as any, auraScanner: null as any, summonManager: null as any, currentTick: tick, targetX: 0, targetY: 0, }) expect(out.executed).toBe(true) expect(out.statesApplied).toContain('maul') const expectedCharge = Math.min(hitIndex, 5) const expectedDmgBonus = expectedCharge * maulStats.damageBonusPerChargePct const state = caster.stateBus.getState('maul') expect(state, `Maul state must exist after hit ${hitIndex}`).toBeDefined() expect(state?.stats['charge']).toBe(expectedCharge) expect(state?.stats['progressive_charges']).toBe(expectedCharge) expect(state?.stats['damagepercent']).toBe(expectedDmgBonus) expect(state?.stats['stunDurationFrames']).toBe(maulStats.stunDurationFrames) expect(state?.durationFrames).toBe(500) // 20.0 seconds expect(state?.expireFrame).toBe(tick + 500) } }) it('4.3 Rabies (#238): successful hit infects target with authentic rabies contagion state', async () => { const reg = await getSharedDataRegistry() const caster = createTestUnit('dru_rab', 'WolfDruid', 'player') caster.stateBus.applyState({ stateNameOrId: 'wolf', slvl: 10, stats: {} }) const target = createTestUnit('target_rab', 'TargetMonster', 'monster', 2000) const skill238 = reg.getSkillById(238)! const slvl = 10 const eval238 = evaluateSkill113c({ registry: reg, skill: skill238, slvl, blvl: 10, statList: caster.statList, stateBus: caster.stateBus }) const rabiesStats = calculateRabiesStats(slvl, 0) const out = executeSkill113c({ registry: reg, skill: skill238, evalResult: eval238, caster, targets: [target], targetPositions: new Map(), corpses: [], missileEngine: null as any, auraScanner: null as any, summonManager: null as any, currentTick: 0, targetX: 0, targetY: 0, }) expect(out.executed).toBe(true) expect(out.statesApplied).toContain('rabies') expect(target.stateBus.hasState('rabies')).toBe(true) const rState = target.stateBus.getState('rabies')! expect(rState.sourceSkillId).toBe(238) expect(rState.durationFrames).toBe(rabiesStats.poisonDurationFrames) expect(rState.stats['min_poison_damage']).toBe(rabiesStats.minPoisonDamage) expect(rState.stats['max_poison_damage']).toBe(rabiesStats.maxPoisonDamage) expect(rState.stats['contagion_radius_subtiles']).toBe(4) expect(rState.stats['contagion_spread_interval']).toBe(4) }) }) })