- 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
93 lines
4 KiB
JavaScript
93 lines
4 KiB
JavaScript
/**
|
|
* minigames/_api.js
|
|
* Gemeinsame API und Hilfsfunktionen für alle Mini-Games
|
|
*
|
|
* Jedes Mini-Game muss folgendes exportieren (als globale Variable window.MG_<NAME>):
|
|
*
|
|
* window.MG_snake = {
|
|
* id: 'snake',
|
|
* emoji: '🐍',
|
|
* name: 'Snake',
|
|
* desc: 'Steuere die Schlange...',
|
|
* controls: 'Pfeiltasten oder WASD',
|
|
* multi: 1, // 1 = einmalig, 3 = max 3x, 99 = unbegrenzt
|
|
* launch: function(wrap, W, H, cfg) { ... return { stop() {} }; },
|
|
* preview: function(wrap, W, H, cfg) { ... return { stop() {} }; },
|
|
* };
|
|
*
|
|
* launch() → vollständiges Spiel, wird in game.html verwendet
|
|
* preview() → kompakte Demo, wird im Editor-Test-Popup verwendet
|
|
* Beide geben ein Objekt { stop() } zurück zum Aufräumen.
|
|
*
|
|
* cfg = { quizData, theme, rules, devName, gameName }
|
|
*/
|
|
|
|
window.MGAPI = (function() {
|
|
|
|
// ── Canvas-Setup-Helfer ──────────────────────────────────────────────────
|
|
function makeCanvas(wrap, W, H) {
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = W;
|
|
canvas.height = H;
|
|
canvas.style.display = 'block';
|
|
canvas.style.margin = '0 auto';
|
|
canvas.style.borderRadius = '12px';
|
|
canvas.style.background = '#0f0e17';
|
|
wrap.appendChild(canvas);
|
|
return { canvas, ctx: canvas.getContext('2d') };
|
|
}
|
|
|
|
// ── Ergebnis-Anzeige (wird von game.html überschrieben) ──────────────────
|
|
// game.html setzt window.MGAPI.onResult = finishMinigame
|
|
// editor.html setzt window.MGAPI.onResult = previewResult
|
|
function onResult(won) {
|
|
if (typeof window._mgOnResult === 'function') {
|
|
window._mgOnResult(won);
|
|
}
|
|
}
|
|
|
|
// ── Schrift-Helfer ───────────────────────────────────────────────────────
|
|
function text(ctx, str, x, y, opts = {}) {
|
|
ctx.save();
|
|
ctx.font = `${opts.weight || 'bold'} ${opts.size || 16}px ${opts.family || 'Nunito,sans-serif'}`;
|
|
ctx.fillStyle = opts.color || '#fff';
|
|
ctx.textAlign = opts.align || 'center';
|
|
ctx.textBaseline = opts.baseline || 'middle';
|
|
if (opts.shadow) {
|
|
ctx.shadowColor = opts.shadow;
|
|
ctx.shadowBlur = opts.shadowBlur || 10;
|
|
}
|
|
ctx.fillText(str, x, y);
|
|
ctx.restore();
|
|
}
|
|
|
|
// ── Runde Rechtecke ──────────────────────────────────────────────────────
|
|
function roundRect(ctx, x, y, w, h, r, fill, stroke) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x + r, y);
|
|
ctx.lineTo(x + w - r, y);
|
|
ctx.arcTo(x + w, y, x + w, y + r, r);
|
|
ctx.lineTo(x + w, y + h - r);
|
|
ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
|
|
ctx.lineTo(x + r, y + h);
|
|
ctx.arcTo(x, y + h, x, y + h - r, r);
|
|
ctx.lineTo(x, y + r);
|
|
ctx.arcTo(x, y, x + r, y, r);
|
|
ctx.closePath();
|
|
if (fill) { ctx.fillStyle = fill; ctx.fill(); }
|
|
if (stroke) { ctx.strokeStyle = stroke; ctx.stroke(); }
|
|
}
|
|
|
|
// ── Game-Over / Win Screen ───────────────────────────────────────────────
|
|
function resultScreen(ctx, W, H, won, msg) {
|
|
ctx.fillStyle = won ? 'rgba(16,185,129,0.85)' : 'rgba(239,68,68,0.85)';
|
|
roundRect(ctx, W/2-120, H/2-50, 240, 100, 16, ctx.fillStyle, null);
|
|
text(ctx, won ? '🎉 Gewonnen!' : '💀 Verloren!', W/2, H/2-16,
|
|
{ size: 22, weight: 'bold', family: "'Fredoka One',cursive", color: '#fff' });
|
|
if (msg) text(ctx, msg, W/2, H/2+16, { size: 13, color: 'rgba(255,255,255,0.85)' });
|
|
}
|
|
|
|
// ── Öffentliche API ──────────────────────────────────────────────────────
|
|
return { makeCanvas, onResult, text, roundRect, resultScreen };
|
|
|
|
})();
|