import type { EntityManager } from '@/game/core/EntityManager' import type { GridMap } from '@/game/map/GridMap' import type { HealthComp, MovementComp } from '@/game/components' import { createGoblin } from '@/game/entities/enemies/goblin' import { createOrc } from '@/game/entities/enemies/orc' import { createWarg } from '@/game/entities/enemies/warg' import { createWraith } from '@/game/entities/enemies/wraith' import { createTroll } from '@/game/entities/enemies/troll' import { createGolem } from '@/game/entities/enemies/golem' import { createNecromancer } from '@/game/entities/enemies/necromancer' import { createDragon } from '@/game/entities/enemies/dragon' import { createLichKing } from '@/game/entities/enemies/lich_king' import { eventBus } from '@/game/core/EventBus' import { getWaveDef, type EnemyType } from '@/data/waves' import { useSettingsStore } from '@/state/settingsStore' import { useGameStore } from '@/state/gameStore' export interface WaveState { active: boolean waveIndex: number spawnQueue: EnemyType[] spawnTimer: number spawnInterval: number aliveCount: number countdown: number } const BETWEEN_WAVE_TIME = 10 export function createWaveState(): WaveState { return { active: false, waveIndex: 0, spawnQueue: [], spawnTimer: 0, spawnInterval: 1.2, aliveCount: 0, countdown: 0, } } export function startWave(state: WaveState, waveIndex: number): void { if (state.active) return const levelId = useGameStore.getState().currentLevelId const def = getWaveDef(waveIndex, levelId) state.active = true state.waveIndex = waveIndex state.spawnQueue = [...def.enemies] state.spawnTimer = 0 state.spawnInterval = def.interval state.aliveCount = state.spawnQueue.length state.countdown = 0 eventBus.emit('wave:start', { waveIndex }) } export function waveSystem( state: WaveState, entities: EntityManager, _map: GridMap, path: { x: number; y: number }[], dt: number, ): void { if (!state.active) return const alive = entities.withTag('enemy').filter((e) => !e.tags.has('dead')) state.aliveCount = alive.length + state.spawnQueue.length if (state.spawnQueue.length > 0) { state.spawnTimer -= dt if (state.spawnTimer <= 0) { const type = state.spawnQueue.shift()! const entity = entities.create() const spawn = path[0] const sx = spawn.x + (Math.random() - 0.5) * 8 const sy = spawn.y + (Math.random() - 0.5) * 8 spawnEnemy(type, entity, path, sx, sy) applyDifficulty(entity) state.spawnTimer = state.spawnInterval } } if (state.spawnQueue.length === 0 && alive.length === 0) { state.active = false state.countdown = BETWEEN_WAVE_TIME eventBus.emit('wave:end', { waveIndex: state.waveIndex }) } } function applyDifficulty(entity: ReturnType): void { const diff = useSettingsStore.getState().difficulty if (diff === 'normal') return const hpMult = diff === 'heroic' ? 1.35 : 1.75 const speedMult = diff === 'heroic' ? 1.1 : 1.25 const hp = entity.health as HealthComp | undefined if (hp) { hp.current = Math.round(hp.current * hpMult); hp.max = Math.round(hp.max * hpMult) } const mv = entity.movement as MovementComp | undefined if (mv) mv.speed = Math.round(mv.speed * speedMult) } function spawnEnemy( type: EnemyType, entity: ReturnType, path: { x: number; y: number }[], sx: number, sy: number, ): void { const flyPath = [path[0], path[path.length - 1]] switch (type) { case 'orc': createOrc(entity, path, sx, sy); break case 'warg': createWarg(entity, path, sx, sy); break case 'wraith': createWraith(entity, path, sx, sy); break case 'troll': createTroll(entity, path, sx, sy); break case 'golem': createGolem(entity, path, sx, sy); break case 'necromancer': createNecromancer(entity, path, sx, sy); break case 'dragon': createDragon(entity, flyPath, sx, sy - 30); break case 'lich_king': createLichKing(entity, path, sx, sy); break default: createGoblin(entity, path, sx, sy); break } }