feat: overhaul PuddingMonsters visuals with new jello-themed assets
- Add jello-items spritesheet for wall and spike decorations - Replace solid-color floor with checkerboard pattern using new palette - Add pulsing animation to target squares - Implement animated beacon rings around targets on level start - Redesign walls and spikes to use sprite frames instead of drawn graphics - Adjust UI text positioning in MahjongMatch, Zuma, and Shift games - Reposition Shift game buttons to bottom-right corner with consistent sizing - Load jello-items spritesheet in PreloadScene
This commit is contained in:
parent
4f23a8470a
commit
e608c5b205
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, '', {
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
Loading…
Reference in New Issue