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.gs.phase === 'awaitPick' && !this.commitSelection()) 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);
|
||||
if (bankable > 0) {
|
||||
await this.animateBank(seat, bankable);
|
||||
this.gs.players[seat].score += bankable;
|
||||
}
|
||||
this.render();
|
||||
this.advance();
|
||||
}
|
||||
|
|
@ -629,8 +634,12 @@ export default class FarkelGame extends Phaser.Scene {
|
|||
await this.delay(360);
|
||||
if (decideReroll(this.gs, skill)) continue;
|
||||
const seat = this.gs.current;
|
||||
bank(this.gs);
|
||||
const { bankable } = bank(this.gs);
|
||||
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) {
|
||||
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 ──────────────────────────────────────────────────────────────────
|
||||
showGameOver() {
|
||||
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.
|
||||
// Returns { bankable, scoreBefore, scoreAfter } so the caller can
|
||||
// handle score updates (e.g. for animations). Does NOT mutate state.score.
|
||||
export function bank(state) {
|
||||
const p = state.players[state.current];
|
||||
const t = state.turn;
|
||||
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);
|
||||
return state;
|
||||
return { bankable, scoreBefore, scoreAfter };
|
||||
}
|
||||
|
||||
// Forfeit the turn total and pass play.
|
||||
|
|
|
|||
Loading…
Reference in New Issue