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