diff --git a/index.html b/index.html
index 531408a..e25fb8f 100644
--- a/index.html
+++ b/index.html
@@ -895,6 +895,9 @@
body:not(.gm-mode) .nav-tab.gm-tab { display: none; }
body.gm-mode .nav-tab.player-tab { display: none; }
+ /* Spoiler-Inhalte nur im Spielleiter-Modus zeigen */
+ body:not(.gm-mode) .gm-only { display: none !important; }
+
/* Dezenter GM-Hinweis-Streifen */
.gm-banner {
display: none;
@@ -945,10 +948,10 @@
-
+
-
+
@@ -1112,9 +1115,11 @@
Die Quests
-
Aufgaben der NPCs — Klicken zum Aufklappen
+
Die Prüfungen für Wendl
-
+
Im Spielleiter-Modus mit allen Details — Spieler sehen nur die Quest-Namen & den Fortschritt.
+
+
@@ -1375,14 +1380,14 @@
-
+
-
@@ -1561,7 +1566,39 @@ async function syncState() {
if (s.questDone) s.questDone.forEach((v, i) => { if (i < questDone.length) questDone[i] = v; });
document.getElementById('drinkCount').textContent = drinks;
const ta = document.getElementById('notes');
- if (ta && typeof s.notesText === 'string') ta.value = s.notesText;
+ // Notizen nicht überschreiben, während jemand tippt
+ if (ta && document.activeElement !== ta && typeof s.notesText === 'string') ta.value = s.notesText;
+}
+
+// Live-Sync: zieht regelmäßig den Server-State und gleicht offene Instanzen ab
+// (Schlucke, Würfel, Quests synchron über mehrere Geräte hinweg).
+let pollTimer = null;
+async function pollState() {
+ const data = await apiGet('/game');
+ if (!data || !data.state) return;
+ const s = data.state;
+ let itemsChanged = false, questsChanged = false;
+ if (s.drinks !== undefined && s.drinks !== drinks) {
+ drinks = s.drinks;
+ const dc = document.getElementById('drinkCount');
+ if (dc) dc.textContent = drinks;
+ }
+ if (s.itemCollected) s.itemCollected.forEach((v, i) => {
+ if (i < itemCollected.length && itemCollected[i] !== v) { itemCollected[i] = v; itemsChanged = true; }
+ });
+ if (s.questDone) s.questDone.forEach((v, i) => {
+ if (i < questDone.length && questDone[i] !== v) { questDone[i] = v; questsChanged = true; }
+ });
+ if (itemsChanged) {
+ for (let i = 0; i < itemCollected.length; i++) {
+ const slot = document.getElementById('inv-' + i);
+ if (slot) slot.classList.toggle('collected', itemCollected[i]);
+ }
+ }
+ if (questsChanged) buildQuests();
+ if (itemsChanged || questsChanged) updateQuestStatus();
+ const ta = document.getElementById('notes');
+ if (ta && document.activeElement !== ta && typeof s.notesText === 'string' && ta.value !== s.notesText) ta.value = s.notesText;
}
// State zum Server speichern
@@ -1704,14 +1741,16 @@ function toggleMode() {
}
}
-function startApp() {
+async function startApp() {
document.getElementById('cover').style.display = 'none';
document.getElementById('app').style.display = 'block';
- buildQuests();
buildDice();
- syncState();
- syncSpieler();
- updateQuestStatus();
+ await syncState(); // erst Server-State holen …
+ await syncSpieler();
+ buildQuests(); // … dann Quests mit korrektem Erledigt-Status bauen
+ refreshUI();
+ // Live-Sync starten (nur einmal, auch wenn startApp doppelt läuft)
+ if (!pollTimer) pollTimer = setInterval(pollState, 3000);
}
// ═══════════════════════════════════════════
@@ -1728,20 +1767,20 @@ function buildQuests() {
card.innerHTML = `
${q.badgeLabel}
${q.name}
-
NPC: ${q.holder}
-
${q.desc}
-
+
NPC: ${q.holder}
+
${q.desc}
+
${q.success}
${q.fail}
-
${q.details}
+
${q.details}
-
▼ Aufklappen für Details
`;
+
▼ Aufklappen für Details
`;
card.addEventListener('click', () => toggleExpand(i));
grid.appendChild(card);
});
@@ -1798,7 +1837,7 @@ function updateQuestStatus() {
color:${questDone[i]?'#2a5a1a':'#5a3a10'};">
${questDone[i] ? '✓' : '○'}
${q.name}
-
${q.item}
+
${q.item}
`).join('');
}
// ═══════════════════════════════════════════