diff --git a/assets/music/hacker-track01.mp3 b/assets/music/hacker-track01.mp3 index 911b5e6..665529a 100644 Binary files a/assets/music/hacker-track01.mp3 and b/assets/music/hacker-track01.mp3 differ diff --git a/data/arcadedark-music.json b/data/arcadedark-music.json new file mode 100644 index 0000000..e035425 --- /dev/null +++ b/data/arcadedark-music.json @@ -0,0 +1,15 @@ +{ + "tracks": [ + { + "file": "arcadedark-track01.mp3", + "artist": "Cthulu", + "title": "Press the Button" + }, + { + "file": "arcadedark-track02.mp3", + "artist": "The Backrooms", + "title": "Subconscious Memories" + } + ], + "volume": 0.8 +} diff --git a/data/hacker-music.json b/data/hacker-music.json new file mode 100644 index 0000000..f1198d7 --- /dev/null +++ b/data/hacker-music.json @@ -0,0 +1,20 @@ +{ + "tracks": [ + { + "file": "hacker-track01.mp3", + "artist": "Cy-Bro", + "title": "Rock the Anomaly" + }, + { + "file": "hacker-track02.mp3", + "artist": "Nadia", + "title": "A time after now, but also before" + }, + { + "file": "hacker-track03.mp3", + "artist": "DV-8-2303", + "title": "Cybercrimes" + } + ], + "volume": 0.3 +} diff --git a/src/data/assetManifest.js b/src/data/assetManifest.js index 0e4afd8..7fae37d 100644 --- a/src/data/assetManifest.js +++ b/src/data/assetManifest.js @@ -135,8 +135,13 @@ export const MANIFEST = { dungeonboss: (scene) => sheetsFrom(scene, 'dungeonboss-artwork', ['roomSheet', 'heroSheet', 'spellSheet', 'bossSheet', 'deckBackSheet', 'iconSheet']), swdbg: (scene) => sheetsFrom(scene, 'swdbg-artwork', ['cardSheet', 'baseSheet']), - balatro: (scene) => sheetsFrom(scene, 'balatro-artwork', - ['jokerSheet', 'tarotSheet', 'planetSheet', 'spectralSheet', 'voucherSheet', 'packSheet', 'deckBackSheet', 'bossIconSheet']), + balatro: [ + (scene) => sheetsFrom(scene, 'balatro-artwork', + ['jokerSheet', 'tarotSheet', 'planetSheet', 'spectralSheet', 'voucherSheet', 'packSheet', 'deckBackSheet', 'bossIconSheet']), + // hacker soundtrack (see services/soundtrack.js) — lazy-loaded here so + // its audio only downloads once Balatro is actually entered. + (scene) => musicFrom(scene, 'hacker-music'), + ], // Optional drop-in art — spec in assets/gamedata/peggle/sprites.md. Missing // files 404 harmlessly and the game renders procedurally: circles for // pegs/ball, gradient starfield for the board background. @@ -194,6 +199,36 @@ export const MANIFEST = { // its audio only downloads once Super Kart is actually entered. (scene) => musicFrom(scene, 'nintendo-music'), ], + coloradodefense: [ + // arcadedark soundtrack (see services/soundtrack.js) — lazy-loaded here + // so its audio only downloads once Colorado Defense is actually entered. + (scene) => musicFrom(scene, 'arcadedark-music'), + ], + tempest: [ + // arcadedark soundtrack (see services/soundtrack.js) — lazy-loaded here + // so its audio only downloads once Tempest is actually entered. + (scene) => musicFrom(scene, 'arcadedark-music'), + ], + mastermind: [ + // hacker soundtrack (see services/soundtrack.js) — lazy-loaded here so + // its audio only downloads once Mastermind is actually entered. + (scene) => musicFrom(scene, 'hacker-music'), + ], + hexsweeper: [ + // hacker soundtrack (see services/soundtrack.js) — lazy-loaded here so + // its audio only downloads once Hexsweeper is actually entered. + (scene) => musicFrom(scene, 'hacker-music'), + ], + dotlink: [ + // hacker soundtrack (see services/soundtrack.js) — lazy-loaded here so + // its audio only downloads once Dot Link is actually entered. + (scene) => musicFrom(scene, 'hacker-music'), + ], + '2048': [ + // hacker soundtrack (see services/soundtrack.js) — lazy-loaded here so + // its audio only downloads once 2048 is actually entered. + (scene) => musicFrom(scene, 'hacker-music'), + ], }; // Returns the full (normalized) descriptor list for a slug — including assets diff --git a/src/games/2048/2048Game.js b/src/games/2048/2048Game.js index 5d0a54a..9bdb12c 100644 --- a/src/games/2048/2048Game.js +++ b/src/games/2048/2048Game.js @@ -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 { getGameSoundtrack } from '../../services/soundtrack.js'; import { playSound, SFX } from '../../ui/Sounds.js'; import { api } from '../../services/api.js'; import { @@ -55,8 +56,8 @@ export default class Game2048 extends Phaser.Scene { create() { try { - const music = this.cache.json.get('music'); - if (music?.tracks) new MusicPlayer(this, music.tracks); + const { tracks, volume } = getGameSoundtrack(this); + if (tracks.length) new MusicPlayer(this, tracks, volume); } catch (_) {} this.schemes = this.cache.json.get('colored-playfields')?.schemes ?? []; diff --git a/src/games/balatro/BalatroGame.js b/src/games/balatro/BalatroGame.js index 73d53e7..3b613ad 100644 --- a/src/games/balatro/BalatroGame.js +++ b/src/games/balatro/BalatroGame.js @@ -11,6 +11,7 @@ import { GAME_WIDTH, GAME_HEIGHT, COLORS } from '../../config.js'; import { Button } from '../../ui/Button.js'; import { playSound, SFX } from '../../ui/Sounds.js'; import { MusicPlayer } from '../../ui/MusicPlayer.js'; +import { getGameSoundtrack } from '../../services/soundtrack.js'; import { api } from '../../services/api.js'; import * as store from '../../services/localStore.js'; import { @@ -85,7 +86,10 @@ export default class BalatroGame extends Phaser.Scene { } create() { - try { const m = this.cache.json.get('music'); if (m?.tracks) new MusicPlayer(this, m.tracks); } catch (_) {} + try { + const { tracks, volume } = getGameSoundtrack(this); + if (tracks.length) new MusicPlayer(this, tracks, volume); + } catch (_) {} this.art = this.cache.json.get('balatro-artwork') || {}; if (!this.textures.exists('balatro-spark')) { diff --git a/src/games/coloradodefense/ColoradoDefenseGame.js b/src/games/coloradodefense/ColoradoDefenseGame.js index 62e248b..7d90215 100644 --- a/src/games/coloradodefense/ColoradoDefenseGame.js +++ b/src/games/coloradodefense/ColoradoDefenseGame.js @@ -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 { getGameSoundtrack } from '../../services/soundtrack.js'; import { playSound, SFX } from '../../ui/Sounds.js'; import { api } from '../../services/api.js'; import { applyArcadeCRTOverlay } from '../../ui/ArcadeCRTOverlay.js'; @@ -50,8 +51,8 @@ export default class ColoradoDefenseGame extends Phaser.Scene { create() { try { - const music = this.cache.json.get('music'); - if (music?.tracks) this.music = new MusicPlayer(this, music.tracks); + const { tracks, volume } = getGameSoundtrack(this); + if (tracks.length) this.music = new MusicPlayer(this, tracks, volume); } catch (_) { /* optional */ } this.input.mouse?.disableContextMenu(); diff --git a/src/games/dotlink/DotLinkGame.js b/src/games/dotlink/DotLinkGame.js index 959ad18..cf4fed7 100644 --- a/src/games/dotlink/DotLinkGame.js +++ b/src/games/dotlink/DotLinkGame.js @@ -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 { getGameSoundtrack } from '../../services/soundtrack.js'; import { playSound, SFX, playDotLinkSound, playScifiLaunch } from '../../ui/Sounds.js'; import { api } from '../../services/api.js'; import { @@ -94,8 +95,8 @@ export default class DotLinkGame extends Phaser.Scene { async create() { try { - const music = this.cache.json.get('music'); - if (music?.tracks) new MusicPlayer(this, music.tracks); + const { tracks, volume } = getGameSoundtrack(this); + if (tracks.length) new MusicPlayer(this, tracks, volume); } catch (_) { /* optional */ } this.buildTextures(); diff --git a/src/games/hexsweeper/HexsweeperGame.js b/src/games/hexsweeper/HexsweeperGame.js index 9ae79ce..f826cfe 100644 --- a/src/games/hexsweeper/HexsweeperGame.js +++ b/src/games/hexsweeper/HexsweeperGame.js @@ -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 { getGameSoundtrack } from '../../services/soundtrack.js'; import { playSound, SFX } from '../../ui/Sounds.js'; import { api } from '../../services/api.js'; import { @@ -44,8 +45,8 @@ export default class HexsweeperGame extends Phaser.Scene { create() { try { - const music = this.cache.json.get('music'); - if (music?.tracks) new MusicPlayer(this, music.tracks); + const { tracks, volume } = getGameSoundtrack(this); + if (tracks.length) new MusicPlayer(this, tracks, volume); } catch (_) { /* optional */ } // Right-click should flag, not open the browser context menu. diff --git a/src/games/mastermind/MastermindGame.js b/src/games/mastermind/MastermindGame.js index 0147d90..b100e29 100644 --- a/src/games/mastermind/MastermindGame.js +++ b/src/games/mastermind/MastermindGame.js @@ -4,6 +4,7 @@ import { Button } from '../../ui/Button.js'; import { createOpponentPortrait } from '../../ui/Portrait.js'; import { playSound, SFX } from '../../ui/Sounds.js'; import { MusicPlayer } from '../../ui/MusicPlayer.js'; +import { getGameSoundtrack } from '../../services/soundtrack.js'; import { makeConfig, createInitialState, setPlayerCode, applyGuess, isGameOver, } from './MastermindLogic.js'; @@ -73,7 +74,10 @@ export default class MastermindGame extends Phaser.Scene { } create() { - try { new MusicPlayer(this, this.cache.json.get('music')?.tracks ?? []); } catch (_) {} + try { + const { tracks, volume } = getGameSoundtrack(this); + if (tracks.length) new MusicPlayer(this, tracks, volume); + } catch (_) {} this.gs = createInitialState(this.config); this.buildTextures(); diff --git a/src/games/tempest/TempestGame.js b/src/games/tempest/TempestGame.js index 2878b39..d3f1200 100644 --- a/src/games/tempest/TempestGame.js +++ b/src/games/tempest/TempestGame.js @@ -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 { getGameSoundtrack } from '../../services/soundtrack.js'; import { playSound, SFX } from '../../ui/Sounds.js'; import { api } from '../../services/api.js'; import { applyArcadeCRTOverlay } from '../../ui/ArcadeCRTOverlay.js'; @@ -43,8 +44,8 @@ export default class TempestGame extends Phaser.Scene { create() { try { - const music = this.cache.json.get('music'); - if (music?.tracks) this.music = new MusicPlayer(this, music.tracks); + const { tracks, volume } = getGameSoundtrack(this); + if (tracks.length) this.music = new MusicPlayer(this, tracks, volume); } catch (_) { /* optional */ } this.input.mouse?.disableContextMenu(); diff --git a/src/scenes/PreloadScene.js b/src/scenes/PreloadScene.js index bc5b3ff..821aae3 100644 --- a/src/scenes/PreloadScene.js +++ b/src/scenes/PreloadScene.js @@ -44,6 +44,8 @@ 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.json('nintendo-music', 'data/nintendo-music.json'); + this.load.json('arcadedark-music', 'data/arcadedark-music.json'); + this.load.json('hacker-music', 'data/hacker-music.json'); this.load.json('rushhour', 'data/rushhour.json'); this.load.json('puddingmonsters', 'data/puddingmonsters.json'); this.load.json('shift-artwork', 'data/shift-artwork.json'); diff --git a/src/services/soundtrack.js b/src/services/soundtrack.js index 63d3fb4..3e8dada 100644 --- a/src/services/soundtrack.js +++ b/src/services/soundtrack.js @@ -18,6 +18,13 @@ // if (tracks.length) this.music = new MusicPlayer(this, tracks, volume); export const GAME_SOUNDTRACK_OVERRIDES = { superkart: 'nintendo', + coloradodefense: 'arcadedark', + tempest: 'arcadedark', + mastermind: 'hacker', + balatro: 'hacker', + hexsweeper: 'hacker', + dotlink: 'hacker', + '2048': 'hacker', }; // Resolve the track list (and optional volume override) a game scene's