feat: add Burli Kong game mode with sprite animations and level select
- Add Burli Kong game with animated Kong sprite and idle animations - Implement ladder-based barrel falling mechanics (50% chance) - Increase barrel speeds across all levels for improved difficulty - Add HTML mode selector to MainMenu for switching between games - Propagate selectedGame through GameManager and LevelComplete scenes - Move Kong rendering from gfx to sprite-based with proper depth layers - Fix HUD lives text positioning
This commit is contained in:
parent
afb95c91d1
commit
d39249e9c8
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
Binary file not shown.
|
|
@ -34,6 +34,7 @@ export default class BurliKong extends Phaser.Scene {
|
|||
|
||||
preload() {
|
||||
this.load.json('bkConfig', 'src/games/burli-kong/config.json');
|
||||
this.load.spritesheet('burliSprite', 'assets/images/burli-sprite.png', { frameWidth: 135, frameHeight: 135 });
|
||||
this.load.audio('bkMusic', 'assets/music/06-burli-kong.mp3');
|
||||
this.load.audio('bkComplete', 'assets/fx/complete.mp3');
|
||||
this.load.audio('bkJump', 'assets/fx/launch.mp3');
|
||||
|
|
@ -55,7 +56,13 @@ export default class BurliKong extends Phaser.Scene {
|
|||
this.barrelsCleared = 0;
|
||||
this.spawnTimer = 0;
|
||||
this.barrels = [];
|
||||
this.kongArmRaise = 0; // seconds remaining for Kong arm-raise animation
|
||||
this.kongArmRaise = 0;
|
||||
|
||||
// Idle animation state
|
||||
this.kongIdleTimer = 0;
|
||||
this.kongIdleInterval = 3.5;
|
||||
this.kongIsIdling = false;
|
||||
this.kongIdleSeq = 0; // incremented to invalidate stale delayed callbacks
|
||||
|
||||
this.player = {
|
||||
x: 200,
|
||||
|
|
@ -66,10 +73,26 @@ export default class BurliKong extends Phaser.Scene {
|
|||
platformIndex: 0,
|
||||
facingDir: 1,
|
||||
ladderIndex: -1,
|
||||
jumpedOverBarrels: new Set(), // track barrels already scored as jump-overs
|
||||
jumpedOverBarrels: new Set(),
|
||||
};
|
||||
|
||||
this.mainGfx = this.add.graphics();
|
||||
// Background drawn once at depth 0
|
||||
const bgGfx = this.add.graphics().setDepth(0);
|
||||
this._drawBackground(bgGfx);
|
||||
|
||||
// Kong sprite at depth 1, bottom-anchored to platform surface
|
||||
this.kongSprite = this.add.sprite(95, PLATFORMS[4].y - PLATFORM_H, 'burliSprite', 0)
|
||||
.setOrigin(0.5, 1)
|
||||
.setDepth(1);
|
||||
|
||||
// Sprite 5: static, 20px to the right of Kong
|
||||
this.add.image(250, PLATFORMS[4].y - PLATFORM_H, 'burliSprite', 5)
|
||||
.setOrigin(0.5, 1)
|
||||
.setDepth(1);
|
||||
|
||||
// Game elements (platforms, ladders, barrels, player) at depth 2
|
||||
this.mainGfx = this.add.graphics().setDepth(2);
|
||||
|
||||
this.cursors = this.input.keyboard.createCursorKeys();
|
||||
this.keyA = this.input.keyboard.addKey('A');
|
||||
this.keyAPressed = false;
|
||||
|
|
@ -86,6 +109,7 @@ export default class BurliKong extends Phaser.Scene {
|
|||
this._spawnBarrels(dt);
|
||||
this._updateBarrels(dt);
|
||||
this._checkCollisions();
|
||||
this._updateKongSprite(dt);
|
||||
this._draw();
|
||||
this._updateHUD();
|
||||
}
|
||||
|
|
@ -220,6 +244,7 @@ export default class BurliKong extends Phaser.Scene {
|
|||
vy: 0,
|
||||
alive: true,
|
||||
cleared: false,
|
||||
ladderRollsDone: new Set(),
|
||||
});
|
||||
|
||||
this.kongArmRaise = 0.6;
|
||||
|
|
@ -252,10 +277,27 @@ export default class BurliKong extends Phaser.Scene {
|
|||
b.vy = 0;
|
||||
b.platformIndex = nextIdx;
|
||||
b.dir = BARREL_DIR[nextIdx];
|
||||
b.ladderRollsDone = new Set();
|
||||
}
|
||||
} else {
|
||||
b.x += b.dir * cfg.barrelSpeed * dt;
|
||||
|
||||
// 50% chance to fall down a ladder when rolling over one
|
||||
for (let li = 0; li < LADDERS.length; li++) {
|
||||
const l = LADDERS[li];
|
||||
if (l.fromPlatform + 1 !== b.platformIndex) continue;
|
||||
if (b.ladderRollsDone.has(li)) continue;
|
||||
if (Math.abs(b.x - l.x) < 32) {
|
||||
b.ladderRollsDone.add(li);
|
||||
if (Math.random() < 0.5) {
|
||||
b.x = l.x;
|
||||
b.falling = true;
|
||||
b.vy = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const plat = PLATFORMS[b.platformIndex];
|
||||
// Fall off edge
|
||||
if (b.dir > 0 && b.x > plat.xMax - BARREL_R) {
|
||||
|
|
@ -357,10 +399,8 @@ export default class BurliKong extends Phaser.Scene {
|
|||
const g = this.mainGfx;
|
||||
g.clear();
|
||||
|
||||
this._drawBackground(g);
|
||||
this._drawPlatforms(g);
|
||||
this._drawLadders(g);
|
||||
this._drawKong(g);
|
||||
this._drawBarrels(g);
|
||||
this._drawPlayer(g);
|
||||
}
|
||||
|
|
@ -373,6 +413,43 @@ export default class BurliKong extends Phaser.Scene {
|
|||
for (let y = 0; y < 900; y += 60) g.lineBetween(0, y, 1600, y);
|
||||
}
|
||||
|
||||
// ─── Kong sprite ──────────────────────────────────────────────────────────
|
||||
|
||||
_updateKongSprite(dt) {
|
||||
if (this.kongArmRaise > 0) {
|
||||
// Throwing — invalidate any in-flight idle sequence
|
||||
this.kongIdleSeq++;
|
||||
this.kongIsIdling = false;
|
||||
this.kongIdleTimer = 0;
|
||||
this.kongSprite.setFrame(1);
|
||||
} else {
|
||||
if (!this.kongIsIdling) {
|
||||
this.kongSprite.setFrame(0);
|
||||
this.kongIdleTimer += dt;
|
||||
if (this.kongIdleTimer >= this.kongIdleInterval) {
|
||||
this._playKongIdleAnimation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_playKongIdleAnimation() {
|
||||
this.kongIsIdling = true;
|
||||
this.kongIdleTimer = 0;
|
||||
const seq = ++this.kongIdleSeq;
|
||||
|
||||
this.kongSprite.setFrame(2);
|
||||
this.time.delayedCall(450, () => {
|
||||
if (!this.gameActive || this.kongIdleSeq !== seq) return;
|
||||
this.kongSprite.setFrame(3);
|
||||
this.time.delayedCall(450, () => {
|
||||
if (!this.gameActive || this.kongIdleSeq !== seq) return;
|
||||
this.kongSprite.setFrame(0);
|
||||
this.kongIsIdling = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_drawPlatforms(g) {
|
||||
for (const plat of PLATFORMS) {
|
||||
const w = plat.xMax - plat.xMin;
|
||||
|
|
@ -402,45 +479,6 @@ export default class BurliKong extends Phaser.Scene {
|
|||
}
|
||||
}
|
||||
|
||||
_drawKong(g) {
|
||||
// Kong sits above Kong platform (platform index 4)
|
||||
const kx = 95, ky = PLATFORMS[4].y - PLATFORM_H;
|
||||
const armRaised = this.kongArmRaise > 0;
|
||||
|
||||
// Body (trapezoid approximated as rect)
|
||||
g.fillStyle(0x552200);
|
||||
g.fillRect(kx - 32, ky - 95, 64, 60);
|
||||
|
||||
// Head (circle approximation via fillCircle)
|
||||
g.fillStyle(0x663300);
|
||||
g.fillCircle(kx, ky - 105, 28);
|
||||
|
||||
// Eyes
|
||||
g.fillStyle(0xffd700);
|
||||
g.fillCircle(kx - 9, ky - 110, 5);
|
||||
g.fillCircle(kx + 9, ky - 110, 5);
|
||||
g.fillStyle(0x000000);
|
||||
g.fillCircle(kx - 9, ky - 110, 2);
|
||||
g.fillCircle(kx + 9, ky - 110, 2);
|
||||
|
||||
// Arms
|
||||
g.fillStyle(0x552200);
|
||||
if (armRaised) {
|
||||
// Left arm raised
|
||||
g.fillRect(kx - 60, ky - 145, 30, 18);
|
||||
// Right arm normal
|
||||
g.fillRect(kx + 32, ky - 70, 30, 18);
|
||||
} else {
|
||||
g.fillRect(kx - 60, ky - 70, 30, 18);
|
||||
g.fillRect(kx + 32, ky - 70, 30, 18);
|
||||
}
|
||||
|
||||
// Feet
|
||||
g.fillStyle(0x442200);
|
||||
g.fillRect(kx - 28, ky - 37, 22, 18);
|
||||
g.fillRect(kx + 6, ky - 37, 22, 18);
|
||||
}
|
||||
|
||||
_drawBarrels(g) {
|
||||
for (const b of this.barrels) {
|
||||
if (!b.alive) continue;
|
||||
|
|
@ -489,7 +527,7 @@ export default class BurliKong extends Phaser.Scene {
|
|||
// ─── HUD ──────────────────────────────────────────────────────────────────
|
||||
|
||||
_buildHUD() {
|
||||
this.livesText = this.add.text(20, 16, '', {
|
||||
this.livesText = this.add.text(340, 16, '', {
|
||||
fontSize: '26px', fontFamily: 'monospace',
|
||||
color: '#ff4444', stroke: '#000000', strokeThickness: 4,
|
||||
}).setDepth(10);
|
||||
|
|
@ -521,8 +559,12 @@ export default class BurliKong extends Phaser.Scene {
|
|||
if (!this.gameActive) return;
|
||||
this.gameActive = false;
|
||||
|
||||
if (success) this._playFx('bkComplete', 0.8);
|
||||
else this._playFx('bkHit', 0.7);
|
||||
if (success) {
|
||||
this._playFx('bkComplete', 0.8);
|
||||
this.kongSprite.setFrame(4);
|
||||
} else {
|
||||
this._playFx('bkHit', 0.7);
|
||||
}
|
||||
|
||||
const color = success ? '#00ff44' : '#ff2222';
|
||||
this.add.text(800, 440, msg, {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"levels": {
|
||||
"1": { "playerSpeed": 240, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 180, "barrelSpeed": 140, "barrelSpawnInterval": 4.0, "maxBarrels": 3, "barrelsToPass": 5, "stompBounce": 320 },
|
||||
"2": { "playerSpeed": 240, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 180, "barrelSpeed": 172, "barrelSpawnInterval": 3.5, "maxBarrels": 3, "barrelsToPass": 7, "stompBounce": 320 },
|
||||
"3": { "playerSpeed": 240, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 185, "barrelSpeed": 200, "barrelSpawnInterval": 3.1, "maxBarrels": 4, "barrelsToPass": 9, "stompBounce": 320 },
|
||||
"4": { "playerSpeed": 245, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 188, "barrelSpeed": 224, "barrelSpawnInterval": 2.8, "maxBarrels": 4, "barrelsToPass": 11, "stompBounce": 320 },
|
||||
"5": { "playerSpeed": 245, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 190, "barrelSpeed": 245, "barrelSpawnInterval": 2.5, "maxBarrels": 5, "barrelsToPass": 14, "stompBounce": 320 },
|
||||
"6": { "playerSpeed": 250, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 193, "barrelSpeed": 264, "barrelSpawnInterval": 2.2, "maxBarrels": 5, "barrelsToPass": 17, "stompBounce": 340 },
|
||||
"7": { "playerSpeed": 250, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 196, "barrelSpeed": 281, "barrelSpawnInterval": 2.0, "maxBarrels": 6, "barrelsToPass": 20, "stompBounce": 340 },
|
||||
"8": { "playerSpeed": 255, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 198, "barrelSpeed": 296, "barrelSpawnInterval": 1.8, "maxBarrels": 6, "barrelsToPass": 23, "stompBounce": 340 },
|
||||
"9": { "playerSpeed": 255, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 200, "barrelSpeed": 309, "barrelSpawnInterval": 1.7, "maxBarrels": 7, "barrelsToPass": 26, "stompBounce": 360 },
|
||||
"10": { "playerSpeed": 260, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 200, "barrelSpeed": 320, "barrelSpawnInterval": 1.6, "maxBarrels": 7, "barrelsToPass": 30, "stompBounce": 360 }
|
||||
"1": { "playerSpeed": 240, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 180, "barrelSpeed": 200, "barrelSpawnInterval": 4.0, "maxBarrels": 3, "barrelsToPass": 5, "stompBounce": 320 },
|
||||
"2": { "playerSpeed": 240, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 180, "barrelSpeed": 240, "barrelSpawnInterval": 3.5, "maxBarrels": 3, "barrelsToPass": 7, "stompBounce": 320 },
|
||||
"3": { "playerSpeed": 240, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 185, "barrelSpeed": 280, "barrelSpawnInterval": 3.1, "maxBarrels": 4, "barrelsToPass": 9, "stompBounce": 320 },
|
||||
"4": { "playerSpeed": 245, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 188, "barrelSpeed": 315, "barrelSpawnInterval": 2.8, "maxBarrels": 4, "barrelsToPass": 11, "stompBounce": 320 },
|
||||
"5": { "playerSpeed": 245, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 190, "barrelSpeed": 345, "barrelSpawnInterval": 2.5, "maxBarrels": 5, "barrelsToPass": 14, "stompBounce": 320 },
|
||||
"6": { "playerSpeed": 250, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 193, "barrelSpeed": 375, "barrelSpawnInterval": 2.2, "maxBarrels": 5, "barrelsToPass": 17, "stompBounce": 340 },
|
||||
"7": { "playerSpeed": 250, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 196, "barrelSpeed": 400, "barrelSpawnInterval": 2.0, "maxBarrels": 6, "barrelsToPass": 20, "stompBounce": 340 },
|
||||
"8": { "playerSpeed": 255, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 198, "barrelSpeed": 420, "barrelSpawnInterval": 1.8, "maxBarrels": 6, "barrelsToPass": 23, "stompBounce": 340 },
|
||||
"9": { "playerSpeed": 255, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 200, "barrelSpeed": 440, "barrelSpawnInterval": 1.7, "maxBarrels": 7, "barrelsToPass": 26, "stompBounce": 360 },
|
||||
"10": { "playerSpeed": 260, "jumpVelocity": 520, "gravity": 1000, "climbSpeed": 200, "barrelSpeed": 460, "barrelSpawnInterval": 1.6, "maxBarrels": 7, "barrelsToPass": 30, "stompBounce": 360 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ export default class GameManager extends Phaser.Scene {
|
|||
this.config = data.config;
|
||||
this.level = data.level || 1;
|
||||
this.lives = data.lives ?? this.config.lives ?? 3;
|
||||
this.selectedGame = data.selectedGame ?? null;
|
||||
this.queue = [];
|
||||
this.queueIndex = 0;
|
||||
}
|
||||
|
|
@ -29,10 +30,14 @@ export default class GameManager extends Phaser.Scene {
|
|||
}
|
||||
|
||||
_buildQueue() {
|
||||
const pool = [...this.config.gamePool];
|
||||
const count = this.config.levels[String(this.level)]?.gameCount ?? 3;
|
||||
Phaser.Utils.Array.Shuffle(pool);
|
||||
this.queue = pool.slice(0, count);
|
||||
if (this.selectedGame) {
|
||||
this.queue = Array(count).fill(this.selectedGame);
|
||||
} else {
|
||||
const pool = [...this.config.gamePool];
|
||||
Phaser.Utils.Array.Shuffle(pool);
|
||||
this.queue = pool.slice(0, count);
|
||||
}
|
||||
this.queueIndex = 0;
|
||||
}
|
||||
|
||||
|
|
@ -126,6 +131,7 @@ export default class GameManager extends Phaser.Scene {
|
|||
level: this.level,
|
||||
lives: this.lives,
|
||||
hasNextLevel: hasNext,
|
||||
selectedGame: this.selectedGame,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export default class LevelComplete extends Phaser.Scene {
|
|||
this.level = data.level;
|
||||
this.lives = data.lives;
|
||||
this.hasNextLevel = data.hasNextLevel;
|
||||
this.selectedGame = data.selectedGame ?? null;
|
||||
}
|
||||
|
||||
create() {
|
||||
|
|
@ -54,6 +55,7 @@ export default class LevelComplete extends Phaser.Scene {
|
|||
config: this.config,
|
||||
level: this.level + 1,
|
||||
lives: this.lives,
|
||||
selectedGame: this.selectedGame,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
import { playMusic, stopMusic } from '../audio.js';
|
||||
|
||||
const MODES = [
|
||||
{ label: 'MAIN GAME', key: null },
|
||||
{ label: 'COLORADO DEFENSE', key: 'ColoradoDefense' },
|
||||
{ label: 'CODE BUG INVADERS', key: 'CodeBugInvaders' },
|
||||
{ label: 'DOT DUDE', key: 'DotDude' },
|
||||
{ label: 'SMASH OUT', key: 'SmashOut' },
|
||||
{ label: 'VORTEX', key: 'Vortex' },
|
||||
{ label: 'BURLI KONG', key: 'BurliKong' },
|
||||
];
|
||||
|
||||
export default class MainMenu extends Phaser.Scene {
|
||||
constructor() {
|
||||
super({ key: 'MainMenu' });
|
||||
|
|
@ -15,6 +25,11 @@ export default class MainMenu extends Phaser.Scene {
|
|||
|
||||
shutdown() {
|
||||
stopMusic(this);
|
||||
this.scale.off('resize', this._repositionSelect, this);
|
||||
if (this.selectEl) {
|
||||
document.body.removeChild(this.selectEl);
|
||||
this.selectEl = null;
|
||||
}
|
||||
}
|
||||
|
||||
create() {
|
||||
|
|
@ -22,87 +37,96 @@ export default class MainMenu extends Phaser.Scene {
|
|||
|
||||
const cx = 800, cy = 450;
|
||||
|
||||
// Add background image
|
||||
// Background
|
||||
const bg = this.add.image(cx, cy, 'main-menu').setOrigin(0.5);
|
||||
const scaleX = this.cameras.main.width / bg.width;
|
||||
const scaleY = this.cameras.main.height / bg.height;
|
||||
bg.setScale(Math.max(scaleX, scaleY));
|
||||
|
||||
// Images
|
||||
const logoPeople = this.add.image(cx, 350, 'logo-people').setOrigin(0.5).setScale(.70).setAngle(-15);
|
||||
const logoTitle = this.add.image(cx, 350, 'logo-title').setOrigin(0.5).setScale(.75).setAngle(5);
|
||||
// Logos
|
||||
const logoPeople = this.add.image(cx, 350, 'logo-people').setOrigin(0.5).setScale(0.70).setAngle(-15);
|
||||
const logoTitle = this.add.image(cx, 350, 'logo-title').setOrigin(0.5).setScale(0.75).setAngle(5);
|
||||
|
||||
this.tweens.add({
|
||||
targets: logoPeople,
|
||||
scaleX: 0.80,
|
||||
scaleY: 0.80,
|
||||
duration: 3000,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut',
|
||||
});
|
||||
this.tweens.add({
|
||||
targets: logoPeople,
|
||||
angle: 15,
|
||||
duration: 2000,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut',
|
||||
});
|
||||
this.tweens.add({
|
||||
targets: logoTitle,
|
||||
angle: -5,
|
||||
duration: 5000,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut',
|
||||
});
|
||||
this.tweens.add({
|
||||
targets: logoTitle,
|
||||
scaleX: 0.90,
|
||||
scaleY: 0.90,
|
||||
duration: 8000,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
ease: 'Sine.easeInOut',
|
||||
});
|
||||
this.tweens.add({ targets: logoPeople, scaleX: 0.80, scaleY: 0.80, duration: 3000, yoyo: true, repeat: -1, ease: 'Sine.easeInOut' });
|
||||
this.tweens.add({ targets: logoPeople, angle: 15, duration: 2000, yoyo: true, repeat: -1, ease: 'Sine.easeInOut' });
|
||||
this.tweens.add({ targets: logoTitle, angle: -5, duration: 5000, yoyo: true, repeat: -1, ease: 'Sine.easeInOut' });
|
||||
this.tweens.add({ targets: logoTitle, scaleX: 0.90, scaleY: 0.90, duration: 8000, yoyo: true, repeat: -1, ease: 'Sine.easeInOut' });
|
||||
|
||||
// // Game list
|
||||
// const games = [
|
||||
// '★ COLORADO DEFENSE (Missile Command)',
|
||||
// '★ CODE BUG INVADERS (Space Invaders)',
|
||||
// '★ DOT DUDE (Pac-Man)',
|
||||
// '★ SMASH OUT (Arkanoid)',
|
||||
// ];
|
||||
// games.forEach((txt, i) => {
|
||||
// this.add.text(cx, 470 + i * 38, txt, {
|
||||
// fontSize: '22px',
|
||||
// fontFamily: 'monospace',
|
||||
// color: '#ffff00',
|
||||
// }).setOrigin(0.5);
|
||||
// });
|
||||
// "SELECT MODE:" canvas label
|
||||
this.add.text(cx, 572, 'SELECT MODE:', {
|
||||
fontSize: '22px', fontFamily: 'monospace', color: '#667799',
|
||||
}).setOrigin(0.5, 1);
|
||||
|
||||
// Blink "Press SPACE"
|
||||
const prompt = this.add.text(cx, 730, 'PRESS SPACE OR CLICK TO START', {
|
||||
fontSize: '32px',
|
||||
fontFamily: 'monospace',
|
||||
color: '#ffffff',
|
||||
}).setOrigin(0.5);
|
||||
// Native HTML <select> overlaid on the canvas
|
||||
this._buildSelect();
|
||||
this.scale.on('resize', this._repositionSelect, this);
|
||||
|
||||
this.tweens.add({
|
||||
targets: prompt,
|
||||
alpha: 0,
|
||||
duration: 600,
|
||||
yoyo: true,
|
||||
repeat: -1,
|
||||
// Blinking start prompt
|
||||
const prompt = this.add.text(cx, 690, 'PRESS SPACE OR CLICK TO START', {
|
||||
fontSize: '32px', fontFamily: 'monospace', color: '#ffffff',
|
||||
}).setOrigin(0.5).setInteractive({ useHandCursor: true });
|
||||
|
||||
this.tweens.add({ targets: prompt, alpha: 0, duration: 600, yoyo: true, repeat: -1 });
|
||||
prompt.on('pointerdown', () => this._startGame());
|
||||
|
||||
// Space starts (guard: not while select has focus)
|
||||
this.input.keyboard.on('keydown-SPACE', () => {
|
||||
if (document.activeElement !== this.selectEl) this._startGame();
|
||||
});
|
||||
|
||||
this.input.keyboard.once('keydown-SPACE', () => this.startGame());
|
||||
this.input.once('pointerdown', () => this.startGame());
|
||||
}
|
||||
|
||||
startGame() {
|
||||
_buildSelect() {
|
||||
const select = document.createElement('select');
|
||||
select.style.cssText = [
|
||||
'position: fixed',
|
||||
'font-family: monospace',
|
||||
'background: #000c18',
|
||||
'color: #00ff88',
|
||||
'border: 2px solid #00aa55',
|
||||
'border-radius: 4px',
|
||||
'padding: 8px 16px',
|
||||
'cursor: pointer',
|
||||
'outline: none',
|
||||
'z-index: 10',
|
||||
'transform-origin: center center',
|
||||
].join(';');
|
||||
|
||||
MODES.forEach((mode, i) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = String(i);
|
||||
opt.textContent = mode.label;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
|
||||
document.body.appendChild(select);
|
||||
this.selectEl = select;
|
||||
this._repositionSelect();
|
||||
}
|
||||
|
||||
_repositionSelect() {
|
||||
if (!this.selectEl) return;
|
||||
const rect = this.game.canvas.getBoundingClientRect();
|
||||
const s = rect.width / 1600; // canvas scale factor
|
||||
|
||||
// Canvas coordinate (800, 620) → screen position
|
||||
const screenX = rect.left + 0.5 * rect.width;
|
||||
const screenY = rect.top + (620 / 900) * rect.height;
|
||||
|
||||
this.selectEl.style.left = `${screenX}px`;
|
||||
this.selectEl.style.top = `${screenY}px`;
|
||||
this.selectEl.style.fontSize = `${26 * s}px`;
|
||||
this.selectEl.style.width = `${440 * s}px`;
|
||||
this.selectEl.style.padding = `${8 * s}px ${16 * s}px`;
|
||||
this.selectEl.style.transform = `translate(-50%, -50%)`;
|
||||
}
|
||||
|
||||
_startGame() {
|
||||
const cfg = this.cache.json.get('gameConfig');
|
||||
this.scene.start('GameManager', { config: cfg, level: 1, lives: cfg.lives });
|
||||
const selectedGame = MODES[parseInt(this.selectEl.value, 10)].key;
|
||||
if (this.selectEl) {
|
||||
document.body.removeChild(this.selectEl);
|
||||
this.selectEl = null;
|
||||
}
|
||||
this.scene.start('GameManager', { config: cfg, level: 1, lives: cfg.lives, selectedGame });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue