feat: add visual animation when banking scores in Farkel
Refactor bank() to return bankable amount instead of mutating state, enabling the UI layer to handle score updates and display a floating +score animation that slides to the player's score pad. Applies to both human and AI turns.
This commit is contained in:
parent
23c0804a54
commit
701b4f75e6
|
|
@ -559,8 +559,13 @@ export default class FarkelGame extends Phaser.Scene {
|
||||||
if (this.busy || !this.isHumanTurn()) return;
|
if (this.busy || !this.isHumanTurn()) return;
|
||||||
if (this.gs.phase === 'awaitPick' && !this.commitSelection()) return;
|
if (this.gs.phase === 'awaitPick' && !this.commitSelection()) return;
|
||||||
if (this.gs.phase !== 'awaitDecision') return;
|
if (this.gs.phase !== 'awaitDecision') return;
|
||||||
bank(this.gs);
|
const seat = this.gs.current;
|
||||||
|
const { bankable } = bank(this.gs);
|
||||||
playSound(this, SFX.PENCIL_WRITE);
|
playSound(this, SFX.PENCIL_WRITE);
|
||||||
|
if (bankable > 0) {
|
||||||
|
await this.animateBank(seat, bankable);
|
||||||
|
this.gs.players[seat].score += bankable;
|
||||||
|
}
|
||||||
this.render();
|
this.render();
|
||||||
this.advance();
|
this.advance();
|
||||||
}
|
}
|
||||||
|
|
@ -629,8 +634,12 @@ export default class FarkelGame extends Phaser.Scene {
|
||||||
await this.delay(360);
|
await this.delay(360);
|
||||||
if (decideReroll(this.gs, skill)) continue;
|
if (decideReroll(this.gs, skill)) continue;
|
||||||
const seat = this.gs.current;
|
const seat = this.gs.current;
|
||||||
bank(this.gs);
|
const { bankable } = bank(this.gs);
|
||||||
playSound(this, SFX.PENCIL_WRITE);
|
playSound(this, SFX.PENCIL_WRITE);
|
||||||
|
if (bankable > 0) {
|
||||||
|
await this.animateBank(seat, bankable);
|
||||||
|
this.gs.players[seat].score += bankable;
|
||||||
|
}
|
||||||
if (this.gs.players[seat].score >= 1000) {
|
if (this.gs.players[seat].score >= 1000) {
|
||||||
this.portraitCtrls[seat]?.controller?.playEmotion?.('happy');
|
this.portraitCtrls[seat]?.controller?.playEmotion?.('happy');
|
||||||
}
|
}
|
||||||
|
|
@ -659,6 +668,37 @@ export default class FarkelGame extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
animateBank(seat, banked) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
playSound(this, SFX.CASINO_WIN);
|
||||||
|
const row = this.scratchRows[seat];
|
||||||
|
const destX = PAPER_X + PAPER_W - 22;
|
||||||
|
const destY = row.y;
|
||||||
|
|
||||||
|
// Create floating text at dice area
|
||||||
|
const label = this.add.text(TRAY_CX, TRAY_CY, `+${banked}`, {
|
||||||
|
fontFamily: 'Righteous', fontSize: '80px', color: COLORS.goldHex,
|
||||||
|
}).setOrigin(0.5).setDepth(DEPTH.toast);
|
||||||
|
|
||||||
|
// Hold for 1 second
|
||||||
|
this.time.delayedCall(1000, () => {
|
||||||
|
// Animate to score pad
|
||||||
|
this.tweens.add({
|
||||||
|
targets: label,
|
||||||
|
x: destX,
|
||||||
|
y: destY,
|
||||||
|
scale: 0.5,
|
||||||
|
duration: 1000,
|
||||||
|
ease: 'Cubic.Out',
|
||||||
|
onComplete: () => {
|
||||||
|
label.destroy();
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ── game over ──────────────────────────────────────────────────────────────────
|
// ── game over ──────────────────────────────────────────────────────────────────
|
||||||
showGameOver() {
|
showGameOver() {
|
||||||
if (this.gameOverShown) return;
|
if (this.gameOverShown) return;
|
||||||
|
|
|
||||||
|
|
@ -191,13 +191,17 @@ export function applySetAside(state, indices) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bank the turn total (subject to the on-board minimum) and pass play.
|
// Bank the turn total (subject to the on-board minimum) and pass play.
|
||||||
|
// Returns { bankable, scoreBefore, scoreAfter } so the caller can
|
||||||
|
// handle score updates (e.g. for animations). Does NOT mutate state.score.
|
||||||
export function bank(state) {
|
export function bank(state) {
|
||||||
const p = state.players[state.current];
|
const p = state.players[state.current];
|
||||||
const t = state.turn;
|
const t = state.turn;
|
||||||
const bankable = (p.onBoard || t.kept >= ON_BOARD_MIN) ? t.kept : 0;
|
const bankable = (p.onBoard || t.kept >= ON_BOARD_MIN) ? t.kept : 0;
|
||||||
if (bankable > 0) { p.score += bankable; p.onBoard = true; }
|
const scoreBefore = p.score;
|
||||||
|
const scoreAfter = scoreBefore + bankable;
|
||||||
|
if (bankable > 0) p.onBoard = true;
|
||||||
advanceTurn(state);
|
advanceTurn(state);
|
||||||
return state;
|
return { bankable, scoreBefore, scoreAfter };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forfeit the turn total and pass play.
|
// Forfeit the turn total and pass play.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue