Skip to content

Commit c7ed023

Browse files
author
DavidQ
committed
Route game asset loading through Workspace Manager catalogs; remove hardcoded Asteroids/SpaceInvaders/SpaceDuel asset paths.
1 parent d9844be commit c7ed023

14 files changed

Lines changed: 638 additions & 63 deletions
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"schema": "html-js-gaming.game-asset-catalog",
3+
"version": 1,
4+
"gameId": "asteroids",
5+
"assets": {
6+
"audio.asteroids.fire": {
7+
"path": "/games/Asteroids/assets/audio/fire.wav",
8+
"kind": "audio",
9+
"source": "workspace-manager"
10+
},
11+
"audio.asteroids.bang-large": {
12+
"path": "/games/Asteroids/assets/audio/bangLarge.wav",
13+
"kind": "audio",
14+
"source": "workspace-manager"
15+
},
16+
"audio.asteroids.bang-medium": {
17+
"path": "/games/Asteroids/assets/audio/bangMedium.wav",
18+
"kind": "audio",
19+
"source": "workspace-manager"
20+
},
21+
"audio.asteroids.bang-small": {
22+
"path": "/games/Asteroids/assets/audio/bangSmall.wav",
23+
"kind": "audio",
24+
"source": "workspace-manager"
25+
},
26+
"audio.asteroids.beat-1": {
27+
"path": "/games/Asteroids/assets/audio/beat1.wav",
28+
"kind": "audio",
29+
"source": "workspace-manager"
30+
},
31+
"audio.asteroids.beat-2": {
32+
"path": "/games/Asteroids/assets/audio/beat2.wav",
33+
"kind": "audio",
34+
"source": "workspace-manager"
35+
},
36+
"audio.asteroids.extra-ship": {
37+
"path": "/games/Asteroids/assets/audio/extraShip.wav",
38+
"kind": "audio",
39+
"source": "workspace-manager"
40+
},
41+
"audio.asteroids.thrust": {
42+
"path": "/games/Asteroids/assets/audio/thrust.wav",
43+
"kind": "audio",
44+
"source": "workspace-manager"
45+
},
46+
"audio.asteroids.saucer-large": {
47+
"path": "/games/Asteroids/assets/audio/saucerBig.wav",
48+
"kind": "audio",
49+
"source": "workspace-manager"
50+
},
51+
"audio.asteroids.saucer-small": {
52+
"path": "/games/Asteroids/assets/audio/saucerSmall.wav",
53+
"kind": "audio",
54+
"source": "workspace-manager"
55+
},
56+
"image.asteroids.bezel": {
57+
"path": "/games/Asteroids/assets/images/bezel.png",
58+
"kind": "image",
59+
"source": "workspace-manager"
60+
}
61+
}
62+
}

games/Asteroids/game/FullscreenBezelOverlay.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
const DEFAULT_FULLSCREEN_BEZEL_ASSET_PATH = "games/Asteroids/assets/images/bezel.png";
1+
import { resolveWorkspaceGameAssetPath } from "../../shared/workspaceGameAssetCatalog.js";
2+
3+
const ASTEROIDS_GAME_ID = "Asteroids";
4+
const DEFAULT_FULLSCREEN_BEZEL_ASSET_ID = "image.asteroids.bezel";
25
const FULLSCREEN_BEZEL_DRAW_MODES = Object.freeze(["overlay", "underlay"]);
36

47
function toSafeString(value, fallback = "") {
58
return typeof value === "string" && value.trim() ? value.trim() : fallback;
69
}
710

811
export function resolveFullscreenBezelAssetPath(assetPath) {
9-
return toSafeString(assetPath, DEFAULT_FULLSCREEN_BEZEL_ASSET_PATH);
12+
const explicitPath = toSafeString(assetPath, "");
13+
if (explicitPath) {
14+
return explicitPath;
15+
}
16+
return toSafeString(
17+
resolveWorkspaceGameAssetPath(ASTEROIDS_GAME_ID, DEFAULT_FULLSCREEN_BEZEL_ASSET_ID),
18+
""
19+
);
1020
}
1121

1222
export function normalizeFullscreenBezelDrawMode(drawMode) {
1323
const normalized = toSafeString(drawMode, "overlay").toLowerCase();
1424
return FULLSCREEN_BEZEL_DRAW_MODES.includes(normalized) ? normalized : "overlay";
1525
}
1626

17-
export { DEFAULT_FULLSCREEN_BEZEL_ASSET_PATH, FULLSCREEN_BEZEL_DRAW_MODES };
27+
export { DEFAULT_FULLSCREEN_BEZEL_ASSET_ID, FULLSCREEN_BEZEL_DRAW_MODES };
1828

1929
export default class FullscreenBezelOverlay {
2030
constructor(options = {}) {

games/Asteroids/systems/AsteroidsAudio.js

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ David Quesenberry
55
AsteroidsAudio.js
66
*/
77
import { GaplessLoopPlayer, HtmlAudioMediaBackend, MediaTrackService } from '../../../src/engine/audio/index.js';
8+
import { resolveWorkspaceGameAssetPath } from '../../shared/workspaceGameAssetCatalog.js';
9+
10+
const ASTEROIDS_GAME_ID = "Asteroids";
11+
const ASTEROIDS_AUDIO_ASSET_IDS = Object.freeze({
12+
fire: "audio.asteroids.fire",
13+
bangLarge: "audio.asteroids.bang-large",
14+
bangMedium: "audio.asteroids.bang-medium",
15+
bangSmall: "audio.asteroids.bang-small",
16+
beat1: "audio.asteroids.beat-1",
17+
beat2: "audio.asteroids.beat-2",
18+
extraShip: "audio.asteroids.extra-ship",
19+
thrust: "audio.asteroids.thrust",
20+
saucerLarge: "audio.asteroids.saucer-large",
21+
saucerSmall: "audio.asteroids.saucer-small"
22+
});
823

924
export default class AsteroidsAudio {
1025
constructor() {
@@ -17,21 +32,36 @@ export default class AsteroidsAudio {
1732
this.activeUfoTrack = null;
1833
}
1934

35+
resolveAudioPath(assetId) {
36+
return resolveWorkspaceGameAssetPath(ASTEROIDS_GAME_ID, assetId);
37+
}
38+
2039
ensureInitialized() {
2140
if (this.initialized) {
2241
return;
2342
}
2443

25-
this.media.register('fire', { src: '/games/Asteroids/assets/audio/fire.wav', volume: 0.55 });
26-
this.media.register('bangLarge', { src: '/games/Asteroids/assets/audio/bangLarge.wav', volume: 0.65 });
27-
this.media.register('bangMedium', { src: '/games/Asteroids/assets/audio/bangMedium.wav', volume: 0.6 });
28-
this.media.register('bangSmall', { src: '/games/Asteroids/assets/audio/bangSmall.wav', volume: 0.55 });
29-
this.media.register('beat1', { src: '/games/Asteroids/assets/audio/beat1.wav', volume: 0.45 });
30-
this.media.register('beat2', { src: '/games/Asteroids/assets/audio/beat2.wav', volume: 0.45 });
31-
this.loopPlayer.register('extraShip', { src: '/games/Asteroids/assets/audio/extraShip.wav', volume: 0.6, overlapSeconds: 0.035, fadeSeconds: 0.012 });
32-
this.loopPlayer.register('thrust', { src: '/games/Asteroids/assets/audio/thrust.wav', volume: 0.35, overlapSeconds: 0.03, fadeSeconds: 0.01 });
33-
this.loopPlayer.register('saucerLarge', { src: '/games/Asteroids/assets/audio/saucerBig.wav', volume: 0.35, overlapSeconds: 0.03, fadeSeconds: 0.01 });
34-
this.loopPlayer.register('saucerSmall', { src: '/games/Asteroids/assets/audio/saucerSmall.wav', volume: 0.4, overlapSeconds: 0.03, fadeSeconds: 0.01 });
44+
const firePath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.fire);
45+
const bangLargePath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.bangLarge);
46+
const bangMediumPath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.bangMedium);
47+
const bangSmallPath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.bangSmall);
48+
const beat1Path = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.beat1);
49+
const beat2Path = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.beat2);
50+
const extraShipPath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.extraShip);
51+
const thrustPath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.thrust);
52+
const saucerLargePath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.saucerLarge);
53+
const saucerSmallPath = this.resolveAudioPath(ASTEROIDS_AUDIO_ASSET_IDS.saucerSmall);
54+
55+
if (firePath) this.media.register('fire', { src: firePath, volume: 0.55 });
56+
if (bangLargePath) this.media.register('bangLarge', { src: bangLargePath, volume: 0.65 });
57+
if (bangMediumPath) this.media.register('bangMedium', { src: bangMediumPath, volume: 0.6 });
58+
if (bangSmallPath) this.media.register('bangSmall', { src: bangSmallPath, volume: 0.55 });
59+
if (beat1Path) this.media.register('beat1', { src: beat1Path, volume: 0.45 });
60+
if (beat2Path) this.media.register('beat2', { src: beat2Path, volume: 0.45 });
61+
if (extraShipPath) this.loopPlayer.register('extraShip', { src: extraShipPath, volume: 0.6, overlapSeconds: 0.035, fadeSeconds: 0.012 });
62+
if (thrustPath) this.loopPlayer.register('thrust', { src: thrustPath, volume: 0.35, overlapSeconds: 0.03, fadeSeconds: 0.01 });
63+
if (saucerLargePath) this.loopPlayer.register('saucerLarge', { src: saucerLargePath, volume: 0.35, overlapSeconds: 0.03, fadeSeconds: 0.01 });
64+
if (saucerSmallPath) this.loopPlayer.register('saucerSmall', { src: saucerSmallPath, volume: 0.4, overlapSeconds: 0.03, fadeSeconds: 0.01 });
3565
this.initialized = true;
3666
}
3767

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"schema": "html-js-gaming.game-asset-catalog",
3+
"version": 1,
4+
"gameId": "spaceduel",
5+
"assets": {
6+
"audio.space-duel.thrust": {
7+
"path": "/games/SpaceDuel/assets/audio/effects/thrust.wav",
8+
"kind": "audio",
9+
"source": "workspace-manager"
10+
},
11+
"audio.space-duel.fire": {
12+
"path": "/games/SpaceDuel/assets/audio/effects/fire.wav",
13+
"kind": "audio",
14+
"source": "workspace-manager"
15+
},
16+
"audio.space-duel.explosion": {
17+
"path": "/games/SpaceDuel/assets/audio/effects/explosion.wav",
18+
"kind": "audio",
19+
"source": "workspace-manager"
20+
},
21+
"audio.space-duel.player-death": {
22+
"path": "/games/SpaceDuel/assets/audio/effects/player_death.wav",
23+
"kind": "audio",
24+
"source": "workspace-manager"
25+
},
26+
"audio.space-duel.enemy-split": {
27+
"path": "/games/SpaceDuel/assets/audio/effects/enemy_split.wav",
28+
"kind": "audio",
29+
"source": "workspace-manager"
30+
},
31+
"audio.space-duel.bonus": {
32+
"path": "/games/SpaceDuel/assets/audio/effects/bonus.wav",
33+
"kind": "audio",
34+
"source": "workspace-manager"
35+
},
36+
"audio.space-duel.start": {
37+
"path": "/games/SpaceDuel/assets/audio/effects/start.wav",
38+
"kind": "audio",
39+
"source": "workspace-manager"
40+
},
41+
"audio.space-duel.game-over": {
42+
"path": "/games/SpaceDuel/assets/audio/effects/game_over.wav",
43+
"kind": "audio",
44+
"source": "workspace-manager"
45+
}
46+
}
47+
}

games/SpaceDuel/game/SoundController.js

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ David Quesenberry
44
03/25/2026
55
SoundController.js
66
*/
7-
const EFFECTS = {
8-
thrust: '../assets/effects/thrust.wav',
9-
fire: '../assets/effects/fire.wav',
10-
explosion: '../assets/effects/explosion.wav',
11-
playerDeath: '../assets/effects/player_death.wav',
12-
enemySplit: '../assets/effects/enemy_split.wav',
13-
bonus: '../assets/effects/bonus.wav',
14-
start: '../assets/effects/start.wav',
15-
gameOver: '../assets/effects/game_over.wav',
16-
};
7+
import { resolveWorkspaceGameAssetPath } from "../../shared/workspaceGameAssetCatalog.js";
8+
9+
const SPACE_DUEL_GAME_ID = "SpaceDuel";
10+
const EFFECT_ASSET_IDS = Object.freeze({
11+
thrust: "audio.space-duel.thrust",
12+
fire: "audio.space-duel.fire",
13+
explosion: "audio.space-duel.explosion",
14+
playerDeath: "audio.space-duel.player-death",
15+
enemySplit: "audio.space-duel.enemy-split",
16+
bonus: "audio.space-duel.bonus",
17+
start: "audio.space-duel.start",
18+
gameOver: "audio.space-duel.game-over"
19+
});
1720

1821
export default class SoundController {
1922
constructor({ baseUrl = import.meta.url } = {}) {
@@ -23,8 +26,16 @@ export default class SoundController {
2326
this.lastPlayedAt = new Map();
2427
}
2528

29+
resolveEffectPath(effectId) {
30+
const assetId = EFFECT_ASSET_IDS[effectId];
31+
if (!assetId) {
32+
return "";
33+
}
34+
return resolveWorkspaceGameAssetPath(SPACE_DUEL_GAME_ID, assetId);
35+
}
36+
2637
play(effectId, { volume = 0.42 } = {}) {
27-
const path = EFFECTS[effectId];
38+
const path = this.resolveEffectPath(effectId);
2839
if (!this.enabled || !path) {
2940
return null;
3041
}

games/SpaceDuel/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ <h3>Gameplay Loop</h3>
3939
</section>
4040
<section>
4141
<h3>Audio</h3>
42-
<p>This build uses generated placeholder WAV files under <code>/games/SpaceDuel/assets/effects</code> for easy asset replacement later.</p>
42+
<p>Audio assets resolve through the Workspace Manager game asset catalog for this game.</p>
4343
</section>
4444
<section>
4545
<h3>Engine Classes Used</h3>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"schema": "html-js-gaming.game-asset-catalog",
3+
"version": 1,
4+
"gameId": "spaceinvaders",
5+
"assets": {
6+
"audio.space-invaders.shoot": {
7+
"path": "/games/SpaceInvaders/assets/audio/effects/shoot.wav",
8+
"kind": "audio",
9+
"source": "workspace-manager"
10+
},
11+
"audio.space-invaders.invaderkilled": {
12+
"path": "/games/SpaceInvaders/assets/audio/effects/invaderkilled.wav",
13+
"kind": "audio",
14+
"source": "workspace-manager"
15+
},
16+
"audio.space-invaders.explosion": {
17+
"path": "/games/SpaceInvaders/assets/audio/effects/explosion.wav",
18+
"kind": "audio",
19+
"source": "workspace-manager"
20+
},
21+
"audio.space-invaders.fastinvader1": {
22+
"path": "/games/SpaceInvaders/assets/audio/effects/fastinvader1.wav",
23+
"kind": "audio",
24+
"source": "workspace-manager"
25+
},
26+
"audio.space-invaders.fastinvader2": {
27+
"path": "/games/SpaceInvaders/assets/audio/effects/fastinvader2.wav",
28+
"kind": "audio",
29+
"source": "workspace-manager"
30+
},
31+
"audio.space-invaders.fastinvader3": {
32+
"path": "/games/SpaceInvaders/assets/audio/effects/fastinvader3.wav",
33+
"kind": "audio",
34+
"source": "workspace-manager"
35+
},
36+
"audio.space-invaders.fastinvader4": {
37+
"path": "/games/SpaceInvaders/assets/audio/effects/fastinvader4.wav",
38+
"kind": "audio",
39+
"source": "workspace-manager"
40+
},
41+
"audio.space-invaders.ufo-lowpitch": {
42+
"path": "/games/SpaceInvaders/assets/audio/effects/ufo_lowpitch.wav",
43+
"kind": "audio",
44+
"source": "workspace-manager"
45+
},
46+
"audio.space-invaders.ufo-highpitch": {
47+
"path": "/games/SpaceInvaders/assets/audio/effects/ufo_highpitch.wav",
48+
"kind": "audio",
49+
"source": "workspace-manager"
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)