modified: templates/quiz_buzzer_multiplayer.html
This commit is contained in:
@@ -243,6 +243,9 @@
|
|||||||
let decayRate = parseInt(localStorage.getItem('buzzer_decay_rate')) || 50;
|
let decayRate = parseInt(localStorage.getItem('buzzer_decay_rate')) || 50;
|
||||||
let gameStarted = false;
|
let gameStarted = false;
|
||||||
let currentBuzzer = null;
|
let currentBuzzer = null;
|
||||||
|
let buzzedPlayers = []; // Liste der Spieler die schon gebuzzert haben
|
||||||
|
let canBuzz = false; // Kann man buzzern?
|
||||||
|
let pendingPoints = 0; // Punkte die noch nicht gezählt wurden
|
||||||
|
|
||||||
// Lade Team-Namen und Anzahl
|
// Lade Team-Namen und Anzahl
|
||||||
const teamCount = parseInt(localStorage.getItem('team_count')) || 4;
|
const teamCount = parseInt(localStorage.getItem('team_count')) || 4;
|
||||||
@@ -256,6 +259,14 @@
|
|||||||
{ id: 4, name: teamNames[3] || 'Spieler 4', score: {{ player_scores[3] if player_scores else 0 }} }
|
{ id: 4, name: teamNames[3] || 'Spieler 4', score: {{ player_scores[3] if player_scores else 0 }} }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Leertaste zum Buzzern
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.code === 'Space' && canBuzz && !currentBuzzer) {
|
||||||
|
e.preventDefault();
|
||||||
|
triggerBuzz();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
window.onSpotifyWebPlaybackSDKReady = () => {
|
window.onSpotifyWebPlaybackSDKReady = () => {
|
||||||
const token = '{{ access_token }}';
|
const token = '{{ access_token }}';
|
||||||
const player = new Spotify.Player({
|
const player = new Spotify.Player({
|
||||||
@@ -330,6 +341,7 @@
|
|||||||
gameStarted = true;
|
gameStarted = true;
|
||||||
document.getElementById('startButton').classList.add('disabled');
|
document.getElementById('startButton').classList.add('disabled');
|
||||||
document.getElementById('startButton').style.cursor = 'wait';
|
document.getElementById('startButton').style.cursor = 'wait';
|
||||||
|
document.getElementById('buzzerHint').style.display = 'block';
|
||||||
|
|
||||||
const device_id = window.deviceId;
|
const device_id = window.deviceId;
|
||||||
const startPosition = getOption('startPosition', 'start');
|
const startPosition = getOption('startPosition', 'start');
|
||||||
@@ -344,21 +356,19 @@
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('Play command sent');
|
console.log('Play command sent');
|
||||||
document.getElementById('startButton').style.display = 'none';
|
document.getElementById('startButton').style.display = 'none';
|
||||||
// Aktiviere Buzzer
|
canBuzz = true;
|
||||||
for (let i = 1; i <= 4; i++) {
|
|
||||||
document.getElementById(`buzzer${i}`).classList.remove('disabled');
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => console.error('Error starting playback:', error));
|
.catch(error => console.error('Error starting playback:', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
function startBuzzerTimer() {
|
function startBuzzerTimer() {
|
||||||
startTime = Date.now();
|
startTime = Date.now();
|
||||||
|
canBuzz = true;
|
||||||
updateTimer();
|
updateTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTimer() {
|
function updateTimer() {
|
||||||
if (currentBuzzer) return;
|
if (!canBuzz && currentBuzzer) return;
|
||||||
|
|
||||||
const elapsed = (Date.now() - startTime) / 1000;
|
const elapsed = (Date.now() - startTime) / 1000;
|
||||||
const timerDisplay = document.getElementById('buzzerTimer');
|
const timerDisplay = document.getElementById('buzzerTimer');
|
||||||
@@ -394,11 +404,11 @@
|
|||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buzz(playerId) {
|
function triggerBuzz() {
|
||||||
if (currentBuzzer !== null) return;
|
if (currentBuzzer !== null) return;
|
||||||
if (!gameStarted || !startTime) return;
|
if (!canBuzz) return;
|
||||||
|
|
||||||
currentBuzzer = playerId;
|
canBuzz = false; // Verhindere weitere Buzzes
|
||||||
buzzTime = Date.now();
|
buzzTime = Date.now();
|
||||||
const elapsed = (buzzTime - startTime) / 1000;
|
const elapsed = (buzzTime - startTime) / 1000;
|
||||||
|
|
||||||
@@ -408,17 +418,33 @@
|
|||||||
window.spotifyPlayer.pause();
|
window.spotifyPlayer.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Markiere Spieler
|
// Zeige Spielerauswahl
|
||||||
document.getElementById(`player${playerId}`).classList.add('buzzed');
|
document.getElementById('playerSelection').style.display = 'block';
|
||||||
document.getElementById('currentPlayer').textContent = `${players[playerId - 1].name} hat gebuzzert!`;
|
document.getElementById('currentPlayer').textContent = 'Wer hat gebuzzert?';
|
||||||
|
pendingPoints = calculatePoints(elapsed);
|
||||||
// Deaktiviere alle Buzzer
|
}
|
||||||
for (let i = 1; i <= 4; i++) {
|
|
||||||
document.getElementById(`buzzer${i}`).classList.add('disabled');
|
function selectPlayer(playerId) {
|
||||||
|
if (buzzedPlayers.includes(playerId)) {
|
||||||
|
alert('Dieser Spieler hat bereits gebuzzert!');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentBuzzer = playerId;
|
||||||
|
buzzedPlayers.push(playerId);
|
||||||
|
|
||||||
|
// Markiere Spieler
|
||||||
|
document.getElementById(`player${playerId}`).classList.add('buzzed');
|
||||||
|
document.getElementById('currentPlayer').textContent = `${players[playerId - 1].name} antwortet...`;
|
||||||
|
|
||||||
|
// Verstecke Spielerauswahl, zeige Antwortfeld
|
||||||
|
document.getElementById('playerSelection').style.display = 'none';
|
||||||
document.getElementById('answerSection').classList.add('active');
|
document.getElementById('answerSection').classList.add('active');
|
||||||
window.earnedPoints = calculatePoints(elapsed);
|
window.earnedPoints = pendingPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buzz(playerId) {
|
||||||
|
selectPlayer(playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCorrectAnswer() {
|
function setCorrectAnswer() {
|
||||||
@@ -486,6 +512,10 @@
|
|||||||
const guess = document.getElementById('answerInput').value;
|
const guess = document.getElementById('answerInput').value;
|
||||||
if (!guess) return;
|
if (!guess) return;
|
||||||
|
|
||||||
|
// Disable input während Prüfung
|
||||||
|
document.getElementById('answerInput').disabled = true;
|
||||||
|
document.querySelector('button[onclick="checkAnswer()"]').disabled = true;
|
||||||
|
|
||||||
fetch('/check_answer_buzzer', {
|
fetch('/check_answer_buzzer', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -495,7 +525,8 @@
|
|||||||
game_mode: currentGameMode,
|
game_mode: currentGameMode,
|
||||||
playlist_id: '{{ playlist_id }}',
|
playlist_id: '{{ playlist_id }}',
|
||||||
earned_points: window.earnedPoints,
|
earned_points: window.earnedPoints,
|
||||||
player_id: currentBuzzer
|
player_id: currentBuzzer,
|
||||||
|
apply_points: false // Punkte NICHT sofort anwenden
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
@@ -503,45 +534,95 @@
|
|||||||
const resultContainer = document.getElementById('resultContainer');
|
const resultContainer = document.getElementById('resultContainer');
|
||||||
|
|
||||||
if (data.correct) {
|
if (data.correct) {
|
||||||
players[currentBuzzer - 1].score += window.earnedPoints;
|
// Speichere Punkte, aber wende sie noch NICHT an
|
||||||
updateScoreboard();
|
pendingPoints = window.earnedPoints;
|
||||||
|
|
||||||
resultContainer.innerHTML = `
|
resultContainer.innerHTML = `
|
||||||
<div style="background:linear-gradient(135deg, rgba(76, 175, 80, 0.2) 0%, rgba(56, 142, 60, 0.2) 100%); padding:20px; border-radius:12px; border:2px solid #4CAF50;">
|
<div style="background:linear-gradient(135deg, rgba(76, 175, 80, 0.2) 0%, rgba(56, 142, 60, 0.2) 100%); padding:20px; border-radius:12px; border:2px solid #4CAF50;">
|
||||||
<h3 style="color:#4CAF50; margin-bottom:10px;">✅ ${i18n.correct || 'Correct'}</h3>
|
<h3 style="color:#4CAF50; margin-bottom:10px;">✅ ${i18n.correct || 'Richtig'}</h3>
|
||||||
<p style="font-size:1.5em; margin:10px 0;"><strong style="color:#1DB954;">+${window.earnedPoints}</strong> Punkte für ${players[currentBuzzer - 1].name}!</p>
|
<p style="font-size:1.5em; margin:10px 0;"><strong style="color:#1DB954;">+${window.earnedPoints}</strong> Punkte für ${players[currentBuzzer - 1].name}!</p>
|
||||||
|
<div style="margin-top:20px; padding:20px; background:rgba(15,20,25,0.8); border-radius:12px;">
|
||||||
|
<h4 style="color:#1DB954; margin-bottom:15px;">Song Info:</h4>
|
||||||
|
<p style="margin:8px 0;"><strong style="color:#1DB954;">Titel:</strong> <span style="color:#e0e0e0;">{{ track.name | clean }}</span></p>
|
||||||
|
<p style="margin:8px 0;"><strong style="color:#1DB954;">Künstler:</strong> <span style="color:#e0e0e0;">{{ track.artists[0].name | clean }}</span></p>
|
||||||
|
<p style="margin:8px 0;"><strong style="color:#1DB954;">Album:</strong> <span style="color:#e0e0e0;">{{ track.album.name | clean }}</span></p>
|
||||||
|
<a href="{{ track.external_urls.spotify }}" target="_blank" style="display:inline-block; margin-top:10px; padding:8px 16px; background:linear-gradient(135deg, #1DB954 0%, #1ed760 100%); color:#fff; text-decoration:none; border-radius:20px; font-weight:600;">Auf Spotify öffnen</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
document.getElementById('nextQuestionBtn').style.display = 'inline-block';
|
||||||
|
document.getElementById('nextQuestionBtn').onclick = function() {
|
||||||
|
// Wende Punkte beim Klick auf "Nächster Song" an
|
||||||
|
players[currentBuzzer - 1].score += pendingPoints;
|
||||||
|
updateScoreboard();
|
||||||
|
window.location.href = '/quiz/{{ playlist_id }}?mode={{ game_mode }}&buzzer=1&local_multiplayer=1';
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
|
// Minuspunkte: 25% der möglichen Punkte
|
||||||
|
const minusPoints = Math.floor(window.earnedPoints * 0.25);
|
||||||
|
pendingPoints = -minusPoints;
|
||||||
|
|
||||||
resultContainer.innerHTML = `
|
resultContainer.innerHTML = `
|
||||||
<div style="background:linear-gradient(135deg, rgba(244, 67, 54, 0.2) 0%, rgba(211, 47, 47, 0.2) 100%); padding:20px; border-radius:12px; border:2px solid #f44336;">
|
<div style="background:linear-gradient(135deg, rgba(244, 67, 54, 0.2) 0%, rgba(211, 47, 47, 0.2) 100%); padding:20px; border-radius:12px; border:2px solid #f44336;">
|
||||||
<h3 style="color:#f44336; margin-bottom:10px;">❌ ${i18n.incorrect || 'Incorrect'}</h3>
|
<h3 style="color:#f44336; margin-bottom:10px;">❌ Falsch</h3>
|
||||||
<p style="font-size:1.1em; margin:10px 0;">${i18n.your_answer || 'Your answer'}: <strong style="color:#f44336;">${guess}</strong></p>
|
<p style="font-size:1.1em; margin:10px 0;">Deine Antwort: <strong style="color:#f44336;">${guess}</strong></p>
|
||||||
<p style="font-size:1.1em; margin:10px 0;">${i18n.correct_answer || 'Correct answer'}: <strong style="color:#4CAF50;">${data.correct_answer}</strong></p>
|
<p style="font-size:1.1em; margin:10px 0;"><strong style="color:#f44336;">-${minusPoints}</strong> Punkte für ${players[currentBuzzer - 1].name}</p>
|
||||||
|
<div style="margin-top:20px;">
|
||||||
|
<button class="btn" onclick="continueGame()" style="background:linear-gradient(135deg, #FFA500 0%, #FF8C00 100%);">🔄 Weiterbuzzern</button>
|
||||||
|
<button class="btn" onclick="showSolution()" style="background:linear-gradient(135deg, #1DB954 0%, #1ed760 100%);">📝 Lösung anzeigen</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.comparison) {
|
|
||||||
resultContainer.innerHTML += `
|
|
||||||
<div style="margin-top:20px; padding:20px; background:rgba(15,20,25,0.8); border-radius:12px;">
|
|
||||||
<h4 style="color:#1DB954; margin-bottom:15px;">${i18n.song_info || 'Song Info'}:</h4>
|
|
||||||
<p style="margin:8px 0;"><strong style="color:#1DB954;">${i18n.title || 'Title'}:</strong> <span style="color:#e0e0e0;">{{ track.name | clean }}</span></p>
|
|
||||||
<p style="margin:8px 0;"><strong style="color:#1DB954;">${i18n.artist || 'Artist'}:</strong> <span style="color:#e0e0e0;">{{ track.artists[0].name | clean }}</span></p>
|
|
||||||
<p style="margin:8px 0;"><strong style="color:#1DB954;">${i18n.album || 'Album'}:</strong> <span style="color:#e0e0e0;">{{ track.album.name | clean }}</span></p>
|
|
||||||
<p style="margin:8px 0;"><strong style="color:#1DB954;">${i18n.year || 'Year'}:</strong> <span style="color:#e0e0e0;">{{ track.album.release_date[:4] }}</span></p>
|
|
||||||
<a href="{{ track.external_urls.spotify }}" target="_blank" style="display:inline-block; margin-top:10px; padding:8px 16px; background:linear-gradient(135deg, #1DB954 0%, #1ed760 100%); color:#fff; text-decoration:none; border-radius:20px; font-weight:600; transition:all 0.3s ease;">${i18n.open_on_spotify || 'Open on Spotify'}</a>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('nextQuestionBtn').style.display = 'inline-block';
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error checking answer:', error);
|
console.error('Error checking answer:', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function continueGame() {
|
||||||
|
// Wende Minuspunkte an
|
||||||
|
players[currentBuzzer - 1].score += pendingPoints;
|
||||||
|
updateScoreboard();
|
||||||
|
|
||||||
|
// Reset für nächsten Buzzer
|
||||||
|
document.getElementById('answerSection').classList.remove('active');
|
||||||
|
document.getElementById('answerInput').value = '';
|
||||||
|
document.getElementById('answerInput').disabled = false;
|
||||||
|
document.querySelector('button[onclick="checkAnswer()"]').disabled = false;
|
||||||
|
document.getElementById('resultContainer').innerHTML = '';
|
||||||
|
document.getElementById(`player${currentBuzzer}`).classList.remove('buzzed');
|
||||||
|
|
||||||
|
currentBuzzer = null;
|
||||||
|
canBuzz = true;
|
||||||
|
|
||||||
|
// Musik weiterspielen
|
||||||
|
if (window.spotifyPlayer) {
|
||||||
|
window.spotifyPlayer.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer weiterlaufen lassen
|
||||||
|
buzzTimer = requestAnimationFrame(updateTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSolution() {
|
||||||
|
// Wende Minuspunkte an
|
||||||
|
players[currentBuzzer - 1].score += pendingPoints;
|
||||||
|
updateScoreboard();
|
||||||
|
|
||||||
|
const resultContainer = document.getElementById('resultContainer');
|
||||||
|
resultContainer.innerHTML = `
|
||||||
|
<div style="margin-top:20px; padding:20px; background:rgba(15,20,25,0.8); border-radius:12px; border:2px solid #1DB954;">
|
||||||
|
<h4 style="color:#1DB954; margin-bottom:15px;">Lösung:</h4>
|
||||||
|
<p style="margin:8px 0;"><strong style="color:#1DB954;">Titel:</strong> <span style="color:#e0e0e0;">{{ track.name | clean }}</span></p>
|
||||||
|
<p style="margin:8px 0;"><strong style="color:#1DB954;">Künstler:</strong> <span style="color:#e0e0e0;">{{ track.artists[0].name | clean }}</span></p>
|
||||||
|
<p style="margin:8px 0;"><strong style="color:#1DB954;">Album:</strong> <span style="color:#e0e0e0;">{{ track.album.name | clean }}</span></p>
|
||||||
|
<a href="{{ track.external_urls.spotify }}" target="_blank" style="display:inline-block; margin-top:10px; padding:8px 16px; background:linear-gradient(135deg, #1DB954 0%, #1ed760 100%); color:#fff; text-decoration:none; border-radius:20px; font-weight:600;">Auf Spotify öffnen</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.getElementById('nextQuestionBtn').style.display = 'inline-block';
|
||||||
|
}
|
||||||
|
|
||||||
function switchGameMode(mode) {
|
function switchGameMode(mode) {
|
||||||
window.location.href = `/reset_quiz/{{ playlist_id }}?next_mode=${mode}&buzzer=1&local_multiplayer=1`;
|
window.location.href = `/reset_quiz/{{ playlist_id }}?next_mode=${mode}&buzzer=1&local_multiplayer=1`;
|
||||||
}
|
}
|
||||||
@@ -592,23 +673,35 @@
|
|||||||
|
|
||||||
<div class="current-player" id="currentPlayer">Drücke START um zu beginnen</div>
|
<div class="current-player" id="currentPlayer">Drücke START um zu beginnen</div>
|
||||||
|
|
||||||
<!-- Buzzer Buttons -->
|
<!-- Buzzer Hint -->
|
||||||
<div class="buzzer-grid">
|
<div id="buzzerHint" style="text-align:center; font-size:1.5em; color:#1DB954; margin:20px 0; display:none;">
|
||||||
<button class="player-buzzer start" id="startButton" onclick="startGame()">
|
⌨️ Drücke LEERTASTE zum Buzzern!
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Player Selection (versteckt bis gebuzzert) -->
|
||||||
|
<div id="playerSelection" style="display:none; text-align:center; margin:30px 0;">
|
||||||
|
<h3 style="margin-bottom:20px; color:#1DB954;">Wer hat gebuzzert?</h3>
|
||||||
|
<div class="buzzer-grid">
|
||||||
|
<button class="player-buzzer" id="selectPlayer1" onclick="selectPlayer(1)">
|
||||||
|
Spieler 1
|
||||||
|
</button>
|
||||||
|
<button class="player-buzzer" id="selectPlayer2" onclick="selectPlayer(2)">
|
||||||
|
Spieler 2
|
||||||
|
</button>
|
||||||
|
<button class="player-buzzer" id="selectPlayer3" onclick="selectPlayer(3)">
|
||||||
|
Spieler 3
|
||||||
|
</button>
|
||||||
|
<button class="player-buzzer" id="selectPlayer4" onclick="selectPlayer(4)">
|
||||||
|
Spieler 4
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- START Button -->
|
||||||
|
<div style="text-align:center; margin:30px 0;">
|
||||||
|
<button class="player-buzzer start" id="startButton" onclick="startGame()" style="width:300px; height:200px; font-size:2em;">
|
||||||
▶️<br>START
|
▶️<br>START
|
||||||
</button>
|
</button>
|
||||||
<button class="player-buzzer disabled" id="buzzer1" onclick="buzz(1)">
|
|
||||||
🔴<br>Spieler 1
|
|
||||||
</button>
|
|
||||||
<button class="player-buzzer disabled" id="buzzer2" onclick="buzz(2)">
|
|
||||||
🔴<br>Spieler 2
|
|
||||||
</button>
|
|
||||||
<button class="player-buzzer disabled" id="buzzer3" onclick="buzz(3)">
|
|
||||||
🔴<br>Spieler 3
|
|
||||||
</button>
|
|
||||||
<button class="player-buzzer disabled" id="buzzer4" onclick="buzz(4)">
|
|
||||||
🔴<br>Spieler 4
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Answer Section -->
|
<!-- Answer Section -->
|
||||||
|
|||||||
Reference in New Issue
Block a user