modified: templates/quiz_buzzer_multiplayer.html

modified:   templates/team_setup.html
This commit is contained in:
Simon
2025-12-13 19:15:52 +01:00
parent 5435901143
commit cc07d21138
2 changed files with 298 additions and 5 deletions

View File

@@ -255,6 +255,8 @@
// 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;
const teamNames = JSON.parse(localStorage.getItem('team_names') || '["Spieler 1", "Spieler 2", "Spieler 3", "Spieler 4"]'); const teamNames = JSON.parse(localStorage.getItem('team_names') || '["Spieler 1", "Spieler 2", "Spieler 3", "Spieler 4"]');
const buzzerMode = localStorage.getItem('buzzer_mode') || 'central';
const keyMappings = JSON.parse(localStorage.getItem('key_mappings') || '{"1":"Q","2":"W","3":"E","4":"R"}');
// Spieler-Daten // Spieler-Daten
let players = [ let players = [
@@ -264,15 +266,39 @@
{ 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 Starten/Buzzern, aber nicht in Eingabefeldern // Tastatur-Events für beide Modi
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
const active = document.activeElement; const active = document.activeElement;
const isInput = active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA'); const isInput = active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA');
if (e.code === 'Space' && !isInput) { if (isInput) return; // Keine Aktionen in Eingabefeldern
e.preventDefault();
const key = e.key.toUpperCase();
if (!gameStarted) { if (!gameStarted) {
// Im persönlichen Modus: Nur die erste Spieler-Taste startet
if (buzzerMode === 'personal' && key === keyMappings['1']) {
e.preventDefault();
startGame(); startGame();
}
// Im zentralen Modus: Leertaste startet
else if (buzzerMode === 'central' && e.code === 'Space') {
e.preventDefault();
startGame();
}
} else if (canBuzz && !currentBuzzer) { } else if (canBuzz && !currentBuzzer) {
// Persönlicher Modus: Jede Spieler-Taste buzzert direkt
if (buzzerMode === 'personal') {
for (let i = 1; i <= teamCount; i++) {
if (key === keyMappings[i.toString()] && !buzzedPlayers.includes(i)) {
e.preventDefault();
triggerBuzzPersonal(i);
return;
}
}
}
// Zentraler Modus: Leertaste -> Spielerauswahl
else if (buzzerMode === 'central' && e.code === 'Space') {
e.preventDefault();
triggerBuzz(); triggerBuzz();
} }
} }
@@ -339,6 +365,18 @@
} }
} }
} }
// Update Buzzer-Hint basierend auf Modus
const hintElement = document.getElementById('buzzerHint');
if (buzzerMode === 'personal') {
let hintText = '⌨️ Buzzer-Tasten: ';
for (let i = 1; i <= teamCount; i++) {
hintText += `${players[i-1].name}=[${keyMappings[i.toString()]}] `;
}
hintElement.innerHTML = hintText;
} else {
hintElement.innerHTML = '⌨️ Drücke LEERTASTE zum Buzzern!';
}
} }
function updateScoreboard() { function updateScoreboard() {
@@ -451,6 +489,49 @@
pendingPoints = points; pendingPoints = points;
} }
function triggerBuzzPersonal(playerId) {
if (currentBuzzer !== null) return;
if (!canBuzz) return;
if (buzzedPlayers.includes(playerId)) {
alert(`${players[playerId - 1].name} hat bereits gebuzzert!`);
return;
}
canBuzz = false; // Verhindere weitere Buzzes
buzzTime = Date.now();
const elapsed = (buzzTime - startTime) / 1000;
// Stoppe Timer-Updates
cancelAnimationFrame(buzzTimer);
// Friere Timer-Anzeige ein
const timerDisplay = document.getElementById('buzzerTimer');
const pointsDisplay = document.getElementById('pointsDisplay');
timerDisplay.textContent = elapsed.toFixed(2) + 's';
const points = calculatePoints(elapsed);
pointsDisplay.textContent = points + ' Punkte';
// Speichere die Zeit beim Buzzern für spätere Berechnung
window.pausedAt = Date.now();
if (window.spotifyPlayer) {
window.spotifyPlayer.pause();
}
// Direkt Spieler setzen (keine Auswahl nötig)
currentBuzzer = playerId;
buzzedPlayers.push(playerId);
pendingPoints = points;
// Markiere Spieler
document.getElementById(`player${playerId}`).classList.add('buzzed');
document.getElementById('currentPlayer').textContent = `${players[playerId - 1].name} antwortet...`;
// Zeige Antwortfeld direkt (keine Spielerauswahl)
document.getElementById('answerSection').classList.add('active');
window.earnedPoints = pendingPoints;
}
function selectPlayer(playerId) { function selectPlayer(playerId) {
if (buzzedPlayers.includes(playerId)) { if (buzzedPlayers.includes(playerId)) {
alert('Dieser Spieler hat bereits gebuzzert!'); alert('Dieser Spieler hat bereits gebuzzert!');
@@ -668,6 +749,14 @@
gracePeriod = parseInt(localStorage.getItem('buzzer_grace_period')) || 5; gracePeriod = parseInt(localStorage.getItem('buzzer_grace_period')) || 5;
decayRate = parseInt(localStorage.getItem('buzzer_decay_rate')) || 50; decayRate = parseInt(localStorage.getItem('buzzer_decay_rate')) || 50;
document.getElementById('pointsDisplay').textContent = maxPoints + ' Punkte'; document.getElementById('pointsDisplay').textContent = maxPoints + ' Punkte';
// Update Starttext basierend auf Modus
const currentPlayerText = document.getElementById('currentPlayer');
if (buzzerMode === 'personal') {
currentPlayerText.textContent = `Drücke [${keyMappings['1']}] zum Starten`;
} else {
currentPlayerText.textContent = 'Drücke LEERTASTE zum Starten';
}
}; };
</script> </script>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">

View File

@@ -135,9 +135,149 @@
text-align: center; text-align: center;
margin-top: 30px; margin-top: 30px;
} }
.buzzer-mode-section {
margin-bottom: 30px;
padding: 20px;
background: rgba(15, 20, 25, 0.5);
border-radius: 15px;
border: 2px solid rgba(29, 185, 84, 0.2);
}
.mode-label {
font-size: 1.2em;
margin-bottom: 15px;
color: #e0e0e0;
text-align: center;
}
.mode-buttons {
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.mode-btn {
padding: 12px 25px;
border-radius: 12px;
border: 2px solid rgba(29, 185, 84, 0.3);
background: rgba(15, 20, 25, 0.8);
color: #e0e0e0;
font-size: 1em;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.mode-btn:hover {
border-color: #1DB954;
transform: translateY(-2px);
}
.mode-btn.active {
background: linear-gradient(135deg, #1DB954 0%, #1ed760 100%);
border-color: #1DB954;
color: white;
box-shadow: 0 4px 15px rgba(29, 185, 84, 0.5);
}
.key-mapping-section {
display: none;
margin-top: 20px;
}
.key-mapping-section.active {
display: block;
}
.key-input-group {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 15px;
}
.key-label {
flex: 1;
font-size: 1em;
color: #e0e0e0;
}
.key-input {
flex: 1;
padding: 10px 15px;
border-radius: 8px;
border: 2px solid rgba(29, 185, 84, 0.3);
background: rgba(15, 20, 25, 0.9);
color: #1DB954;
font-size: 1.1em;
font-weight: bold;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
.key-input:focus {
outline: none;
border-color: #1DB954;
box-shadow: 0 0 10px rgba(29, 185, 84, 0.5);
}
.key-input.disabled {
opacity: 0.4;
cursor: not-allowed;
}
.info-text {
font-size: 0.9em;
color: #999;
text-align: center;
margin-top: 10px;
font-style: italic;
}
</style> </style>
<script> <script>
let teamCount = 2; let teamCount = 2;
let buzzerMode = 'central'; // 'central' oder 'personal'
let keyMappings = {1: 'Q', 2: 'W', 3: 'E', 4: 'R'};
function setBuzzerMode(mode) {
buzzerMode = mode;
// Update aktive Button
document.querySelectorAll('.mode-btn').forEach(btn => {
btn.classList.remove('active');
});
document.getElementById(`mode-${mode}`).classList.add('active');
// Zeige/Verstecke Tastenauswahl
const keySection = document.getElementById('keyMappingSection');
if (mode === 'personal') {
keySection.classList.add('active');
} else {
keySection.classList.remove('active');
}
localStorage.setItem('buzzer_mode', mode);
}
function setKeyForPlayer(playerNum) {
const input = document.getElementById(`key${playerNum}`);
if (input.disabled) return;
input.value = 'Drücke eine Taste...';
input.focus();
const handleKeyPress = (e) => {
e.preventDefault();
const key = e.key.toUpperCase();
// Prüfe ob Taste bereits vergeben
const usedKeys = Object.values(keyMappings).filter((v, k) => k !== playerNum);
if (usedKeys.includes(key)) {
alert(`Die Taste "${key}" ist bereits vergeben!`);
input.value = keyMappings[playerNum];
return;
}
keyMappings[playerNum] = key;
input.value = key;
input.blur();
document.removeEventListener('keydown', handleKeyPress);
};
document.addEventListener('keydown', handleKeyPress);
}
let buzzerMode = 'central'; // 'central' oder 'personal'
let keyMappings = {1: 'Q', 2: 'W', 3: 'E', 4: 'R'};
function setTeamCount(count) { function setTeamCount(count) {
teamCount = count; teamCount = count;
@@ -151,12 +291,22 @@
// Enable/Disable Inputs // Enable/Disable Inputs
for (let i = 1; i <= 4; i++) { for (let i = 1; i <= 4; i++) {
const input = document.getElementById(`team${i}`); const input = document.getElementById(`team${i}`);
const keyInput = document.getElementById(`key${i}`);
if (i <= count) { if (i <= count) {
input.disabled = false; input.disabled = false;
input.classList.remove('disabled'); input.classList.remove('disabled');
if (keyInput) {
keyInput.disabled = false;
keyInput.classList.remove('disabled');
}
} else { } else {
input.disabled = true; input.disabled = true;
input.classList.add('disabled'); input.classList.add('disabled');
if (keyInput) {
keyInput.disabled = true;
keyInput.classList.add('disabled');
}
} }
} }
@@ -174,12 +324,16 @@
// Speichere in localStorage // Speichere in localStorage
localStorage.setItem('team_names', JSON.stringify(teams)); localStorage.setItem('team_names', JSON.stringify(teams));
localStorage.setItem('team_count', teamCount); localStorage.setItem('team_count', teamCount);
localStorage.setItem('buzzer_mode', buzzerMode);
localStorage.setItem('key_mappings', JSON.stringify(keyMappings));
} }
window.onload = function() { window.onload = function() {
// Lade gespeicherte Werte // Lade gespeicherte Werte
const savedCount = parseInt(localStorage.getItem('team_count')) || 2; const savedCount = parseInt(localStorage.getItem('team_count')) || 2;
const savedNames = JSON.parse(localStorage.getItem('team_names') || '[]'); const savedNames = JSON.parse(localStorage.getItem('team_names') || '[]');
const savedMode = localStorage.getItem('buzzer_mode') || 'central';
const savedKeys = JSON.parse(localStorage.getItem('key_mappings') || '{"1":"Q","2":"W","3":"E","4":"R"}');
// Setze Team-Anzahl // Setze Team-Anzahl
setTeamCount(savedCount); setTeamCount(savedCount);
@@ -190,6 +344,18 @@
document.getElementById(`team${index + 1}`).value = name; document.getElementById(`team${index + 1}`).value = name;
} }
}); });
// Setze Buzzer-Modus
setBuzzerMode(savedMode);
// Setze Tasten
keyMappings = savedKeys;
for (let i = 1; i <= 4; i++) {
const keyInput = document.getElementById(`key${i}`);
if (keyInput) {
keyInput.value = keyMappings[i] || 'Q';
}
}
}; };
</script> </script>
</head> </head>
@@ -206,6 +372,44 @@
</div> </div>
</div> </div>
<div class="buzzer-mode-section">
<div class="mode-label">🎮 Buzzer-Modus</div>
<div class="mode-buttons">
<button class="mode-btn active" id="mode-central" onclick="setBuzzerMode('central')">
Zentraler Modus<br>
<span style="font-size:0.85em; font-weight:normal;">(Eine Taste für alle)</span>
</button>
<button class="mode-btn" id="mode-personal" onclick="setBuzzerMode('personal')">
Persönlicher Modus<br>
<span style="font-size:0.85em; font-weight:normal;">(Eigene Taste pro Spieler)</span>
</button>
</div>
<div class="key-mapping-section" id="keyMappingSection">
<div class="info-text">Klicke auf ein Feld und drücke die gewünschte Taste</div>
<div class="key-input-group">
<div class="key-label">Team 1 Buzzer:</div>
<input type="text" class="key-input" id="key1" value="Q" readonly onclick="setKeyForPlayer(1)">
</div>
<div class="key-input-group">
<div class="key-label">Team 2 Buzzer:</div>
<input type="text" class="key-input" id="key2" value="W" readonly onclick="setKeyForPlayer(2)">
</div>
<div class="key-input-group">
<div class="key-label">Team 3 Buzzer:</div>
<input type="text" class="key-input disabled" id="key3" value="E" readonly onclick="setKeyForPlayer(3)" disabled>
</div>
<div class="key-input-group">
<div class="key-label">Team 4 Buzzer:</div>
<input type="text" class="key-input disabled" id="key4" value="R" readonly onclick="setKeyForPlayer(4)" disabled>
</div>
</div>
</div>
<div class="teams-section"> <div class="teams-section">
<div class="team-input-group"> <div class="team-input-group">
<label class="team-label" for="team1">Team 1</label> <label class="team-label" for="team1">Team 1</label>