dok/public/js/buildings/daily.js
2026-04-14 08:31:15 +01:00

212 lines
8.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ============================================================
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" },
];
let dailyDeckId = null;
let dailyOverlay = null;
/* ── Öffnet den Karten-Pfad ──────────────────────────────── */
export async function openDailyMap(deckId) {
dailyDeckId = deckId;
// 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); }
/* Overlay aufbauen */
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" />
<div id="daily-map-title">☀️ TAGESHERAUSFORDERUNG</div>
<button id="daily-map-close" title="Schließen">✕</button>
${buildCircles(completed)}
<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>
</div>
${completed.length >= 7 ? buildAllDonePanel() : ""}
</div>`;
document.body.appendChild(dailyOverlay);
/* 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);
});
});
}
/* ── HTML der Kreise ─────────────────────────────────────── */
function buildCircles(completed) {
const maxDone = completed.length; // nächster freier = maxDone + 1
return DAILY_STATIONS.map(s => {
const isDone = completed.includes(s.id);
const isAvailable = !isDone && s.id === maxDone + 1;
const isLocked = !isDone && !isAvailable;
const cls = isDone ? "done" : isAvailable ? "available" : "locked";
const inner = isDone ? "" : s.label; // done zeigt ✓ per CSS ::after
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("");
}
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>`;
}
/* ── Station starten ─────────────────────────────────────── */
function startDailyStation(station) {
if (!dailyDeckId) {
alert("Bitte zuerst ein Deck auswählen.");
return;
}
const socket = window._socket;
if (!socket) {
console.error("[Daily] Kein 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";
}
/* 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_error", data => {
if (circle) { circle.style.animation = ""; circle.style.opacity = ""; circle.style.cursor = ""; }
alert(data.message || "Fehler beim Starten.");
});
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();
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;";
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 = `
<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-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 */
}
/* ── Overlay schließen ───────────────────────────────────── */
function closeDailyMap() {
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);
}
}
}