340 lines
15 KiB
JavaScript
340 lines
15 KiB
JavaScript
/* wizard.js — Schritt-Navigation + Screens 1-3 (Grundinfos, Gestaltung, Regeln) */
|
|
/* ══════════════════════════════════════════════
|
|
NAVIGATION
|
|
══════════════════════════════════════════════ */
|
|
function goTo(step){
|
|
if(step>cur&&!canLeave(cur))return;
|
|
save();
|
|
const from=document.getElementById('s'+cur);
|
|
from.classList.remove('active');
|
|
from.style.display='none';
|
|
setTimeout(()=>{from.style.display='';},50);
|
|
if(step>cur)ST.highestStep=Math.max(ST.highestStep,step);
|
|
cur=step;
|
|
const to=document.getElementById('s'+cur);
|
|
to.style.display='';
|
|
to.classList.add('active','slide-in');
|
|
to.addEventListener('animationend',()=>to.classList.remove('slide-in'),{once:true});
|
|
const ep=document.getElementById('editorPane');if(ep)ep.scrollTo({top:0,behavior:'smooth'});else window.scrollTo({top:0,behavior:'smooth'});
|
|
updatePB();
|
|
showSummary(step);
|
|
if(step===1)s2init();
|
|
if(step===2){ s3init(); if(typeof maybeStartTour==='function') maybeStartTour(); }
|
|
if(step===3)buildQuiz();
|
|
if(step===4)buildReview();
|
|
showToast('✅ Schritt '+(step+1)+' geöffnet!');
|
|
}
|
|
|
|
function pbClick(step){if(step<cur&&step<=ST.highestStep){goTo(step);}}
|
|
|
|
function canLeave(step){
|
|
if(step===0)return ST.name.trim().length>0;
|
|
if(step===1)return !!(ST.figure&&ST.background) && (ST.playerCount!==2 || (ST.figure2 && ST.figure2!==ST.figure));
|
|
if(step===2)return ST.fields.slice(1,ST.fieldCount-1).filter(Boolean).length>0;
|
|
return true;
|
|
}
|
|
|
|
function updatePB(){
|
|
for(let i=0;i<5;i++){
|
|
const d=document.getElementById('pb'+i);
|
|
d.className='pb-step'+(i===cur?' active':i<cur?' done':'');
|
|
const c=document.getElementById('pbc'+i);
|
|
if(c)c.className='pb-connector'+(i<cur?' done':'');
|
|
}
|
|
document.getElementById('pbFill').style.width=((cur+1)/5*100)+'%';
|
|
}
|
|
|
|
function showSummary(step){
|
|
const idx=step-1;
|
|
if(idx<0)return;
|
|
const banner=document.getElementById('sum'+idx);
|
|
if(!banner)return;
|
|
banner.classList.add('visible');
|
|
const chips=document.getElementById('sum'+idx+'chips');
|
|
if(!chips)return;
|
|
let html='';
|
|
if(idx===0){if(ST.devName)html+=`<div class="sb-chip">👋 ${esc(ST.devName)}</div>`;html+=`<div class="sb-chip">🎮 ${esc(ST.name)}</div>`;if(ST.desc)html+=`<div class="sb-chip">"${esc(ST.desc.slice(0,40))}${ST.desc.length>40?'…':''}"</div>`;}
|
|
if(idx===1){const f=FIGURES.find(x=>x.id===ST.figure);const b=BACKGROUNDS.find(x=>x.id===ST.background);if(f)html+=`<div class="sb-chip">${f.e} ${f.n}</div>`;if(b)html+=`<div class="sb-chip">🌍 ${b.n}</div>`;}
|
|
if(idx===2){const filled=ST.fields.slice(1,ST.fieldCount-1).filter(Boolean).length;html+=`<div class="sb-chip">⬛ ${ST.fieldCount} Felder</div><div class="sb-chip">🕹️ ${filled} Mini-Games</div><div class="sb-chip">📖 ${ST.storyItems.length} Erzähltexte</div>`;}
|
|
if(idx===3){const done=ST.quizData.filter(isQComplete).length;html+=`<div class="sb-chip">❓ ${done}/${ST.quizData.length||0} Quiz-Fragen</div>`;}
|
|
chips.innerHTML=html;
|
|
}
|
|
|
|
function showToast(msg){
|
|
const t=document.getElementById('toast');t.textContent=msg;t.classList.add('show');
|
|
setTimeout(()=>t.classList.remove('show'),2000);
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════
|
|
STEP 1 — GRUNDINFOS
|
|
══════════════════════════════════════════════ */
|
|
function s1init(){
|
|
document.getElementById('s1devname').value=ST.devName;
|
|
document.getElementById('s1name').value=ST.name;
|
|
document.getElementById('s1desc').value=ST.desc;
|
|
s1update();
|
|
}
|
|
function s1update(){
|
|
ST.devName=document.getElementById('s1devname').value.trim();
|
|
ST.name=document.getElementById('s1name').value.trim();
|
|
ST.desc=document.getElementById('s1desc').value;
|
|
const nl=document.getElementById('s1name').value.length;
|
|
const dl=ST.desc.length;
|
|
document.getElementById('s1nc').textContent=nl+' / 40';
|
|
document.getElementById('s1dc').textContent=dl+' / 120';
|
|
document.getElementById('s1dc').className='char-row'+(dl>100?' warn':'');
|
|
const s1dis=!ST.name||!ST.devName;
|
|
document.getElementById('s1next').disabled=s1dis;
|
|
const s1hint=document.getElementById('s1hint');if(s1hint)s1hint.style.display=s1dis?'block':'none';
|
|
// Persönliche Begrüßung
|
|
const grEl=document.getElementById('devGreeting');
|
|
if(ST.devName){
|
|
const greetings=[
|
|
'Hey '+ST.devName+' 👋 Schön, dass du dabei bist!',
|
|
'Los geht\'s, '+ST.devName+'! Dein Spiel wartet 🎮',
|
|
'Hi '+ST.devName+'! Du wirst ein tolles Spiel bauen ✨',
|
|
'Willkommen, '+ST.devName+'! Game on! 🚀',
|
|
];
|
|
grEl.textContent=greetings[ST.devName.length%greetings.length];
|
|
grEl.style.opacity='1';
|
|
} else {grEl.style.opacity='0';}
|
|
// Vorschau
|
|
const show=ST.name.length>0&&ST.devName.length>0;
|
|
document.getElementById('s1preview').style.display=show?'':'none';
|
|
if(show){
|
|
document.getElementById('s1pvname').textContent=ST.name;
|
|
document.getElementById('s1pvdesc').textContent=ST.desc||'Keine Beschreibung.';
|
|
document.getElementById('s1pvdev').textContent='👨💻 Entwickelt von '+ST.devName;
|
|
}
|
|
}
|
|
function suggest(txt){document.getElementById('s1name').value=txt;s1update();}
|
|
|
|
/* ══════════════════════════════════════════════
|
|
STEP 2 — GESTALTUNG
|
|
══════════════════════════════════════════════ */
|
|
function buildFigureGrid(gridId, current, blocked, onPick){
|
|
const g=document.getElementById(gridId); if(!g) return;
|
|
g.innerHTML='';
|
|
FIGURES.forEach(f=>{
|
|
const isBlocked = blocked && blocked===f.id;
|
|
const c=document.createElement('div');
|
|
c.className='sel-card'+(current===f.id?' selected':'')+(isBlocked?' disabled':'');
|
|
c.innerHTML=`<span class="se">${f.e}</span><div class="sl">${f.n}</div>`;
|
|
if(isBlocked){ c.title='Bereits vom anderen Spieler gewählt'; }
|
|
else { c.onclick=()=>onPick(f.id); }
|
|
g.appendChild(c);
|
|
});
|
|
}
|
|
|
|
function setPlayerCount(n){
|
|
ST.playerCount = n;
|
|
if(n===1){ ST.figure2=''; }
|
|
setCodeFocus('figure');
|
|
save();
|
|
s2update();
|
|
}
|
|
|
|
function s2init(){
|
|
buildFigureGrid('figGrid', ST.figure, ST.figure2, id => {
|
|
ST.figure=id; setCodeFocus('figure');
|
|
if(ST.figure2===id) ST.figure2=''; // Konflikt löst sich automatisch
|
|
s2update();
|
|
});
|
|
const bg=document.getElementById('bgGrid');
|
|
bg.innerHTML='';
|
|
BACKGROUNDS.forEach(b=>{
|
|
const c=document.createElement('div');c.className='sel-card'+(ST.background===b.id?' selected':'');
|
|
c.innerHTML=`<span class="se">${b.e}</span><div class="sl">${b.n}</div>`;
|
|
c.onclick=()=>{ST.background=b.id;setCodeFocus('bg');s2update();bg.querySelectorAll('.sel-card').forEach(x=>x.classList.remove('selected'));c.classList.add('selected');};
|
|
bg.appendChild(c);
|
|
});
|
|
s2update();
|
|
}
|
|
function s2update(){
|
|
// Spielmodus-Karten markieren
|
|
document.getElementById('pmode1')?.classList.toggle('active', ST.playerCount!==2);
|
|
document.getElementById('pmode2')?.classList.toggle('active', ST.playerCount===2);
|
|
// Figur-Karten-Titel anpassen
|
|
const t = document.getElementById('figCardTitle');
|
|
const s = document.getElementById('figCardSub');
|
|
if(t){ t.textContent = ST.playerCount===2 ? '👤 Spielfigur Spieler 1' : '👤 Spielfigur'; }
|
|
if(s){ s.textContent = ST.playerCount===2 ? 'Wer bist du im Spiel?' : 'Das bist du im Spiel!'; }
|
|
// P2-Karte ein-/ausblenden + Grid neu bauen (für blocked-State)
|
|
const fig2Card = document.getElementById('fig2Card');
|
|
if(fig2Card){
|
|
fig2Card.style.display = ST.playerCount===2 ? '' : 'none';
|
|
if(ST.playerCount===2){
|
|
buildFigureGrid('fig2Grid', ST.figure2, ST.figure, id => {
|
|
ST.figure2=id; setCodeFocus('figure'); s2update();
|
|
});
|
|
}
|
|
}
|
|
// P1-Grid neu bauen (damit Block-State der gegenüberliegenden Figur stimmt)
|
|
buildFigureGrid('figGrid', ST.figure, ST.playerCount===2 ? ST.figure2 : '', id => {
|
|
ST.figure=id; setCodeFocus('figure');
|
|
if(ST.figure2===id) ST.figure2='';
|
|
s2update();
|
|
});
|
|
// Combo-Preview
|
|
const ok = !!(ST.figure&&ST.background) && (ST.playerCount!==2 || (ST.figure2 && ST.figure2!==ST.figure));
|
|
document.getElementById('s2next').disabled=!ok;
|
|
const combo=document.getElementById('s2combo');
|
|
if(ST.figure && ST.background){
|
|
const f=FIGURES.find(x=>x.id===ST.figure);const b=BACKGROUNDS.find(x=>x.id===ST.background);
|
|
combo.style.display='';
|
|
const f2 = ST.playerCount===2 && ST.figure2 ? FIGURES.find(x=>x.id===ST.figure2) : null;
|
|
document.getElementById('s2scene').textContent = b.scene + ' ' + f.e + (f2?' vs '+f2.e:'');
|
|
document.getElementById('s2clabel').textContent = f.n + (f2?' & '+f2.n:'') + ' im '+ b.n;
|
|
} else { combo.style.display='none'; }
|
|
}
|
|
|
|
/* ══════════════════════════════════════════════
|
|
STEP 3 — SPIELFELD
|
|
══════════════════════════════════════════════ */
|
|
let selGame=null,dragging=null,epFor=null,dragFromIdx=null;
|
|
const STORY_MAX=5,MULTI_MAX=3;
|
|
|
|
// unlockSection: Markiert eine Sektion als "gesehen" für den Live-Code-Preview
|
|
function unlockSection(key){
|
|
if(typeof unlocked !== 'undefined') unlocked.add(key);
|
|
if(typeof setCodeFocus === 'function') setCodeFocus(key);
|
|
}
|
|
|
|
function s3init(){
|
|
unlockSection('movement'); unlockSection('failmode'); unlockSection('fieldcount'); unlockSection('fields'); unlockSection('consequences');
|
|
document.getElementById('cntVal').textContent=ST.fieldCount;
|
|
document.getElementById('cntMinus').disabled=ST.fieldCount<=6;
|
|
document.getElementById('cntPlus').disabled=ST.fieldCount>=50;
|
|
applyRuleUI();
|
|
buildPalette();
|
|
buildBoard();
|
|
updateConsequenceUI();
|
|
}
|
|
function selRule(el){
|
|
const g=el.dataset.g;
|
|
document.querySelectorAll(`[data-g="${g}"]`).forEach(x=>x.classList.remove('selected'));
|
|
el.classList.add('selected');ST.rules[g]=el.dataset.v;
|
|
// Map data-g to focus keys
|
|
const fkMap={'movement':'movement','fail':'failmode'};
|
|
setCodeFocus(fkMap[g]||g);
|
|
if(g==='fail'){
|
|
const sl = document.getElementById('sub_lives');
|
|
const sp = document.getElementById('sub_points');
|
|
if(ST.rules.fail==='lives'){
|
|
sl.classList.add('visible'); sl.style.display='flex';
|
|
sp.classList.remove('visible'); sp.style.display='';
|
|
} else {
|
|
sl.classList.remove('visible'); sl.style.display='';
|
|
sp.classList.add('visible'); sp.style.display='';
|
|
}
|
|
}
|
|
updateConsequenceUI();
|
|
}
|
|
function selSub(el){
|
|
const g=el.dataset.g;
|
|
document.querySelectorAll(`[data-g="${g}"]`).forEach(x=>x.classList.remove('selected'));
|
|
el.classList.add('selected');ST.rules[g]=el.dataset.v;
|
|
// lives/pts sub-options
|
|
setCodeFocus(g==='lives'?'lives':g==='pts'?'pts':'failmode');
|
|
}
|
|
|
|
// ── Konsequenz-Funktionen ──────────────────────────────────────────────────
|
|
|
|
function chgLives(delta){
|
|
const cur = parseInt(ST.rules.lives) || 3;
|
|
const next = Math.min(5, Math.max(1, cur + delta));
|
|
ST.rules.lives = String(next);
|
|
document.getElementById('livesDisplay').textContent = next;
|
|
document.getElementById('livesHearts').textContent = '❤️'.repeat(next);
|
|
setCodeFocus('lives');
|
|
save();
|
|
}
|
|
function selCQ(el,e){
|
|
e&&e.stopPropagation&&e.stopPropagation();
|
|
const side = el.dataset.cq;
|
|
const val = el.dataset.cv;
|
|
document.querySelectorAll(`[data-cq="${side}"]`)
|
|
.forEach(x=>x.classList.remove('selected'));
|
|
el.classList.add('selected');
|
|
ST.consequences[side] = val;
|
|
setCodeFocus('consequences');
|
|
save();
|
|
}
|
|
|
|
function chgCQVal(key, delta, e){
|
|
e && e.stopPropagation();
|
|
const mins = {winVal:0,loseVal:0,winPts:0,losePts:0};
|
|
const maxs = {winVal:6,loseVal:6,winPts:100,losePts:100};
|
|
if(!ST.consequences[key]) ST.consequences[key] = key.includes('Pts') ? 10 : 1;
|
|
let v = (ST.consequences[key]||0) + delta;
|
|
v = Math.max(mins[key]||0, Math.min(maxs[key]||99, v));
|
|
ST.consequences[key] = v;
|
|
const elId = {winVal:'cqWinVal',loseVal:'cqLoseVal',winPts:'cqWinPts',losePts:'cqLosePts'}[key];
|
|
if(elId) document.getElementById(elId).textContent = v;
|
|
setCodeFocus('consequences');
|
|
save();
|
|
}
|
|
|
|
function updateConsequenceUI(){
|
|
const grid = document.getElementById('consequenceGrid');
|
|
if(!grid) return;
|
|
const isDice = ST.rules.movement === 'dice';
|
|
const isLives = ST.rules.fail === 'lives';
|
|
const isPts = ST.rules.fail === 'points';
|
|
// CSS-Klassen für sichtbare Optionen
|
|
// Alle Modus-Klassen zuerst entfernen
|
|
grid.classList.remove('cq-mode-step','cq-mode-lives','cq-mode-pts');
|
|
// Dann die passenden setzen
|
|
if(!isDice) grid.classList.add('cq-mode-step');
|
|
if(isLives) grid.classList.add('cq-mode-lives');
|
|
if(isPts) grid.classList.add('cq-mode-pts');
|
|
// Falls aktuelle Auswahl nicht mehr sichtbar → auf 'nothing' zurücksetzen
|
|
['win','lose'].forEach(side=>{
|
|
const cur = ST.consequences[side];
|
|
const diceOnly = ['forward','back','again','skip'];
|
|
const livesOnly = ['life'];
|
|
const ptsOnly = ['points'];
|
|
let invalid = false;
|
|
if(diceOnly.includes(cur) && !isDice) invalid = true;
|
|
if(livesOnly.includes(cur) && !isLives) invalid = true;
|
|
if(ptsOnly.includes(cur) && !isPts) invalid = true;
|
|
if(invalid){
|
|
ST.consequences[side] = 'nothing';
|
|
}
|
|
// UI aktualisieren
|
|
document.querySelectorAll(`[data-cq="${side}"]`)
|
|
.forEach(x=>x.classList.toggle('selected', x.dataset.cv === ST.consequences[side]));
|
|
});
|
|
// Werte anzeigen
|
|
const c = ST.consequences;
|
|
const wv = document.getElementById('cqWinVal');
|
|
const lv = document.getElementById('cqLoseVal');
|
|
const wp = document.getElementById('cqWinPts');
|
|
const lp = document.getElementById('cqLosePts');
|
|
if(wv) wv.textContent = c.winVal ?? 2;
|
|
if(lv) lv.textContent = c.loseVal ?? 1;
|
|
const ld = document.getElementById('livesDisplay');
|
|
const lh = document.getElementById('livesHearts');
|
|
const ln = parseInt(ST.rules.lives) || 3;
|
|
if(ld) ld.textContent = ln;
|
|
if(lh) lh.textContent = '❤️'.repeat(ln);
|
|
if(wp) wp.textContent = c.winPts ?? 10;
|
|
if(lp) lp.textContent = c.losePts ?? 5;
|
|
}
|
|
|
|
function applyRuleUI(){
|
|
['movement','fail'].forEach(g=>document.querySelectorAll(`[data-g="${g}"]`).forEach(x=>x.classList.toggle('selected',x.dataset.v===ST.rules[g])));
|
|
document.getElementById('sub_lives').classList.toggle('visible',ST.rules.fail==='lives');
|
|
document.getElementById('sub_points').classList.toggle('visible',ST.rules.fail==='points');
|
|
['lives','pts'].forEach(g=>document.querySelectorAll(`[data-g="${g}"]`).forEach(x=>x.classList.toggle('selected',x.dataset.v===ST.rules[g])));
|
|
updateConsequenceUI();
|
|
}
|
|
function chgCount(d){
|
|
const n=ST.fieldCount+d;if(n<6||n>50)return;
|
|
ST.fieldCount=n;setCodeFocus('fieldcount');document.getElementById('cntVal').textContent=n;
|
|
document.getElementById('cntMinus').disabled=n<=6;document.getElementById('cntPlus').disabled=n>=50;
|
|
while(ST.fields.length<n)ST.fields.push(null);
|
|
ST.fields=ST.fields.slice(0,n);
|
|
ST.storyItems=ST.storyItems.filter(s=>s.fieldIndex<n);
|
|
buildBoard();
|
|
}
|