diff --git a/components/Game.tsx b/components/Game.tsx index ecb6a60..ad42945 100644 --- a/components/Game.tsx +++ b/components/Game.tsx @@ -108,6 +108,10 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max const handleGuess = (song: any) => { if (isProcessingGuess) return; + // Prevent guessing if already solved or failed + if (gameState?.isSolved || gameState?.isFailed) { + return; + } setIsProcessingGuess(true); setLastAction('GUESS'); @@ -159,6 +163,9 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max }; const handleSkip = () => { + // Prevent skipping if already solved or failed + if (gameState?.isSolved || gameState?.isFailed) return; + // If user hasn't played audio yet on first attempt, start it instead of skipping if (gameState.guesses.length === 0 && !hasPlayedAudio) { handleStartAudio(); @@ -187,6 +194,9 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max }; const handleGiveUp = () => { + // Prevent giving up if already solved or failed + if (gameState?.isSolved || gameState?.isFailed) return; + setLastAction('SKIP'); addGuess("SKIPPED", false); giveUp(); // Ensure game is marked as failed and score reset to 0 diff --git a/lib/gameState.ts b/lib/gameState.ts index 5281a91..f1d16c9 100644 --- a/lib/gameState.ts +++ b/lib/gameState.ts @@ -61,6 +61,17 @@ export function useGameState( yearGuessed: false }); + const createNewStatistics = (): Statistics => ({ + solvedIn1: 0, + solvedIn2: 0, + solvedIn3: 0, + solvedIn4: 0, + solvedIn5: 0, + solvedIn6: 0, + solvedIn7: 0, + failed: 0, + }); + useEffect(() => { const today = getTodayISOString(); @@ -86,63 +97,90 @@ export function useGameState( await savePlayerState(genreKey, newState, loadedStats); return; } + } else { + // No backend state found - check localStorage before creating new state + const storageKey = getStorageKey(); + const stored = localStorage.getItem(storageKey); + + if (stored) { + const parsed = JSON.parse(stored); + if (parsed.date === today) { + // Migration for existing states without score + if (parsed.score === undefined) { + parsed.score = INITIAL_SCORE; + parsed.replayCount = 0; + parsed.skipCount = 0; + parsed.scoreBreakdown = [{ value: INITIAL_SCORE, reason: 'Start value' }]; + parsed.yearGuessed = false; + } + setGameState(parsed as GameState); + // Also save to backend for cross-domain sync + const statsKey = getStatsKey(); + const storedStats = localStorage.getItem(statsKey); + const stats = storedStats ? JSON.parse(storedStats) : createNewStatistics(); + if (!statistics) { + setStatistics(stats); + } + await savePlayerState(genreKey, parsed as GameState, stats); + return; + } + } + + // No state found - create new state + const newState = createNewState(today); + setGameState(newState); + localStorage.setItem(getStorageKey(), JSON.stringify(newState)); + const newStats = createNewStatistics(); + setStatistics(newStats); + localStorage.setItem(getStatsKey(), JSON.stringify(newStats)); + await savePlayerState(genreKey, newState, newStats); + return; } } catch (error) { console.warn('[gameState] Failed to load from backend, falling back to localStorage:', error); - } - - // Fallback to localStorage - const storageKey = getStorageKey(); - const stored = localStorage.getItem(storageKey); + + // Fallback to localStorage only on error + const storageKey = getStorageKey(); + const stored = localStorage.getItem(storageKey); - if (stored) { - const parsed = JSON.parse(stored); - if (parsed.date === today) { - // Migration for existing states without score - if (parsed.score === undefined) { - parsed.score = INITIAL_SCORE; - parsed.replayCount = 0; - parsed.skipCount = 0; - parsed.scoreBreakdown = [{ value: INITIAL_SCORE, reason: 'Start value' }]; - parsed.yearGuessed = false; + if (stored) { + const parsed = JSON.parse(stored); + if (parsed.date === today) { + // Migration for existing states without score + if (parsed.score === undefined) { + parsed.score = INITIAL_SCORE; + parsed.replayCount = 0; + parsed.skipCount = 0; + parsed.scoreBreakdown = [{ value: INITIAL_SCORE, reason: 'Start value' }]; + parsed.yearGuessed = false; + } + setGameState(parsed as GameState); + + // Load statistics from localStorage + const statsKey = getStatsKey(); + const storedStats = localStorage.getItem(statsKey); + if (storedStats) { + const parsedStats = JSON.parse(storedStats); + if (parsedStats.solvedIn7 === undefined) { + parsedStats.solvedIn7 = 0; + } + setStatistics(parsedStats); + } else { + const newStats = createNewStatistics(); + setStatistics(newStats); + localStorage.setItem(statsKey, JSON.stringify(newStats)); + } + return; } - setGameState(parsed as GameState); - } else { - // New day - const newState = createNewState(today); - setGameState(newState); - localStorage.setItem(storageKey, JSON.stringify(newState)); } - } else { - // No state + + // No state found - create new state const newState = createNewState(today); setGameState(newState); localStorage.setItem(storageKey, JSON.stringify(newState)); - } - - // Load statistics from localStorage - const statsKey = getStatsKey(); - const storedStats = localStorage.getItem(statsKey); - if (storedStats) { - const parsedStats = JSON.parse(storedStats); - // Migration for existing stats without solvedIn7 - if (parsedStats.solvedIn7 === undefined) { - parsedStats.solvedIn7 = 0; - } - setStatistics(parsedStats); - } else { - const newStats: Statistics = { - solvedIn1: 0, - solvedIn2: 0, - solvedIn3: 0, - solvedIn4: 0, - solvedIn5: 0, - solvedIn6: 0, - solvedIn7: 0, - failed: 0, - }; + const newStats = createNewStatistics(); setStatistics(newStats); - localStorage.setItem(statsKey, JSON.stringify(newStats)); + localStorage.setItem(getStatsKey(), JSON.stringify(newStats)); } }; @@ -203,6 +241,8 @@ export function useGameState( const addGuess = (guess: string, correct: boolean) => { if (!gameState) return; + // Prevent adding guesses if already solved or failed + if (gameState.isSolved || gameState.isFailed) return; const newGuesses = [...gameState.guesses, guess]; const isSolved = correct;