feat(enemies): 实现怪物基类与4种怪物及波次管理器

This commit is contained in:
Cloud Bot
2026-03-21 08:04:11 +00:00
parent 84816cf39e
commit 6a21ece3ee
6 changed files with 661 additions and 0 deletions

182
game/enemies/WaveManager.ts Normal file
View File

@@ -0,0 +1,182 @@
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'
interface EnemyGroup {
type: 'FreshGraduate' | 'OldEmployee' | 'TroubleMaker' | 'BossVP'
count: number
interval: number
}
interface WaveConfig {
enemies: EnemyGroup[]
}
const WAVE_CONFIG: WaveConfig[] = [
{ enemies: [{ type: 'FreshGraduate', count: 10, interval: 800 }] },
{
enemies: [
{ type: 'FreshGraduate', count: 8, interval: 800 },
{ type: 'OldEmployee', count: 3, interval: 2000 },
],
},
{
enemies: [
{ type: 'OldEmployee', count: 5, interval: 2000 },
{ type: 'TroubleMaker', count: 3, interval: 1500 },
],
},
{
enemies: [
{ type: 'FreshGraduate', count: 12, interval: 600 },
{ type: 'TroubleMaker', count: 2, interval: 1500 },
],
},
{
enemies: [
{ type: 'OldEmployee', count: 6, interval: 1500 },
{ type: 'TroubleMaker', count: 3, interval: 1500 },
],
},
{
enemies: [
{ type: 'BossVP', count: 1, interval: 0 },
{ type: 'OldEmployee', count: 4, interval: 2000 },
],
},
]
export class WaveManager {
private scene: Phaser.Scene
private activeEnemies: EnemyBase[] = []
private pathPoints: PathPoint[]
private currentWave: number = 0
private isSpawning: boolean = false
private onWave3Complete?: () => void
private onAllWavesComplete?: () => void
private onDestroyRandomTower?: () => void
private wave3Completed: boolean = false
constructor(
scene: Phaser.Scene,
onWave3Complete?: () => void,
onAllWavesComplete?: () => void,
onDestroyRandomTower?: () => void
) {
this.scene = scene
this.pathPoints = buildFullPath()
this.onWave3Complete = onWave3Complete
this.onAllWavesComplete = onAllWavesComplete
this.onDestroyRandomTower = onDestroyRandomTower
}
get totalWaves(): number {
return WAVE_CONFIG.length
}
startNextWave(): void {
if (this.isSpawning || this.currentWave >= WAVE_CONFIG.length) return
const config = WAVE_CONFIG[this.currentWave]
this.currentWave++
this.isSpawning = true
this.spawnWave(config)
}
private spawnWave(config: WaveConfig): void {
// 将所有怪物组展开为按时间排列的生成序列
const spawnQueue: { type: EnemyGroup['type']; delay: number }[] = []
for (const group of config.enemies) {
for (let i = 0; i < group.count; i++) {
spawnQueue.push({ type: group.type, delay: group.interval * i })
}
}
// 按 delay 升序排列,同时生成
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: EnemyGroup['type']): EnemyBase {
let enemy: EnemyBase
const pts = [...this.pathPoints]
switch (type) {
case 'OldEmployee':
enemy = new OldEmployee(this.scene, pts)
break
case 'TroubleMaker':
enemy = new TroubleMaker(this.scene, pts)
break
case 'BossVP':
enemy = new BossVP(this.scene, pts, this.onDestroyRandomTower)
break
default:
enemy = new FreshGraduate(this.scene, pts)
}
// 随机显示语录
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)
}
// 检查第3波完成后触发周报
if (
this.currentWave === 3 &&
!this.isSpawning &&
this.activeEnemies.length === 0 &&
!this.wave3Completed
) {
this.wave3Completed = true
this.onWave3Complete?.()
}
// 检查全部波次完成
if (
this.currentWave >= WAVE_CONFIG.length &&
!this.isSpawning &&
this.activeEnemies.length === 0
) {
this.onAllWavesComplete?.()
}
}
getAllActiveEnemies(): EnemyBase[] {
return this.activeEnemies
}
hasMoreWaves(): boolean {
return this.currentWave < WAVE_CONFIG.length
}
getCurrentWaveNumber(): number {
return this.currentWave
}
}