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