import type Phaser from 'phaser' import { EnemyBase, buildFullPath, type PathPoint } from './EnemyBase' import { FreshGraduate } from './FreshGraduate' import { OldEmployee } from './OldEmployee' import { TroubleMaker } from './TroubleMaker' import { BossVP } from './BossVP' import type { WaveConfigEntry, DifficultyLevel } from '../data/mapConfigs' import { DIFFICULTY_MULTIPLIER } from '../data/mapConfigs' type EnemyType = 'FreshGraduate' | 'OldEmployee' | 'TroubleMaker' | 'BossVP' interface WaveCallbacks { onWaveComplete: () => void onAllWavesComplete: () => void onDestroyRandomTower: () => void } export class WaveManager { private scene: Phaser.Scene private activeEnemies: EnemyBase[] = [] private pathPoints: PathPoint[] private currentWave: number = 0 private isSpawning: boolean = false private waveConfigs: WaveConfigEntry[] private difficulty: DifficultyLevel private callbacks: WaveCallbacks private waveCompleteFired: boolean = false private allCompleteGuard: boolean = false constructor( scene: Phaser.Scene, waveConfigs: WaveConfigEntry[], difficulty: DifficultyLevel, callbacks: WaveCallbacks, waypoints?: readonly { x: number; y: number }[] ) { this.scene = scene this.waveConfigs = waveConfigs this.difficulty = difficulty this.callbacks = callbacks this.pathPoints = buildFullPath(waypoints) } get totalWaves(): number { return this.waveConfigs.length } startNextWave(): void { if (this.isSpawning || this.currentWave >= this.waveConfigs.length) return const config = this.waveConfigs[this.currentWave] this.currentWave++ this.isSpawning = true this.spawnWave(config) } private spawnWave(config: WaveConfigEntry): void { const multiplier = DIFFICULTY_MULTIPLIER[this.difficulty] const spawnQueue: { type: EnemyType; delay: number }[] = [] for (const group of config.enemies) { const count = Math.ceil(group.count * multiplier.enemyCount) for (let i = 0; i < count; i++) { spawnQueue.push({ type: group.type, delay: group.interval * i }) } } spawnQueue.sort((a, b) => a.delay - b.delay) let completed = 0 for (const item of spawnQueue) { this.scene.time.delayedCall(item.delay, () => { this.spawnEnemy(item.type) completed++ if (completed === spawnQueue.length) { this.isSpawning = false } }) } if (spawnQueue.length === 0) this.isSpawning = false } private spawnEnemy(type: EnemyType): EnemyBase { const multiplier = DIFFICULTY_MULTIPLIER[this.difficulty] const pts = [...this.pathPoints] let enemy: EnemyBase const speedMult = multiplier.enemySpeed const hpMult = type === 'BossVP' ? multiplier.bossHp : 1.0 switch (type) { case 'OldEmployee': enemy = new OldEmployee(this.scene, pts, speedMult, hpMult) break case 'TroubleMaker': enemy = new TroubleMaker(this.scene, pts, speedMult, hpMult) break case 'BossVP': enemy = new BossVP(this.scene, pts, this.callbacks.onDestroyRandomTower, speedMult, hpMult) break default: enemy = new FreshGraduate(this.scene, pts, speedMult, hpMult) } this.scene.time.delayedCall(1000 + Math.random() * 2000, () => { if (!enemy.isDead) enemy.showQuote() }) this.activeEnemies.push(enemy) return enemy } update(delta: number): void { for (let i = this.activeEnemies.length - 1; i >= 0; i--) { const e = this.activeEnemies[i] if (e.isDead) { this.activeEnemies.splice(i, 1) continue } e.update(delta) } // 中途波次完成回调(用于触发周报等) if ( this.currentWave === 3 && !this.isSpawning && this.activeEnemies.length === 0 && !this.waveCompleteFired ) { this.waveCompleteFired = true this.callbacks.onWaveComplete() } // 全部波次完成 if ( this.currentWave >= this.waveConfigs.length && !this.isSpawning && this.activeEnemies.length === 0 && !this.allCompleteGuard ) { this.allCompleteGuard = true this.callbacks.onAllWavesComplete() } } getAllActiveEnemies(): EnemyBase[] { return this.activeEnemies } hasMoreWaves(): boolean { return this.currentWave < this.waveConfigs.length } getCurrentWaveNumber(): number { return this.currentWave } /** 清除所有活跃怪物(切图时调用) */ clearAllEnemies(): void { for (const e of this.activeEnemies) { if (!e.isDead) e.destroy() } this.activeEnemies = [] } }