98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
/**
|
|
* Issue #436: Hydra (#62) Summon Cap & Entity Visuals
|
|
*
|
|
* 1.13c Ground Truth Invariants Verification Suite:
|
|
* 1. Summon Cap: Maximum 6 active Hydra groups (18 heads total) concurrently active.
|
|
* 2. FIFO Eviction: When 7th group is cast, the oldest 1st group is automatically evicted (pruned to 0 ttl).
|
|
* 3. Exact 3-Head Offsets: Offsets [(-6, -11), (0, -7), (4, -15)] correctly assigned per group.
|
|
* 4. Slvl Scaling: Hydra damage properly respects caster skill level instead of hardcoded slvl 1.
|
|
*/
|
|
import { describe, expect, it } from 'vitest'
|
|
import { GameEngine } from '../../../src/game/engine.ts'
|
|
import { castSkill } from '../../../src/scene/act-scene.ts'
|
|
|
|
describe('Issue #436: Hydra (#62) Summon Cap & Entity Visuals', () => {
|
|
it('enforces maximum 6 groups (18 heads) and FIFO eviction on successive casts', () => {
|
|
const engine = new GameEngine(
|
|
{ widthPx: 1000, heightPx: 1000, overlap: () => 0 },
|
|
{ spawn: { x: 200, y: 200 }, stats: [], questDefs: [], npcDefs: [] } as any,
|
|
)
|
|
engine.world.player.x = 200
|
|
engine.world.player.y = 200
|
|
engine.world.player.mana = 1000
|
|
|
|
const statusEl = { textContent: '' } as HTMLElement
|
|
const dummyRuntime = {
|
|
walkable: { isWalkable: () => true },
|
|
collision: { isWalkable: () => true },
|
|
} as any
|
|
|
|
// Cast Hydra 6 times (filling cap to 6 groups = 18 heads)
|
|
for (let c = 1; c <= 6; c++) {
|
|
engine.world.player.cooldown = 0
|
|
const ok = castSkill(62, 300 + c * 20, 300, {
|
|
engine,
|
|
runtime: dummyRuntime,
|
|
hudManager: null,
|
|
status: statusEl,
|
|
})
|
|
expect(ok).toBe(true)
|
|
expect(engine.projectiles.length).toBe(c * 3)
|
|
}
|
|
|
|
expect(engine.projectiles.length).toBe(18)
|
|
const firstGroupHeadX = engine.projectiles[0]!.x
|
|
|
|
// Cast 7th time -> triggers FIFO eviction of group 1
|
|
engine.world.player.cooldown = 0
|
|
const ok7 = castSkill(62, 500, 300, {
|
|
engine,
|
|
runtime: dummyRuntime,
|
|
hudManager: null,
|
|
status: statusEl,
|
|
})
|
|
expect(ok7).toBe(true)
|
|
|
|
// Total active heads must be capped at 18 (6 groups * 3 heads)
|
|
expect(engine.projectiles.length).toBe(18)
|
|
|
|
// First group must have been evicted and no longer present in engine.projectiles
|
|
const containsOldHead = engine.projectiles.some(p => p.x === firstGroupHeadX)
|
|
expect(containsOldHead).toBe(false)
|
|
})
|
|
|
|
it('correctly reads slvl from hudManager for damage calculation', () => {
|
|
const engine = new GameEngine(
|
|
{ widthPx: 1000, heightPx: 1000, overlap: () => 0 },
|
|
{ spawn: { x: 200, y: 200 }, stats: [], questDefs: [], npcDefs: [] } as any,
|
|
)
|
|
engine.world.player.x = 200
|
|
engine.world.player.y = 200
|
|
engine.world.player.mana = 1000
|
|
engine.world.player.cooldown = 0
|
|
|
|
const mockHudManager = {
|
|
getSkillLevel: (skillId: number) => (skillId === 62 ? 20 : 0),
|
|
syncPublishedState: () => {},
|
|
}
|
|
|
|
const statusEl = { textContent: '' } as HTMLElement
|
|
const dummyRuntime = {
|
|
walkable: { isWalkable: () => true },
|
|
collision: { isWalkable: () => true },
|
|
} as any
|
|
|
|
castSkill(62, 350, 250, {
|
|
engine,
|
|
runtime: dummyRuntime,
|
|
hudManager: mockHudManager as any,
|
|
status: statusEl,
|
|
})
|
|
|
|
expect(engine.projectiles.length).toBe(3)
|
|
// At slvl 20, damage is ~162 (150-175 range), much higher than slvl 1 (14-19)
|
|
const head = engine.projectiles[0]!
|
|
expect(head.damage).toBeGreaterThan(100)
|
|
})
|
|
})
|