feat: add SynthQuest game mode with World 1 (Waves) — 8 puzzle levels
Gamified synth learning inspired by Turing Complete. Progressive puzzle system teaches oscillators, waveforms, frequency, and mixing through hands-on module patching. Includes world map, level progression with 3-star rating, target audio playback, solution validation, and localStorage persistence. Sandbox mode still accessible via button. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
64
src/game/gameState.js
Normal file
64
src/game/gameState.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* gameState.js — Game progress persistence
|
||||
* Tracks completed levels, stars earned, and unlocks
|
||||
*/
|
||||
|
||||
const STORAGE_KEY = 'synthquest-progress';
|
||||
|
||||
const defaultProgress = {
|
||||
currentWorld: 'w1',
|
||||
completedLevels: {}, // { levelId: { stars: 3, bestTime: 12.5 } }
|
||||
unlockedWorlds: ['w1'],
|
||||
totalStars: 0,
|
||||
};
|
||||
|
||||
let _progress = null;
|
||||
|
||||
export function loadProgress() {
|
||||
if (_progress) return _progress;
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
_progress = raw ? { ...defaultProgress, ...JSON.parse(raw) } : { ...defaultProgress };
|
||||
} catch {
|
||||
_progress = { ...defaultProgress };
|
||||
}
|
||||
return _progress;
|
||||
}
|
||||
|
||||
export function saveProgress() {
|
||||
if (!_progress) return;
|
||||
_progress.totalStars = Object.values(_progress.completedLevels)
|
||||
.reduce((sum, l) => sum + (l.stars || 0), 0);
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(_progress));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function completeLevel(levelId, stars) {
|
||||
const p = loadProgress();
|
||||
const existing = p.completedLevels[levelId];
|
||||
if (!existing || stars > existing.stars) {
|
||||
p.completedLevels[levelId] = { stars, completedAt: Date.now() };
|
||||
}
|
||||
saveProgress();
|
||||
}
|
||||
|
||||
export function getLevelProgress(levelId) {
|
||||
const p = loadProgress();
|
||||
return p.completedLevels[levelId] || null;
|
||||
}
|
||||
|
||||
export function isLevelUnlocked(levelId, worldLevels) {
|
||||
const p = loadProgress();
|
||||
// First level is always unlocked
|
||||
const idx = worldLevels.findIndex(l => l.id === levelId);
|
||||
if (idx === 0) return true;
|
||||
// Previous level must have at least 1 star
|
||||
const prevId = worldLevels[idx - 1]?.id;
|
||||
return prevId && p.completedLevels[prevId]?.stars >= 1;
|
||||
}
|
||||
|
||||
export function resetProgress() {
|
||||
_progress = { ...defaultProgress };
|
||||
saveProgress();
|
||||
}
|
||||
Reference in New Issue
Block a user