feat: add level progression and background music to all games
- Introduce `src/audio.js` module for centralized music playback and cleanup - Add background music tracks for all four games and the main menu - Refactor game config files to support multiple difficulty levels via `levels` object - Update game scenes to load level-specific configurations and track progression - Add win conditions based on level-specific objectives (e.g., bugs to pass, waves cleared) - Update HUDs to display current progress toward level completion
This commit is contained in:
parent
2aabbb158a
commit
47f16c78be
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,22 @@
|
||||||
|
const REGISTRY_KEY = 'bgMusic';
|
||||||
|
|
||||||
|
export function playMusic(scene, key) {
|
||||||
|
const prev = scene.game.registry.get(REGISTRY_KEY);
|
||||||
|
if (prev) {
|
||||||
|
prev.stop();
|
||||||
|
prev.destroy();
|
||||||
|
}
|
||||||
|
const music = scene.sound.add(key, { loop: true, volume: 0.7 });
|
||||||
|
music.play();
|
||||||
|
scene.game.registry.set(REGISTRY_KEY, music);
|
||||||
|
return music;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopMusic(scene) {
|
||||||
|
const music = scene.game.registry.get(REGISTRY_KEY);
|
||||||
|
if (music) {
|
||||||
|
music.stop();
|
||||||
|
music.destroy();
|
||||||
|
scene.game.registry.set(REGISTRY_KEY, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,35 @@
|
||||||
|
import { playMusic, stopMusic } from '../../audio.js';
|
||||||
|
|
||||||
export default class CodeBugInvaders extends Phaser.Scene {
|
export default class CodeBugInvaders extends Phaser.Scene {
|
||||||
constructor() { super({ key: 'CodeBugInvaders' }); }
|
constructor() { super({ key: 'CodeBugInvaders' }); }
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
this.load.json('cbiConfig', 'src/games/code-bug-invaders/config.json');
|
this.load.json('cbiConfig', 'src/games/code-bug-invaders/config.json?v=2');
|
||||||
|
this.load.audio('cbiMusic', 'assets/music/02-code-bug-invaders.mp3');
|
||||||
}
|
}
|
||||||
|
|
||||||
init(data) {
|
init(data) {
|
||||||
this.globalLives = data.lives;
|
this.globalLives = data.lives;
|
||||||
|
this.level = data.level || 1;
|
||||||
this.gameActive = false;
|
this.gameActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.cfg = this.cache.json.get('cbiConfig');
|
const all = this.cache.json.get('cbiConfig');
|
||||||
|
const lvl = String(this.level);
|
||||||
|
this.cfg = all.levels ? (all.levels[lvl] ?? all.levels['1']) : all;
|
||||||
this.bugs = [];
|
this.bugs = [];
|
||||||
this.playerBullets = [];
|
this.playerBullets = [];
|
||||||
this.bugBullets = [];
|
this.bugBullets = [];
|
||||||
|
this.bugsHit = 0;
|
||||||
this.gameActive = true;
|
this.gameActive = true;
|
||||||
this.formationDir = 1; // 1 = right, -1 = left
|
this.formationDir = 1; // 1 = right, -1 = left
|
||||||
this.formationX = 0;
|
this.formationX = 0;
|
||||||
this.formationY = 0;
|
this.formationY = 0;
|
||||||
this.bugFireTimer = this.cfg.bugFireInterval;
|
this.bugFireTimer = this.cfg.bugFireInterval;
|
||||||
|
|
||||||
|
playMusic(this, 'cbiMusic');
|
||||||
|
|
||||||
this._buildBackground();
|
this._buildBackground();
|
||||||
this._buildBugs();
|
this._buildBugs();
|
||||||
this._buildPlayer();
|
this._buildPlayer();
|
||||||
|
|
@ -30,7 +39,7 @@ export default class CodeBugInvaders extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
shutdown() {
|
shutdown() {
|
||||||
// input listeners auto-cleaned by Phaser scene shutdown
|
stopMusic(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildBackground() {
|
_buildBackground() {
|
||||||
|
|
@ -174,11 +183,18 @@ export default class CodeBugInvaders extends Phaser.Scene {
|
||||||
if (!bug.alive) continue;
|
if (!bug.alive) continue;
|
||||||
if (Math.abs(bullet.x - bug.x) < 28 && Math.abs(bullet.y - bug.y) < 22) {
|
if (Math.abs(bullet.x - bug.x) < 28 && Math.abs(bullet.y - bug.y) < 22) {
|
||||||
bug.alive = false;
|
bug.alive = false;
|
||||||
|
this.bugsHit++;
|
||||||
hit = true;
|
hit = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hit) this.playerBullets.splice(bi, 1);
|
if (hit) {
|
||||||
|
this.playerBullets.splice(bi, 1);
|
||||||
|
if (this.bugsHit >= this.cfg.bugsToPass) {
|
||||||
|
this._endGame(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bug bullets vs player
|
// Bug bullets vs player
|
||||||
|
|
@ -282,7 +298,6 @@ export default class CodeBugInvaders extends Phaser.Scene {
|
||||||
g.lineBetween(0, this.cfg.dangerY, 1600, this.cfg.dangerY);
|
g.lineBetween(0, this.cfg.dangerY, 1600, this.cfg.dangerY);
|
||||||
|
|
||||||
// HUD update
|
// HUD update
|
||||||
const aliveCount = this.bugs.filter(b => b.alive).length;
|
this.bugsText.setText(`BUGS ${this.bugsHit}/${this.cfg.bugsToPass}`);
|
||||||
this.bugsText.setText(`BUGS: ${aliveCount}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,24 @@
|
||||||
{
|
{
|
||||||
"cols": 10,
|
"levels": {
|
||||||
"rows": 4,
|
"1": {
|
||||||
"bugSpacingX": 120,
|
"cols": 10, "rows": 4,
|
||||||
"bugSpacingY": 70,
|
"bugSpacingX": 120, "bugSpacingY": 70,
|
||||||
"startX": 180,
|
"startX": 180, "startY": 100,
|
||||||
"startY": 100,
|
"formationSpeed": 60, "formationStepDown": 30,
|
||||||
"formationSpeed": 60,
|
"rightBound": 1540, "leftBound": 60,
|
||||||
"formationStepDown": 30,
|
"bugFireInterval": 1800, "bugBulletSpeed": 280,
|
||||||
"rightBound": 1540,
|
"playerSpeed": 420, "playerBulletSpeed": 700,
|
||||||
"leftBound": 60,
|
"dangerY": 720, "bugsToPass": 15
|
||||||
"bugFireInterval": 1800,
|
},
|
||||||
"bugBulletSpeed": 280,
|
"2": {
|
||||||
"playerSpeed": 420,
|
"cols": 10, "rows": 4,
|
||||||
"playerBulletSpeed": 700,
|
"bugSpacingX": 120, "bugSpacingY": 70,
|
||||||
"dangerY": 720
|
"startX": 180, "startY": 100,
|
||||||
|
"formationSpeed": 85, "formationStepDown": 30,
|
||||||
|
"rightBound": 1540, "leftBound": 60,
|
||||||
|
"bugFireInterval": 1300, "bugBulletSpeed": 350,
|
||||||
|
"playerSpeed": 420, "playerBulletSpeed": 700,
|
||||||
|
"dangerY": 720, "bugsToPass": 28
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,29 @@
|
||||||
|
import { playMusic, stopMusic } from '../../audio.js';
|
||||||
|
|
||||||
export default class ColoradoDefense extends Phaser.Scene {
|
export default class ColoradoDefense extends Phaser.Scene {
|
||||||
constructor() { super({ key: 'ColoradoDefense' }); }
|
constructor() { super({ key: 'ColoradoDefense' }); }
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
this.load.json('cdConfig', 'src/games/colorado-defense/config.json');
|
this.load.json('cdConfig', 'src/games/colorado-defense/config.json?v=2');
|
||||||
|
this.load.audio('cdMusic', 'assets/music/01-colorado-defense.mp3');
|
||||||
}
|
}
|
||||||
|
|
||||||
init(data) {
|
init(data) {
|
||||||
this.globalLives = data.lives;
|
this.globalLives = data.lives;
|
||||||
|
this.level = data.level || 1;
|
||||||
this.gameActive = false;
|
this.gameActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.cfg = this.cache.json.get('cdConfig');
|
const all = this.cache.json.get('cdConfig');
|
||||||
|
const lvl = String(this.level);
|
||||||
|
this.cfg = all.levels ? (all.levels[lvl] ?? all.levels['1']) : all;
|
||||||
this.cities = [];
|
this.cities = [];
|
||||||
this.missiles = [];
|
this.missiles = [];
|
||||||
this.interceptors = [];
|
this.interceptors = [];
|
||||||
this.explosions = [];
|
this.explosions = [];
|
||||||
this.wave = 0;
|
this.wave = 0;
|
||||||
|
this.wavesCleared = 0;
|
||||||
this.spawnTimer = 0;
|
this.spawnTimer = 0;
|
||||||
this.waveSpawned = 0;
|
this.waveSpawned = 0;
|
||||||
this.waveCleared = false;
|
this.waveCleared = false;
|
||||||
|
|
@ -24,6 +31,8 @@ export default class ColoradoDefense extends Phaser.Scene {
|
||||||
this.mouseX = 800;
|
this.mouseX = 800;
|
||||||
this.mouseY = 400;
|
this.mouseY = 400;
|
||||||
|
|
||||||
|
playMusic(this, 'cdMusic');
|
||||||
|
|
||||||
this._buildBackground();
|
this._buildBackground();
|
||||||
this._buildCities();
|
this._buildCities();
|
||||||
this.mainGfx = this.add.graphics();
|
this.mainGfx = this.add.graphics();
|
||||||
|
|
@ -33,6 +42,7 @@ export default class ColoradoDefense extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
shutdown() {
|
shutdown() {
|
||||||
|
stopMusic(this);
|
||||||
this.input.off('pointerdown');
|
this.input.off('pointerdown');
|
||||||
this.input.off('pointermove');
|
this.input.off('pointermove');
|
||||||
}
|
}
|
||||||
|
|
@ -70,8 +80,8 @@ export default class ColoradoDefense extends Phaser.Scene {
|
||||||
this.livesText = this.add.text(800, 40, '♥ '.repeat(this.globalLives).trim(), {
|
this.livesText = this.add.text(800, 40, '♥ '.repeat(this.globalLives).trim(), {
|
||||||
fontSize: '24px', fontFamily: 'monospace', color: '#ff4444',
|
fontSize: '24px', fontFamily: 'monospace', color: '#ff4444',
|
||||||
}).setOrigin(0.5, 0);
|
}).setOrigin(0.5, 0);
|
||||||
this.statusText = this.add.text(1580, 40, 'CITIES: 6/6', {
|
this.statusText = this.add.text(1580, 40, `CITIES: 6/6\nWAVES: 0/${this.cfg.wavesToPass}`, {
|
||||||
fontSize: '24px', fontFamily: 'monospace', color: '#4488ff',
|
fontSize: '22px', fontFamily: 'monospace', color: '#4488ff',
|
||||||
}).setOrigin(1, 0);
|
}).setOrigin(1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,14 +192,19 @@ export default class ColoradoDefense extends Phaser.Scene {
|
||||||
this.missiles.length === 0 &&
|
this.missiles.length === 0 &&
|
||||||
this.interceptors.length === 0) {
|
this.interceptors.length === 0) {
|
||||||
this.waveCleared = true;
|
this.waveCleared = true;
|
||||||
this.time.delayedCall(1800, () => this._nextWave());
|
this.wavesCleared++;
|
||||||
|
if (this.wavesCleared >= this.cfg.wavesToPass) {
|
||||||
|
this.time.delayedCall(1800, () => this._endGame(true));
|
||||||
|
} else {
|
||||||
|
this.time.delayedCall(1800, () => this._nextWave());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check city deaths
|
// Check city deaths
|
||||||
const dead = this.cities.filter(c => !c.alive).length;
|
const dead = this.cities.filter(c => !c.alive).length;
|
||||||
if (dead >= this.cfg.maxCityDeaths) this._endGame(false);
|
if (dead >= this.cfg.maxCityDeaths) this._endGame(false);
|
||||||
|
|
||||||
this.statusText.setText(`CITIES: ${6 - dead}/6`);
|
this.statusText.setText(`CITIES: ${6 - dead}/6\nWAVES: ${this.wavesCleared}/${this.cfg.wavesToPass}`);
|
||||||
this._draw();
|
this._draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,22 @@
|
||||||
{
|
{
|
||||||
"waves": [
|
"levels": {
|
||||||
{ "count": 6, "speed": 55, "spawnInterval": 2800 },
|
"1": {
|
||||||
{ "count": 9, "speed": 80, "spawnInterval": 2000 },
|
"waves": [
|
||||||
{ "count": 14, "speed": 110, "spawnInterval": 1400 }
|
{ "count": 6, "speed": 55, "spawnInterval": 2800 },
|
||||||
],
|
{ "count": 9, "speed": 80, "spawnInterval": 2000 },
|
||||||
"interceptorSpeed": 420,
|
{ "count": 14, "speed": 110, "spawnInterval": 1400 }
|
||||||
"explosionRadius": 75,
|
],
|
||||||
"maxCityDeaths": 3
|
"interceptorSpeed": 420, "explosionRadius": 75,
|
||||||
|
"maxCityDeaths": 3, "wavesToPass": 2
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"waves": [
|
||||||
|
{ "count": 8, "speed": 70, "spawnInterval": 2200 },
|
||||||
|
{ "count": 12, "speed": 100, "spawnInterval": 1600 },
|
||||||
|
{ "count": 16, "speed": 130, "spawnInterval": 1100 }
|
||||||
|
],
|
||||||
|
"interceptorSpeed": 420, "explosionRadius": 75,
|
||||||
|
"maxCityDeaths": 2, "wavesToPass": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,25 +24,37 @@ const LAYOUT = [
|
||||||
[1,1,1,1,1,1,1,1,1,1], // 14 bottom wall
|
[1,1,1,1,1,1,1,1,1,1], // 14 bottom wall
|
||||||
];
|
];
|
||||||
|
|
||||||
|
import { playMusic, stopMusic } from '../../audio.js';
|
||||||
|
|
||||||
export default class DotDude extends Phaser.Scene {
|
export default class DotDude extends Phaser.Scene {
|
||||||
constructor() { super({ key: 'DotDude' }); }
|
constructor() { super({ key: 'DotDude' }); }
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
this.load.json('ddConfig', 'src/games/dot-dude/config.json');
|
this.load.json('ddConfig', 'src/games/dot-dude/config.json?v=2');
|
||||||
|
this.load.audio('ddMusic', 'assets/music/03-dot-dude.mp3');
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown() {
|
||||||
|
stopMusic(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
init(data) {
|
init(data) {
|
||||||
this.globalLives = data.lives;
|
this.globalLives = data.lives;
|
||||||
|
this.level = data.level || 1;
|
||||||
this.gameActive = false;
|
this.gameActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.cfg = this.cache.json.get('ddConfig');
|
const all = this.cache.json.get('ddConfig');
|
||||||
|
const lvl = String(this.level);
|
||||||
|
this.cfg = all.levels ? (all.levels[lvl] ?? all.levels['1']) : all;
|
||||||
|
|
||||||
this.dots = LAYOUT.map(row => row.map(cell => (cell === 0 ? 1 : 0)));
|
this.dots = LAYOUT.map(row => row.map(cell => (cell === 0 ? 1 : 0)));
|
||||||
this.totalDots = this.dots.flat().filter(d => d === 1).length;
|
this.totalDots = this.dots.flat().filter(d => d === 1).length;
|
||||||
this.dotsEaten = 0;
|
this.dotsEaten = 0;
|
||||||
|
|
||||||
|
playMusic(this, 'ddMusic');
|
||||||
|
|
||||||
this._initPlayer();
|
this._initPlayer();
|
||||||
this._initGhosts();
|
this._initGhosts();
|
||||||
|
|
||||||
|
|
@ -133,12 +145,12 @@ export default class DotDude extends Phaser.Scene {
|
||||||
}).setOrigin(0.5).setDepth(10);
|
}).setOrigin(0.5).setDepth(10);
|
||||||
|
|
||||||
// Right panel
|
// Right panel
|
||||||
this.add.text(1350, 80, 'DOTS', {
|
this.add.text(1350, 80, 'EAT', {
|
||||||
fontSize: '26px', fontFamily: 'monospace', color: '#aaaaaa',
|
fontSize: '26px', fontFamily: 'monospace', color: '#aaaaaa',
|
||||||
}).setOrigin(0.5).setDepth(10);
|
}).setOrigin(0.5).setDepth(10);
|
||||||
|
|
||||||
this.dotsText = this.add.text(1350, 140, `${this.totalDots}`, {
|
this.dotsText = this.add.text(1350, 140, `0 / ${this.cfg.pelletsToPass}`, {
|
||||||
fontSize: '48px', fontFamily: 'monospace', color: '#ffff00',
|
fontSize: '36px', fontFamily: 'monospace', color: '#ffff00',
|
||||||
}).setOrigin(0.5).setDepth(10);
|
}).setOrigin(0.5).setDepth(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,7 +178,7 @@ export default class DotDude extends Phaser.Scene {
|
||||||
if (col >= 0 && col < COLS && row >= 0 && row < ROWS && this.dots[row][col] === 1) {
|
if (col >= 0 && col < COLS && row >= 0 && row < ROWS && this.dots[row][col] === 1) {
|
||||||
this.dots[row][col] = 0;
|
this.dots[row][col] = 0;
|
||||||
this.dotsEaten++;
|
this.dotsEaten++;
|
||||||
this.dotsText.setText(`${this.totalDots - this.dotsEaten}`);
|
this.dotsText.setText(`${this.dotsEaten} / ${this.cfg.pelletsToPass}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ghosts
|
// Ghosts
|
||||||
|
|
@ -179,7 +191,7 @@ export default class DotDude extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dotsEaten >= this.totalDots) { this._endGame(true); return; }
|
if (this.dotsEaten >= this.cfg.pelletsToPass) { this._endGame(true); return; }
|
||||||
|
|
||||||
// Mouth animation
|
// Mouth animation
|
||||||
this.mouthAngle += this.mouthDir * 3 * dt;
|
this.mouthAngle += this.mouthDir * 3 * dt;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
{
|
{
|
||||||
"playerSpeed": 155,
|
"levels": {
|
||||||
"ghostSpeed": 105,
|
"1": {
|
||||||
"ghostStartDelay": [2000, 3500, 5000],
|
"playerSpeed": 155, "ghostSpeed": 90,
|
||||||
"dotRadius": 5,
|
"ghostStartDelay": [2000, 4000, 6500],
|
||||||
"powerDotRadius": 10
|
"dotRadius": 5, "pelletsToPass": 20
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"playerSpeed": 155, "ghostSpeed": 120,
|
||||||
|
"ghostStartDelay": [1500, 3000, 4500],
|
||||||
|
"dotRadius": 5, "pelletsToPass": 40
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,36 @@
|
||||||
const BLOCK_COLORS = [0xff2222, 0xff8800, 0xffff00, 0x00ff44, 0x00aaff];
|
const BLOCK_COLORS = [0xff2222, 0xff8800, 0xffff00, 0x00ff44, 0x00aaff];
|
||||||
|
|
||||||
|
import { playMusic, stopMusic } from '../../audio.js';
|
||||||
|
|
||||||
export default class SmashOut extends Phaser.Scene {
|
export default class SmashOut extends Phaser.Scene {
|
||||||
constructor() { super({ key: 'SmashOut' }); }
|
constructor() { super({ key: 'SmashOut' }); }
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
this.load.json('soConfig', 'src/games/smash-out/config.json');
|
this.load.json('soConfig', 'src/games/smash-out/config.json?v=2');
|
||||||
|
this.load.audio('soMusic', 'assets/music/04-smash-out.mp3');
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown() {
|
||||||
|
stopMusic(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
init(data) {
|
init(data) {
|
||||||
this.globalLives = data.lives;
|
this.globalLives = data.lives;
|
||||||
|
this.level = data.level || 1;
|
||||||
this.gameActive = false;
|
this.gameActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.cfg = this.cache.json.get('soConfig');
|
const all = this.cache.json.get('soConfig');
|
||||||
|
const lvl = String(this.level);
|
||||||
|
this.cfg = all.levels ? (all.levels[lvl] ?? all.levels['1']) : all;
|
||||||
this.blocks = [];
|
this.blocks = [];
|
||||||
|
this.bricksDestroyed = 0;
|
||||||
this.gameActive = true;
|
this.gameActive = true;
|
||||||
this.ballLaunched = false;
|
this.ballLaunched = false;
|
||||||
|
|
||||||
|
playMusic(this, 'soMusic');
|
||||||
|
|
||||||
this._buildBackground();
|
this._buildBackground();
|
||||||
this._buildBlocks();
|
this._buildBlocks();
|
||||||
this._buildPaddle();
|
this._buildPaddle();
|
||||||
|
|
@ -176,6 +189,7 @@ export default class SmashOut extends Phaser.Scene {
|
||||||
|
|
||||||
if (dist < r) {
|
if (dist < r) {
|
||||||
block.alive = false;
|
block.alive = false;
|
||||||
|
this.bricksDestroyed++;
|
||||||
|
|
||||||
// Determine bounce axis: use which edge the ball is closest to
|
// Determine bounce axis: use which edge the ball is closest to
|
||||||
const overlapX = (ball.x < bLeft || ball.x > bRight)
|
const overlapX = (ball.x < bLeft || ball.x > bRight)
|
||||||
|
|
@ -191,14 +205,13 @@ export default class SmashOut extends Phaser.Scene {
|
||||||
} else {
|
} else {
|
||||||
ball.vx = -ball.vx;
|
ball.vx = -ball.vx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.bricksDestroyed >= this.cfg.bricksToPass) {
|
||||||
|
this._endGame(true);
|
||||||
|
}
|
||||||
break; // one block per frame
|
break; // one block per frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Win check
|
|
||||||
if (this.blocks.every(b => !b.alive)) {
|
|
||||||
this._endGame(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_endGame(success) {
|
_endGame(success) {
|
||||||
|
|
@ -219,9 +232,7 @@ export default class SmashOut extends Phaser.Scene {
|
||||||
const g = this.mainGfx;
|
const g = this.mainGfx;
|
||||||
g.clear();
|
g.clear();
|
||||||
|
|
||||||
// Blocks
|
this.blocksText.setText(`SMASH ${this.bricksDestroyed}/${this.cfg.bricksToPass}`);
|
||||||
const alive = this.blocks.filter(b => b.alive).length;
|
|
||||||
this.blocksText.setText(`BLOCKS: ${alive}`);
|
|
||||||
|
|
||||||
this.blocks.forEach(b => {
|
this.blocks.forEach(b => {
|
||||||
if (!b.alive) return;
|
if (!b.alive) return;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,18 @@
|
||||||
{
|
{
|
||||||
"paddleWidth": 200,
|
"levels": {
|
||||||
"paddleHeight": 18,
|
"1": {
|
||||||
"paddleY": 855,
|
"paddleWidth": 200, "paddleHeight": 18, "paddleY": 855,
|
||||||
"paddleSpeed": 480,
|
"paddleSpeed": 480, "ballRadius": 12, "ballSpeed": 530,
|
||||||
"ballRadius": 12,
|
"blockCols": 8, "blockRows": 5, "blockWidth": 140, "blockHeight": 30,
|
||||||
"ballSpeed": 530,
|
"blockGapX": 20, "blockGapY": 12, "blocksStartX": 170, "blocksStartY": 70,
|
||||||
"blockCols": 8,
|
"bricksToPass": 15
|
||||||
"blockRows": 5,
|
},
|
||||||
"blockWidth": 140,
|
"2": {
|
||||||
"blockHeight": 30,
|
"paddleWidth": 170, "paddleHeight": 18, "paddleY": 855,
|
||||||
"blockGapX": 20,
|
"paddleSpeed": 520, "ballRadius": 12, "ballSpeed": 640,
|
||||||
"blockGapY": 12,
|
"blockCols": 8, "blockRows": 5, "blockWidth": 140, "blockHeight": 30,
|
||||||
"blocksStartX": 170,
|
"blockGapX": 20, "blockGapY": 12, "blocksStartX": 170, "blocksStartY": 70,
|
||||||
"blocksStartY": 70
|
"bricksToPass": 30
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { playMusic, stopMusic } from '../audio.js';
|
||||||
|
|
||||||
export default class MainMenu extends Phaser.Scene {
|
export default class MainMenu extends Phaser.Scene {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ key: 'MainMenu' });
|
super({ key: 'MainMenu' });
|
||||||
|
|
@ -5,9 +7,16 @@ export default class MainMenu extends Phaser.Scene {
|
||||||
|
|
||||||
preload() {
|
preload() {
|
||||||
this.load.json('gameConfig', 'config/game.json');
|
this.load.json('gameConfig', 'config/game.json');
|
||||||
|
this.load.audio('mainTheme', 'assets/music/00-main-theme.mp3');
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown() {
|
||||||
|
stopMusic(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
|
playMusic(this, 'mainTheme');
|
||||||
|
|
||||||
const cx = 800, cy = 450;
|
const cx = 800, cy = 450;
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue