feat(puzzle): add stats completion sound loop and refine glow/timing logic
- Load new `sfx_stats.mp3` audio for puzzle completion stats display - Increase glow delay from 500ms to 700ms for smoother visual transition - Fix indentation in edge detection filter and normalize spacing in CSS/style assignments - Stop and destroy stats sound on overlay cleanup or completion typing finish - Start looping stats sound when typing puzzle completion stats, ensuring clean lifecycle management
This commit is contained in:
parent
ada2535b62
commit
02e0e70b99
Binary file not shown.
|
|
@ -108,6 +108,9 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
if (!this.cache.audio.exists('sfx_grab')) {
|
if (!this.cache.audio.exists('sfx_grab')) {
|
||||||
this.load.audio('sfx_grab', 'assets/audio/fx/grab.mp3');
|
this.load.audio('sfx_grab', 'assets/audio/fx/grab.mp3');
|
||||||
}
|
}
|
||||||
|
if (!this.cache.audio.exists('sfx_stats')) {
|
||||||
|
this.load.audio('sfx_stats', 'assets/audio/fx/stats.mp3');
|
||||||
|
}
|
||||||
|
|
||||||
// Music track list
|
// Music track list
|
||||||
if (!this.cache.json.exists('music_tracks')) {
|
if (!this.cache.json.exists('music_tracks')) {
|
||||||
|
|
@ -365,7 +368,7 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
|
|
||||||
this._edgeIds = this._pieces
|
this._edgeIds = this._pieces
|
||||||
.filter(p => p.edges.top === 'flat' || p.edges.bottom === 'flat' ||
|
.filter(p => p.edges.top === 'flat' || p.edges.bottom === 'flat' ||
|
||||||
p.edges.left === 'flat' || p.edges.right === 'flat')
|
p.edges.left === 'flat' || p.edges.right === 'flat')
|
||||||
.map(p => p.id);
|
.map(p => p.id);
|
||||||
|
|
||||||
const midCol = Math.ceil(this._cols / 2);
|
const midCol = Math.ceil(this._cols / 2);
|
||||||
|
|
@ -1228,7 +1231,7 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
if (!this._glowingPieceIds) this._glowingPieceIds = new Set();
|
if (!this._glowingPieceIds) this._glowingPieceIds = new Set();
|
||||||
if (!this._glowTimers) this._glowTimers = new Map();
|
if (!this._glowTimers) this._glowTimers = new Map();
|
||||||
|
|
||||||
const GLOW_DELAY = 500; // ms a neighbor must stay in range before glow appears
|
const GLOW_DELAY = 700; // ms a neighbor must stay in range before glow appears
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
// Track which neighbors are in proximity this frame
|
// Track which neighbors are in proximity this frame
|
||||||
|
|
@ -1264,8 +1267,8 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
[
|
[
|
||||||
[20, 0.06],
|
[20, 0.06],
|
||||||
[14, 0.12],
|
[14, 0.12],
|
||||||
[8, 0.30],
|
[8, 0.30],
|
||||||
[4, 0.70],
|
[4, 0.70],
|
||||||
].forEach(([baseLW, baseAlpha]) => {
|
].forEach(([baseLW, baseAlpha]) => {
|
||||||
const lw = Math.max(1, baseLW * intensity);
|
const lw = Math.max(1, baseLW * intensity);
|
||||||
const alpha = baseAlpha * intensity;
|
const alpha = baseAlpha * intensity;
|
||||||
|
|
@ -2176,9 +2179,9 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
if (!this._uiLayer) return;
|
if (!this._uiLayer) return;
|
||||||
const rect = this.sys.game.canvas.getBoundingClientRect();
|
const rect = this.sys.game.canvas.getBoundingClientRect();
|
||||||
Object.assign(this._uiLayer.style, {
|
Object.assign(this._uiLayer.style, {
|
||||||
left: rect.left + 'px',
|
left: rect.left + 'px',
|
||||||
top: rect.top + 'px',
|
top: rect.top + 'px',
|
||||||
transform: `scale(${rect.width / 1920}, ${rect.height / 1080})`,
|
transform: `scale(${rect.width / 1920}, ${rect.height / 1080})`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2188,13 +2191,13 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
// except on the interactive children.
|
// except on the interactive children.
|
||||||
this._uiLayer = document.createElement('div');
|
this._uiLayer = document.createElement('div');
|
||||||
Object.assign(this._uiLayer.style, {
|
Object.assign(this._uiLayer.style, {
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
width: '1920px',
|
width: '1920px',
|
||||||
height: '1080px',
|
height: '1080px',
|
||||||
transformOrigin: 'top left',
|
transformOrigin: 'top left',
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
zIndex: '10',
|
zIndex: '10',
|
||||||
fontFamily: 'Arial, sans-serif',
|
fontFamily: 'Arial, sans-serif',
|
||||||
});
|
});
|
||||||
document.body.appendChild(this._uiLayer);
|
document.body.appendChild(this._uiLayer);
|
||||||
this._syncOverlayToCanvas();
|
this._syncOverlayToCanvas();
|
||||||
|
|
@ -2811,11 +2814,13 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
const typeNext = () => {
|
const typeNext = () => {
|
||||||
if (this._completionOverlay === null) {
|
if (this._completionOverlay === null) {
|
||||||
clearInterval(cursorBlink);
|
clearInterval(cursorBlink);
|
||||||
|
if (this._statsSound) { this._statsSound.stop(); this._statsSound.destroy(); this._statsSound = null; }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineIdx >= lines.length) {
|
if (lineIdx >= lines.length) {
|
||||||
// Done — keep cursor blinking at end
|
// Done — stop stats sound, keep cursor blinking at end
|
||||||
|
if (this._statsSound) { this._statsSound.stop(); this._statsSound.destroy(); this._statsSound = null; }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2855,7 +2860,9 @@ class PuzzleScene extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start typing after a short delay
|
// Start stats sound loop and typing after a short delay
|
||||||
|
this._statsSound = this.sound.add('sfx_stats', { loop: true, volume: 0.5 });
|
||||||
|
this._statsSound.play();
|
||||||
setTimeout(typeNext, 500);
|
setTimeout(typeNext, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue