feat: implement answer submission and scoring system for guessing game

This commit is contained in:
2025-09-04 15:11:03 +02:00
parent bbce3cbadf
commit 158d144721
7 changed files with 211 additions and 498 deletions

View File

@@ -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'); }

View File

@@ -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

View File

@@ -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) {