xzkxdzr
This commit is contained in:
parent
a31431ecaf
commit
49b571699b
@ -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; }
|
||||
|
||||
@ -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 = `
|
||||
<div id="daily-map-wrap">
|
||||
<img id="daily-map-img" src="/images/KI_arena/daily.jpeg"
|
||||
onerror="this.src='/images/items/rueckseite.png'" alt="Daily" />
|
||||
<img id="daily-map-img"
|
||||
src="/images/KI_arena/daily.jpeg"
|
||||
onerror="this.src='/images/items/rueckseite.png'" />
|
||||
|
||||
${buildSvg(completed)}
|
||||
|
||||
<div id="daily-map-title">☀️ TAGESHERAUSFORDERUNG</div>
|
||||
<button id="daily-map-close" title="Schließen">✕</button>
|
||||
${buildCircles(completed)}
|
||||
<button id="daily-map-close">✕</button>
|
||||
|
||||
<div id="daily-progress-bar-wrap">
|
||||
<div id="daily-progress-text">${completed.length} / 7 Stationen abgeschlossen</div>
|
||||
<div id="daily-progress-track">
|
||||
<div id="daily-progress-fill" style="width:${Math.round((completed.length / 7) * 100)}%"></div>
|
||||
<div id="daily-progress-fill" style="width:${Math.round(completed.length/7*100)}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
${completed.length >= 7 ? buildAllDonePanel() : ""}
|
||||
${completed.length >= 7 ? allDoneHtml() : ''}
|
||||
</div>`;
|
||||
|
||||
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(`<line class="ds-path-line${done ? ' done' : ''}"
|
||||
x1="${a.cx}" y1="${a.cy}" x2="${b.cx}" y2="${b.cy}"/>`);
|
||||
}
|
||||
|
||||
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 `<div
|
||||
class="daily-circle ${cls}"
|
||||
data-station="${s.id}"
|
||||
style="left:${s.left}%;top:${s.top}%;"
|
||||
title="${isDone ? "Abgeschlossen" : isAvailable ? `Station ${s.id} starten` : "Gesperrt"}"
|
||||
>${inner}</div>`;
|
||||
}).join("");
|
||||
}
|
||||
if (isDone) {
|
||||
parts.push(`
|
||||
<g class="ds-station ${cls}" data-station="${s.id}">
|
||||
<circle class="ds-overlay" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
||||
<text class="ds-check" x="${s.cx}" y="${s.cy}"
|
||||
text-anchor="middle" dominant-baseline="central"
|
||||
font-size="${fSize * 1.1}">✓</text>
|
||||
</g>`);
|
||||
|
||||
function buildAllDonePanel() {
|
||||
return `
|
||||
<div id="daily-all-done">
|
||||
<div class="done-icon">🏆</div>
|
||||
<div class="done-title">TAGESQUEST ABGESCHLOSSEN!</div>
|
||||
<div class="done-sub">Du hast alle 7 Stationen gemeistert.</div>
|
||||
<button class="done-btn" onclick="document.getElementById('daily-map-overlay')?.remove()">
|
||||
✔ SCHLIESSEN
|
||||
</button>
|
||||
</div>`;
|
||||
} else if (isAvail) {
|
||||
parts.push(`
|
||||
<g class="ds-station ${cls}" data-station="${s.id}">
|
||||
<!-- Pulsierender Ring 1 -->
|
||||
<circle class="ds-ring1" cx="${s.cx}" cy="${s.cy}" r="${R + 1.2}">
|
||||
<animate attributeName="r"
|
||||
values="${R+1.2};${R+2.6};${R+1.2}" dur="2s" repeatCount="indefinite"/>
|
||||
<animate attributeName="opacity"
|
||||
values=".7;0;.7" dur="2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<!-- Pulsierender Ring 2 (versetzt) -->
|
||||
<circle class="ds-ring2" cx="${s.cx}" cy="${s.cy}" r="${R + 2}">
|
||||
<animate attributeName="r"
|
||||
values="${R+2};${R+3.6};${R+2}" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||
<animate attributeName="opacity"
|
||||
values=".4;0;.4" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<!-- Sichtbarer Kreis -->
|
||||
<circle class="ds-overlay" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
||||
<!-- Nummer -->
|
||||
<text class="ds-num" x="${s.cx}" y="${s.cy}"
|
||||
text-anchor="middle" dominant-baseline="central"
|
||||
font-size="${fSize}">${s.id}</text>
|
||||
<!-- Unsichtbare Hitbox -->
|
||||
<circle class="ds-hit" cx="${s.cx}" cy="${s.cy}" r="${R + 1.5}"
|
||||
data-station="${s.id}"/>
|
||||
</g>`);
|
||||
|
||||
} else {
|
||||
parts.push(`
|
||||
<g class="ds-station ${cls}" data-station="${s.id}">
|
||||
<circle class="ds-overlay" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
||||
<text class="ds-num" x="${s.cx}" y="${s.cy}"
|
||||
text-anchor="middle" dominant-baseline="central"
|
||||
font-size="${fSize}">${s.id}</text>
|
||||
</g>`);
|
||||
}
|
||||
});
|
||||
|
||||
return `<svg id="daily-map-svg"
|
||||
viewBox="0 0 100 100"
|
||||
preserveAspectRatio="none">${parts.join('')}</svg>`;
|
||||
}
|
||||
|
||||
/* ── 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 = `
|
||||
<circle class="ds-overlay" cx="${STATIONS[station-1].cx}" cy="${STATIONS[station-1].cy}" r="${R}"/>
|
||||
<text class="ds-check" x="${STATIONS[station-1].cx}" y="${STATIONS[station-1].cy}"
|
||||
text-anchor="middle" dominant-baseline="central" font-size="${R*0.8}">✓</text>`;
|
||||
}
|
||||
|
||||
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 = `
|
||||
<circle class="ds-ring1" cx="${nextStation.cx}" cy="${nextStation.cy}" r="${R+1.2}">
|
||||
<animate attributeName="r" values="${R+1.2};${R+2.6};${R+1.2}" dur="2s" repeatCount="indefinite"/>
|
||||
<animate attributeName="opacity" values=".7;0;.7" dur="2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle class="ds-ring2" cx="${nextStation.cx}" cy="${nextStation.cy}" r="${R+2}">
|
||||
<animate attributeName="r" values="${R+2};${R+3.6};${R+2}" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||
<animate attributeName="opacity" values=".4;0;.4" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle class="ds-overlay" cx="${nextStation.cx}" cy="${nextStation.cy}" r="${R}"/>
|
||||
<text class="ds-num" x="${nextStation.cx}" y="${nextStation.cy}"
|
||||
text-anchor="middle" dominant-baseline="central" font-size="${fSize}">${station+1}</text>
|
||||
<circle class="ds-hit" cx="${nextStation.cx}" cy="${nextStation.cy}" r="${R+1.5}"
|
||||
data-station="${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 = `
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;background:rgba(5,8,20,.95);border-bottom:1px solid rgba(80,140,255,.3);padding:0 16px;height:42px;flex-shrink:0;">
|
||||
<span style="font-family:'Cinzel',serif;font-size:13px;letter-spacing:4px;color:rgba(160,200,255,.85);text-transform:uppercase;">☀️ Daily · vs ${opponentName}</span>
|
||||
<span style="font-family:'Cinzel',serif;font-size:13px;letter-spacing:4px;color:rgba(160,200,255,.85);text-transform:uppercase;">☀️ Daily · vs ${opponent}</span>
|
||||
<span style="font-size:11px;color:rgba(255,255,255,.22);">${matchId}</span>
|
||||
</div>
|
||||
<iframe src="${src}" allowfullscreen style="flex:1;border:none;width:100%;display:block;"></iframe>`;
|
||||
|
||||
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 `<div id="daily-all-done">
|
||||
<div class="done-icon">🏆</div>
|
||||
<div class="done-title">TAGESQUEST ABGESCHLOSSEN!</div>
|
||||
<div class="done-sub">Du hast alle 7 Stationen gemeistert.</div>
|
||||
<button class="done-btn" onclick="document.getElementById('daily-map-overlay')?.remove()">✔ SCHLIESSEN</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user