import { DamageSystem, FIREBALL_KILL_RADIUS, SMOKE_KILL_RADIUS } from '@logic/DamageSystem'; import { PlayerStateMachine } from '@logic/PlayerStateMachine'; import { PlayerColorState } from '@common/Constants'; function setup() { const psm = new PlayerStateMachine(); return { psm, ds: new DamageSystem(psm) }; } describe('DamageSystem — fireball distance gate (req 10.4)', () => { it('misses when distance exceeds FIREBALL_KILL_RADIUS', () => { const { ds } = setup(); const r = ds.applyToPlayer({ attackType: 'fireball', attackerX: 0, attackerY: 0, victimX: FIREBALL_KILL_RADIUS + 10, victimY: 0, }); expect(r).toBeNull(); }); it('kills when within radius regardless of color', () => { const { psm, ds } = setup(); psm.pickupCrystalJade(); psm.pickupCrystalJade(); const r = ds.applyToPlayer({ attackType: 'fireball', attackerX: 0, attackerY: 0, victimX: 50, victimY: 0, }); expect(r!.kind).toBe('died'); expect(psm.color).toBe(PlayerColorState.Red); }); }); describe('DamageSystem — smoke bomb distance gate (req 10.5)', () => { it('misses when distance exceeds SMOKE_KILL_RADIUS', () => { const { ds } = setup(); const r = ds.applyToPlayer({ attackType: 'smoke_bomb', attackerX: 0, attackerY: 0, victimX: SMOKE_KILL_RADIUS + 1, victimY: 0, }); expect(r).toBeNull(); }); it('kills when within radius', () => { const { ds } = setup(); const r = ds.applyToPlayer({ attackType: 'smoke_bomb', attackerX: 0, attackerY: 0, victimX: 40, victimY: 0, }); expect(r!.kind).toBe('died'); }); }); describe('DamageSystem — precedence (req 10.3)', () => { it('i-frames beat fireball distance check', () => { const { psm, ds } = setup(); psm.takeHit('shuriken'); // die → consumes a life and starts i-frames const r = ds.applyToPlayer({ attackType: 'fireball', attackerX: 0, attackerY: 0, victimX: 10, victimY: 0, }); expect(r).toEqual({ kind: 'no_effect', reason: 'iframe' }); }); }); describe('DamageSystem — applyToEnemy helper', () => { it('reduces HP and floors at 0', () => { const { ds } = setup(); expect(ds.applyToEnemy(3, 2)).toBe(1); expect(ds.applyToEnemy(1, 5)).toBe(0); }); });