Merge pull request 'Game-Polish' (#9) from Game-Polish into main

Reviewed-on: #9
This commit is contained in:
brianfertig 2026-09-02 02:51:15 +00:00
commit e3d2fdee34
5 changed files with 47 additions and 18 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

View File

@ -1,13 +1,13 @@
{ {
"default": "back-0", "default": "back-0",
"cardBacks": [ "cardBacks": [
{ "id": "back-0", "name": "Card Back 1", "key": "cardbacks", "spriteIndex": 0, "fallbackColor": "#1a3a6b" }, { "id": "back-0", "name": "Cthulhu", "key": "cardbacks", "spriteIndex": 0, "fallbackColor": "#1a3a6b" },
{ "id": "back-1", "name": "Card Back 2", "key": "cardbacks", "spriteIndex": 1, "fallbackColor": "#1a3a6b" }, { "id": "back-1", "name": "Cyberpunk", "key": "cardbacks", "spriteIndex": 1, "fallbackColor": "#1a3a6b" },
{ "id": "back-2", "name": "Card Back 3", "key": "cardbacks", "spriteIndex": 2, "fallbackColor": "#1a3a6b" }, { "id": "back-2", "name": "Nautical", "key": "cardbacks", "spriteIndex": 2, "fallbackColor": "#1a3a6b" },
{ "id": "back-3", "name": "Card Back 4", "key": "cardbacks", "spriteIndex": 3, "fallbackColor": "#1a3a6b" }, { "id": "back-3", "name": "Steampunk", "key": "cardbacks", "spriteIndex": 3, "fallbackColor": "#1a3a6b" },
{ "id": "back-4", "name": "Card Back 5", "key": "cardbacks", "spriteIndex": 4, "fallbackColor": "#1a3a6b" }, { "id": "back-4", "name": "Jungle", "key": "cardbacks", "spriteIndex": 4, "fallbackColor": "#1a3a6b" },
{ "id": "back-5", "name": "Card Back 6", "key": "cardbacks", "spriteIndex": 5, "fallbackColor": "#1a3a6b" }, { "id": "back-5", "name": "Hi Tech", "key": "cardbacks", "spriteIndex": 5, "fallbackColor": "#1a3a6b" },
{ "id": "back-6", "name": "Card Back 7", "key": "cardbacks", "spriteIndex": 6, "fallbackColor": "#1a3a6b" }, { "id": "back-6", "name": "Fall Mountain", "key": "cardbacks", "spriteIndex": 6, "fallbackColor": "#1a3a6b" },
{ "id": "back-7", "name": "Card Back 8", "key": "cardbacks", "spriteIndex": 7, "fallbackColor": "#1a3a6b" } { "id": "back-7", "name": "Neon Miami", "key": "cardbacks", "spriteIndex": 7, "fallbackColor": "#1a3a6b" }
] ]
} }

View File

@ -81,7 +81,17 @@ export default class MahjongGame extends Phaser.Scene {
const { tracks, volume } = getGameSoundtrack(this); const { tracks, volume } = getGameSoundtrack(this);
if (tracks.length) new MusicPlayer(this, tracks, volume); if (tracks.length) new MusicPlayer(this, tracks, volume);
} catch { /* optional */ } } catch { /* optional */ }
this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, FELT).setDepth(DEPTH.bg); // Backdrop: the playfield chosen on the opponent-select screen (image
// texture, or the generated canvas texture for the "colored" playfields),
// falling back to its flat color and finally to the classic deep green.
const pf = this.playfield;
if (pf?.key && this.textures.exists(pf.key)) {
this.add.image(GAME_WIDTH / 2, GAME_HEIGHT / 2, pf.key)
.setDisplaySize(GAME_WIDTH, GAME_HEIGHT).setDepth(DEPTH.bg);
} else {
const color = pf?.fallbackColor ? parseInt(pf.fallbackColor.replace('#', ''), 16) : FELT;
this.add.rectangle(GAME_WIDTH / 2, GAME_HEIGHT / 2, GAME_WIDTH, GAME_HEIGHT, color).setDepth(DEPTH.bg);
}
const names = [auth.user?.username ?? 'You']; const names = [auth.user?.username ?? 'You'];
const skills = { 0: 5 }; const skills = { 0: 5 };
@ -130,11 +140,16 @@ export default class MahjongGame extends Phaser.Scene {
} }
buildPortraits() { buildPortraits() {
// Each portrait sits just to the left of its own tiles, at the vertical
// centre of that area (rows/columns are all centred on their midpoints),
// so it reads as belonging to that player without ever covering a tile.
// Keep ~18-20px clearance: human hand left edge ≥ 391 (14 tiles), left
// column left edge 133, right column left edge 1836, top row ≥ 616.
const spots = [ const spots = [
{ x: 90, y: 870, r: 46 }, // human — bottom left { x: 325, y: 975, r: 46 }, // human — left of the bottom hand row
{ x: 1830, y: 170, r: 40 }, // seat 1 — right { x: 1778, y: 580, r: 40 }, // seat 1 — left of the right column
{ x: 560, y: 95, r: 40 }, // seat 2 — top { x: 558, y: 70, r: 40 }, // seat 2 — left of the top row
{ x: 90, y: 310, r: 40 }, // seat 3 — left { x: 75, y: 580, r: 40 }, // seat 3 — left of the left column
]; ];
for (let seat = 0; seat < 4; seat++) { for (let seat = 0; seat < 4; seat++) {
const { x, y, r } = spots[seat]; const { x, y, r } = spots[seat];
@ -219,7 +234,10 @@ export default class MahjongGame extends Phaser.Scene {
}).setOrigin(0.5)); }).setOrigin(0.5));
this.refPanel = panel; this.refPanel = panel;
this.refBtn = new Button(this, 1810, 44, 'Hands', () => this.toggleReference(), { // Parked just left of the reference panel's open position (1490) so it
// never ends up under it; y=210 clears seat 2's melds (y≤98) and bonus
// tiles (y≤157), which sit at x 1330+.
this.refBtn = new Button(this, 1399, 210, 'Hands', () => this.toggleReference(), {
width: 150, height: 52, fontSize: 22, variant: 'ghost', width: 150, height: 52, fontSize: 22, variant: 'ghost',
}).setDepth(DEPTH.ref + 1); }).setDepth(DEPTH.ref + 1);
} }
@ -233,6 +251,15 @@ export default class MahjongGame extends Phaser.Scene {
ease: 'Quad.Out', ease: 'Quad.Out',
}); });
this.refBtn.setLabel(this.refOpen ? 'Close' : 'Hands'); this.refBtn.setLabel(this.refOpen ? 'Close' : 'Hands');
// Seat 1's portrait sits under the open panel. Its <video> is a DOM node
// that always renders above canvas content, so hide it while covered.
this.portraitCtrls[1]?.controller?.setVideoVisible?.(!this.refOpen);
}
// The claim row and the self win/kong buttons sit under the open panel's
// footprint — close it whenever they appear so nothing clickable is covered.
closeReferenceIfOpen() {
if (this.refOpen) this.toggleReference();
} }
// ── tile drawing ────────────────────────────────────────────────────────────── // ── tile drawing ──────────────────────────────────────────────────────────────
@ -382,7 +409,7 @@ export default class MahjongGame extends Phaser.Scene {
bx += 40; bx += 40;
} }
} else { // sides — vertical column of rotated backs } else { // sides — vertical column of rotated backs
const x = seat === 1 ? 1858 : 62; const x = seat === 1 ? 1858 : 155; // columns stay right of their portraits (seat 1: 1778+40, seat 3: 75+40)
let y = 580 - ((n - 1) * step) / 2; let y = 580 - ((n - 1) * step) / 2;
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
const back = this.makeTileBack(SM_W, SM_H); const back = this.makeTileBack(SM_W, SM_H);
@ -390,7 +417,7 @@ export default class MahjongGame extends Phaser.Scene {
this.dyn.add(back); this.dyn.add(back);
y += step; y += step;
} }
const mx = seat === 1 ? 1745 : 175; const mx = seat === 1 ? 1745 : 270; // melds/bonus offset from their column
let my = 330; let my = 330;
for (const m of p.melds) { for (const m of p.melds) {
const row = this.makeMeldRow(m, 38, 52, false); const row = this.makeMeldRow(m, 38, 52, false);
@ -481,6 +508,7 @@ export default class MahjongGame extends Phaser.Scene {
btn.setVisible(!!spec); btn.setVisible(!!spec);
if (spec) btn.setLabel(`Kong ${shortName(spec.kind)}`); if (spec) btn.setLabel(`Kong ${shortName(spec.kind)}`);
}); });
if (show && (acts?.canWin || this.kongSpecs.length > 0)) this.closeReferenceIfOpen();
} }
// ── human input ─────────────────────────────────────────────────────────────── // ── human input ───────────────────────────────────────────────────────────────
@ -521,6 +549,7 @@ export default class MahjongGame extends Phaser.Scene {
return new Promise((resolve) => { return new Promise((resolve) => {
this.claimResolve = resolve; this.claimResolve = resolve;
this.chowOptions = opts.chows; this.chowOptions = opts.chows;
this.closeReferenceIfOpen();
const visible = []; const visible = [];
if (opts.win) visible.push(this.claimBtns.win); if (opts.win) visible.push(this.claimBtns.win);
if (opts.kong) visible.push(this.claimBtns.kong); if (opts.kong) visible.push(this.claimBtns.kong);

View File

@ -55,8 +55,8 @@ export class Button extends Phaser.GameObjects.Container {
if (this._active) return; if (this._active) return;
const { bgHover: bgh, textHoverColor: thc, variant: v } = this.options; const { bgHover: bgh, textHoverColor: thc, variant: v } = this.options;
if (v === 'ghost') { if (v === 'ghost') {
this._drawBg(bgh, 0.18); this._drawBg(bgh, 0.9);
this.text.setColor(COLORS.goldHex); this.text.setColor(thc);
} else { } else {
this._drawBg(bgh, 1); this._drawBg(bgh, 1);
this.text.setColor(thc); this.text.setColor(thc);