From 72dea52f49ed41c317624edb452af27b7cbc059b Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Sat, 6 Jun 2026 19:20:38 -0600 Subject: [PATCH] feat(farkel): add header/shelf panels and enhance score display - Extract header into buildHeader() with black background panel and depth layering - Add background panel to shelf area with accent border - Adjust shelf dice positioning to align with new panel - Show pending +selected score during awaitPick phase in scratch pad --- public/src/games/farkel/FarkelGame.js | 60 +++++++++++++++++++++------ 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/public/src/games/farkel/FarkelGame.js b/public/src/games/farkel/FarkelGame.js index 9ec36e7..190ab6d 100644 --- a/public/src/games/farkel/FarkelGame.js +++ b/public/src/games/farkel/FarkelGame.js @@ -84,12 +84,7 @@ export default class FarkelGame extends Phaser.Scene { } this.gs = createInitialState({ playerCount, names, skills }); - this.add.text(TRAY_CX, 56, 'Farkle', { - fontFamily: 'Righteous', fontSize: '60px', color: COLORS.textHex, - }).setOrigin(0.5); - this.statusText = this.add.text(TRAY_CX, 128, '', { - fontFamily: '"Julius Sans One"', fontSize: '26px', color: COLORS.accentHex, - }).setOrigin(0.5); + this.buildHeader(); this.buildTray(); this.buildDice(); @@ -116,6 +111,26 @@ export default class FarkelGame extends Phaser.Scene { } } + buildHeader() { + const titleText = this.add.text(TRAY_CX, 56, 'Farkle', { + fontFamily: 'Righteous', fontSize: '60px', color: COLORS.textHex, + }).setOrigin(0.5).setDepth(DEPTH.panel + 1); + this.statusText = this.add.text(TRAY_CX, 128, '', { + fontFamily: '"Julius Sans One"', fontSize: '26px', color: COLORS.accentHex, + }).setOrigin(0.5).setDepth(DEPTH.panel + 2); + + // Fixed-size panel (wide enough for longest status messages) + const pw = 680; + const ph = 160; + const panel = this.add.graphics().setDepth(DEPTH.panel + 1); + panel.fillStyle(0x000000, 0.75); + panel.fillRoundedRect(TRAY_CX - pw / 2, 10, pw, ph, 12); + panel.lineStyle(3, COLORS.accent, 1); + panel.strokeRoundedRect(TRAY_CX - pw / 2, 10, pw, ph, 12); + this.headerPanel = panel; + this.headerTitle = titleText; + } + buildTray() { // Felt throwing area. const g = this.add.graphics().setDepth(DEPTH.panel); @@ -144,9 +159,24 @@ export default class FarkelGame extends Phaser.Scene { buildShelf() { this.shelfGfx = this.add.graphics().setDepth(DEPTH.die); - this.add.text(TRAY_CX - TRAY_W / 2, SHELF_Y - SDIE / 2 - 24, 'Set aside this turn', { - fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex, - }).setOrigin(0, 0.5); + + // Background panel for the shelf area + const shelfLeft = TRAY_CX - TRAY_W / 2 + 4; + const shelfW = DICE * (SDIE + SDIE_GAP) + 12; + const shelfH = SDIE + 70; + const shelfX = shelfLeft; + const shelfY = SHELF_Y - SDIE / 2 - 46; + const shelfPanel = this.add.graphics().setDepth(DEPTH.die - 1); + shelfPanel.fillStyle(0x000000, 0.55); + shelfPanel.fillRoundedRect(shelfX, shelfY, shelfW, shelfH, 8); + shelfPanel.lineStyle(2, COLORS.accent, 0.6); + shelfPanel.strokeRoundedRect(shelfX, shelfY, shelfW, shelfH, 8); + this.shelfPanel = shelfPanel; + + // Text on top of panel + this.add.text(TRAY_CX - TRAY_W / 2 + 20, SHELF_Y - SDIE / 2 - 24, 'Set aside this turn', { + fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.textHex, + }).setOrigin(0, 0.5).setDepth(DEPTH.die); } buildScoringPanel() { @@ -380,7 +410,7 @@ export default class FarkelGame extends Phaser.Scene { const g = this.shelfGfx; g.clear(); const dice = this.gs.turn.setAsideDice; - const startX = TRAY_CX - TRAY_W / 2 + SDIE / 2 + 4; + const startX = TRAY_CX - TRAY_W / 2 + SDIE / 2 + 24; for (let i = 0; i < dice.length; i++) { const x = startX + i * (SDIE + SDIE_GAP); this.paintDie(g, x, SHELF_Y, SDIE, dice[i], { locked: true }); @@ -392,8 +422,14 @@ export default class FarkelGame extends Phaser.Scene { for (let seat = 0; seat < this.scratchRows.length; seat++) { const p = this.gs.players[seat]; const row = this.scratchRows[seat]; - const showTurn = (seat === cur && !isGameOver(this.gs) && this.gs.turn.kept > 0); - row.score.setText(showTurn ? `${p.score} +${this.gs.turn.kept}` : String(p.score)); + const hasPending = this.gs.turn.kept > 0 || (this.gs.phase === 'awaitPick' && this.selectionScore() > 0); + const showTurn = (seat === cur && !isGameOver(this.gs) && hasPending); + if (showTurn) { + const sel = (this.gs.phase === 'awaitPick') ? this.selectionScore() : 0; + row.score.setText(`${p.score} +${this.gs.turn.kept + sel}`); + } else { + row.score.setText(String(p.score)); + } row.star.setVisible(p.onBoard); } // Underline the active player's row.