- editor.html: 5-step game editor with live Python code split-screen - game.html: playable board game engine - codegen.js: Python live-code generator (extracted from editor) - logo.png: PH Weingarten logo (extracted from Base64) - minigames/_api.js: shared Mini-Game API (makeCanvas, onResult, helpers) - 12 Mini-Games as individual modules (launch + preview API): snake, flappy, memory, quiz, reaction, basketball, catch, maze, simon, typing, puzzle, spotdiff - README.md with architecture docs and deployment guide Refactoring highlights: - editor.html: 251 KB → 102 KB (-59%) - Mini-games fully decoupled, each ~100-200 lines - All 12 games now have working launch() + preview() - Maze uses recursive backtracker algorithm - spotdiff uses canvas-drawn scene with 5 differences
111 lines
4.3 KiB
JavaScript
111 lines
4.3 KiB
JavaScript
/**
|
|
* minigames/quiz.js
|
|
* ❓ Quiz — Beantworte die Frage richtig!
|
|
*/
|
|
window.MG_quiz = (function() {
|
|
|
|
const ID = 'quiz';
|
|
const EMOJI = '❓';
|
|
const NAME = 'Quiz';
|
|
const DESC = 'Beantworte die Frage richtig!';
|
|
const CONTROLS = 'Mausklick / Tippen';
|
|
const MULTI = 99; // unbegrenzt, je Quiz-Feld eine Frage
|
|
|
|
function run(wrap, W, H, cfg, onDone) {
|
|
const theme = cfg.theme || { primary: '#f59e0b' };
|
|
const quizData = cfg.quizData || [];
|
|
const fieldIdx = cfg.fieldIndex ?? null;
|
|
|
|
// Passende Frage suchen
|
|
let q = null;
|
|
if (fieldIdx !== null) q = quizData.find(d => d.fieldIndex === fieldIdx);
|
|
if (!q && quizData.length > 0) q = quizData[Math.floor(Math.random() * quizData.length)];
|
|
|
|
// Fallback wenn keine Frage hinterlegt
|
|
if (!q || !q.question) {
|
|
wrap.innerHTML = `
|
|
<div style="text-align:center;padding:40px 20px;font-family:'Nunito',sans-serif">
|
|
<div style="font-size:3rem;margin-bottom:12px">❓</div>
|
|
<div style="color:#a7a3c2;font-size:14px;margin-bottom:20px">Keine Quiz-Frage für dieses Feld hinterlegt.</div>
|
|
<button onclick="MGAPI.onResult(true)"
|
|
style="background:linear-gradient(135deg,#7c3aed,#f5a623);color:#fff;font-family:'Fredoka One',cursive;
|
|
font-size:1rem;border:none;border-radius:10px;padding:12px 28px;cursor:pointer">
|
|
✅ Trotzdem bestanden!
|
|
</button>
|
|
</div>`;
|
|
return { stop() {} };
|
|
}
|
|
|
|
// HTML-Quiz (kein Canvas — besser lesbar)
|
|
const answers = q.answers || [];
|
|
const correct = q.correct ?? 0;
|
|
const COLORS = ['#7c3aed','#2563eb','#059669','#d97706'];
|
|
const LABELS = ['A','B','C','D'];
|
|
|
|
const container = document.createElement('div');
|
|
container.style.cssText = `
|
|
display:flex;flex-direction:column;gap:10px;padding:16px;
|
|
font-family:'Nunito',sans-serif;width:100%;box-sizing:border-box;
|
|
`;
|
|
|
|
// Frage
|
|
const qEl = document.createElement('div');
|
|
qEl.style.cssText = `
|
|
background:rgba(255,255,255,0.05);border-radius:12px;padding:16px;
|
|
color:#fff;font-size:15px;font-weight:700;line-height:1.5;text-align:center;
|
|
`;
|
|
qEl.textContent = q.question;
|
|
container.appendChild(qEl);
|
|
|
|
// Antworten
|
|
let answered = false;
|
|
answers.forEach((a, i) => {
|
|
if (!a) return;
|
|
const btn = document.createElement('button');
|
|
btn.style.cssText = `
|
|
background:${COLORS[i] || '#333'}22;border:2px solid ${COLORS[i] || '#333'}88;
|
|
color:#fff;border-radius:10px;padding:12px 16px;cursor:pointer;
|
|
font-family:'Nunito',sans-serif;font-size:13px;font-weight:700;
|
|
text-align:left;transition:all 0.2s;display:flex;gap:10px;align-items:center;
|
|
`;
|
|
btn.innerHTML = `<span style="background:${COLORS[i]};border-radius:6px;padding:2px 8px;font-size:12px">${LABELS[i]}</span> ${a}`;
|
|
btn.addEventListener('click', () => {
|
|
if (answered) return;
|
|
answered = true;
|
|
const won = (i === correct);
|
|
// Feedback-Farben
|
|
answers.forEach((_, j) => {
|
|
const b = container.querySelectorAll('button')[j];
|
|
if (!b) return;
|
|
if (j === correct) {
|
|
b.style.background = 'rgba(16,185,129,0.3)';
|
|
b.style.borderColor = '#10b981';
|
|
} else if (j === i && !won) {
|
|
b.style.background = 'rgba(239,68,68,0.3)';
|
|
b.style.borderColor = '#ef4444';
|
|
}
|
|
b.style.cursor = 'default';
|
|
});
|
|
// Ergebnis-Text
|
|
const result = document.createElement('div');
|
|
result.style.cssText = `
|
|
text-align:center;font-family:'Fredoka One',cursive;font-size:1.2rem;
|
|
padding:10px;color:${won ? '#10b981' : '#ef4444'};
|
|
`;
|
|
result.textContent = won ? '🎉 Richtig!' : `❌ Falsch! Richtig wäre: ${answers[correct]}`;
|
|
container.appendChild(result);
|
|
setTimeout(() => onDone(won), 1400);
|
|
});
|
|
container.appendChild(btn);
|
|
});
|
|
|
|
wrap.appendChild(container);
|
|
return { stop() { wrap.innerHTML = ''; } };
|
|
}
|
|
|
|
function launch(wrap, W, H, cfg) { return run(wrap, W, H, cfg, won => MGAPI.onResult(won)); }
|
|
function preview(wrap, W, H, cfg) { return run(wrap, W, H, cfg, won => MGAPI.onResult(won)); }
|
|
|
|
return { id: ID, emoji: EMOJI, name: NAME, desc: DESC, controls: CONTROLS, multi: MULTI, launch, preview };
|
|
|
|
})();
|