Files
test1/game/enemies/WaveManager.ts

165 lines
4.6 KiB
TypeScript

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 = []
}
}