/** * minigames/simon.js * 🔴 Simon Says — Merke und wiederhole die Farb-Sequenz! */ window.MG_simon = (function() { const ID = 'simon'; const EMOJI = '🔴'; const NAME = 'Simon Says'; const DESC = 'Merke die Farb-Reihenfolge und tippe sie nach!'; const CONTROLS = 'Mausklick / Tippen'; const MULTI = 3; const COLORS = [ { id: 0, fill: '#22c55e', glow: 'rgba(34,197,94,0.6)', label: '🟢' }, { id: 1, fill: '#ef4444', glow: 'rgba(239,68,68,0.6)', label: '🔴' }, { id: 2, fill: '#3b82f6', glow: 'rgba(59,130,246,0.6)', label: '🔵' }, { id: 3, fill: '#f59e0b', glow: 'rgba(245,158,11,0.6)', label: '🟡' }, ]; function run(wrap, W, H, cfg, onDone) { const { canvas, ctx } = MGAPI.makeCanvas(wrap, W, H); const WIN_ROUNDS = cfg.winRounds || 4; // Layout: 2×2 Grid const PAD = 16; const GW = (W - PAD * 3) / 2; const GH = (H - PAD * 3 - 32) / 2; const POS = [ { x: PAD, y: 32 + PAD }, { x: PAD*2+GW, y: 32 + PAD }, { x: PAD, y: 32 + PAD*2 + GH }, { x: PAD*2+GW, y: 32 + PAD*2 + GH }, ]; let sequence = []; let userSeq = []; let pressedIdx = -1; let showIdx = -1; // welcher Schritt der Anzeige läuft let phase = 'show'; // 'show' | 'input' | 'result' let litIdx = -1; let stopped = false; let raf, stepTimer, endTimer, result = null; function addStep() { sequence.push(Math.floor(Math.random() * 4)); } function startShow() { phase = 'show'; litIdx = -1; showIdx = 0; userSeq = []; stepTimer = setInterval(() => { litIdx = (litIdx === -1) ? sequence[showIdx] : -1; if (litIdx === -1) { showIdx++; if (showIdx >= sequence.length) { clearInterval(stepTimer); litIdx = -1; phase = 'input'; } } }, 500); } function checkInput(colorId) { if (phase !== 'input') return; pressedIdx = colorId; setTimeout(() => { pressedIdx = -1; }, 200); userSeq.push(colorId); const idx = userSeq.length - 1; if (userSeq[idx] !== sequence[idx]) { // Falsch clearTimeout(endTimer); result = 'lose'; endTimer = setTimeout(() => onDone(false), 1000); phase = 'result'; return; } if (userSeq.length === sequence.length) { if (sequence.length >= WIN_ROUNDS) { clearTimeout(endTimer); result = 'win'; endTimer = setTimeout(() => onDone(true), 900); phase = 'result'; } else { // Nächste Runde phase = 'wait'; setTimeout(() => { addStep(); startShow(); }, 800); } } } // Klick auf Farb-Button canvas.addEventListener('click', e => { if (phase !== 'input') return; const rect = canvas.getBoundingClientRect(); const mx = (e.clientX - rect.left) * (canvas.width / rect.width / (window.devicePixelRatio || 1)); const my = (e.clientY - rect.top) * (canvas.height / rect.height / (window.devicePixelRatio || 1)); POS.forEach((p, i) => { if (mx >= p.x && mx < p.x + GW && my >= p.y && my < p.y + GH) checkInput(i); }); }); // Starten addStep(); startShow(); function loop() { if (stopped) return; raf = requestAnimationFrame(loop); ctx.fillStyle = '#050508'; ctx.fillRect(0, 0, W, H); // Felder zeichnen POS.forEach((p, i) => { const col = COLORS[i]; const isLit = litIdx === i; const inUser = pressedIdx === i; ctx.shadowColor = isLit || inUser ? col.glow : 'transparent'; ctx.shadowBlur = isLit || inUser ? 30 : 0; MGAPI.roundRect(ctx, p.x, p.y, GW, GH, 12, isLit || inUser ? col.fill : `${col.fill}44`, isLit || inUser ? col.fill : `${col.fill}88`); ctx.shadowBlur = 0; // Emoji mittig ctx.font = `${Math.min(GW, GH) * 0.4}px serif`; ctx.textAlign = 'center'; ctx.fillText(col.label, p.x + GW / 2, p.y + GH / 2 + GH * 0.14); }); // HUD const phaseText = phase === 'show' ? '👀 Schau zu...' : phase === 'input' ? '👆 Deine Runde!' : phase === 'wait' ? '✅ Richtig!' : ''; MGAPI.text(ctx, phaseText, W / 2, 18, { size: 13, color: phase === 'input' ? '#f59e0b' : '#a7a3c2' }); MGAPI.text(ctx, `Runde ${sequence.length} / ${WIN_ROUNDS}`, W / 2, H - 10, { size: 11, color: 'rgba(255,255,255,0.35)' }); if (result) MGAPI.resultScreen(ctx, W, H, result === 'win', result === 'win' ? `${sequence.length} Runden gemeistert!` : 'Falsche Farbe!'); } raf = requestAnimationFrame(loop); return { stop() { stopped = true; clearInterval(stepTimer); clearTimeout(endTimer); cancelAnimationFrame(raf); }, }; } 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 }; })();