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
This commit is contained in:
Brian Fertig 2026-06-06 19:20:38 -06:00
parent 6aa331cf97
commit 72dea52f49
1 changed files with 48 additions and 12 deletions

View File

@ -84,12 +84,7 @@ export default class FarkelGame extends Phaser.Scene {
} }
this.gs = createInitialState({ playerCount, names, skills }); this.gs = createInitialState({ playerCount, names, skills });
this.add.text(TRAY_CX, 56, 'Farkle', { this.buildHeader();
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.buildTray(); this.buildTray();
this.buildDice(); 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() { buildTray() {
// Felt throwing area. // Felt throwing area.
const g = this.add.graphics().setDepth(DEPTH.panel); const g = this.add.graphics().setDepth(DEPTH.panel);
@ -144,9 +159,24 @@ export default class FarkelGame extends Phaser.Scene {
buildShelf() { buildShelf() {
this.shelfGfx = this.add.graphics().setDepth(DEPTH.die); 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, // Background panel for the shelf area
}).setOrigin(0, 0.5); 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() { buildScoringPanel() {
@ -380,7 +410,7 @@ export default class FarkelGame extends Phaser.Scene {
const g = this.shelfGfx; const g = this.shelfGfx;
g.clear(); g.clear();
const dice = this.gs.turn.setAsideDice; 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++) { for (let i = 0; i < dice.length; i++) {
const x = startX + i * (SDIE + SDIE_GAP); const x = startX + i * (SDIE + SDIE_GAP);
this.paintDie(g, x, SHELF_Y, SDIE, dice[i], { locked: true }); 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++) { for (let seat = 0; seat < this.scratchRows.length; seat++) {
const p = this.gs.players[seat]; const p = this.gs.players[seat];
const row = this.scratchRows[seat]; const row = this.scratchRows[seat];
const showTurn = (seat === cur && !isGameOver(this.gs) && this.gs.turn.kept > 0); const hasPending = this.gs.turn.kept > 0 || (this.gs.phase === 'awaitPick' && this.selectionScore() > 0);
row.score.setText(showTurn ? `${p.score} +${this.gs.turn.kept}` : String(p.score)); 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); row.star.setVisible(p.onBoard);
} }
// Underline the active player's row. // Underline the active player's row.