overrun/js/systems/WaveManager.js

112 lines
3.0 KiB
JavaScript

import { ChaseEnemy } from '../entities/enemies/ChaseEnemy.js';
import { ShooterEnemy } from '../entities/enemies/ShooterEnemy.js';
import { SwarmerEnemy } from '../entities/enemies/SwarmerEnemy.js';
const ENEMY_CLASSES = { ChaseEnemy, ShooterEnemy, SwarmerEnemy };
const SPAWN_MARGIN = 40;
export class WaveManager {
constructor(scene, player) {
this.scene = scene;
this.player = player;
this.zones = [];
this.zoneIndex = 0;
this.waveIndex = 0;
this.enemies = [];
this.active = false;
}
load(data) {
this.zones = data;
}
start() {
this.active = true;
this._spawnWave();
}
get currentZone() { return this.zones[this.zoneIndex]; }
get currentWave() { return this.currentZone?.waves[this.waveIndex]; }
get zoneNum() { return this.zoneIndex + 1; }
get waveNum() { return this.waveIndex + 1; }
get totalWaves() { return this.currentZone?.waves.length ?? 0; }
_spawnWave() {
const wave = this.currentWave;
if (!wave) return;
this.scene.events.emit('wave-start', { zone: this.zoneNum, wave: this.waveNum, totalWaves: this.totalWaves });
wave.enemies.forEach(({ type, count }) => {
const EnemyClass = ENEMY_CLASSES[type];
if (!EnemyClass) return;
for (let i = 0; i < count; i++) {
const [x, y] = this._edgePosition();
const enemy = new EnemyClass(this.scene, x, y, this.player);
this.enemies.push(enemy);
}
});
}
_edgePosition() {
const W = this.scene.scale.width;
const H = this.scene.scale.height;
const side = Phaser.Math.Between(0, 3);
switch (side) {
case 0: return [Phaser.Math.Between(0, W), -SPAWN_MARGIN];
case 1: return [W + SPAWN_MARGIN, Phaser.Math.Between(0, H)];
case 2: return [Phaser.Math.Between(0, W), H + SPAWN_MARGIN];
case 3: return [-SPAWN_MARGIN, Phaser.Math.Between(0, H)];
}
}
update(delta) {
if (!this.active) return;
// Prune dead enemies
this.enemies = this.enemies.filter(e => e.active);
// Update living enemies
this.enemies.forEach(e => e.update(delta));
// Check wave clear
if (this.enemies.length === 0) {
this.active = false;
this.scene.time.delayedCall(1500, () => this._advance());
}
}
_advance() {
this.waveIndex++;
if (this.waveIndex >= this.currentZone.waves.length) {
// All waves cleared — let the player exit to trigger next zone
this.scene.events.emit('zone-clear', { zone: this.zoneNum });
this.scene.events.emit('zone-waves-complete');
} else {
this.active = true;
this._spawnWave();
}
}
/** Called by GameScene after the player exits the screen. */
startNextZone() {
this.waveIndex = 0;
this.zoneIndex++;
if (this.zoneIndex >= this.zones.length) {
this.scene.events.emit('victory');
return;
}
this.active = true;
this._spawnWave();
}
reset() {
this.enemies.forEach(e => { if (e.active) e.destroy(); });
this.enemies = [];
this.zoneIndex = 0;
this.waveIndex = 0;
this.active = false;
}
}