feat: add per-game soundtracks for multiple games
Introduce game-specific music overrides using the soundtrack service. Adds arcadedark and hacker music packs, and wires them to Balatro, Colorado Defense, Tempest, Mastermind, Hexsweeper, Dot Link, and 2048. Games now lazy-load their soundtrack assets on entry instead of sharing a single generic music cache.
This commit is contained in:
parent
e1a5c21180
commit
7713089466
Binary file not shown.
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ?? [];
|
||||
|
|
|
|||
|
|
@ -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')) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue