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:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { Check, ChevronLeft, Crown, Eye, EyeOff, Gift, Lock, LogOut, MapPin, Pencil, Search, Star, Upload, Users, Volume2 } from "lucide-react";
|
||||
import { Check, ChevronLeft, Crown, Eye, EyeOff, Gift, Lock, LogOut, MapPin, Music, Pencil, Search, Star, Upload, Users, Volume2 } from "lucide-react";
|
||||
import { useRef, useState } from "react";
|
||||
import { ScreenHeader, ScreenShell } from "@/components/online/ScreenHeader";
|
||||
import { RankBadge } from "@/components/online/RankBadge";
|
||||
@@ -667,11 +667,12 @@ function CityRewardCard() {
|
||||
|
||||
function SoundSettings() {
|
||||
const { t } = useI18n();
|
||||
const { sfx, toggleSfx } = useSoundStore();
|
||||
const { sfx, toggleSfx, music, toggleMusic } = useSoundStore();
|
||||
return (
|
||||
<div className="panel rounded-2xl p-4">
|
||||
<h3 className="text-sm font-bold text-cream/80 mb-2">{t("settings.audio")}</h3>
|
||||
<ToggleRow icon={<Volume2 className="size-4 text-gold-400" />} label={t("settings.sound")} on={sfx} onClick={toggleSfx} />
|
||||
<ToggleRow icon={<Music className="size-4 text-gold-400" />} label={t("settings.music")} on={music} onClick={toggleMusic} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ function defaultProfile(session: AuthSession): UserProfile {
|
||||
plan: "free",
|
||||
level: 1,
|
||||
xp: 0,
|
||||
coins: 1000,
|
||||
coins: 2000,
|
||||
rating: 1000,
|
||||
stats: {
|
||||
games: 0,
|
||||
|
||||
+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