import { openDailyMap, markDailyStationDone } from './daily.js';
/* ============================================================
public/js/buildings/himmelstor.js
Himmelstor – Tages- und Wochenherausforderung
Gleiche Struktur wie arena.js, aber Daily & Weekly
============================================================ */
export async function loadHimmelstor() {
const ui = document.querySelector(".building-ui");
if (!ui) return;
/* ── Decks laden ─────────────────────────────────────────── */
let decks = [];
try {
const res = await fetch("/api/decks");
if (res.ok) decks = await res.json();
} catch (e) { console.error("[Himmelstor] Decks:", e); }
const deckOptions = decks.length === 0
? ``
: `` +
decks.filter(d => d.card_count > 0)
.map(d => ``)
.join("") +
(decks.some(d => d.card_count === 0)
? decks.filter(d => d.card_count === 0)
.map(d => ``)
.join("")
: "");
ui.innerHTML = `
🌤️ Himmelstor
Wähle Deck und Herausforderung
☀️
Daily
Tagesherausforderung
🌙
Weekly
Wochenherausforderung
${decks.length === 0
? `
⚠️ Kein Deck vorhanden – gehe zur Burg → Kartendeck
`
: ""}
`;
injectHimmelstorStyles();
initHimmelstorModes();
}
/* ── Styles ──────────────────────────────────────────────── */
function injectHimmelstorStyles() {
if (document.getElementById("ht-popup-styles")) return;
const style = document.createElement("style");
style.id = "ht-popup-styles";
style.textContent = `
@keyframes htFadeIn { from{opacity:0} to{opacity:1} }
@keyframes htScaleIn { from{transform:scale(0.94);opacity:0} to{transform:scale(1);opacity:1} }
@keyframes htPulse { 0%,100%{opacity:1} 50%{opacity:0.5} }
#himmelstor-ui { display:flex; flex-direction:column; height:100%; }
#himmelstor-mode-screen {
display:flex; flex-direction:column; align-items:center; padding:16px;
}
.ht-title {
font-family:"Cinzel",serif; font-size:18px; color:#f0d060;
letter-spacing:3px; text-align:center; margin-bottom:4px;
}
.ht-subtitle {
font-family:"Cinzel",serif; font-size:11px; color:#a08060;
margin:0 0 14px;
}
.ht-modes {
display:flex; gap:10px; justify-content:center;
flex-wrap:wrap; width:100%;
}
.ht-mode-wrap {
flex:1; min-width:120px; max-width:195px;
display:flex; flex-direction:column; gap:6px;
}
.ht-mode-card {
flex:1; min-width:120px; max-width:195px;
background:linear-gradient(180deg,#1a1a2e,#0f0f1a);
border:2px solid #4a3a6b; border-radius:12px;
padding:21px 12px; cursor:pointer; text-align:center;
transition:border-color .2s, transform .1s;
box-shadow: inset 0 1px 0 rgba(160,120,255,0.1), 0 4px 16px rgba(0,0,0,0.6);
}
.ht-mode-card:hover { border-color:#9b72cf; transform:translateY(-2px); }
.ht-mode-card.searching {
opacity:.6; pointer-events:none;
border-color:rgba(155,114,207,.5) !important;
}
.ht-mode-locked {
opacity:.45; cursor:not-allowed !important;
border-color:#2a1e40 !important;
}
.ht-mode-locked:hover { transform:none !important; border-color:#2a1e40 !important; }
.ht-mode-icon { font-size:36px; margin-bottom:9px; }
.ht-mode-label {
font-family:"Cinzel",serif; font-size:19px;
color:#c8a0ff; font-weight:bold;
}
.ht-mode-desc { font-size:15px; color:#6a5080; margin-top:6px; line-height:1.4; }
.ht-deck-select {
width:100%; background:#0f0f1a; border:1px solid #4a3a6b;
border-radius:6px; color:#e0d0ff; font-family:"Cinzel",serif;
font-size:15px; padding:8px 9px; cursor:pointer;
appearance:none; -webkit-appearance:none;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23806090'/%3E%3C/svg%3E");
background-repeat:no-repeat; background-position:right 6px center;
padding-right:28px;
}
.ht-deck-select:focus { outline:none; border-color:#9b72cf; }
.ht-deck-select option { background:#0f0f1a; color:#e0d0ff; }
.ht-no-deck-hint {
margin-top:10px; padding:8px 12px; border-radius:7px;
background:rgba(231,76,60,.1); border:1px solid rgba(231,76,60,.3);
color:#e07060; font-family:"Cinzel",serif; font-size:11px; text-align:center;
}
.ht-no-deck-hint strong { color:#c8a0ff; }
#ht-queue-status {
margin-top:12px; padding:10px 18px; border-radius:10px;
width:100%; box-sizing:border-box;
background:rgba(155,114,207,.08); border:1px solid rgba(155,114,207,.3);
color:#c8a0ff; font-family:"Cinzel",serif; font-size:12px;
letter-spacing:1px; text-align:center; animation:htPulse 2s ease-in-out infinite;
}
.ht-cancel {
display:inline-block; margin-top:6px; font-size:11px;
color:rgba(255,100,100,.8); cursor:pointer; text-decoration:underline;
animation:none;
}
.ht-cancel:hover { color:#e74c3c; }
/* Match-Found Overlay */
#ht-match-found-overlay {
position:fixed; inset:0; z-index:10000;
background:rgba(0,0,0,.9); display:flex; flex-direction:column;
align-items:center; justify-content:center; animation:htFadeIn .3s ease;
}
.ht-mfo-title {
font-family:"Cinzel",serif; font-size:36px; color:#c8a0ff;
text-shadow:0 0 30px rgba(155,114,207,.6);
letter-spacing:6px; margin-bottom:12px;
}
.ht-mfo-vs {
font-family:"Cinzel",serif; font-size:18px;
color:rgba(255,255,255,.75); letter-spacing:3px;
}
.ht-mfo-bar {
width:300px; height:4px; background:rgba(155,114,207,.2);
border-radius:2px; margin-top:24px; overflow:hidden;
}
.ht-mfo-bar-fill {
height:100%; background:#9b72cf; width:0%;
border-radius:2px; transition:width 1.5s ease;
}
/* Popup iframe */
#ht-backdrop {
position:fixed; inset:0; background:rgba(0,0,0,.82);
backdrop-filter:blur(5px); z-index:9998; animation:htFadeIn .25s ease;
}
#ht-popup {
position:fixed; inset:50px; z-index:9999; display:flex;
flex-direction:column; border-radius:14px; overflow:hidden;
box-shadow:0 0 0 1px rgba(155,114,207,.35),0 30px 90px rgba(0,0,0,.85);
animation:htScaleIn .28s cubic-bezier(.22,1,.36,1);
}
#ht-popup-titlebar {
display:flex; align-items:center; justify-content:space-between;
background:rgba(10,8,20,.95); border-bottom:1px solid rgba(155,114,207,.3);
padding:0 16px; height:42px; flex-shrink:0;
}
#ht-popup-titlebar .ht-ap-title {
font-family:"Cinzel",serif; font-size:13px; letter-spacing:4px;
color:rgba(200,160,255,.85); text-transform:uppercase;
}
#ht-popup-titlebar .ht-ap-url { font-size:11px; color:rgba(255,255,255,.22); }
#ht-popup iframe { flex:1; border:none; width:100%; display:block; }
`;
document.head.appendChild(style);
}
/* ── State ───────────────────────────────────────────────── */
let htSelectedDeckId = null;
/* ── Modus-Initialisierung ───────────────────────────────── */
function initHimmelstorModes() {
/* Deck-Dropdown: Karte freischalten wenn Deck gewählt */
document.querySelectorAll(".ht-deck-select").forEach(select => {
select.addEventListener("change", () => {
const mode = select.dataset.mode;
const card = document.querySelector(`.ht-mode-card[data-mode="${mode}"]`);
if (!card) return;
if (select.value) {
card.classList.remove("ht-mode-locked");
htSelectedDeckId = Number(select.value);
} else {
card.classList.add("ht-mode-locked");
}
});
});
/* Modus-Karte klicken */
document.querySelectorAll(".ht-mode-card").forEach(card => {
card.addEventListener("click", () => {
if (card.classList.contains("ht-mode-locked")) return;
const mode = card.dataset.mode;
const select = document.querySelector(`.ht-deck-select[data-mode="${mode}"]`);
htSelectedDeckId = select ? Number(select.value) : null;
if (!htSelectedDeckId) return;
sessionStorage.setItem("selectedDeckId", htSelectedDeckId);
if (mode === 'daily') {
// Karten-Pfad öffnen statt direktes Matchmaking
openDailyMap(htSelectedDeckId);
return;
}
handleHtModeClick(card, mode);
});
});
}
/* ── Station-Complete Event vom Server ──────────────────── */
function setupHtSocketListeners() {
const socket = window._socket;
if (!socket) return;
socket.off('ht_station_complete');
socket.on('ht_station_complete', data => {
markDailyStationDone(data.station);
});
}
// Listener einrichten sobald Socket verfügbar
if (window._socket) setupHtSocketListeners();
else document.addEventListener('socket_ready', setupHtSocketListeners);
/* ── Modus klicken ───────────────────────────────────────── */
async function handleHtModeClick(card, mode) {
if (card.classList.contains("searching")) return;
let me;
try {
const res = await fetch("/arena/me");
if (!res.ok) throw new Error("Status " + res.status);
me = await res.json();
} catch {
showHtError("Spielerdaten konnten nicht geladen werden.");
return;
}
setHtCardSearching(card, mode, true);
showHtQueueStatus(mode);
const socket = window._socket;
if (!socket) { showHtError("Keine Verbindung zum Server."); return; }
socket.off("ht_match_found");
socket.off("ht_queue_status");
socket.on("ht_queue_status", data => {
if (data.status === "waiting") showHtQueueStatus(mode, data.poolSize);
else if (data.status === "left") { setHtCardSearching(card, mode, false); hideHtQueueStatus(); }
});
socket.once("ht_match_found", data => {
socket.off("ht_queue_status");
setHtCardSearching(card, mode, false);
hideHtQueueStatus();
showHtMatchFoundOverlay(me.name, data.opponent?.name || "Gegner", () => {
openHtPopup(
`/himmelstor/${mode}?match=${encodeURIComponent(data.matchId)}&slot=${encodeURIComponent(data.mySlot)}&deck=${encodeURIComponent(htSelectedDeckId || '')}&opponent=${encodeURIComponent(data.opponent?.name || '')}`,
data.opponent?.name || "Gegner", data.matchId
);
});
});
socket.emit("ht_join", {
id: me.id, name: me.name, level: me.level, mode,
});
}
/* ── UI Hilfsfunktionen ──────────────────────────────────── */
const HT_LABELS = { daily: "Daily", weekly: "Weekly" };
const HT_DESCS = { daily: "Tagesherausforderung", weekly: "Wochenherausforderung" };
function setHtCardSearching(card, mode, searching) {
const label = card.querySelector(".ht-mode-label");
const desc = card.querySelector(".ht-mode-desc");
if (searching) {
card.classList.add("searching");
label.textContent = "⏳ Suche…";
desc.textContent = "Warte auf Gegner…";
} else {
card.classList.remove("searching");
label.textContent = HT_LABELS[mode] || mode;
desc.textContent = HT_DESCS[mode] || "";
}
}
function showHtQueueStatus(mode, poolSize) {
const box = document.getElementById("ht-queue-status");
if (!box) return;
const pool = poolSize ? ` · ${poolSize} im Pool` : "";
const label = mode === "daily" ? "Tagesherausforderung" : "Wochenherausforderung";
box.style.display = "block";
box.innerHTML = `⏳ Suche ${label}${pool}
Suche abbrechen`;
document.getElementById("ht-cancel-btn")?.addEventListener("click", () => {
cancelHtQueue(document.querySelector(`.ht-mode-card[data-mode="${mode}"]`), mode);
});
}
function hideHtQueueStatus() {
const box = document.getElementById("ht-queue-status");
if (box) box.style.display = "none";
}
function showHtError(msg) {
const box = document.getElementById("ht-queue-status");
if (!box) return;
box.style.cssText += ";display:block;animation:none;border-color:rgba(231,76,60,.5);color:#e74c3c;";
box.textContent = "❌ " + msg;
setTimeout(() => {
box.style.display = "none";
box.style.animation = "";
box.style.borderColor = "";
box.style.color = "";
}, 3000);
}
function cancelHtQueue(card, mode) {
const socket = window._socket;
if (socket) { socket.emit("ht_leave", { mode }); socket.off("ht_match_found"); socket.off("ht_queue_status"); }
if (card) setHtCardSearching(card, mode, false);
hideHtQueueStatus();
}
function showHtMatchFoundOverlay(myName, opponentName, onDone) {
if (document.getElementById("ht-match-found-overlay")) return;
const overlay = document.createElement("div");
overlay.id = "ht-match-found-overlay";
overlay.innerHTML = `
🌤️ Herausforderung!
${myName} vs ${opponentName}
`;
document.body.appendChild(overlay);
requestAnimationFrame(() => {
const b = document.getElementById("htMfBar");
if (b) b.style.width = "100%";
});
setTimeout(() => { overlay.remove(); onDone(); }, 1600);
}
function openHtPopup(src, opponentName, matchId) {
document.getElementById("ht-backdrop")?.remove();
document.getElementById("ht-popup")?.remove();
const backdrop = document.createElement("div"); backdrop.id = "ht-backdrop";
const popup = document.createElement("div"); popup.id = "ht-popup";
popup.innerHTML = `
`;
document.body.appendChild(backdrop);
document.body.appendChild(popup);
}