|
|
|
|
@@ -96,6 +96,10 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
if (gameState.isSolved && !gameState.yearGuessed && dailyPuzzle?.releaseYear) {
|
|
|
|
|
setShowYearModal(true);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Reset states when gameState is null (e.g., during loading)
|
|
|
|
|
setHasWon(false);
|
|
|
|
|
setHasLost(false);
|
|
|
|
|
}
|
|
|
|
|
}, [gameState, dailyPuzzle]);
|
|
|
|
|
|
|
|
|
|
@@ -163,6 +167,10 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
if (!gameState) return <div>{t('loadingState')}</div>;
|
|
|
|
|
|
|
|
|
|
// Use gameState directly for isSolved/isFailed to ensure consistency when returning to completed puzzles
|
|
|
|
|
const isSolved = gameState?.isSolved ?? hasWon;
|
|
|
|
|
const isFailed = gameState?.isFailed ?? hasLost;
|
|
|
|
|
|
|
|
|
|
const handleGuess = (song: any) => {
|
|
|
|
|
if (isProcessingGuess) return;
|
|
|
|
|
@@ -176,6 +184,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
if (song.id === dailyPuzzle.songId) {
|
|
|
|
|
addGuess(song.title, true);
|
|
|
|
|
setHasWon(true);
|
|
|
|
|
// gameState.isSolved will be updated by useGameState
|
|
|
|
|
// Track puzzle solved event
|
|
|
|
|
if (typeof window !== 'undefined' && window.plausible) {
|
|
|
|
|
window.plausible('puzzle_solved', {
|
|
|
|
|
@@ -196,6 +205,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
if (gameState.guesses.length + 1 >= maxAttempts) {
|
|
|
|
|
setHasLost(true);
|
|
|
|
|
setHasWon(false);
|
|
|
|
|
// gameState.isFailed will be updated by useGameState
|
|
|
|
|
// Track puzzle lost event
|
|
|
|
|
if (typeof window !== 'undefined' && window.plausible) {
|
|
|
|
|
window.plausible('puzzle_solved', {
|
|
|
|
|
@@ -236,6 +246,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
if (gameState.guesses.length + 1 >= maxAttempts) {
|
|
|
|
|
setHasLost(true);
|
|
|
|
|
setHasWon(false);
|
|
|
|
|
// gameState.isFailed will be updated by useGameState
|
|
|
|
|
// Track puzzle lost event
|
|
|
|
|
if (typeof window !== 'undefined' && window.plausible) {
|
|
|
|
|
window.plausible('puzzle_solved', {
|
|
|
|
|
@@ -260,6 +271,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
giveUp(); // Ensure game is marked as failed and score reset to 0
|
|
|
|
|
setHasLost(true);
|
|
|
|
|
setHasWon(false);
|
|
|
|
|
// gameState.isFailed will be updated by useGameState
|
|
|
|
|
// Track puzzle lost event
|
|
|
|
|
if (typeof window !== 'undefined' && window.plausible) {
|
|
|
|
|
window.plausible('puzzle_solved', {
|
|
|
|
|
@@ -409,19 +421,19 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
if (i < gameState.guesses.length) {
|
|
|
|
|
if (gameState.guesses[i] === 'SKIPPED') {
|
|
|
|
|
emojiGrid += '⬛';
|
|
|
|
|
} else if (hasWon && i === gameState.guesses.length - 1) {
|
|
|
|
|
} else if (isSolved && i === gameState.guesses.length - 1) {
|
|
|
|
|
emojiGrid += '🟩';
|
|
|
|
|
} else {
|
|
|
|
|
emojiGrid += '🟥';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// If game is lost, fill remaining slots with black squares
|
|
|
|
|
emojiGrid += hasLost ? '⬛' : '⬜';
|
|
|
|
|
emojiGrid += isFailed ? '⬛' : '⬜';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const speaker = hasWon ? '🔉' : '🔇';
|
|
|
|
|
const bonusStar = (hasWon && gameState.yearGuessed && dailyPuzzle.releaseYear && gameState.scoreBreakdown.some(item => item.reason === 'Bonus: Correct Year')) ? '⭐' : '';
|
|
|
|
|
const speaker = isSolved ? '🔉' : '🔇';
|
|
|
|
|
const bonusStar = (isSolved && gameState.yearGuessed && dailyPuzzle.releaseYear && gameState.scoreBreakdown.some(item => item.reason === 'Bonus: Correct Year')) ? '⭐' : '';
|
|
|
|
|
const genreText = genre ? `${isSpecial ? t('special') : t('genre')}: ${genre}\n` : '';
|
|
|
|
|
|
|
|
|
|
// Use current domain from window.location to support both hoerdle.de and hördle.de
|
|
|
|
|
@@ -534,7 +546,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
src={dailyPuzzle.audioUrl}
|
|
|
|
|
unlockedSeconds={unlockedSeconds}
|
|
|
|
|
startTime={dailyPuzzle.startTime}
|
|
|
|
|
autoPlay={lastAction === 'SKIP' || (lastAction === 'GUESS' && !hasWon && !hasLost)}
|
|
|
|
|
autoPlay={lastAction === 'SKIP' || (lastAction === 'GUESS' && !isSolved && !isFailed)}
|
|
|
|
|
onReplay={addReplay}
|
|
|
|
|
onHasPlayedChange={setHasPlayedAudio}
|
|
|
|
|
/>
|
|
|
|
|
@@ -543,7 +555,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
|
|
|
|
|
<div className="guess-list">
|
|
|
|
|
{gameState.guesses.map((guess, i) => {
|
|
|
|
|
const isCorrect = hasWon && i === gameState.guesses.length - 1;
|
|
|
|
|
const isCorrect = isSolved && i === gameState.guesses.length - 1;
|
|
|
|
|
return (
|
|
|
|
|
<div key={i} className="guess-item">
|
|
|
|
|
<span className="guess-number">#{i + 1}</span>
|
|
|
|
|
@@ -555,7 +567,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{!hasWon && !hasLost && (
|
|
|
|
|
{!isSolved && !isFailed && (
|
|
|
|
|
<>
|
|
|
|
|
<div id="tour-input">
|
|
|
|
|
<GuessInput onGuess={handleGuess} disabled={isProcessingGuess} />
|
|
|
|
|
@@ -586,13 +598,13 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{(hasWon || hasLost) && (
|
|
|
|
|
<div className={`message-box ${hasWon ? 'success' : 'failure'}`}>
|
|
|
|
|
{(isSolved || isFailed) && (
|
|
|
|
|
<div className={`message-box ${isSolved ? 'success' : 'failure'}`}>
|
|
|
|
|
<h2 style={{ fontSize: '1.5rem', fontWeight: 'bold', marginBottom: '0.5rem' }}>
|
|
|
|
|
{hasWon ? t('won') : t('lost')}
|
|
|
|
|
{isSolved ? t('won') : t('lost')}
|
|
|
|
|
</h2>
|
|
|
|
|
|
|
|
|
|
<div style={{ fontSize: '2rem', fontWeight: 'bold', margin: '1rem 0', color: hasWon ? 'var(--success)' : 'var(--danger)' }}>
|
|
|
|
|
<div style={{ fontSize: '2rem', fontWeight: 'bold', margin: '1rem 0', color: isSolved ? 'var(--success)' : 'var(--danger)' }}>
|
|
|
|
|
{t('score')}: {gameState.score}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@@ -610,7 +622,7 @@ export default function Game({ dailyPuzzle, genre = null, isSpecial = false, max
|
|
|
|
|
</ul>
|
|
|
|
|
</details>
|
|
|
|
|
|
|
|
|
|
<p>{hasWon ? t('comeBackTomorrow') : t('theSongWas')}</p>
|
|
|
|
|
<p>{isSolved ? t('comeBackTomorrow') : t('theSongWas')}</p>
|
|
|
|
|
|
|
|
|
|
<div style={{ margin: '1.5rem 0', padding: '1rem', background: 'rgba(255,255,255,0.5)', borderRadius: '0.5rem', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
|
|
|
<img
|
|
|
|
|
|