feat: enhance Craps roll results with sequential badges and fireworks
Adds animated "Win!" or "Lose" badges that appear sequentially for each player after a roll, accompanied by sound effects. Introduces a colorful fireworks particle animation for wins. Adjusts dice landing positions for better visibility and removes redundant emotion/sound calls in favor of the new sequential result display.
This commit is contained in:
parent
327d3d7019
commit
f8d406ab4e
|
|
@ -259,7 +259,7 @@ export default class CrapsGame extends Phaser.Scene {
|
|||
const ox = SEAT_X[this.gs.shooterIndex];
|
||||
const oy = SEAT_Y - 60;
|
||||
return Promise.all(this.diceGfx.map((g, i) =>
|
||||
this.throwOneDie(g, finalDice[i], ox + (i ? 34 : -34), oy, i)));
|
||||
this.throwOneDie(g, finalDice[i], ox + (i ? 44 : -44), oy, i)));
|
||||
}
|
||||
|
||||
throwOneDie(g, face, ox, oy, idx) {
|
||||
|
|
@ -270,7 +270,7 @@ export default class CrapsGame extends Phaser.Scene {
|
|||
|
||||
const wallX = TABLE.x + TABLE.w - 38 + (Math.random() * 24 - 12); // upper-right corner wall
|
||||
const wallY = TABLE.y + 44 + Math.random() * 22;
|
||||
const landX = LAND.x + (idx ? 32 : -32) + (Math.random() * 20 - 10);
|
||||
const landX = LAND.x + (idx ? 50 : -50) + (Math.random() * 14 - 7);
|
||||
const landY = LAND.y + (Math.random() * 24 - 12);
|
||||
const outMs = 320 + idx * 28;
|
||||
const backMs = 540 + idx * 44;
|
||||
|
|
@ -696,16 +696,12 @@ export default class CrapsGame extends Phaser.Scene {
|
|||
if (res.pointEstablished) this.movePuck(res.pointEstablished);
|
||||
else if (res.newComeOut) this.movePuck(null);
|
||||
|
||||
// Animate per-player net outcome.
|
||||
// Chip animations fire immediately for all players.
|
||||
for (let i = 0; i < this.gs.players.length; i++) {
|
||||
const delta = this.gs.players[i].lastDelta;
|
||||
if (delta > 0) { this.animateChips(i, true, delta); this.portraits[i]?.playEmotion?.('happy'); }
|
||||
else if (delta < 0) { this.animateChips(i, false, -delta); this.portraits[i]?.playEmotion?.('upset'); }
|
||||
if (delta > 0) this.animateChips(i, true, delta);
|
||||
else if (delta < 0) this.animateChips(i, false, -delta);
|
||||
}
|
||||
const humanDelta = this.gs.players[0].lastDelta;
|
||||
if (humanDelta > 0) playSound(this, SFX.CASINO_WIN);
|
||||
else if (humanDelta < 0) playSound(this, SFX.CASINO_LOSE);
|
||||
|
||||
this.setStatus(this.outcomeMessage(res, total));
|
||||
this.renderHumanBets();
|
||||
this.renderAiWagers();
|
||||
|
|
@ -713,6 +709,9 @@ export default class CrapsGame extends Phaser.Scene {
|
|||
this.updateBalances();
|
||||
this.persistChips();
|
||||
|
||||
// Win/lose badges + emotions play sequentially left to right.
|
||||
this.showRollResults();
|
||||
|
||||
this.time.delayedCall(1400, () => {
|
||||
this.animating = false;
|
||||
this.diceGfx.forEach((g) => g.setVisible(true)); // leave dice resting on the felt
|
||||
|
|
@ -752,6 +751,79 @@ export default class CrapsGame extends Phaser.Scene {
|
|||
return `Rolled ${total}.`;
|
||||
}
|
||||
|
||||
// ── Win / lose result badges (sequential left → right) ────────────────────
|
||||
showRollResults() {
|
||||
const entries = this.gs.players
|
||||
.map((p, i) => ({ i, delta: p.lastDelta }))
|
||||
.filter(e => e.delta !== 0)
|
||||
.sort((a, b) => SEAT_X[a.i] - SEAT_X[b.i]);
|
||||
|
||||
entries.forEach(({ i, delta }, idx) => {
|
||||
this.time.delayedCall(idx * 420, () => {
|
||||
this.portraits[i]?.playEmotion?.(delta > 0 ? 'happy' : 'upset');
|
||||
this.animatePlayerResult(i, delta);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
animatePlayerResult(pi, delta) {
|
||||
const isWin = delta > 0;
|
||||
const x = SEAT_X[pi];
|
||||
const textY = SEAT_Y - PORTRAIT_R - 58;
|
||||
|
||||
const badge = this.add.text(x, textY, isWin ? 'Win!' : 'Lose', {
|
||||
fontFamily: '"Julius Sans One"',
|
||||
fontSize: isWin ? '52px' : '44px',
|
||||
color: isWin ? '#f5d020' : '#e05c5c',
|
||||
fontStyle: 'bold',
|
||||
stroke: '#000000', strokeThickness: 5,
|
||||
shadow: isWin ? { offsetX: 0, offsetY: 0, color: '#f5d020', blur: 24, fill: true } : undefined,
|
||||
}).setOrigin(0.5).setAlpha(0).setScale(1.4).setDepth(D.modal);
|
||||
|
||||
this.tweens.add({ targets: badge, alpha: 1, scaleX: 1, scaleY: 1, duration: 200, ease: 'Back.Out' });
|
||||
|
||||
playSound(this, isWin ? SFX.CASINO_WIN : SFX.CASINO_LOSE);
|
||||
if (isWin) this.animateFireworks(x, textY);
|
||||
|
||||
this.time.delayedCall(isWin ? 1200 : 900, () => {
|
||||
this.tweens.add({
|
||||
targets: badge, alpha: 0, y: textY - 30,
|
||||
duration: 350, ease: 'Power2',
|
||||
onComplete: () => badge.destroy(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
animateFireworks(cx, cy) {
|
||||
const palette = [0xf5d020, 0xff8c00, 0x5cb85c, 0x4a90d9, 0xff69b4, 0xffffff, 0x00e5ff];
|
||||
for (let burst = 0; burst < 3; burst++) {
|
||||
this.time.delayedCall(burst * 380, () => {
|
||||
if (!this.scene.isActive('CrapsGame')) return;
|
||||
const bx = cx + (Math.random() - 0.5) * 140;
|
||||
const by = cy - 10 + (Math.random() - 0.5) * 80;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const angle = (i / 10) * Math.PI * 2 + Math.random() * 0.3;
|
||||
const dist = 65 + Math.random() * 65;
|
||||
const color = palette[Math.floor(Math.random() * palette.length)];
|
||||
const dot = this.add.graphics().setDepth(D.modal + 5);
|
||||
dot.fillStyle(color, 1);
|
||||
dot.fillCircle(0, 0, 4 + Math.random() * 3);
|
||||
dot.x = bx; dot.y = by;
|
||||
this.tweens.add({
|
||||
targets: dot,
|
||||
x: bx + Math.cos(angle) * dist,
|
||||
y: by + Math.sin(angle) * dist,
|
||||
alpha: 0, scaleX: 0.1, scaleY: 0.1,
|
||||
duration: 700 + Math.random() * 400,
|
||||
delay: Math.random() * 100,
|
||||
ease: 'Power2',
|
||||
onComplete: () => dot.destroy(),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Chip win/loss animation (aggregated per player) ─────────────────────────
|
||||
animateChips(playerIndex, toPlayer, amount) {
|
||||
const seatX = SEAT_X[playerIndex];
|
||||
|
|
|
|||
Loading…
Reference in New Issue