// BootScene – runs once at startup to generate all shared textures, then hands // off to MenuScene. Textures live in Phaser's global TextureManager so every // subsequent scene can use them without re-creating them. export default class BootScene extends Phaser.Scene { constructor() { super({ key: 'BootScene' }); } preload() { this.load.audio('title', 'js/audio/music/title.mp3'); this.load.audio('track01', 'js/audio/music/track01.mp3'); this.load.audio('track02', 'js/audio/music/track02.mp3'); this.load.audio('track03', 'js/audio/music/track03.mp3'); this.load.audio('track04', 'js/audio/music/track04.mp3'); this.load.audio('track05', 'js/audio/music/track05.mp3'); this.load.audio('track06', 'js/audio/music/track06.mp3'); this.load.audio('track07', 'js/audio/music/track07.mp3'); this.load.audio('sfx_player_shoot', 'js/audio/fx/player_shoot.mp3'); this.load.audio('sfx_player_death', 'js/audio/fx/player_death.mp3'); this.load.audio('sfx_alien_shoot', 'js/audio/fx/alien_shoot.mp3'); this.load.audio('sfx_missle', 'js/audio/fx/missle.mp3'); this.load.audio('sfx_impact', 'js/audio/fx/impact.mp3'); this.load.audio('sfx_alien_death', 'js/audio/fx/alien_death.mp3'); this.load.audio('sfx_thruster', 'js/audio/fx/thruster.mp3'); } create() { this._createTextures(); this.scene.start('MenuScene'); } // ─── Texture Generation ─────────────────────────────────────────────────── _createTextures() { // Player ship – 64×32, nose points right (angle 0) let g = this.make.graphics({ add: false }); g.lineStyle(2, 0x00ff44, 1); g.strokeTriangle(56, 16, 12, 4, 12, 28); g.generateTexture('player', 64, 32); g.destroy(); // Player ship with thruster flame g = this.make.graphics({ add: false }); g.lineStyle(2, 0x00ff44, 1); g.strokeTriangle(56, 16, 12, 4, 12, 28); g.lineStyle(2, 0xff6600, 1); g.strokeTriangle(12, 11, 12, 21, 0, 16); g.generateTexture('player_thrust', 64, 32); g.destroy(); // Large asteroid – 120×120, radius 50, 12-sided g = this.make.graphics({ add: false }); g.lineStyle(2, 0xcccccc, 1); g.strokePoints(this._asteroidPoints(60, 60, 50, 12, [1.0, 0.82, 1.08, 0.88, 1.0, 0.78, 1.1, 0.9, 0.94, 1.0, 0.84, 1.06] ), true); g.generateTexture('asteroid_large', 120, 120); g.destroy(); // Medium asteroid – 64×64, radius 26, 10-sided g = this.make.graphics({ add: false }); g.lineStyle(2, 0xcccccc, 1); g.strokePoints(this._asteroidPoints(32, 32, 26, 10, [1.0, 0.78, 1.1, 0.88, 1.0, 0.82, 1.0, 0.92, 1.06, 0.80] ), true); g.generateTexture('asteroid_medium', 64, 64); g.destroy(); // Small asteroid – 32×32, radius 12, 8-sided g = this.make.graphics({ add: false }); g.lineStyle(2, 0xcccccc, 1); g.strokePoints(this._asteroidPoints(16, 16, 12, 8, [1.0, 0.78, 1.12, 0.84, 1.0, 0.9, 0.80, 1.04] ), true); g.generateTexture('asteroid_small', 32, 32); g.destroy(); // Alien saucer – 64×48 g = this.make.graphics({ add: false }); g.lineStyle(2, 0x00ffff, 1); g.strokeEllipse(32, 30, 52, 20); g.strokeEllipse(32, 22, 30, 18); g.generateTexture('alien', 64, 48); g.destroy(); // Player bullet – 8×8 yellow circle g = this.make.graphics({ add: false }); g.fillStyle(0xffff00, 1); g.fillCircle(4, 4, 4); g.generateTexture('bullet', 8, 8); g.destroy(); // Alien bullet – 8×8 red circle g = this.make.graphics({ add: false }); g.fillStyle(0xff4444, 1); g.fillCircle(4, 4, 4); g.generateTexture('alien_bullet', 8, 8); g.destroy(); // Spread-shot alien – 72×54, orange/red menacing saucer g = this.make.graphics({ add: false }); g.lineStyle(2, 0xff4400, 1); g.strokeEllipse(36, 34, 62, 18); // wide flat hull g.strokeEllipse(36, 24, 38, 22); // taller upper dome g.fillStyle(0xff8800, 1); g.fillCircle(36, 26, 5); // central cannon port g.lineStyle(2, 0xff2200, 1); g.beginPath(); g.moveTo(5, 34); g.lineTo(0, 27); g.strokePath(); // left spike g.beginPath(); g.moveTo(67, 34); g.lineTo(72, 27); g.strokePath(); // right spike g.generateTexture('alien_spread', 72, 54); g.destroy(); // Homing-missile alien – 80×60, purple/magenta angular diamond g = this.make.graphics({ add: false }); g.lineStyle(2, 0xcc00ff, 1); g.strokePoints([ { x: 40, y: 5 }, { x: 72, y: 30 }, { x: 40, y: 55 }, { x: 8, y: 30 } ], true); g.lineStyle(1, 0x9900cc, 1); g.strokePoints([ { x: 40, y: 18 }, { x: 58, y: 30 }, { x: 40, y: 42 }, { x: 22, y: 30 } ], true); g.fillStyle(0xff00ff, 1); g.fillCircle(40, 30, 5); g.generateTexture('alien_missile', 80, 60); g.destroy(); // Homing missile projectile – 14×6, magenta body + orange nose g = this.make.graphics({ add: false }); g.fillStyle(0xff00ff, 1); g.fillRect(0, 1, 10, 4); g.fillStyle(0xff8800, 1); g.fillRect(10, 0, 4, 6); g.generateTexture('homing_missile', 14, 6); g.destroy(); // Reticle outer layer – outer ring + diagonal ticks (rotates one way) g = this.make.graphics({ add: false }); g.lineStyle(1.5, 0x00ff44, 1); g.strokeCircle(32, 32, 28); g.lineStyle(1, 0x00ff44, 0.5); g.beginPath(); g.moveTo(12, 12); g.lineTo(18, 18); g.strokePath(); g.beginPath(); g.moveTo(52, 12); g.lineTo(46, 18); g.strokePath(); g.beginPath(); g.moveTo(12, 52); g.lineTo(18, 46); g.strokePath(); g.beginPath(); g.moveTo(52, 52); g.lineTo(46, 46); g.strokePath(); g.generateTexture('reticle_outer', 64, 64); g.destroy(); // Reticle inner layer – inner ring + cross lines (rotates opposite way) g = this.make.graphics({ add: false }); g.lineStyle(1.5, 0x00ff44, 1); g.strokeCircle(32, 32, 6); g.beginPath(); g.moveTo(32, 2); g.lineTo(32, 24); g.strokePath(); // top g.beginPath(); g.moveTo(32, 40); g.lineTo(32, 62); g.strokePath(); // bottom g.beginPath(); g.moveTo( 2, 32); g.lineTo(24, 32); g.strokePath(); // left g.beginPath(); g.moveTo(40, 32); g.lineTo(62, 32); g.strokePath(); // right g.generateTexture('reticle_inner', 64, 64); g.destroy(); } _asteroidPoints(cx, cy, radius, numPoints, offsets) { return offsets.map((scale, i) => { const angle = (i / numPoints) * Math.PI * 2; return { x: cx + Math.cos(angle) * radius * scale, y: cy + Math.sin(angle) * radius * scale }; }); } }