Refine audio feedback and add AI intro speech for Forbidden Island

- Replace generic card/place SFX with thematic water sounds (splash, sink, raise, dry)
- Preload new water sound effects in PreloadScene
- Introduce AI character speech via SpeechQueue on character reveal
- Clear speech queue when advancing past character selection
This commit is contained in:
Brian Fertig 2026-06-06 12:45:26 -06:00
parent 9d8366ac5b
commit 88dcaf8e15
6 changed files with 16 additions and 4 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,6 +2,7 @@ import * as Phaser from 'phaser';
import { GAME_WIDTH, GAME_HEIGHT, COLORS } from '../../config.js';
import { Button } from '../../ui/Button.js';
import { MusicPlayer } from '../../ui/MusicPlayer.js';
import { enqueue as enqueueSpeech, resetQueue as resetSpeechQueue } from '../../ui/SpeechQueue.js';
import {
createInitialState, cloneState, applyPendingFlood, peekFloodDraw, peekTreasureDraw,
legalActions, applyAction, endActions, discardCard,
@ -524,6 +525,7 @@ export default class ForbiddenIslandGame extends Phaser.Scene {
borderGfx.lineStyle(3, 0xd4c08a, 1);
borderGfx.strokeRoundedRect(-100, -100, 200, 200, 12);
if (this.cache.audio.exists('sfx-card-show')) this.sound.play('sfx-card-show', { volume: 0.35 });
if (this.cache.audio.exists('sfx-water-dry')) this.sound.play('sfx-water-dry', { volume: 0.7 });
this.tweens.add({
targets: cont, scaleX: 1, duration: 150, ease: 'Linear',
onComplete: () => {
@ -1289,9 +1291,8 @@ export default class ForbiddenIslandGame extends Phaser.Scene {
// 6. Apply tile state (flooded or sunk) and animate
this.gs.tiles[tileId].state = willSink ? 'sunk' : 'flooded';
this.drawTile(this.gs.tiles[tileId]);
if (this.sound.get('sfx-card-place') || this.cache.audio.exists('sfx-card-place')) {
this.sound.play('sfx-card-place', { volume: willSink ? 0.55 : 0.3 });
}
const sfxKey = willSink ? 'sfx-water-sink' : 'sfx-water-splash';
if (this.cache.audio.exists(sfxKey)) this.sound.play(sfxKey, { volume: 0.7 });
const tileView = this.tileViews[tileId];
if (willSink && tileView) {
// Brief shake on the tile to signal sinking
@ -1580,7 +1581,7 @@ export default class ForbiddenIslandGame extends Phaser.Scene {
markG.x = barX;
markG.y = oldMarkerY;
if (this.cache.audio.exists('sfx-card-place')) this.sound.play('sfx-card-place', { volume: 0.5 });
if (this.cache.audio.exists('sfx-water-raise')) this.sound.play('sfx-water-raise', { volume: 0.7 });
// Marker slides up quickly, segment fades in slowly
this.tweens.add({
@ -1790,6 +1791,12 @@ export default class ForbiddenIslandGame extends Phaser.Scene {
}).setOrigin(0.5).setDepth(D + 3));
}
// Intro speech for AI opponents
if (opp?.speech?.intro?.length) {
const clip = opp.speech.intro[Math.floor(Math.random() * opp.speech.intro.length)];
enqueueSpeech(clip);
}
// Player name below portrait
reg(this.add.text(portraitCX, portraitY + portraitH - 44, playerName, {
fontFamily: 'Righteous', fontSize: '22px', color: COLORS.textHex, align: 'center',
@ -1880,6 +1887,7 @@ export default class ForbiddenIslandGame extends Phaser.Scene {
const btnLabel = isLast ? "Let's Play!" : 'Next';
const btn = new Button(this, cx + pw / 2 - 140, py + ph - 44, btnLabel, () => {
if (domVid?.node) { try { domVid.node.pause(); domVid.node.src = ''; } catch { /* ignore */ } }
resetSpeechQueue();
for (const o of objs) { try { o.destroy(); } catch { /* ignore */ } }
onNext();
}, { width: 240, height: 52, fontSize: 20 });

View File

@ -60,6 +60,10 @@ export default class PreloadScene extends Phaser.Scene {
this.load.json('card-backs', '/data/card-backs.json');
this.load.json('music', '/data/music.json');
this.load.audio('sfx-water-splash', '/assets/fx/water-splash.mp3');
this.load.audio('sfx-water-sink', '/assets/fx/water-sink.mp3');
this.load.audio('sfx-water-raise', '/assets/fx/water-raise.mp3');
this.load.audio('sfx-water-dry', '/assets/fx/water-dry.mp3');
this.load.audio('sfx-card-deal', '/assets/fx/card-deal.mp3');
this.load.audio('sfx-card-place', '/assets/fx/card-place.mp3');
this.load.audio('sfx-card-show', '/assets/fx/card-show.mp3');