Add player death mechanics with enemy freeze, projectile cleanup, and event-driven state management

This commit is contained in:
Brian Fertig 2026-03-08 16:38:28 -06:00
parent 04ecdcccc3
commit 840f6788ab
4 changed files with 45 additions and 3 deletions

View File

@ -188,6 +188,7 @@ export class Player {
} }
this.scene.sound.play('sfx-death', { volume: 0.6 }); this.scene.sound.play('sfx-death', { volume: 0.6 });
this.scene.events.emit('player-died');
this.hp = this.stats.maxHp; this.hp = this.stats.maxHp;
this.invincible = true; this.invincible = true;
@ -229,6 +230,7 @@ export class Player {
onComplete: () => { onComplete: () => {
this.sprite.setAlpha(1); this.sprite.setAlpha(1);
this.invincible = false; this.invincible = false;
this.scene.events.emit('player-respawned');
}, },
}); });
} }

View File

@ -85,6 +85,8 @@ export class GameScene extends Phaser.Scene {
this.events.on('wave-start', () => this.sound.play('sfx-enemy-wave', { volume: 0.5 })); this.events.on('wave-start', () => this.sound.play('sfx-enemy-wave', { volume: 0.5 }));
this.events.on('zone-waves-complete', () => this._startZoneExit()); this.events.on('zone-waves-complete', () => this._startZoneExit());
this.events.on('player-died', () => this._onPlayerDied());
this.events.on('player-respawned', () => { this.waveManager.enemiesFrozen = false; });
this.reticle = new Reticle(this); this.reticle = new Reticle(this);
@ -269,6 +271,27 @@ export class GameScene extends Phaser.Scene {
}); });
} }
_onPlayerDied() {
const W = this.scale.width;
const H = this.scale.height;
const cx = W / 2;
const cy = H / 2;
// Destroy all enemy projectiles
this._enemyProjectiles.forEach(p => { if (p?.active) p.destroy(); });
this._enemyProjectiles = [];
// Destroy enemies within 150px of screen centre
this.waveManager.enemies.forEach(e => {
if (e.active && Phaser.Math.Distance.Between(e.x, e.y, cx, cy) < 150) {
e.takeDamage(99999);
}
});
// Freeze remaining enemies for the duration of invincibility
this.waveManager.enemiesFrozen = true;
}
_checkBulletHits() { _checkBulletHits() {
const bullets = this.player.bullets.getChildren(); const bullets = this.player.bullets.getChildren();
const enemies = this.waveManager.enemies; const enemies = this.waveManager.enemies;
@ -490,7 +513,17 @@ export class GameScene extends Phaser.Scene {
} }
shutdown() { shutdown() {
this.events.removeAllListeners(); // Remove only our custom listeners — removeAllListeners() would also strip
// Phaser's internal scene lifecycle listeners and corrupt the next create().
this.events.off('enemy-killed');
this.events.off('enemy-projectile-spawned');
this.events.off('level-up');
this.events.off('game-over');
this.events.off('victory');
this.events.off('wave-start');
this.events.off('zone-waves-complete');
this.events.off('player-died');
this.events.off('player-respawned');
this._barrierColliders?.forEach(c => c.destroy()); this._barrierColliders?.forEach(c => c.destroy());
this.reticle?.destroy(); this.reticle?.destroy();
this.barrierManager?.destroy(); this.barrierManager?.destroy();

View File

@ -16,6 +16,7 @@ export class WaveManager {
this.waveIndex = 0; this.waveIndex = 0;
this.enemies = []; this.enemies = [];
this.active = false; this.active = false;
this.enemiesFrozen = false;
this.spriteGroup = scene.physics.add.group(); this.spriteGroup = scene.physics.add.group();
} }
@ -70,8 +71,12 @@ export class WaveManager {
// Prune dead enemies // Prune dead enemies
this.enemies = this.enemies.filter(e => e.active); this.enemies = this.enemies.filter(e => e.active);
// Update living enemies // Update living enemies (skip when frozen after player death)
if (this.enemiesFrozen) {
this.enemies.forEach(e => { if (e.sprite?.body) e.sprite.body.setVelocity(0, 0); });
} else {
this.enemies.forEach(e => e.update(delta)); this.enemies.forEach(e => e.update(delta));
}
// Check wave clear // Check wave clear
if (this.enemies.length === 0) { if (this.enemies.length === 0) {

View File

@ -72,6 +72,8 @@ export class HUD {
} }
destroy() { destroy() {
this.scene.events.off('wave-start');
this.scene.events.off('zone-clear');
[this._hpLabel, this._hpBg, this._hpFill, this._xpBg, this._xpFill, this._xpLabel, this._zoneText, ...this._lifeIcons] [this._hpLabel, this._hpBg, this._hpFill, this._xpBg, this._xpFill, this._xpLabel, this._zoneText, ...this._lifeIcons]
.forEach(o => o?.destroy()); .forEach(o => o?.destroy());
} }