feat: implement answer submission and scoring system for guessing game
This commit is contained in:
@@ -125,6 +125,20 @@
|
||||
<input id="volumeSlider" type="range" min="0" max="1" step="0.01" class="w-40 accent-indigo-600" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Answer form: everyone can try to guess title & artist for a coin -->
|
||||
<form id="answerForm" class="mt-4 w-full flex flex-col md:flex-row gap-2 md:items-end">
|
||||
<label class="flex-1 text-sm">
|
||||
<span class="text-slate-700 dark:text-slate-300">Titel</span>
|
||||
<input id="guessTitle" name="title" placeholder="Songtitel" autocomplete="off" class="mt-1 w-full h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3" />
|
||||
</label>
|
||||
<label class="flex-1 text-sm">
|
||||
<span class="text-slate-700 dark:text-slate-300">Künstler</span>
|
||||
<input id="guessArtist" name="artist" placeholder="Künstler" autocomplete="off" class="mt-1 w-full h-10 rounded-lg border border-slate-300 dark:border-slate-700 bg-white/80 dark:bg-slate-800 px-3" />
|
||||
</label>
|
||||
<button id="submitAnswer" type="submit" class="h-10 px-4 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white font-medium">Abschicken</button>
|
||||
</form>
|
||||
<div id="answerResult" class="mt-1 text-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex flex-wrap items-center gap-3">
|
||||
@@ -147,7 +161,6 @@
|
||||
|
||||
<div class="flex items-center gap-3 text-slate-700 dark:text-slate-300">
|
||||
Tokens: <span id="tokens" class="font-semibold">0</span>
|
||||
<button id="earnToken" class="h-9 px-3 rounded-lg border border-slate-300 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800 text-sm">+1 (Titel & Künstler richtig)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,6 +38,11 @@ export const $leaveRoom = el('leaveRoom');
|
||||
export const $earnToken = el('earnToken');
|
||||
export const $dashboardList = el('dashboardList');
|
||||
export const $toast = el('toast');
|
||||
// Answer form elements
|
||||
export const $answerForm = el('answerForm');
|
||||
export const $guessTitle = el('guessTitle');
|
||||
export const $guessArtist = el('guessArtist');
|
||||
export const $answerResult = el('answerResult');
|
||||
|
||||
export function showLobby() { $lobby.classList.remove('hidden'); $room.classList.add('hidden'); }
|
||||
export function showRoom() { $lobby.classList.add('hidden'); $room.classList.remove('hidden'); }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { $audio, $copyRoomCode, $leaveRoom, $nameDisplay, $nameLobby, $npArtist, $npTitle, $npYear, $pauseBtn, $placeBtn, $readyChk, $roomId, $roomCode, $slotSelect, $startGame, $volumeSlider, $playBtn, $nextBtn, $createRoom, $joinRoom, $lobby, $room, $setNameLobby } from './dom.js';
|
||||
import { $audio, $copyRoomCode, $leaveRoom, $nameDisplay, $nameLobby, $npArtist, $npTitle, $npYear, $pauseBtn, $placeBtn, $readyChk, $roomId, $roomCode, $slotSelect, $startGame, $volumeSlider, $playBtn, $nextBtn, $createRoom, $joinRoom, $lobby, $room, $setNameLobby, $guessTitle, $guessArtist, $answerResult } from './dom.js';
|
||||
import { state } from './state.js';
|
||||
import { connectWS, sendMsg } from './ws.js';
|
||||
import { renderRoom } from './render.js';
|
||||
@@ -44,6 +44,10 @@ function handlePlayTrack(msg) {
|
||||
$npTitle.textContent = '???';
|
||||
$npArtist.textContent = '';
|
||||
$npYear.textContent = '';
|
||||
// reset answer UI
|
||||
if ($guessTitle) $guessTitle.value = '';
|
||||
if ($guessArtist) $guessArtist.value = '';
|
||||
if ($answerResult) { $answerResult.textContent=''; $answerResult.className='mt-1 text-sm'; }
|
||||
try {
|
||||
$audio.preload = 'auto';
|
||||
} catch {}
|
||||
@@ -151,6 +155,25 @@ function onMessage(ev) {
|
||||
case 'game_ended':
|
||||
handleGameEnded(msg);
|
||||
break;
|
||||
case 'answer_result': {
|
||||
if ($answerResult) {
|
||||
if (!msg.ok) {
|
||||
$answerResult.textContent = '⛔ Eingabe ungültig oder gerade nicht möglich';
|
||||
$answerResult.className = 'mt-1 text-sm text-rose-600';
|
||||
} else {
|
||||
const okBoth = !!(msg.correctTitle && msg.correctArtist);
|
||||
const parts = [];
|
||||
parts.push(msg.correctTitle ? 'Titel ✓' : 'Titel ✗');
|
||||
parts.push(msg.correctArtist ? 'Künstler ✓' : 'Künstler ✗');
|
||||
let coin = '';
|
||||
if (msg.awarded) coin = ' +1 Token';
|
||||
else if (msg.alreadyAwarded) coin = ' (bereits erhalten)';
|
||||
$answerResult.textContent = `${parts.join(' · ')}${coin}`;
|
||||
$answerResult.className = okBoth ? 'mt-1 text-sm text-emerald-600' : 'mt-1 text-sm text-amber-600';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -241,6 +264,20 @@ function wireUi() {
|
||||
});
|
||||
$roomId.style.cursor = 'pointer';
|
||||
}
|
||||
// Answer submit
|
||||
const form = document.getElementById('answerForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
const title = ($guessTitle?.value || '').trim();
|
||||
const artist = ($guessArtist?.value || '').trim();
|
||||
if (!title || !artist) {
|
||||
if ($answerResult) { $answerResult.textContent = 'Bitte Titel und Künstler eingeben'; $answerResult.className = 'mt-1 text-sm text-amber-600'; }
|
||||
return;
|
||||
}
|
||||
sendMsg({ type: 'submit_answer', guess: { title, artist } });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// boot
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { state } from './state.js';
|
||||
import { badgeColorForYear } from '../utils/colors.js';
|
||||
import { $dashboardList, $guesser, $lobby, $nameDisplay, $nextArea, $np, $placeArea, $readyChk, $revealBanner, $room, $roomId, $slotSelect, $startGame, $status, $timeline, $tokens } from './dom.js';
|
||||
import { $answerForm, $answerResult, $dashboardList, $guesser, $lobby, $nameDisplay, $nextArea, $np, $placeArea, $readyChk, $revealBanner, $room, $roomId, $slotSelect, $startGame, $status, $timeline, $tokens } from './dom.js';
|
||||
|
||||
export function renderRoom(room) {
|
||||
state.room = room; if (!room) { $lobby.classList.remove('hidden'); $room.classList.add('hidden'); return; }
|
||||
@@ -87,6 +87,10 @@ export function renderRoom(room) {
|
||||
if ($revealBanner) { const inReveal = room.state.phase === 'reveal'; if (!inReveal) { $revealBanner.className = 'hidden'; $revealBanner.textContent=''; } }
|
||||
const canNext = room.state.status==='playing' && room.state.phase==='reveal' && (isHost || room.state.currentGuesser===state.playerId);
|
||||
if ($nextArea) $nextArea.classList.toggle('hidden', !canNext);
|
||||
// Answer form visible during guess phase while a track is active
|
||||
const showAnswer = room.state.status==='playing' && room.state.phase==='guess' && !!room.state.currentTrack;
|
||||
if ($answerForm) $answerForm.classList.toggle('hidden', !showAnswer);
|
||||
if ($answerResult && !showAnswer) { $answerResult.textContent=''; $answerResult.className='mt-1 text-sm'; }
|
||||
}
|
||||
|
||||
export function shortName(id) {
|
||||
|
||||
Reference in New Issue
Block a user