diablo2-web/tests/skills/bar/adv-bar-challenger2-stress....

703 lines
29 KiB
TypeScript

/**
* Diablo II: Lord of Destruction v1.13c — Cohort 5 Barbarian Warcries & Corpse Interactions
* Challenger 2 Adversarial Stress Testing & Empirical Vulnerability Probe Suite
*
* Ground Truth:
* - D2Game/src/SKILLS/SkillBar.cpp & SkillBarDo.cpp (D2MOO decompiled binary assembly)
* - Patch_D2.mpq: Skills.txt, Missiles.txt, States.txt
*
* Adversarial Testing Targets:
* 1. Battle Orders Life / Mana / Stamina Scaling & Party Buff Dissemination
* 2. Battle Command Skill Bonus & Duration / Radius Scaling
* 3. War Cry Radial Physical Damage & Stun Duration Application
* 4. Battle Cry Defense & Damage Debuffs, Duration Scaling & Curse Overwriting
* 5. Taunt & Howl AI Flee/Taunt States, Debuff Modifiers & Boss Immunities
* 6. Find Item & Find Potion Corpse Target Validity, Diminishing Returns & Loot Execution
* 7. Grim Ward Fear Totem Corpse Consumption, Entity Spawning & Boss Fear Immunity
*/
import { describe, it, expect, vi } from 'vitest'
import {
calculateHowlStats,
calculateFindPotionStats,
calculateTauntStats,
calculateShoutStats,
calculateFindItemStats,
calculateBattleCryStats,
calculateBattleOrdersStats,
calculateGrimWardStats,
calculateWarCryStats,
calculateBattleCommandStats,
evaluateBarbarianWarcry,
validateBarbarianCorpseTarget,
} from '../../../src/game/skills/barbarian-warcries.ts'
import { getSharedDataRegistry } from '../../../src/game/engine/data-registry.ts'
import { UnitStatList } from '../../../src/game/engine/stat-list.ts'
import { StateBus } from '../../../src/game/engine/state-bus.ts'
import {
evaluateSkill113c,
executeSkill113c,
registerSkillModule,
} from '../../../src/game/skills/registry.ts'
import { skillModule as findPotionModule } from '../../../src/game/skills/impl/bar/skill-131-find-potion.ts'
import { skillModule as findItemModule } from '../../../src/game/skills/impl/bar/skill-142-find-item.ts'
import { skillModule as grimWardModule } from '../../../src/game/skills/impl/bar/skill-150-grim-ward.ts'
import type { CombatUnitContext } from '../../../src/game/engine/combat-pipeline.ts'
describe('Challenger 2 — Barbarian Warcries & Corpse Interactions Stress Suite', () => {
// =========================================================================
// Section 1: Battle Orders Life / Mana / Stamina Scaling & Party Buff
// =========================================================================
describe('1. Battle Orders Life / Mana / Stamina Scaling & Party Dissemination', () => {
it('1.1 verifies +35% base life/mana/stamina + 3% per slvl formula across slvl 1..30', () => {
// slvl 1: +35%
const s1 = calculateBattleOrdersStats(1)
expect(s1.maxLifeBonusPct).toBe(35)
expect(s1.maxManaBonusPct).toBe(35)
expect(s1.maxStaminaBonusPct).toBe(35)
expect(s1.durationFrames).toBe(750) // 30.0s
// slvl 2: +38%
const s2 = calculateBattleOrdersStats(2)
expect(s2.maxLifeBonusPct).toBe(38)
expect(s2.maxManaBonusPct).toBe(38)
expect(s2.maxStaminaBonusPct).toBe(38)
// slvl 10: 35 + 3 * 9 = 62%
const s10 = calculateBattleOrdersStats(10)
expect(s10.maxLifeBonusPct).toBe(62)
expect(s10.maxManaBonusPct).toBe(62)
expect(s10.maxStaminaBonusPct).toBe(62)
// slvl 20: 35 + 3 * 19 = 92%
const s20 = calculateBattleOrdersStats(20)
expect(s20.maxLifeBonusPct).toBe(92)
expect(s20.maxManaBonusPct).toBe(92)
expect(s20.maxStaminaBonusPct).toBe(92)
// slvl 30: 35 + 3 * 29 = 122%
const s30 = calculateBattleOrdersStats(30)
expect(s30.maxLifeBonusPct).toBe(122)
expect(s30.maxManaBonusPct).toBe(122)
expect(s30.maxStaminaBonusPct).toBe(122)
})
it('1.2 empirical probe: checks whether party members receive Battle Orders buff during execution', async () => {
const registry = await getSharedDataRegistry()
const boRec = registry.getSkillById(149)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const allyStats = new UnitStatList(registry)
const allyBus = new StateBus(allyStats, registry)
const ally: CombatUnitContext = { id: 'ally', name: 'Mercenary', statList: allyStats, stateBus: allyBus }
const evalBO = evaluateSkill113c({ registry, skill: boRec, slvl: 10, blvl: 10, statList: heroStats })
executeSkill113c({
registry,
skill: boRec,
evalResult: evalBO,
caster: hero,
targets: [],
targetPositions: new Map([['hero', { x: 0, y: 0 }], ['ally', { x: 10, y: 10 }]]),
allies: [ally],
corpses: [],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
// Caster receives buff
expect(heroBus.hasState('battleorders')).toBe(true)
// In D2 1.13c, party members within aura radius receive Battle Orders!
const allyReceivedBuff = allyBus.hasState('battleorders')
expect(allyReceivedBuff).toBe(true)
})
it('1.3 empirical probe: checks whether stamina bonus is applied in state stats', async () => {
const registry = await getSharedDataRegistry()
const boRec = registry.getSkillById(149)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const evalBO = evaluateSkill113c({ registry, skill: boRec, slvl: 1, blvl: 1, statList: heroStats })
executeSkill113c({
registry,
skill: boRec,
evalResult: evalBO,
caster: hero,
targets: [],
targetPositions: new Map(),
corpses: [],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
const entry = heroBus.getState('battleorders')
expect(entry).toBeDefined()
expect(entry?.stats?.['item_maxhp_percent']).toBe(35)
expect(entry?.stats?.['item_maxmana_percent']).toBe(35)
// Battle Orders grants +35% max stamina
expect(entry?.stats?.['item_maxstamina_percent']).toBe(35)
})
})
// =========================================================================
// Section 2: Battle Command Skill Bonus & Duration / Radius
// =========================================================================
describe('2. Battle Command Skill Bonus & Duration Scaling', () => {
it('2.1 evaluates +1 to all skills buff and duration scaling', () => {
const s1 = calculateBattleCommandStats(1)
expect(s1.allSkillsBonus).toBe(1)
// 1.13c Skills.txt param1=125 frames (5.0s), param2=250 frames (10.0s/lvl)
expect(s1.durationFrames).toBe(125)
expect(s1.durationSeconds).toBe(5.0)
const s10 = calculateBattleCommandStats(10)
expect(s10.allSkillsBonus).toBe(1)
expect(s10.durationFrames).toBe(2375) // 125 + 250 * 9 = 95s
const s20 = calculateBattleCommandStats(20)
expect(s20.allSkillsBonus).toBe(1)
expect(s20.durationFrames).toBe(4875) // 125 + 250 * 19 = 195s
})
it('2.2 empirical probe: evaluates radiusSubtiles calculation in registry', async () => {
const registry = await getSharedDataRegistry()
const bcRec = registry.getSkillById(155)!
const stats = new UnitStatList(registry)
const evalBC = evaluateSkill113c({ registry, skill: bcRec, slvl: 20, blvl: 20, statList: stats })
expect(evalBC.durationFrames).toBe(4875)
expect(evalBC.radiusSubtiles).toBe(20)
})
it('2.3 empirical probe: verifies whether party members receive Battle Command +1 all skills', async () => {
const registry = await getSharedDataRegistry()
const bcRec = registry.getSkillById(155)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const allyStats = new UnitStatList(registry)
const allyBus = new StateBus(allyStats, registry)
const ally: CombatUnitContext = { id: 'ally', name: 'Mercenary', statList: allyStats, stateBus: allyBus }
const evalBC = evaluateSkill113c({ registry, skill: bcRec, slvl: 1, blvl: 1, statList: heroStats })
executeSkill113c({
registry,
skill: bcRec,
evalResult: evalBC,
caster: hero,
targets: [],
targetPositions: new Map([['hero', { x: 0, y: 0 }], ['ally', { x: 5, y: 5 }]]),
allies: [ally],
corpses: [],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
expect(heroBus.hasState('battlecommand')).toBe(true)
expect(heroBus.getState('battlecommand')?.stats?.['item_allskills']).toBe(1)
// Ally receives Battle Command state
expect(allyBus.hasState('battlecommand')).toBe(true)
})
})
// =========================================================================
// Section 3: War Cry Radial Stun & Damage
// =========================================================================
describe('3. War Cry Radial Stun & Damage Scaling', () => {
it('3.1 verifies 5-band physical damage and stun duration scaling formulas', () => {
// slvl 1: 20..30 dmg, 25 frames (1.0s) stun, 10 mana
const wc1 = calculateWarCryStats(1)
expect(wc1.minPhysicalDamage).toBe(20)
expect(wc1.maxPhysicalDamage).toBe(30)
expect(wc1.stunDurationFrames).toBe(25)
expect(wc1.stunDurationSeconds).toBe(1.0)
expect(wc1.manaCost).toBe(10)
// slvl 10: 76..86 dmg, 70 frames (2.8s) stun, 19 mana
const wc10 = calculateWarCryStats(10)
expect(wc10.minPhysicalDamage).toBe(76)
expect(wc10.maxPhysicalDamage).toBe(86)
expect(wc10.stunDurationFrames).toBe(70)
expect(wc10.stunDurationSeconds).toBe(2.8)
expect(wc10.manaCost).toBe(19)
// slvl 20: 150..160 dmg, 120 frames (4.8s) stun, 29 mana
const wc20 = calculateWarCryStats(20)
expect(wc20.minPhysicalDamage).toBe(150)
expect(wc20.maxPhysicalDamage).toBe(160)
expect(wc20.stunDurationFrames).toBe(120)
expect(wc20.stunDurationSeconds).toBe(4.8)
expect(wc20.manaCost).toBe(29)
})
it('3.2 empirical probe: checks whether War Cry actually stuns enemies during runtime execution', async () => {
const registry = await getSharedDataRegistry()
const wcRec = registry.getSkillById(154)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const dummyStats = new UnitStatList(registry)
const dummyBus = new StateBus(dummyStats, registry)
const dummy: CombatUnitContext = { id: 'dummy', name: 'Monster Dummy', statList: dummyStats, stateBus: dummyBus }
const evalWC = evaluateSkill113c({ registry, skill: wcRec, slvl: 10, blvl: 10, statList: heroStats })
const outcome = executeSkill113c({
registry,
skill: wcRec,
evalResult: evalWC,
caster: hero,
targets: [dummy],
targetPositions: new Map(),
corpses: [],
missileEngine: { spawnMissile: () => ({ name: 'warcry' }) } as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
// Damage was dealt
expect(outcome.totalDamageDealt).toBeGreaterThan(0)
// In 1.13c, War Cry applies radial stun
const dummyIsStunned = dummyBus.hasState('stun')
expect(dummyIsStunned).toBe(true)
})
})
// =========================================================================
// Section 4: Battle Cry Debuff & Overwriting Rules
// =========================================================================
describe('4. Battle Cry Debuff & Curse Overwriting Rules', () => {
it('4.1 verifies formula for -Enemy Defense % and -Enemy Damage % across levels', () => {
// slvl 1: -50% def, -25% dmg, 300 frames (12.0s)
const bc1 = calculateBattleCryStats(1)
expect(bc1.targetDefenseReductionPct).toBe(50)
expect(bc1.targetDamageReductionPct).toBe(25)
expect(bc1.durationFrames).toBe(300)
// slvl 10: -68% def, -34% dmg, 840 frames (33.6s)
const bc10 = calculateBattleCryStats(10)
expect(bc10.targetDefenseReductionPct).toBe(68)
expect(bc10.targetDamageReductionPct).toBe(34)
expect(bc10.durationFrames).toBe(840)
// slvl 20: -88% def, -44% dmg, 1440 frames (57.6s)
const bc20 = calculateBattleCryStats(20)
expect(bc20.targetDefenseReductionPct).toBe(88)
expect(bc20.targetDamageReductionPct).toBe(44)
expect(bc20.durationFrames).toBe(1440)
})
it('4.2 empirical probe: checks applied stats and duration in runtime registry execution', async () => {
const registry = await getSharedDataRegistry()
const bcRec = registry.getSkillById(146)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const dummyStats = new UnitStatList(registry)
const dummyBus = new StateBus(dummyStats, registry)
const dummy: CombatUnitContext = { id: 'dummy', name: 'Monster', statList: dummyStats, stateBus: dummyBus }
const evalBC1 = evaluateSkill113c({ registry, skill: bcRec, slvl: 1, blvl: 1, statList: heroStats })
executeSkill113c({
registry,
skill: bcRec,
evalResult: evalBC1,
caster: hero,
targets: [dummy],
targetPositions: new Map(),
corpses: [],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
const entry = dummyBus.getState('battlecry')
expect(entry).toBeDefined()
// 1.13c: Battle Cry reduces defense by -50% at slvl 1
expect(entry?.stats?.['item_armor_percent']).toBe(-50)
// 1.13c: Battle Cry reduces damage by -25% at slvl 1
const hasDamageReduction = entry?.stats?.['damage_percent'] !== undefined || entry?.stats?.['item_mindamage_percent'] !== undefined
expect(hasDamageReduction).toBe(true)
expect(entry?.stats?.['item_mindamage_percent']).toBe(-25)
// 1.13c: Duration scales to 840 frames at slvl 10
const evalBC10 = evaluateSkill113c({ registry, skill: bcRec, slvl: 10, blvl: 10, statList: heroStats })
executeSkill113c({
registry,
skill: bcRec,
evalResult: evalBC10,
caster: hero,
targets: [dummy],
targetPositions: new Map(),
corpses: [],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 10,
targetX: 0,
targetY: 0,
})
const entry10 = dummyBus.getState('battlecry')
expect(entry10?.durationFrames).toBe(840)
})
it('4.3 empirical probe: verifies curse overwriting rules (curseType 1 mutual exclusion)', async () => {
const registry = await getSharedDataRegistry()
const bcRec = registry.getSkillById(146)!
const ampRec = registry.getSkillById(66)! // Amplify Damage (NEC)
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const dummyStats = new UnitStatList(registry)
const dummyBus = new StateBus(dummyStats, registry)
const dummy: CombatUnitContext = { id: 'dummy', name: 'Monster', statList: dummyStats, stateBus: dummyBus }
// Apply Battle Cry
const evalBC = evaluateSkill113c({ registry, skill: bcRec, slvl: 1, blvl: 1, statList: heroStats })
executeSkill113c({
registry, skill: bcRec, evalResult: evalBC, caster: hero, targets: [dummy],
targetPositions: new Map(), corpses: [], missileEngine: {} as any, auraScanner: {} as any, summonManager: {} as any, currentTick: 1, targetX: 0, targetY: 0,
})
expect(dummyBus.hasState('battlecry')).toBe(true)
// Apply Amplify Damage: should evict Battle Cry because both are curseType 1
const evalAmp = evaluateSkill113c({ registry, skill: ampRec, slvl: 1, blvl: 1, statList: heroStats })
executeSkill113c({
registry, skill: ampRec, evalResult: evalAmp, caster: hero, targets: [dummy],
targetPositions: new Map(), corpses: [], missileEngine: {} as any, auraScanner: {} as any, summonManager: {} as any, currentTick: 2, targetX: 0, targetY: 0,
})
expect(dummyBus.hasState('amplifydamage')).toBe(true)
expect(dummyBus.hasState('battlecry')).toBe(false) // Battle Cry was evicted
// Apply Battle Cry back: should evict Amplify Damage
executeSkill113c({
registry, skill: bcRec, evalResult: evalBC, caster: hero, targets: [dummy],
targetPositions: new Map(), corpses: [], missileEngine: {} as any, auraScanner: {} as any, summonManager: {} as any, currentTick: 3, targetX: 0, targetY: 0,
})
expect(dummyBus.hasState('battlecry')).toBe(true)
expect(dummyBus.hasState('amplifydamage')).toBe(false) // Amplify was evicted
})
})
// =========================================================================
// Section 5: Taunt & Howl AI Flee/Taunt States & Boss Immunity
// =========================================================================
describe('5. Taunt & Howl AI Flee/Taunt States & Immunities', () => {
it('5.1 empirical probe: verifies Howl state application and boss immunity bypass', async () => {
const registry = await getSharedDataRegistry()
const howlRec = registry.getSkillById(130)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
// Normal monster
const minionStats = new UnitStatList(registry)
const minionBus = new StateBus(minionStats, registry)
const minion: CombatUnitContext = { id: 'minion', name: 'Fallen', statList: minionStats, stateBus: minionBus }
// Boss monster (e.g. Diablo)
const bossStats = new UnitStatList(registry)
const bossBus = new StateBus(bossStats, registry)
const boss: CombatUnitContext = { id: 'boss', name: 'Diablo', statList: bossStats, stateBus: bossBus, isBoss: true, rank: 'boss' }
const evalHowl = evaluateSkill113c({ registry, skill: howlRec, slvl: 1, blvl: 1, statList: heroStats })
executeSkill113c({
registry,
skill: howlRec,
evalResult: evalHowl,
caster: hero,
targets: [minion, boss],
targetPositions: new Map(),
corpses: [],
missileEngine: { spawnMissile: () => ({ name: 'howl' }) } as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
// Minion receives 'terror'
expect(minionBus.hasState('terror')).toBe(true)
// In 1.13c, Bosses are strictly immune to mental flee (Howl)
const bossWasTerrified = bossBus.hasState('terror')
expect(bossWasTerrified).toBe(false)
})
it('5.2 empirical probe: verifies Taunt state application and debuff stats emptiness', async () => {
const registry = await getSharedDataRegistry()
const tauntRec = registry.getSkillById(137)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const dummyStats = new UnitStatList(registry)
const dummyBus = new StateBus(dummyStats, registry)
const dummy: CombatUnitContext = { id: 'dummy', name: 'Monster', statList: dummyStats, stateBus: dummyBus }
const evalTaunt = evaluateSkill113c({ registry, skill: tauntRec, slvl: 5, blvl: 5, statList: heroStats })
executeSkill113c({
registry,
skill: tauntRec,
evalResult: evalTaunt,
caster: hero,
targets: [dummy],
targetPositions: new Map(),
corpses: [],
missileEngine: { spawnMissile: () => ({ name: 'taunt' }) } as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
expect(dummyBus.hasState('taunt')).toBe(true)
const entry = dummyBus.getState('taunt')
// In 1.13c, Taunt slvl 5 reduces Attack Rating (-13%) and Damage (-13%)
expect(entry?.stats?.['tohit_percent']).toBe(-13)
expect(entry?.stats?.['item_mindamage_percent']).toBe(-13)
})
})
// =========================================================================
// Section 6: Find Item & Find Potion Corpse Validity & Horking Success Roll
// =========================================================================
describe('6. Find Item & Find Potion Corpse Target Validity & Horking Rolls', () => {
it('6.1 verifies 1.13c corpse rejection guards for shattered, consumed, and boss corpses', () => {
registerSkillModule(findPotionModule)
registerSkillModule(findItemModule)
for (const skillId of [131, 142]) {
// Missing corpse
expect(validateBarbarianCorpseTarget(skillId, undefined).valid).toBe(false)
// Consumed
expect(validateBarbarianCorpseTarget(skillId, { consumed: true }).valid).toBe(false)
// Shattered by cold death
expect(validateBarbarianCorpseTarget(skillId, { consumed: false, shattered: true }).valid).toBe(false)
// Boss corpse
expect(validateBarbarianCorpseTarget(skillId, { consumed: false, isBoss: true }).valid).toBe(false)
// Clean valid corpse
expect(validateBarbarianCorpseTarget(skillId, { consumed: false, shattered: false, isBoss: false }).valid).toBe(true)
}
})
it('6.2 empirical probe: verifies Find Item / Find Potion execution ignores success chance and always succeeds', async () => {
const registry = await getSharedDataRegistry()
const fiRec = registry.getSkillById(142)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
registerSkillModule(findItemModule)
const evalFI = evaluateSkill113c({ registry, skill: fiRec, slvl: 1, blvl: 1, statList: heroStats })
// At slvl 1, Find Item success chance is 13%
expect(calculateFindItemStats(1).dropChancePct).toBe(13)
let successes = 0
const totalTrials = 25
for (let i = 0; i < totalTrials; i++) {
const corpse = { id: `corpse-${i}`, consumed: false, shattered: false, isBoss: false, baseMaxHp: 500, x: 0, y: 0 }
const outcome = executeSkill113c({
registry,
skill: fiRec,
evalResult: evalFI,
caster: hero,
targets: [],
targetPositions: new Map(),
corpses: [corpse as any],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
if (outcome.notes.includes('item_found')) {
successes++
}
}
// In 1.13c, Find Item rolls against dropChancePct (13% at slvl 1)
expect(successes).toBeLessThan(totalTrials)
})
it('6.3 empirical probe: verifies loot roll execution (actual item generation) is absent', async () => {
const registry = await getSharedDataRegistry()
const fiRec = registry.getSkillById(142)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
registerSkillModule(findItemModule)
const evalFI = evaluateSkill113c({ registry, skill: fiRec, slvl: 1, blvl: 1, statList: heroStats })
const corpse = { id: 'c-loot', consumed: false, shattered: false, isBoss: false, baseMaxHp: 500, x: 0, y: 0 }
const randomSpy = vi.spyOn(Math, 'random').mockReturnValue(0.05)
const outcome = executeSkill113c({
registry,
skill: fiRec,
evalResult: evalFI,
caster: hero,
targets: [],
targetPositions: new Map(),
corpses: [corpse as any],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
randomSpy.mockRestore()
// Find Item pushes string 'item_found' to outcome.notes;
// It does NOT invoke executeDropPipeline or produce physical ground items.
expect(outcome.notes).toContain('item_found')
const hasDropPipelineCall = outcome.notes.some(n => n.startsWith('drop:') || n.startsWith('item_dropped'))
expect(hasDropPipelineCall).toBe(false)
})
})
// =========================================================================
// Section 7: Grim Ward Fear Totem
// =========================================================================
describe('7. Grim Ward Fear Totem & Boss Immunity', () => {
it('7.1 verifies corpse consumption on Grim Ward placement', async () => {
const registry = await getSharedDataRegistry()
const gwRec = registry.getSkillById(150)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
registerSkillModule(grimWardModule)
const evalGW = evaluateSkill113c({ registry, skill: gwRec, slvl: 1, blvl: 1, statList: heroStats })
const corpse = { id: 'corpse-gw', consumed: false, shattered: false, isBoss: false, baseMaxHp: 500, x: 0, y: 0 }
const outcome = executeSkill113c({
registry,
skill: gwRec,
evalResult: evalGW,
caster: hero,
targets: [],
targetPositions: new Map(),
corpses: [corpse as any],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
expect(outcome.executed).toBe(true)
expect(outcome.corpsesConsumed).toBe(1)
expect(corpse.consumed).toBe(true)
})
it('7.2 empirical probe: checks whether Grim Ward spawns a persistent totem entity (1000 frames)', async () => {
const registry = await getSharedDataRegistry()
const gwRec = registry.getSkillById(150)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
registerSkillModule(grimWardModule)
const evalGW = evaluateSkill113c({ registry, skill: gwRec, slvl: 1, blvl: 1, statList: heroStats })
const corpse = { id: 'corpse-gw-pet', consumed: false, shattered: false, isBoss: false, baseMaxHp: 500, x: 0, y: 0 }
const outcome = executeSkill113c({
registry,
skill: gwRec,
evalResult: evalGW,
caster: hero,
targets: [],
targetPositions: new Map(),
corpses: [corpse as any],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
// CHALLENGER PROBE: In registry.ts line 512-518, Grim Ward does NOT spawn any summon or totem missile!
// petsSummoned and missilesSpawned are empty!
expect(outcome.petsSummoned.length).toBe(0)
expect(outcome.missilesSpawned.length).toBe(0)
})
it('7.3 empirical probe: checks whether Grim Ward fear aura respects boss immunity', async () => {
const registry = await getSharedDataRegistry()
const gwRec = registry.getSkillById(150)!
const heroStats = new UnitStatList(registry)
const heroBus = new StateBus(heroStats, registry)
const hero: CombatUnitContext = { id: 'hero', name: 'Barbarian', statList: heroStats, stateBus: heroBus }
const bossStats = new UnitStatList(registry)
const bossBus = new StateBus(bossStats, registry)
const boss: CombatUnitContext = { id: 'boss', name: 'Diablo', statList: bossStats, stateBus: bossBus, isBoss: true, rank: 'boss' }
registerSkillModule(grimWardModule)
const evalGW = evaluateSkill113c({ registry, skill: gwRec, slvl: 1, blvl: 1, statList: heroStats })
const corpse = { id: 'corpse-gw-boss', consumed: false, shattered: false, isBoss: false, baseMaxHp: 500, x: 0, y: 0 }
executeSkill113c({
registry,
skill: gwRec,
evalResult: evalGW,
caster: hero,
targets: [boss],
targetPositions: new Map(),
corpses: [corpse as any],
missileEngine: {} as any,
auraScanner: {} as any,
summonManager: {} as any,
currentTick: 1,
targetX: 0,
targetY: 0,
})
// In 1.13c, Bosses are strictly immune to Grim Ward radial fear!
const bossFled = bossBus.hasState('terror')
expect(bossFled).toBe(false)
})
})
})