From 49b571699bc3e7dd1ee58dd0c709044d12ac4976 Mon Sep 17 00:00:00 2001 From: cay Date: Tue, 14 Apr 2026 08:46:50 +0100 Subject: [PATCH] xzkxdzr --- public/css/daily.css | 278 ++++++++++++++------------- public/js/buildings/daily.js | 363 ++++++++++++++++++++--------------- 2 files changed, 353 insertions(+), 288 deletions(-) diff --git a/public/css/daily.css b/public/css/daily.css index c3e41c0..1d8440c 100644 --- a/public/css/daily.css +++ b/public/css/daily.css @@ -1,198 +1,208 @@ /* ============================================================ - public/css/daily.css - Daily Herausforderung – Karten-Pfad Overlay + public/css/daily.css – Daily World Map (SVG-Overlay Ansatz) ============================================================ */ +@keyframes dailyFadeIn { from{opacity:0} to{opacity:1} } +@keyframes dailyScaleIn { from{opacity:0;transform:translate(-50%,-50%) scale(.92)} to{opacity:1;transform:translate(-50%,-50%) scale(1)} } + /* ── Overlay ─────────────────────────────────────────────── */ #daily-map-overlay { position: fixed; inset: 0; z-index: 10000; - background: rgba(0, 0, 0, 0.92); + background: rgba(0,0,0,.88); display: flex; align-items: center; justify-content: center; - animation: dailyFadeIn 0.35s ease; + animation: dailyFadeIn .3s ease; } -@keyframes dailyFadeIn { from { opacity: 0; } to { opacity: 1; } } -@keyframes dailyPulse { 0%, 100% { box-shadow: 0 0 14px 4px rgba(80,160,255,0.7); } 50% { box-shadow: 0 0 28px 10px rgba(80,160,255,1); } } -@keyframes dailyGlow { 0%, 100% { opacity: 1; } 50% { opacity: 0.65; } } - /* ── Map Container ───────────────────────────────────────── */ #daily-map-wrap { position: relative; - max-width: min(95vw, 1000px); - max-height: 90vh; - border-radius: 14px; + max-width: min(96vw, 1020px); + border-radius: 16px; overflow: hidden; - box-shadow: 0 0 0 2px rgba(80, 140, 255, 0.4), 0 30px 80px rgba(0, 0, 0, 0.9); + animation: dailyScaleIn .3s cubic-bezier(.22,1,.36,1); + box-shadow: + 0 0 0 1px rgba(80,140,255,.4), + 0 0 0 3px rgba(80,140,255,.1), + 0 30px 80px rgba(0,0,0,.9); } #daily-map-img { display: block; width: 100%; height: auto; - max-height: 90vh; - object-fit: cover; user-select: none; pointer-events: none; } -/* ── Close Button ────────────────────────────────────────── */ -#daily-map-close { +/* ── SVG Overlay (liegt auf dem Bild) ────────────────────── */ +#daily-map-svg { position: absolute; - top: 12px; - right: 14px; - background: rgba(0, 0, 0, 0.7); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 50%; - width: 34px; - height: 34px; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - font-size: 16px; - cursor: pointer; - z-index: 10; - transition: background 0.2s; + inset: 0; + width: 100%; + height: 100%; + overflow: visible; } -#daily-map-close:hover { background: rgba(200, 50, 50, 0.8); } -/* ── Title Banner ────────────────────────────────────────── */ +/* ── Stationen (SVG-Gruppen) ─────────────────────────────── */ +.ds-hit { + cursor: pointer; + fill: transparent; +} +.ds-locked .ds-hit { cursor: default; } + +/* Pfadlinie */ +.ds-path-line { + fill: none; + stroke: rgba(80,160,255,.25); + stroke-width: .7; + stroke-dasharray: 3 2.5; +} +.ds-path-line.done { + stroke: rgba(100,220,120,.4); +} + +/* Locked: dunkle Abdunklung über dem Bildkreis */ +.ds-locked .ds-overlay { + fill: rgba(0,0,5,.62); + stroke: rgba(50,60,100,.45); + stroke-width: .8; +} +.ds-locked .ds-num { + fill: rgba(120,130,160,.38); + font-family: "Cinzel", serif; + font-weight: 700; +} + +/* Available: transparente Mitte, leuchtender Rand */ +.ds-available .ds-overlay { + fill: rgba(40,100,255,.08); + stroke: rgba(100,200,255,.9); + stroke-width: 1.2; +} +.ds-available .ds-num { + fill: #fff; + font-family: "Cinzel", serif; + font-weight: 700; + filter: drop-shadow(0 0 3px rgba(180,220,255,.9)); +} +/* Pulsierender Außenring */ +.ds-available .ds-ring1 { + fill: none; + stroke: rgba(80,180,255,.65); + stroke-width: .9; +} +.ds-available .ds-ring2 { + fill: none; + stroke: rgba(80,180,255,.3); + stroke-width: .6; +} +/* Hover */ +.ds-available:hover .ds-overlay { + fill: rgba(60,130,255,.18); + stroke: rgba(150,220,255,1); + stroke-width: 1.5; +} +.ds-available:hover .ds-num { + filter: drop-shadow(0 0 6px rgba(200,240,255,1)); +} + +/* Done: grüne Einfärbung */ +.ds-done .ds-overlay { + fill: rgba(30,160,70,.35); + stroke: rgba(80,220,110,.85); + stroke-width: 1.2; +} +.ds-done .ds-check { + fill: #fff; + font-family: sans-serif; + font-weight: 700; + filter: drop-shadow(0 0 2px rgba(50,200,80,.8)); +} + +/* ── Titel ───────────────────────────────────────────────── */ #daily-map-title { position: absolute; top: 14px; left: 50%; transform: translateX(-50%); - background: rgba(0, 0, 0, 0.72); - border: 1px solid rgba(80, 140, 255, 0.45); - border-radius: 8px; - padding: 6px 22px; + background: linear-gradient(135deg, rgba(5,8,20,.9), rgba(8,12,28,.92)); + border: 1px solid rgba(80,140,255,.45); + border-radius: 20px; + padding: 7px 26px; font-family: "Cinzel", serif; - font-size: 15px; + font-size: 13px; color: #a0c8ff; - letter-spacing: 3px; + letter-spacing: 4px; white-space: nowrap; z-index: 10; + pointer-events: none; + box-shadow: 0 4px 20px rgba(0,0,0,.6); } -/* ── Circles ─────────────────────────────────────────────── */ -.daily-circle { +/* ── Schließen ───────────────────────────────────────────── */ +#daily-map-close { position: absolute; - width: 7%; /* scales with image width */ - aspect-ratio: 1; + top: 12px; right: 14px; + background: rgba(0,0,0,.65); + border: 1px solid rgba(255,255,255,.15); border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-family: "Cinzel", serif; - font-size: clamp(12px, 1.8vw, 22px); - font-weight: bold; - transform: translate(-50%, -50%); - cursor: default; - transition: transform 0.15s ease; - z-index: 5; - user-select: none; -} - -/* Completed station ─ grün */ -.daily-circle.done { - background: radial-gradient(circle at 40% 35%, #4ecf6a, #1a7a30); - border: 3px solid #7af09a; - color: #fff; - box-shadow: 0 0 14px rgba(78, 207, 106, 0.6); -} -.daily-circle.done::after { - content: "✓"; - font-size: clamp(10px, 1.5vw, 18px); -} - -/* Available station ─ blaues Pulsieren */ -.daily-circle.available { - background: radial-gradient(circle at 40% 35%, #5090ff, #1a3a9a); - border: 3px solid #90c0ff; - color: #fff; + width: 32px; height: 32px; + display: flex; align-items: center; justify-content: center; + color: rgba(255,255,255,.7); + font-size: 14px; cursor: pointer; - animation: dailyPulse 2s ease-in-out infinite; -} -.daily-circle.available:hover { - transform: translate(-50%, -50%) scale(1.15); - animation: none; - box-shadow: 0 0 30px 8px rgba(80, 160, 255, 0.9); + z-index: 20; + transition: .2s; } +#daily-map-close:hover { background: rgba(180,40,40,.8); color:#fff; } -/* Locked station ─ ausgegraut */ -.daily-circle.locked { - background: radial-gradient(circle at 40% 35%, #2a2a3a, #141420); - border: 3px solid rgba(80, 100, 150, 0.4); - color: rgba(150, 160, 200, 0.35); -} - -/* ── Progress Text ───────────────────────────────────────── */ +/* ── Progress Bar ────────────────────────────────────────── */ #daily-progress-bar-wrap { position: absolute; - bottom: 14px; - left: 50%; + bottom: 14px; left: 50%; transform: translateX(-50%); width: 60%; - background: rgba(0, 0, 0, 0.72); - border: 1px solid rgba(80, 140, 255, 0.35); - border-radius: 8px; - padding: 8px 16px; - z-index: 10; - text-align: center; - font-family: "Cinzel", serif; + background: rgba(5,8,20,.82); + border: 1px solid rgba(80,140,255,.3); + border-radius: 10px; + padding: 8px 18px 9px; + z-index: 10; pointer-events: none; } - #daily-progress-text { - font-size: 12px; - color: #a0c8ff; - letter-spacing: 1px; - margin-bottom: 5px; + font-family: "Cinzel", serif; + font-size: 11px; color: rgba(160,200,255,.8); + letter-spacing: 1px; text-align: center; margin-bottom: 6px; } - #daily-progress-track { - height: 6px; - background: rgba(80, 140, 255, 0.15); - border-radius: 3px; - overflow: hidden; + height: 5px; background: rgba(80,140,255,.12); border-radius: 3px; overflow: hidden; } - #daily-progress-fill { height: 100%; - background: linear-gradient(90deg, #3060cc, #60a0ff); - border-radius: 3px; - transition: width 0.6s ease; + background: linear-gradient(90deg, #2060dd, #60b0ff); + border-radius: 3px; transition: width .7s ease; + box-shadow: 0 0 8px rgba(80,160,255,.6); } /* ── All Done ────────────────────────────────────────────── */ #daily-all-done { - position: absolute; - inset: 0; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - background: rgba(0, 0, 0, 0.78); - border-radius: 14px; - z-index: 20; - animation: dailyFadeIn 0.4s ease; + position: absolute; inset: 0; + display: flex; flex-direction: column; + align-items: center; justify-content: center; + background: rgba(0,5,15,.8); border-radius: 16px; + z-index: 20; backdrop-filter: blur(4px); + animation: dailyFadeIn .4s ease; } -#daily-all-done .done-icon { font-size: 56px; margin-bottom: 14px; } -#daily-all-done .done-title { font-family: "Cinzel", serif; font-size: 22px; color: #7af09a; letter-spacing: 4px; margin-bottom: 8px; } -#daily-all-done .done-sub { font-family: "Cinzel", serif; font-size: 12px; color: #a0c8a0; margin-bottom: 24px; } +#daily-all-done .done-icon { font-size:56px; margin-bottom:16px; } +#daily-all-done .done-title { font-family:"Cinzel",serif; font-size:22px; color:#7af09a; letter-spacing:4px; margin-bottom:8px; } +#daily-all-done .done-sub { font-family:"Cinzel",serif; font-size:12px; color:#a0c8a0; margin-bottom:26px; } #daily-all-done .done-btn { - background: linear-gradient(135deg, #1a4a28, #27ae60); - border: 2px solid rgba(100, 220, 100, 0.6); - border-radius: 10px; - color: #fff; - font-family: "Cinzel", serif; - font-size: 13px; - letter-spacing: 3px; - padding: 11px 32px; - cursor: pointer; - transition: 0.2s; + background:linear-gradient(135deg,#1a4a28,#27ae60); + border:2px solid rgba(100,220,100,.6); border-radius:10px; color:#fff; + font-family:"Cinzel",serif; font-size:13px; letter-spacing:3px; + padding:11px 34px; cursor:pointer; transition:.2s; } -#daily-all-done .done-btn:hover { border-color: #7af09a; background: linear-gradient(135deg, #2a6a38, #37be70); } +#daily-all-done .done-btn:hover { border-color:#7af09a; } diff --git a/public/js/buildings/daily.js b/public/js/buildings/daily.js index 5747a38..d7d6006 100644 --- a/public/js/buildings/daily.js +++ b/public/js/buildings/daily.js @@ -1,211 +1,266 @@ /* ============================================================ public/js/buildings/daily.js - Tagesherausforderung – Karten-Pfad mit 7 Stationen gegen KI -============================================================ */ -/* ── Stationen: Position auf dem Bild (% left, % top) ───── */ -const DAILY_STATIONS = [ - { id: 1, left: 49.5, top: 86.5, label: "1" }, - { id: 2, left: 37.0, top: 73.0, label: "2" }, - { id: 3, left: 44.5, top: 62.0, label: "3" }, - { id: 4, left: 54.5, top: 53.5, label: "4" }, - { id: 5, left: 49.0, top: 46.0, label: "5" }, - { id: 6, left: 44.5, top: 37.5, label: "6" }, - { id: 7, left: 50.0, top: 22.0, label: "7" }, +/* ── Positionen (% in viewBox 0 0 100 100) ───────────────── */ +const STATIONS = [ + { id:1, cx:49.5, cy:86.5 }, + { id:2, cx:37.0, cy:73.0 }, + { id:3, cx:44.5, cy:62.0 }, + { id:4, cx:54.5, cy:53.5 }, + { id:5, cx:49.0, cy:46.0 }, + { id:6, cx:44.5, cy:37.5 }, + { id:7, cx:50.0, cy:22.0 }, ]; +const R = 4.8; // Kreis-Radius in SVG-Einheiten -let dailyDeckId = null; -let dailyOverlay = null; +let dailyDeckId = null; +let dailyOverlay = null; -/* ── Öffnet den Karten-Pfad ──────────────────────────────── */ +/* ── Karten-Pfad öffnen ──────────────────────────────────── */ export async function openDailyMap(deckId) { dailyDeckId = deckId; + document.getElementById('daily-map-overlay')?.remove(); - // Kein Duplikat - document.getElementById("daily-map-overlay")?.remove(); - - /* Fortschritt vom Server laden */ let completed = []; try { - const res = await fetch("/api/himmelstor/daily/progress"); - if (res.ok) { - const data = await res.json(); - completed = data.completed ?? []; - } - } catch (e) { console.error("[Daily] Fortschritt laden:", e); } + const r = await fetch('/api/himmelstor/daily/progress'); + if (r.ok) completed = (await r.json()).completed ?? []; + } catch {} - /* Overlay aufbauen */ - dailyOverlay = document.createElement("div"); - dailyOverlay.id = "daily-map-overlay"; + dailyOverlay = document.createElement('div'); + dailyOverlay.id = 'daily-map-overlay'; dailyOverlay.innerHTML = `
- Daily + + + ${buildSvg(completed)} +
☀️ TAGESHERAUSFORDERUNG
- - ${buildCircles(completed)} + +
${completed.length} / 7 Stationen abgeschlossen
-
+
- ${completed.length >= 7 ? buildAllDonePanel() : ""} + ${completed.length >= 7 ? allDoneHtml() : ''}
`; document.body.appendChild(dailyOverlay); + document.getElementById('daily-map-close').addEventListener('click', closeMap); + dailyOverlay.addEventListener('click', e => { if (e.target === dailyOverlay) closeMap(); }); - /* Event-Listener */ - document.getElementById("daily-map-close").addEventListener("click", closeDailyMap); - dailyOverlay.addEventListener("click", e => { if (e.target === dailyOverlay) closeDailyMap(); }); - - /* Klickbare Kreise */ - dailyOverlay.querySelectorAll(".daily-circle.available").forEach(circle => { - circle.addEventListener("click", () => { - const station = parseInt(circle.dataset.station, 10); - startDailyStation(station); - }); + /* Klick-Listener auf SVG-Hitboxen */ + dailyOverlay.querySelectorAll('.ds-hit[data-station]').forEach(el => { + el.addEventListener('click', () => startStation(parseInt(el.dataset.station, 10))); }); } -/* ── HTML der Kreise ─────────────────────────────────────── */ -function buildCircles(completed) { - const maxDone = completed.length; // nächster freier = maxDone + 1 +/* ── SVG aufbauen ────────────────────────────────────────── */ +function buildSvg(completed) { + const maxDone = completed.length; + const parts = []; - return DAILY_STATIONS.map(s => { - const isDone = completed.includes(s.id); - const isAvailable = !isDone && s.id === maxDone + 1; - const isLocked = !isDone && !isAvailable; + /* Pfadlinien zwischen Stationen */ + for (let i = 0; i < STATIONS.length - 1; i++) { + const a = STATIONS[i]; + const b = STATIONS[i + 1]; + const done = completed.includes(a.id); + parts.push(``); + } - const cls = isDone ? "done" : isAvailable ? "available" : "locked"; - const inner = isDone ? "" : s.label; // done zeigt ✓ per CSS ::after + /* Stationskreise */ + STATIONS.forEach(s => { + const isDone = completed.includes(s.id); + const isAvail = !isDone && s.id === maxDone + 1; + const cls = isDone ? 'ds-done' : isAvail ? 'ds-available' : 'ds-locked'; + const fSize = R * 0.72; - return `
${inner}
`; - }).join(""); -} + if (isDone) { + parts.push(` + + + + `); -function buildAllDonePanel() { - return ` -
-
🏆
-
TAGESQUEST ABGESCHLOSSEN!
-
Du hast alle 7 Stationen gemeistert.
- -
`; + } else if (isAvail) { + parts.push(` + + + + + + + + + + + + + + + ${s.id} + + + `); + + } else { + parts.push(` + + + ${s.id} + `); + } + }); + + return `${parts.join('')}`; } /* ── Station starten ─────────────────────────────────────── */ -function startDailyStation(station) { - if (!dailyDeckId) { - alert("Bitte zuerst ein Deck auswählen."); - return; - } - +function startStation(station) { + if (!dailyDeckId) { alert('Bitte zuerst ein Deck auswählen.'); return; } const socket = window._socket; - if (!socket) { - console.error("[Daily] Kein Socket."); - return; - } + if (!socket) return; - /* Kreis als "lädt" markieren */ - const circle = dailyOverlay.querySelector(`.daily-circle[data-station="${station}"]`); - if (circle) { - circle.style.animation = "none"; - circle.style.opacity = "0.5"; - circle.style.cursor = "wait"; - } + /* Hitbox deaktivieren während Laden */ + dailyOverlay.querySelectorAll(`.ds-hit[data-station="${station}"]`).forEach(el => { + el.style.pointerEvents = 'none'; + el.style.opacity = '.4'; + }); - /* Einmaligen Listener für Match-Gefunden */ - socket.once("ht_daily_match_found", data => { - closeDailyMap(); - openHtDailyPopup( - `/himmelstor/daily?match=${encodeURIComponent(data.matchId)}&slot=${encodeURIComponent(data.mySlot)}&deck=${encodeURIComponent(dailyDeckId)}&station=${station}&opponent=${encodeURIComponent("Wächter " + station)}`, - "Wächter " + station, - data.matchId + socket.once('ht_daily_match_found', data => { + closeMap(); + openPopup( + `/himmelstor/daily?match=${encodeURIComponent(data.matchId)}` + + `&slot=${encodeURIComponent(data.mySlot)}` + + `&deck=${encodeURIComponent(dailyDeckId)}` + + `&station=${station}` + + `&opponent=${encodeURIComponent('Wächter ' + station)}`, + 'Wächter ' + station, data.matchId ); }); - socket.once("ht_daily_error", data => { - if (circle) { circle.style.animation = ""; circle.style.opacity = ""; circle.style.cursor = ""; } - alert(data.message || "Fehler beim Starten."); + socket.once('ht_daily_error', data => { + dailyOverlay.querySelectorAll(`.ds-hit[data-station="${station}"]`).forEach(el => { + el.style.pointerEvents = ''; + el.style.opacity = ''; + }); + alert(data.message || 'Fehler beim Starten.'); }); - socket.emit("ht_join_daily", { - station, - deckId: dailyDeckId, - }); + socket.emit('ht_join_daily', { station, deckId: dailyDeckId }); } -/* ── Popup öffnen (Iframe wie bei Arena) ─────────────────── */ -function openHtDailyPopup(src, opponentName, matchId) { - document.getElementById("ht-daily-backdrop")?.remove(); - document.getElementById("ht-daily-popup")?.remove(); +/* ── Station als erledigt markieren ─────────────────────── */ +export function markDailyStationDone(station) { + if (!dailyOverlay) return; - const backdrop = document.createElement("div"); - backdrop.id = "ht-daily-backdrop"; - backdrop.style.cssText = "position:fixed;inset:0;background:rgba(0,0,0,.82);backdrop-filter:blur(5px);z-index:9998;"; + /* SVG-Gruppe aktualisieren */ + const g = dailyOverlay.querySelector(`.ds-station[data-station="${station}"]`); + if (g) { + g.className.baseVal = 'ds-station ds-done'; + g.innerHTML = ` + + `; + } - const popup = document.createElement("div"); - popup.id = "ht-daily-popup"; - popup.style.cssText = "position:fixed;inset:50px;z-index:9999;display:flex;flex-direction:column;border-radius:14px;overflow:hidden;box-shadow:0 0 0 1px rgba(80,140,255,.4),0 30px 90px rgba(0,0,0,.85);"; - popup.innerHTML = ` + /* Pfadlinie grün */ + const line = dailyOverlay.querySelector(`.ds-path-line:nth-of-type(${station})`); + if (line) line.classList.add('done'); + + /* Nächste Station freischalten */ + const nextStation = STATIONS.find(s => s.id === station + 1); + const nextG = nextStation + ? dailyOverlay.querySelector(`.ds-station[data-station="${station + 1}"]`) + : null; + + if (nextG && nextStation) { + const fSize = R * 0.72; + nextG.className.baseVal = 'ds-station ds-available'; + nextG.innerHTML = ` + + + + + + + + + + ${station+1} + `; + nextG.querySelector('.ds-hit')?.addEventListener('click', () => startStation(station + 1)); + } + + /* Fortschritt */ + const done = dailyOverlay.querySelectorAll('.ds-done').length; + const pct = Math.round(done / 7 * 100); + const fill = document.getElementById('daily-progress-fill'); + const text = document.getElementById('daily-progress-text'); + if (fill) fill.style.width = pct + '%'; + if (text) text.textContent = `${done} / 7 Stationen abgeschlossen`; + + if (done >= 7) { + const wrap = document.getElementById('daily-map-wrap'); + if (wrap && !document.getElementById('daily-all-done')) { + wrap.insertAdjacentHTML('beforeend', allDoneHtml()); + } + } +} + +/* ── Popup öffnen ────────────────────────────────────────── */ +function openPopup(src, opponent, matchId) { + document.getElementById('ht-daily-backdrop')?.remove(); + document.getElementById('ht-daily-popup')?.remove(); + + const bd = document.createElement('div'); + bd.id = 'ht-daily-backdrop'; + bd.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.82);backdrop-filter:blur(5px);z-index:9998;'; + + const pp = document.createElement('div'); + pp.id = 'ht-daily-popup'; + pp.style.cssText = 'position:fixed;inset:50px;z-index:9999;display:flex;flex-direction:column;border-radius:14px;overflow:hidden;box-shadow:0 0 0 1px rgba(80,140,255,.4),0 30px 90px rgba(0,0,0,.85);'; + pp.innerHTML = `
- ☀️ Daily · vs ${opponentName} + ☀️ Daily · vs ${opponent} ${matchId}
`; - document.body.appendChild(backdrop); - document.body.appendChild(popup); - - /* Cleanup wenn Iframe-Spiel beendet → closeToArena ruft parent auf */ + document.body.appendChild(bd); + document.body.appendChild(pp); } -/* ── Overlay schließen ───────────────────────────────────── */ -function closeDailyMap() { - document.getElementById("daily-map-overlay")?.remove(); +function closeMap() { + document.getElementById('daily-map-overlay')?.remove(); dailyOverlay = null; } -/* ── Extern: nach gewonnenem Match Kreis aktualisieren ───── */ -export function markDailyStationDone(station) { - if (!dailyOverlay) return; - const circle = dailyOverlay.querySelector(`.daily-circle[data-station="${station}"]`); - if (!circle) return; - circle.className = "daily-circle done"; - circle.textContent = ""; - circle.style = ""; - - /* Nächste Station freischalten */ - const next = dailyOverlay.querySelector(`.daily-circle[data-station="${station + 1}"]`); - if (next) { - next.className = "daily-circle available"; - next.textContent = String(station + 1); - next.addEventListener("click", () => startDailyStation(station + 1)); - } - - /* Fortschrittsbalken updaten */ - const done = dailyOverlay.querySelectorAll(".daily-circle.done").length; - const pct = Math.round((done / 7) * 100); - const fill = document.getElementById("daily-progress-fill"); - const text = document.getElementById("daily-progress-text"); - if (fill) fill.style.width = pct + "%"; - if (text) text.textContent = `${done} / 7 Stationen abgeschlossen`; - - /* Alle 7 fertig? */ - if (done >= 7) { - const wrap = document.getElementById("daily-map-wrap"); - if (wrap && !document.getElementById("daily-all-done")) { - const panel = document.createElement("div"); - panel.innerHTML = buildAllDonePanel(); - wrap.appendChild(panel.firstElementChild); - } - } +function allDoneHtml() { + return `
+
🏆
+
TAGESQUEST ABGESCHLOSSEN!
+
Du hast alle 7 Stationen gemeistert.
+ +
`; }