overrun/js/scenes/GameScene.js

169 lines
4.7 KiB
JavaScript

import { Player } from '../entities/Player.js';
import { WaveManager } from '../systems/WaveManager.js';
import { XPSystem } from '../systems/XPSystem.js';
import { SkillTree } from '../systems/SkillTree.js';
import { HUD } from '../ui/HUD.js';
import { SkillTreeUI } from '../ui/SkillTreeUI.js';
export class GameScene extends Phaser.Scene {
constructor() {
super({ key: 'GameScene' });
}
preload() {
this.load.json('zones', './js/data/zones.json');
this.load.json('skillTree', './js/data/skillTree.json');
}
create() {
const W = this.scale.width;
const H = this.scale.height;
this._drawArena(W, H);
this.physics.world.setBounds(0, 0, W, H);
// Core systems — data already loaded by preload()
this.skillTree = new SkillTree();
this.skillTree.load(this.cache.json.get('skillTree'));
this.player = new Player(this, W / 2, H / 2);
this.xpSystem = new XPSystem(this);
this.waveManager = new WaveManager(this, this.player);
this.waveManager.load(this.cache.json.get('zones'));
this.hud = new HUD(this, this.player, this.xpSystem);
// Enemy projectile group for collision
this._enemyProjectiles = [];
// Event wiring
this.events.on('enemy-killed', ({ xp }) => this.xpSystem.addXP(xp));
this.events.on('enemy-projectile-spawned', proj => this._enemyProjectiles.push(proj));
this.events.on('level-up', level => this._showLevelUp(level));
this.events.on('game-over', () => this._onGameOver());
this.events.on('victory', () => this._onVictory());
this._levelUpPending = false;
this._frozen = false;
this.waveManager.start();
}
_drawArena(W, H) {
// Dark background
this.add.rectangle(W / 2, H / 2, W, H, 0x111118);
// Grid lines for depth cue
const g = this.add.graphics();
g.lineStyle(1, 0x222233, 0.5);
for (let x = 0; x <= W; x += 80) { g.lineBetween(x, 0, x, H); }
for (let y = 0; y <= H; y += 80) { g.lineBetween(0, y, W, y); }
// Arena border
g.lineStyle(3, 0x334466, 1);
g.strokeRect(2, 2, W - 4, H - 4);
}
update(time, delta) {
if (!this.player || this._frozen) return;
this.player.update(delta);
this.waveManager.update(delta);
this.hud.update();
this._checkBulletHits();
this._checkEnemyProjectileHits();
this._pruneEnemyProjectiles();
}
_checkBulletHits() {
const bullets = this.player.bullets.getChildren();
const enemies = this.waveManager.enemies;
for (const bullet of bullets) {
if (!bullet.active) continue;
for (const enemy of enemies) {
if (!enemy.active) continue;
const dist = Phaser.Math.Distance.Between(bullet.x, bullet.y, enemy.x, enemy.y);
if (dist < enemy.radius + 5) {
enemy.takeDamage(bullet.damage);
bullet.destroy();
break;
}
}
}
}
_checkEnemyProjectileHits() {
this._enemyProjectiles = this._enemyProjectiles.filter(p => p?.active);
this._enemyProjectiles.forEach(proj => {
if (!proj.active) return;
const dist = Phaser.Math.Distance.Between(proj.x, proj.y, this.player.x, this.player.y);
if (dist < 16 + 6) {
this.player.takeDamage(proj.damage);
proj.destroy();
}
});
}
_pruneEnemyProjectiles() {
this._enemyProjectiles = this._enemyProjectiles.filter(p => p?.active);
}
_showLevelUp(level) {
if (this._levelUpPending) return;
this._levelUpPending = true;
// Only show if there are skills available
const available = this.skillTree.getAvailable();
if (available.length === 0) {
this._levelUpPending = false;
return;
}
this._frozen = true;
this.physics.world.pause();
new SkillTreeUI(this, this.skillTree, this.player, () => {
this._levelUpPending = false;
this._frozen = false;
this.physics.world.resume();
});
}
_onGameOver() {
this.scene.launch('GameOverScene');
this.scene.pause();
}
_onVictory() {
const W = this.scale.width;
const H = this.scale.height;
this.add.rectangle(W / 2, H / 2, W, H, 0x000000, 0.7).setDepth(30);
this.add.text(W / 2, H / 2 - 40, 'VICTORY!', {
fontSize: '72px', fill: '#ffdd00', fontStyle: 'bold'
}).setOrigin(0.5).setDepth(31);
const prompt = this.add.text(W / 2, H / 2 + 60, 'Press R to return to menu', {
fontSize: '22px', fill: '#ffffff'
}).setOrigin(0.5).setDepth(31);
this.tweens.add({ targets: prompt, alpha: 0, duration: 700, yoyo: true, repeat: -1 });
this.input.keyboard.once('keydown-R', () => {
this.scene.start('IntroScene');
});
}
shutdown() {
this.events.removeAllListeners();
this.player?.destroy();
this.waveManager?.reset();
this.hud?.destroy();
this._enemyProjectiles = [];
}
}