diff --git a/public/assets/images/jello-items.png b/public/assets/images/jello-items.png new file mode 100644 index 0000000..4c101cd Binary files /dev/null and b/public/assets/images/jello-items.png differ diff --git a/public/assets/images/jello-items.psd b/public/assets/images/jello-items.psd new file mode 100644 index 0000000..54a6f3f Binary files /dev/null and b/public/assets/images/jello-items.psd differ diff --git a/public/src/games/mahjongmatch/MahjongMatchGame.js b/public/src/games/mahjongmatch/MahjongMatchGame.js index 5ae637e..a69e77b 100644 --- a/public/src/games/mahjongmatch/MahjongMatchGame.js +++ b/public/src/games/mahjongmatch/MahjongMatchGame.js @@ -311,7 +311,7 @@ export default class MahjongMatchGame extends Phaser.Scene { this.movesText = this.add.text(cx, 100, '', { fontFamily: '"Julius Sans One"', fontSize: '24px', color: COLORS.mutedHex, }).setOrigin(0.5).setDepth(D.ui); - this.timerText = this.add.text(GAME_WIDTH - 50, 80, '', { + this.timerText = this.add.text(GAME_WIDTH - 50, 155, '', { fontFamily: 'Righteous', fontSize: '34px', color: COLORS.textHex, }).setOrigin(1, 0.5).setDepth(D.ui); this.layer.add([this.tilesText, this.movesText, this.timerText]); diff --git a/public/src/games/puddingmonsters/PuddingMonstersGame.js b/public/src/games/puddingmonsters/PuddingMonstersGame.js index 911ac72..cdf7eb7 100644 --- a/public/src/games/puddingmonsters/PuddingMonstersGame.js +++ b/public/src/games/puddingmonsters/PuddingMonstersGame.js @@ -10,14 +10,11 @@ import { const BG = 0x161226; const FRAME = 0x241c3a; -const FLOOR = 0x2b2347; -const FLOOR_LN = 0x372c59; -const WALL = 0x0e0a1a; -const WALL_HI = 0x3a2f56; -const SPIKE_BG = 0x3a1320; -const SPIKE = 0xe0506b; -const TARGET = 0xffd54a; -const TARGET_LN = 0xffec99; +const FLOOR_A = 0xffaa33; +const FLOOR_B = 0xffe066; +const FLOOR_LN = 0xd4880f; +const TARGET = 0x00c8ff; +const TARGET_LN = 0x88e4ff; const PALETTE = [0xff7eb6, 0x7ed957, 0x5bc0ff, 0xffd166, 0xc792ff, 0xff9e64, 0x4ecdc4, 0xf6e58d]; @@ -60,6 +57,7 @@ export default class PuddingMonstersGame extends Phaser.Scene { this.eyeTimers = []; this.eyeAnimActive = false; this.blobGfx = []; + this.beaconTimer = null; } async create() { @@ -96,6 +94,7 @@ export default class PuddingMonstersGame extends Phaser.Scene { this.eyeAnimActive = false; this.eyeTimers.forEach((t) => t.remove(false)); this.eyeTimers = []; + if (this.beaconTimer) { this.beaconTimer.remove(false); this.beaconTimer = null; } this.eyeOffsets = new Map(); this.layer.removeAll(true); this.blobGfx = []; @@ -374,41 +373,85 @@ export default class PuddingMonstersGame extends Phaser.Scene { for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { const k = `${x},${y}`; - if (this.state.walls.has(k)) continue; - floor.fillStyle(FLOOR, 1); + floor.fillStyle((x + y) % 2 === 0 ? FLOOR_A : FLOOR_B, 1); floor.fillRoundedRect(this.cellLeft(x) + 2, this.cellTop(y) + 2, this.cell - 4, this.cell - 4, 8); floor.lineStyle(1, FLOOR_LN, 1); floor.strokeRoundedRect(this.cellLeft(x) + 2, this.cellTop(y) + 2, this.cell - 4, this.cell - 4, 8); } } - for (const [tx, ty] of (this.levelDef.targets ?? [])) { - floor.fillStyle(TARGET, 0.30); - floor.fillRoundedRect(this.cellLeft(tx) + 2, this.cellTop(ty) + 2, this.cell - 4, this.cell - 4, 8); - } this.layer.add(floor); - const deco = this.add.graphics().setDepth(D.deco); + const targetBg = this.add.graphics().setDepth(2); + for (const [tx, ty] of (this.levelDef.targets ?? [])) { + const L = this.cellLeft(tx), T = this.cellTop(ty), c = this.cell; + targetBg.fillStyle(TARGET, 1); + targetBg.fillRoundedRect(L + 4, T + 4, c - 8, c - 8, 7); + targetBg.fillStyle(TARGET_LN, 0.8); + targetBg.fillRoundedRect(L + c * 0.22, T + c * 0.22, c * 0.56, c * 0.56, 5); + } + this.layer.add(targetBg); + this.tweens.add({ + targets: targetBg, + alpha: { from: 0.15, to: 0.85 }, + duration: 700, + ease: 'Sine.easeInOut', + yoyo: true, + repeat: -1, + }); + for (const k of this.state.walls) { const [x, y] = k.split(',').map(Number); - deco.fillStyle(WALL, 1); - deco.fillRoundedRect(this.cellLeft(x) + 2, this.cellTop(y) + 2, this.cell - 4, this.cell - 4, 8); - deco.fillStyle(WALL_HI, 1); - deco.fillRoundedRect(this.cellLeft(x) + 6, this.cellTop(y) + 6, this.cell - 12, this.cell * 0.22, 6); + const frame = 1 + Math.floor(Math.random() * 3); + const sprite = this.add.image( + this.cellLeft(x) + this.cell / 2, + this.cellTop(y) + this.cell / 2, + 'jello-items', frame, + ).setDisplaySize(this.cell, this.cell).setDepth(D.deco); + this.layer.add(sprite); } for (const k of this.state.spikes) { const [x, y] = k.split(',').map(Number); - const L = this.cellLeft(x), T = this.cellTop(y), c = this.cell; - deco.fillStyle(SPIKE_BG, 1); - deco.fillRoundedRect(L + 2, T + 2, c - 4, c - 4, 8); - deco.fillStyle(SPIKE, 1); - const baseY = T + c - c * 0.18; - const tipY = T + c * 0.24; - for (let i = 0; i < 3; i++) { - const bx = L + c * (0.2 + i * 0.3); - deco.fillTriangle(bx, baseY, bx + c * 0.2, baseY, bx + c * 0.1, tipY); - } + const sprite = this.add.image( + this.cellLeft(x) + this.cell / 2, + this.cellTop(y) + this.cell / 2, + 'jello-items', 0, + ).setDisplaySize(this.cell, this.cell).setDepth(D.deco); + this.layer.add(sprite); } - this.layer.add(deco); + this.startTargetBeacons(); + } + + startTargetBeacons() { + const targets = this.levelDef?.targets ?? []; + if (!targets.length) return; + + const spawnRings = () => { + if (!this.eyeAnimActive || this.overlayUp) return; + for (const [tx, ty] of targets) { + const c = this.cell; + const cx = this.cellLeft(tx) + c / 2; + const cy = this.cellTop(ty) + c / 2; + const startSz = c * 0.28; + const ring = this.add.graphics().setDepth(3); + ring.lineStyle(3, TARGET_LN, 1); + ring.strokeRoundedRect(-startSz / 2, -startSz / 2, startSz, startSz, 4); + ring.setPosition(cx, cy); + this.layer.add(ring); + this.layer.sort('depth'); + this.tweens.add({ + targets: ring, + scaleX: (c - 8) / startSz, + scaleY: (c - 8) / startSz, + alpha: 0, + duration: 1100, + ease: 'Quad.easeOut', + onComplete: () => { if (ring.scene) ring.destroy(); }, + }); + } + }; + + spawnRings(); + this.beaconTimer = this.time.addEvent({ delay: 700, callback: spawnRings, loop: true }); } // Yellow target-square outlines, drawn above the monsters so coverage stays diff --git a/public/src/games/shift/ShiftGame.js b/public/src/games/shift/ShiftGame.js index e5aa581..e1ade98 100644 --- a/public/src/games/shift/ShiftGame.js +++ b/public/src/games/shift/ShiftGame.js @@ -430,13 +430,15 @@ export default class ShiftGame extends Phaser.Scene { fontFamily: '"Julius Sans One"', fontSize: '24px', color: COLORS.goldHex, }).setOrigin(0.5, 0.5).setDepth(D.ui); - const shuffleBtn = new Button(this, GAME_WIDTH - 200, 60, 'Shuffle', + const BTN_W = 220, BTN_H = 56, BTN_PAD = 20, BTN_GAP = 16; + const btnX = GAME_WIDTH - BTN_PAD - BTN_W / 2; + const shuffleBtn = new Button(this, btnX, GAME_HEIGHT - BTN_PAD - BTN_H / 2, 'Shuffle', () => this._reshuffleBoard(), - { width: 220, height: 56, fontSize: 22 }).setDepth(D.ui); + { width: BTN_W, height: BTN_H, fontSize: 22 }).setDepth(D.ui); - const backBtn = new Button(this, GAME_WIDTH - 440, 60, 'Back', + const backBtn = new Button(this, btnX, GAME_HEIGHT - BTN_PAD - BTN_H - BTN_GAP - BTN_H / 2, 'Back', () => this.showDifficultySelect(), - { variant: 'ghost', width: 200, height: 56, fontSize: 22 }).setDepth(D.ui); + { variant: 'ghost', width: BTN_W, height: BTN_H, fontSize: 22 }).setDepth(D.ui); this.layer.add([titleT, diffT, this.movesText, this.bestText, shuffleBtn, backBtn]); } diff --git a/public/src/games/zuma/ZumaGame.js b/public/src/games/zuma/ZumaGame.js index 3887ef4..d3d4b46 100644 --- a/public/src/games/zuma/ZumaGame.js +++ b/public/src/games/zuma/ZumaGame.js @@ -418,7 +418,7 @@ export default class ZumaGame extends Phaser.Scene { const title = this.add.text(40, 50, `ZUMA — Level ${this.level}: ${this.levelDef.name}`, { fontFamily: 'Righteous', fontSize: '30px', color: COLORS.goldHex, }).setOrigin(0, 0.5).setDepth(D.ui); - this.scoreText = this.add.text(GAME_WIDTH - 50, 50, '', { + this.scoreText = this.add.text(GAME_WIDTH - 50, 125, '', { fontFamily: 'Righteous', fontSize: '30px', color: COLORS.textHex, }).setOrigin(1, 0.5).setDepth(D.ui); this.effectsText = this.add.text(GAME_WIDTH - 50, 92, '', { diff --git a/public/src/scenes/PreloadScene.js b/public/src/scenes/PreloadScene.js index 135c480..d619f2b 100644 --- a/public/src/scenes/PreloadScene.js +++ b/public/src/scenes/PreloadScene.js @@ -122,6 +122,7 @@ export default class PreloadScene extends Phaser.Scene { this.load.audio('sfx-squish', '/assets/fx/squish.mp3'); this.load.audio('sfx-squash', '/assets/fx/squash.mp3'); this.load.audio('sfx-woosh', '/assets/fx/woosh.mp3'); + this.load.spritesheet('jello-items', '/assets/images/jello-items.png', { frameWidth: 132, frameHeight: 132 }); this.load.audio('sfx-jello-monsters','/assets/fx/jello-monsters.mp3'); this.load.spritesheet('catan-special-cards', '/assets/images/catan-special-cards.png', { frameWidth: 270, frameHeight: 390 });