music: re-enable background loop (default = playful) + profile on/off; coins 2000
- sound.ts: restored startMusic (was a no-op stub) playing the selected track through musicGain (in-game mute still applies); default track switched to "playful" (per user). Music auto-starts on init when enabled. - Profile → Audio: re-added a music on/off toggle (so players can disable it outside the game too); SFX toggle unchanged. - Economy tune: starting coins 1000 → 2000 (mock defaultProfile + server ProfileDto) so new players start a bit richer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+30
-3
@@ -38,7 +38,7 @@ class SoundManager {
|
||||
sfxEnabled = loadBool(LS_SFX);
|
||||
musicEnabled = loadBool(LS_MUSIC);
|
||||
musicTrack: MusicTrack =
|
||||
(typeof window !== "undefined" && (localStorage.getItem(LS_TRACK) as MusicTrack)) || "santoor";
|
||||
(typeof window !== "undefined" && (localStorage.getItem(LS_TRACK) as MusicTrack)) || "playful";
|
||||
|
||||
/** Must be called from a user gesture to unlock audio. */
|
||||
init() {
|
||||
@@ -210,8 +210,35 @@ class SoundManager {
|
||||
},
|
||||
};
|
||||
|
||||
/** Background music was removed from the game — these are inert no-ops. */
|
||||
startMusic() {}
|
||||
/** Start the ambient loop for the current track (plays through musicGain so
|
||||
* the in-game mute / music volume apply). Idempotent + needs an unlocked ctx. */
|
||||
startMusic() {
|
||||
if (!this.musicEnabled || this.musicTimer || !this.ctx || !this.musicGain) return;
|
||||
const tick = () => {
|
||||
if (!this.ctx || !this.musicGain) return;
|
||||
const cfg = this.TRACKS[this.musicTrack];
|
||||
const now = this.ctx.currentTime;
|
||||
const freq = cfg.notes[this.step % cfg.notes.length];
|
||||
this.step++;
|
||||
const voice = (f: number, scale = 1) => {
|
||||
const osc = this.ctx!.createOscillator();
|
||||
const g = this.ctx!.createGain();
|
||||
osc.type = cfg.type;
|
||||
osc.frequency.value = f;
|
||||
g.gain.setValueAtTime(0.0001, now);
|
||||
g.gain.linearRampToValueAtTime(cfg.peak * scale, now + cfg.attack);
|
||||
g.gain.exponentialRampToValueAtTime(0.0001, now + cfg.dur);
|
||||
osc.connect(g);
|
||||
g.connect(this.musicGain!);
|
||||
osc.start(now);
|
||||
osc.stop(now + cfg.dur + 0.05);
|
||||
};
|
||||
voice(freq);
|
||||
if (cfg.fifth) voice(freq * 1.5, 0.45); // soft fifth above
|
||||
};
|
||||
tick();
|
||||
this.musicTimer = setInterval(tick, this.TRACKS[this.musicTrack].gap);
|
||||
}
|
||||
|
||||
stopMusic() {
|
||||
if (this.musicTimer) {
|
||||
|
||||
Reference in New Issue
Block a user