|
|
|
|
@ -28,8 +28,9 @@ export interface GameSessionState { |
|
|
|
|
} | null; |
|
|
|
|
showAnswer: boolean; |
|
|
|
|
wrongTeamIds: string[]; // Teams that answered wrong for current question
|
|
|
|
|
lastAnsweredTeamId: string | null; // Track who answered last
|
|
|
|
|
lastAnswerCorrect: boolean | null; // Was it correct or wrong
|
|
|
|
|
lastAnsweredTeamId: string | null; // Track who answered last (for current question feedback)
|
|
|
|
|
lastAnswerCorrect: boolean | null; // Was it correct or wrong (for current question feedback)
|
|
|
|
|
lastCorrectTeamId: string | null; // Track who answered correctly last (persists across questions for clue selection)
|
|
|
|
|
|
|
|
|
|
// Daily Double
|
|
|
|
|
dailyDoubleWager: number | null; |
|
|
|
|
@ -37,7 +38,6 @@ export interface GameSessionState { |
|
|
|
|
// Final Round
|
|
|
|
|
finalCategoryRevealed: boolean; // Has the final category been revealed
|
|
|
|
|
finalWagers: Record<string, number>; |
|
|
|
|
finalAnswers: Record<string, string>; |
|
|
|
|
finalRevealed: string[]; // Team IDs that have been revealed
|
|
|
|
|
|
|
|
|
|
// Timer
|
|
|
|
|
@ -146,10 +146,10 @@ class GameSessionStore { |
|
|
|
|
wrongTeamIds: [], |
|
|
|
|
lastAnsweredTeamId: null, |
|
|
|
|
lastAnswerCorrect: null, |
|
|
|
|
lastCorrectTeamId: null, |
|
|
|
|
dailyDoubleWager: null, |
|
|
|
|
finalCategoryRevealed: false, |
|
|
|
|
finalWagers: {}, |
|
|
|
|
finalAnswers: {}, |
|
|
|
|
finalRevealed: [], |
|
|
|
|
timerRunning: false, |
|
|
|
|
timerSeconds: 0, |
|
|
|
|
@ -274,12 +274,6 @@ class GameSessionStore { |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
toggleAnswer() { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
this.state.showAnswer = !this.state.showAnswer; |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
revealAnswer() { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
this.state.showAnswer = true; |
|
|
|
|
@ -300,9 +294,11 @@ class GameSessionStore { |
|
|
|
|
team.score += points; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Track last answer
|
|
|
|
|
// Track last answer (current question feedback)
|
|
|
|
|
this.state.lastAnsweredTeamId = teamId; |
|
|
|
|
this.state.lastAnswerCorrect = true; |
|
|
|
|
// Track last correct answer (persists for clue selection)
|
|
|
|
|
this.state.lastCorrectTeamId = teamId; |
|
|
|
|
|
|
|
|
|
// Show answer and start reveal countdown
|
|
|
|
|
this.state.showAnswer = true; |
|
|
|
|
@ -387,22 +383,12 @@ class GameSessionStore { |
|
|
|
|
wager: this.state.dailyDoubleWager ?? undefined, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// Mark as revealed
|
|
|
|
|
// Mark as revealed and increment counter
|
|
|
|
|
question.isRevealed = true; |
|
|
|
|
|
|
|
|
|
// Increment questions answered counter
|
|
|
|
|
this.state.questionsAnswered++; |
|
|
|
|
|
|
|
|
|
// Reset state
|
|
|
|
|
this.state.currentQuestion = null; |
|
|
|
|
this.state.showAnswer = false; |
|
|
|
|
this.state.wrongTeamIds = []; |
|
|
|
|
this.state.dailyDoubleWager = null; |
|
|
|
|
this.state.activeTeamId = null; |
|
|
|
|
this.state.timeoutCountdown = null; |
|
|
|
|
this.state.revealCountdown = null; |
|
|
|
|
this.state.skippingQuestion = false; |
|
|
|
|
this.state.phase = "board"; |
|
|
|
|
// Reset question state using consolidated helper
|
|
|
|
|
this.resetQuestionState(); |
|
|
|
|
|
|
|
|
|
// Check if round is complete
|
|
|
|
|
this.checkRoundComplete(); |
|
|
|
|
@ -420,7 +406,7 @@ class GameSessionStore { |
|
|
|
|
if (allRevealed) { |
|
|
|
|
// Move to next round or final
|
|
|
|
|
if (this.state.currentRoundIndex < this.state.rounds.length - 1) { |
|
|
|
|
this.state.currentRoundIndex++; |
|
|
|
|
this.transitionToNextRound(); |
|
|
|
|
} else if (this.state.settings.enableFinalRound && this.state.finalRound) { |
|
|
|
|
this.state.phase = "final-category"; |
|
|
|
|
} else { |
|
|
|
|
@ -454,15 +440,6 @@ class GameSessionStore { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setScore(teamId: string, score: number) { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
const team = this.state.teams.find(t => t.id === teamId); |
|
|
|
|
if (team) { |
|
|
|
|
team.score = score; |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================
|
|
|
|
|
// Round Management
|
|
|
|
|
// ============================================
|
|
|
|
|
@ -470,12 +447,7 @@ class GameSessionStore { |
|
|
|
|
nextRound() { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
if (this.state.currentRoundIndex < this.state.rounds.length - 1) { |
|
|
|
|
this.state.currentRoundIndex++; |
|
|
|
|
this.state.phase = "intro"; |
|
|
|
|
this.state.introCategoryIndex = -1; |
|
|
|
|
this.state.categoriesIntroduced = false; // Reset for new round
|
|
|
|
|
this.state.boardRevealed = false; // Reset for new round
|
|
|
|
|
this.state.questionResults = []; |
|
|
|
|
this.transitionToNextRound(); |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -521,13 +493,6 @@ class GameSessionStore { |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Reveal the final answer (after all teams judged)
|
|
|
|
|
revealFinalAnswer() { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
this.state.showAnswer = true; |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Select a team to judge their final answer
|
|
|
|
|
selectFinalTeam(teamId: string) { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
@ -555,6 +520,11 @@ class GameSessionStore { |
|
|
|
|
this.state.finalRevealed.push(teamId); |
|
|
|
|
this.state.activeTeamId = null; |
|
|
|
|
|
|
|
|
|
// Auto-reveal answer when all teams are judged
|
|
|
|
|
if (this.state.finalRevealed.length === this.state.teams.length) { |
|
|
|
|
this.state.showAnswer = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -590,7 +560,7 @@ class GameSessionStore { |
|
|
|
|
this.persist(); |
|
|
|
|
|
|
|
|
|
if (this.state.revealCountdown === 0) { |
|
|
|
|
// Return to board
|
|
|
|
|
// Mark question as revealed
|
|
|
|
|
if (this.state.currentQuestion) { |
|
|
|
|
const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion; |
|
|
|
|
const question = this.state.rounds[roundIndex]?.categories[categoryIndex]?.questions[questionIndex]; |
|
|
|
|
@ -600,16 +570,8 @@ class GameSessionStore { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Reset state and return to board
|
|
|
|
|
this.state.currentQuestion = null; |
|
|
|
|
this.state.showAnswer = false; |
|
|
|
|
this.state.wrongTeamIds = []; |
|
|
|
|
this.state.dailyDoubleWager = null; |
|
|
|
|
this.state.activeTeamId = null; |
|
|
|
|
this.state.timeoutCountdown = null; |
|
|
|
|
this.state.revealCountdown = null; |
|
|
|
|
this.state.skippingQuestion = false; |
|
|
|
|
this.state.phase = "board"; |
|
|
|
|
// Reset state using consolidated helper
|
|
|
|
|
this.resetQuestionState(); |
|
|
|
|
this.checkRoundComplete(); |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
@ -645,12 +607,6 @@ class GameSessionStore { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setTimerMax(seconds: number) { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
this.state.timerMax = seconds; |
|
|
|
|
this.persist(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Call this from moderator view only
|
|
|
|
|
enableTimerControl() { |
|
|
|
|
this.startInternalTimer(); |
|
|
|
|
@ -680,7 +636,36 @@ class GameSessionStore { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================
|
|
|
|
|
// Helpers
|
|
|
|
|
// Private Helpers
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
|
|
|
// Consolidated state reset after question completion
|
|
|
|
|
private resetQuestionState() { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
this.state.currentQuestion = null; |
|
|
|
|
this.state.showAnswer = false; |
|
|
|
|
this.state.wrongTeamIds = []; |
|
|
|
|
this.state.dailyDoubleWager = null; |
|
|
|
|
this.state.activeTeamId = null; |
|
|
|
|
this.state.timeoutCountdown = null; |
|
|
|
|
this.state.revealCountdown = null; |
|
|
|
|
this.state.skippingQuestion = false; |
|
|
|
|
this.state.phase = "board"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Consolidated round transition logic
|
|
|
|
|
private transitionToNextRound() { |
|
|
|
|
if (!this.state) return; |
|
|
|
|
this.state.currentRoundIndex++; |
|
|
|
|
this.state.phase = "intro"; |
|
|
|
|
this.state.introCategoryIndex = -1; |
|
|
|
|
this.state.categoriesIntroduced = false; |
|
|
|
|
this.state.boardRevealed = false; |
|
|
|
|
this.state.questionResults = []; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================
|
|
|
|
|
// Public Helpers
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
|
|
|
get currentRound(): Round | null { |
|
|
|
|
|