Add voice reading, hide questions from players
This commit is contained in:
@@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<button id="newQuestion" class="control-btn primary">New Question</button>
|
<button id="newQuestion" class="control-btn primary">New Question</button>
|
||||||
|
<button id="readQuestion" class="control-btn primary" disabled>🔊 Read Question</button>
|
||||||
<button id="activateBuzzer" class="control-btn" disabled>Activate Buzzers</button>
|
<button id="activateBuzzer" class="control-btn" disabled>Activate Buzzers</button>
|
||||||
<button id="markCorrect" class="control-btn success" style="display: none;">✓ Correct (+4)</button>
|
<button id="markCorrect" class="control-btn success" style="display: none;">✓ Correct (+4)</button>
|
||||||
<button id="markIncorrect" class="control-btn danger" style="display: none;">✗ Incorrect (-4)</button>
|
<button id="markIncorrect" class="control-btn danger" style="display: none;">✗ Incorrect (-4)</button>
|
||||||
|
|||||||
67
display.js
67
display.js
@@ -15,10 +15,13 @@ const markCorrectBtn = document.getElementById('markCorrect');
|
|||||||
const markIncorrectBtn = document.getElementById('markIncorrect');
|
const markIncorrectBtn = document.getElementById('markIncorrect');
|
||||||
const showAnswerBtn = document.getElementById('showAnswer');
|
const showAnswerBtn = document.getElementById('showAnswer');
|
||||||
const resetGameBtn = document.getElementById('resetGame');
|
const resetGameBtn = document.getElementById('resetGame');
|
||||||
|
const readQuestionBtn = document.getElementById('readQuestion');
|
||||||
|
|
||||||
let currentQuestion = null;
|
let currentQuestion = null;
|
||||||
let currentQuestionNumber = 1;
|
let currentQuestionNumber = 1;
|
||||||
let currentBuzzedPlayer = null;
|
let currentBuzzedPlayer = null;
|
||||||
|
let speechSynthesis = window.speechSynthesis;
|
||||||
|
let currentUtterance = null;
|
||||||
|
|
||||||
// Initialize game state
|
// Initialize game state
|
||||||
database.ref('gameState').set({
|
database.ref('gameState').set({
|
||||||
@@ -30,7 +33,8 @@ database.ref('gameState').set({
|
|||||||
player3: 0,
|
player3: 0,
|
||||||
player4: 0
|
player4: 0
|
||||||
},
|
},
|
||||||
questionNumber: 1
|
questionNumber: 1,
|
||||||
|
isReading: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for game state changes
|
// Listen for game state changes
|
||||||
@@ -55,6 +59,11 @@ database.ref('gameState').on('value', (snapshot) => {
|
|||||||
|
|
||||||
buzzedPlayer.textContent = `Player ${playerNum} buzzed in!`;
|
buzzedPlayer.textContent = `Player ${playerNum} buzzed in!`;
|
||||||
|
|
||||||
|
// Stop reading when someone buzzes
|
||||||
|
if (currentUtterance) {
|
||||||
|
speechSynthesis.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
// Show grading buttons
|
// Show grading buttons
|
||||||
markCorrectBtn.style.display = 'inline-block';
|
markCorrectBtn.style.display = 'inline-block';
|
||||||
markIncorrectBtn.style.display = 'inline-block';
|
markIncorrectBtn.style.display = 'inline-block';
|
||||||
@@ -80,6 +89,10 @@ newQuestionBtn.addEventListener('click', async () => {
|
|||||||
body: JSON.stringify({ categories })
|
body: JSON.stringify({ categories })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('API request failed');
|
||||||
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
currentQuestion = data;
|
currentQuestion = data;
|
||||||
|
|
||||||
@@ -89,12 +102,13 @@ newQuestionBtn.addEventListener('click', async () => {
|
|||||||
questionNum.textContent = currentQuestionNumber;
|
questionNum.textContent = currentQuestionNumber;
|
||||||
|
|
||||||
// Update Firebase
|
// Update Firebase
|
||||||
database.ref('gameState').update({
|
await database.ref('gameState').update({
|
||||||
currentQuestion: currentQuestion,
|
currentQuestion: currentQuestion,
|
||||||
buzzerActive: false,
|
buzzerActive: false,
|
||||||
buzzer: null,
|
buzzer: null,
|
||||||
playerAnswer: null,
|
playerAnswer: null,
|
||||||
questionNumber: currentQuestionNumber
|
questionNumber: currentQuestionNumber,
|
||||||
|
isReading: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset UI
|
// Reset UI
|
||||||
@@ -106,14 +120,51 @@ newQuestionBtn.addEventListener('click', async () => {
|
|||||||
showAnswerBtn.style.display = 'none';
|
showAnswerBtn.style.display = 'none';
|
||||||
|
|
||||||
activateBuzzerBtn.disabled = false;
|
activateBuzzerBtn.disabled = false;
|
||||||
|
readQuestionBtn.disabled = false;
|
||||||
currentQuestionNumber++;
|
currentQuestionNumber++;
|
||||||
|
|
||||||
|
console.log('Question loaded successfully:', currentQuestion);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching question:', error);
|
console.error('Error fetching question:', error);
|
||||||
alert('Error loading question. Check console.');
|
alert('Error loading question. Check console for details. The API might be down or CORS is blocking it.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Read question aloud
|
||||||
|
readQuestionBtn.addEventListener('click', () => {
|
||||||
|
if (!currentQuestion) {
|
||||||
|
alert('Load a question first!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any current speech
|
||||||
|
speechSynthesis.cancel();
|
||||||
|
|
||||||
|
// Create utterance
|
||||||
|
currentUtterance = new SpeechSynthesisUtterance(currentQuestion.tossup_question);
|
||||||
|
currentUtterance.rate = 0.9; // Slightly slower for clarity
|
||||||
|
currentUtterance.pitch = 1;
|
||||||
|
currentUtterance.volume = 1;
|
||||||
|
|
||||||
|
// Update Firebase that we're reading
|
||||||
|
database.ref('gameState/isReading').set(true);
|
||||||
|
|
||||||
|
currentUtterance.onend = () => {
|
||||||
|
database.ref('gameState/isReading').set(false);
|
||||||
|
console.log('Finished reading question');
|
||||||
|
};
|
||||||
|
|
||||||
|
speechSynthesis.speak(currentUtterance);
|
||||||
|
readQuestionBtn.textContent = '🔊 Reading...';
|
||||||
|
readQuestionBtn.disabled = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
readQuestionBtn.textContent = '🔊 Read Question';
|
||||||
|
readQuestionBtn.disabled = false;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
// Activate buzzers
|
// Activate buzzers
|
||||||
activateBuzzerBtn.addEventListener('click', () => {
|
activateBuzzerBtn.addEventListener('click', () => {
|
||||||
database.ref('gameState').update({
|
database.ref('gameState').update({
|
||||||
@@ -166,6 +217,7 @@ showAnswerBtn.addEventListener('click', () => {
|
|||||||
resetGameBtn.addEventListener('click', () => {
|
resetGameBtn.addEventListener('click', () => {
|
||||||
if (confirm('Reset all scores and start over?')) {
|
if (confirm('Reset all scores and start over?')) {
|
||||||
currentQuestionNumber = 1;
|
currentQuestionNumber = 1;
|
||||||
|
speechSynthesis.cancel();
|
||||||
database.ref('gameState').set({
|
database.ref('gameState').set({
|
||||||
buzzerActive: false,
|
buzzerActive: false,
|
||||||
currentQuestion: null,
|
currentQuestion: null,
|
||||||
@@ -175,7 +227,8 @@ resetGameBtn.addEventListener('click', () => {
|
|||||||
player3: 0,
|
player3: 0,
|
||||||
player4: 0
|
player4: 0
|
||||||
},
|
},
|
||||||
questionNumber: 1
|
questionNumber: 1,
|
||||||
|
isReading: false
|
||||||
});
|
});
|
||||||
questionText.textContent = 'Click "New Question" to start';
|
questionText.textContent = 'Click "New Question" to start';
|
||||||
answerSection.style.display = 'none';
|
answerSection.style.display = 'none';
|
||||||
@@ -183,10 +236,12 @@ resetGameBtn.addEventListener('click', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function resetForNextQuestion() {
|
function resetForNextQuestion() {
|
||||||
|
speechSynthesis.cancel();
|
||||||
database.ref('gameState').update({
|
database.ref('gameState').update({
|
||||||
buzzerActive: false,
|
buzzerActive: false,
|
||||||
buzzer: null,
|
buzzer: null,
|
||||||
playerAnswer: null
|
playerAnswer: null,
|
||||||
|
isReading: false
|
||||||
});
|
});
|
||||||
|
|
||||||
buzzedPlayer.textContent = '';
|
buzzedPlayer.textContent = '';
|
||||||
|
|||||||
15
player.js
15
player.js
@@ -33,9 +33,15 @@ database.ref('gameState').on('value', (snapshot) => {
|
|||||||
playerScore.textContent = state.scores[`player${playerId}`];
|
playerScore.textContent = state.scores[`player${playerId}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update question
|
// Hide question text - players only hear it
|
||||||
if (state.currentQuestion) {
|
if (state.isReading) {
|
||||||
questionDisplay.textContent = state.currentQuestion.tossup_question;
|
questionDisplay.textContent = '🔊 Listen to the question...';
|
||||||
|
questionDisplay.style.display = 'block';
|
||||||
|
} else if (state.currentQuestion) {
|
||||||
|
questionDisplay.textContent = 'Question loaded. Waiting for moderator to read...';
|
||||||
|
questionDisplay.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
questionDisplay.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update buzzer state
|
// Update buzzer state
|
||||||
@@ -58,7 +64,8 @@ database.ref('gameState').on('value', (snapshot) => {
|
|||||||
answerContainer.style.display = 'block';
|
answerContainer.style.display = 'block';
|
||||||
answerInput.focus();
|
answerInput.focus();
|
||||||
} else if (state.buzzer && state.buzzer.playerId) {
|
} else if (state.buzzer && state.buzzer.playerId) {
|
||||||
statusMessage.textContent = `${state.buzzer.playerId} buzzed in`;
|
const buzzedPlayerNum = state.buzzer.playerId.replace('player', '');
|
||||||
|
statusMessage.textContent = `Player ${buzzedPlayerNum} buzzed in`;
|
||||||
buzzer.classList.add('locked');
|
buzzer.classList.add('locked');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user