feat: URL-sharing system with QR code + share buttons

- buildShareURL(): JSON → pako.deflate → Base64 → URL-Hash
- doPublish(): Share-Modal mit QR-Code, Kopieren, E-Mail, WhatsApp, Native Share
- game.html: loadGameConfig() liest URL-Hash vor localStorage
  - Format 'z:' = komprimiert (pako), 'j:' = plain Base64
  - Fallback auf localStorage (Editor-Test), dann Demo-Config
- CDN: pako 2.1.0 + qrcodejs 1.0.0 (cloudflare)
- Kein Backend nötig: Link ist selbsttragend (~800-1100 Zeichen)
- QR-Code-tauglich für alle normalen Konfigurationen
This commit is contained in:
Spiel-Generator Workshop 2026-03-14 22:37:52 +00:00
parent 43752fa8e4
commit 4afa0e0a99
2 changed files with 2580 additions and 19 deletions

File diff suppressed because it is too large Load diff

View file

@ -360,6 +360,7 @@ canvas#bgCanvas{
<div id="transOverlay"></div>
<div id="toast"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js"></script>
<script>
/* roundRect polyfill for older browsers */
if(!CanvasRenderingContext2D.prototype.roundRect){
@ -374,7 +375,39 @@ if(!CanvasRenderingContext2D.prototype.roundRect){
}
/* ══════════ CONFIG ══════════ */
const cfg = JSON.parse(localStorage.getItem('gameConfig') || '{"name":"Mein Spiel","desc":"Ein tolles Brettspiel!","figure":"robot","background":"space","fieldCount":10,"fields":["snake","flappy","quiz","memory","reaction",null,"snake","quiz",null,""],"rules":{"movement":"dice","fail":"lives","lives":"3","pts":"10"}}');
// 1. Versuche URL-Hash (geteilter Link), 2. Fallback localStorage, 3. Demo-Config
function loadGameConfig() {
const hash = window.location.hash.slice(1); // '#' entfernen
if (hash) {
try {
if (hash.startsWith('z:')) {
// Komprimiert: Base64 → binary → pako.inflate → JSON
const b64 = hash.slice(2);
const binary = atob(b64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
const json = pako.inflate(bytes, { to: 'string' });
return JSON.parse(json);
} else if (hash.startsWith('j:')) {
// Unkomprimiert Base64
return JSON.parse(decodeURIComponent(escape(atob(hash.slice(2)))));
}
} catch(e) {
console.warn('URL-Hash konnte nicht gelesen werden:', e);
}
}
// Fallback: localStorage (vom Editor)
try {
const stored = localStorage.getItem('gameConfig');
if (stored) return JSON.parse(stored);
} catch(e) {}
// Demo-Config
return {name:'Mein Spiel',desc:'Ein tolles Brettspiel!',figure:'robot',
background:'space',fieldCount:10,
fields:['snake','flappy','quiz','memory','reaction',null,'snake','quiz',null,''],
rules:{movement:'dice',fail:'lives',lives:'3',pts:'10'}};
}
const cfg = loadGameConfig();
const fields = (cfg.fields||[]).map(f=>f||null);
const fieldCount = cfg.fieldCount || fields.length || 10;
const rules = cfg.rules || {movement:'dice',fail:'lives',lives:'3',pts:'10'};