feat(monopoly): add sound effects for payment and pawn animations

- Play sfx-monopoly-pay when rent/payment animation begins
- Play sfx-piece-click when pawn/token hop animation completes
- Load new audio asset and register in SFX constants
This commit is contained in:
Brian Fertig 2026-06-07 17:17:22 -06:00
parent 359740f4f7
commit c8dc0a05e0
3 changed files with 51 additions and 9 deletions

View File

@ -708,21 +708,56 @@ export default class MonopolyGame extends Phaser.Scene {
if (!isHumanTurn && !auctionIsHuman) return;
if (inAuction) return; // auction panel handles its own buttons
const btnY0 = this.diceY + 56;
const btnW = RP_W - 20;
let yOff = 0;
const BTN_H = 52;
const BTN_GAP = 10; // spacing between buttons (62 = BTN_H + BTN_GAP)
// First pass: count buttons to determine starting Y
const p = gs.players[this.humanSeat];
const phase = gs.phase;
let btnCount = 0;
if (phase === 'preroll' || phase === 'endturn') {
if (phase === 'preroll') {
if (p.jailed) {
if (p.getOutOfJailFree > 0) btnCount++;
btnCount++;
btnCount++;
} else {
btnCount++;
}
}
if (phase === 'endturn') btnCount++;
if (PURCHASABLE.some(idx =>
canBuildHouse(gs, this.humanSeat, idx) || canBuildHotel(gs, this.humanSeat, idx))) {
btnCount++;
}
if (PURCHASABLE.some(idx => {
const own = gs.board[idx];
return own?.owner === this.humanSeat && !own.mortgaged && own.houses === 0 && !own.hotel;
}) || PURCHASABLE.some(idx => {
const own = gs.board[idx];
return own?.owner === this.humanSeat && own.mortgaged &&
p.cash >= Math.ceil(SPACES[idx].mortgage * 1.1);
})) {
btnCount++;
}
}
// Second pass: draw buttons aligned to board bottom (BT + BS)
const boardBottom = BT + BS;
const totalBtnH = btnCount * BTN_H + (btnCount - 1) * BTN_GAP;
const btnY0 = boardBottom - totalBtnH;
let yOff = 0;
const mkBtn = (label, cb, enabled=true, opts={}) => {
const btn = new Button(this, RP_X + btnW/2 + 10, btnY0 + yOff, label, cb,
{ width: btnW, height: 52, fontSize: 22, ...opts });
{ width: btnW, height: BTN_H, fontSize: 22, ...opts });
btn.setDepth(DEPTH.ui);
if (!enabled) btn.setEnabled(false);
this.reg(btn);
yOff += 62;
yOff += BTN_H + BTN_GAP;
};
const p = gs.players[this.humanSeat];
const phase = gs.phase;
if (phase === 'preroll' || phase === 'endturn') {
if (phase === 'preroll') {
if (p.jailed) {
@ -810,6 +845,8 @@ export default class MonopolyGame extends Phaser.Scene {
await this.delay(250);
playSound(this, SFX.MONOPAY);
// Phase 3: Amount arches to receiver's panel (1200 ms), turns green, adds plus
amtTxt.setText(`+$${amount.toLocaleString()}`);
amtTxt.setColor('#44FF88');
@ -1478,8 +1515,8 @@ export default class MonopolyGame extends Phaser.Scene {
async executeRoll(seat) {
const d1 = Math.floor(Math.random() * 6) + 1;
const d2 = Math.floor(Math.random() * 6) + 1;
playSound(this, SFX.DICE_ROLL);
await this.animateDice(d1, d2);
playSound(this, SFX.diceRoll);
const prevPos = this.gs.players[seat].position;
const wasJailed = this.gs.players[seat].jailed;
this.gs = rollDice(this.gs, seat, d1, d2);
@ -1572,7 +1609,10 @@ export default class MonopolyGame extends Phaser.Scene {
pawn.x = inv * inv * start.x + 2 * inv * t * ctrl.x + t * t * ex;
pawn.y = inv * inv * start.y + 2 * inv * t * ctrl.y + t * t * ey;
},
onComplete: hopOne,
onComplete: () => {
playSound(this, SFX.PIECE_CLICK);
hopOne();
},
});
};

View File

@ -138,6 +138,7 @@ export default class PreloadScene extends Phaser.Scene {
this.load.spritesheet('monopoly-cards', '/assets/images/monopoly-cards.png', { frameWidth: 200, frameHeight: 300 });
this.load.audio('sfx-monopoly-purchase', '/assets/fx/monopoly-purchase.mp3');
this.load.audio('sfx-monopoly-expense', '/assets/fx/monopoly-expense.mp3');
this.load.audio('sfx-monopoly-pay', '/assets/fx/monopoly-pay.mp3');
this.load.audio('sfx-monopoly-paid', '/assets/fx/monopoly-paid.mp3');
}

View File

@ -34,6 +34,7 @@ export const SFX = {
SCIFI_WOOSH: 'sfx-scifi-woosh',
MONOPOLY_PURCHASE: 'sfx-monopoly-purchase',
MONOPOLY_EXPENSE: 'sfx-monopoly-expense',
MONOPAY: 'sfx-monopoly-pay',
MONOPOLY_PAID: 'sfx-monopoly-paid',
};