dok/public/js/hud.js
2026-04-14 09:56:18 +01:00

268 lines
10 KiB
JavaScript
Raw Permalink 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.

/* ================================
HUD Charakter & Währungsanzeige
================================ */
async function fetchHud() {
const res = await fetch("/api/hud");
if (!res.ok) throw new Error("HUD API Fehler");
return await res.json();
}
function applyHudData(data) {
const set = (id, val) => {
const el = document.getElementById(id);
if (el) el.textContent = formatNumber(val);
};
const nameEl = document.getElementById("hud-name");
if (nameEl) nameEl.textContent = data.name;
set("hud-gems", data.gems);
set("hud-gold", data.gold);
set("hud-wood", data.wood);
set("hud-stone", data.stone);
/* ── Energie anzeigen ────────────────────────────── */
const energy = data.energy ?? 40;
const energyMax = data.energy_max ?? 40;
const energyEl = document.getElementById("hud-energy-value");
if (energyEl) energyEl.textContent = energy + " / " + energyMax;
/* Farbe je nach Stand */
if (energyEl) {
energyEl.style.color =
energy <= 0 ? "#e74c3c" // leer → rot
: energy <= 10 ? "#e67e22" // niedrig → orange
: energy < energyMax ? "#f0d9a6" // teilweise → normal
: "#7de87d"; // voll → grün
}
}
export async function loadHud() {
try {
const data = await fetchHud();
applyHudData(data);
// Auto-Refresh alle 30 Sekunden
setInterval(async () => {
try {
const d = await fetchHud();
applyHudData(d);
} catch {}
}, 30000);
} catch (err) {
console.error("HUD Fehler:", err);
}
}
export async function refreshHud() {
try {
const data = await fetchHud();
applyHudData(data);
} catch (err) {
console.error("HUD Refresh Fehler:", err);
}
}
function formatNumber(n) {
if (n === undefined || n === null) return "0";
return Number(n).toLocaleString("de-DE");
}
/* ════════════════════════════════════════════
Energie kaufen Popup beim + Klick
════════════════════════════════════════════ */
document.getElementById("hud-energy-plus")?.addEventListener("click", openEnergyBuyPopup);
async function openEnergyBuyPopup() {
/* Aktuellen Stand laden */
let hud, energyStatus;
try {
[hud, energyStatus] = await Promise.all([
fetch("/api/hud").then(r => r.json()),
fetch("/api/energy").then(r => r.json()),
]);
} catch {
return;
}
/* Altes Popup entfernen */
document.getElementById("energy-buy-overlay")?.remove();
const alreadyBought = energyStatus.bought_today;
const energyFull = (energyStatus.energy ?? 0) >= (energyStatus.energy_max ?? 40);
const canGems = hud.gems >= 10;
const canGold = hud.gold >= 200;
const overlay = document.createElement("div");
overlay.id = "energy-buy-overlay";
overlay.style.cssText = `
position:fixed; inset:0; z-index:99999;
background:rgba(0,0,0,.75);
display:flex; align-items:center; justify-content:center;`;
/* ── Inhalt je nach Status ── */
let body = "";
if (alreadyBought) {
body = `
<div style="font-size:36px;margin-bottom:12px;">⏳</div>
<div style="font-family:'Cinzel',serif;font-size:16px;color:#f0d060;
letter-spacing:2px;margin-bottom:10px;">BEREITS AUFGEFÜLLT</div>
<p style="font-family:'Cinzel',serif;font-size:12px;color:#a08060;
line-height:1.8;margin:0 0 20px;">
Du hast heute bereits Energie dazugekauft.<br>
<strong style="color:#f0d060;">Morgen wieder verfügbar.</strong>
</p>`;
} else if (energyFull) {
body = `
<div style="font-size:36px;margin-bottom:12px;">⚡</div>
<div style="font-family:'Cinzel',serif;font-size:16px;color:#7de87d;
letter-spacing:2px;margin-bottom:10px;">ENERGIE VOLL</div>
<p style="font-family:'Cinzel',serif;font-size:12px;color:#a08060;
line-height:1.8;margin:0 0 20px;">
Deine Energie ist bereits auf dem Maximum.
</p>`;
} else {
const gemBtn = canGems
? `<button class="ebtn" data-pay="gems"
style="background:linear-gradient(#1a2a4a,#0a1a3a);border:2px solid #4a7acc;
border-radius:9px;color:#a0c8ff;font-family:'Cinzel',serif;
font-size:12px;padding:12px 20px;cursor:pointer;flex:1;transition:.15s;">
<div style="font-size:22px;margin-bottom:5px;">💠</div>
<div style="font-weight:700;font-size:14px;margin-bottom:3px;">10 Gems</div>
<div style="font-size:10px;color:#6090c0;">Verfügbar: ${formatNum(hud.gems)}</div>
</button>`
: `<button disabled style="background:rgba(20,20,30,.6);border:2px solid rgba(80,100,140,.3);
border-radius:9px;color:rgba(100,130,180,.4);font-family:'Cinzel',serif;
font-size:12px;padding:12px 20px;flex:1;cursor:not-allowed;">
<div style="font-size:22px;margin-bottom:5px;">💠</div>
<div style="font-weight:700;font-size:14px;margin-bottom:3px;">10 Gems</div>
<div style="font-size:10px;">Zu wenig Gems</div>
</button>`;
const goldBtn = canGold
? `<button class="ebtn" data-pay="gold"
style="background:linear-gradient(#2a1a04,#1a0f02);border:2px solid #c8960c;
border-radius:9px;color:#f0d060;font-family:'Cinzel',serif;
font-size:12px;padding:12px 20px;cursor:pointer;flex:1;transition:.15s;">
<div style="font-size:22px;margin-bottom:5px;">🪙</div>
<div style="font-weight:700;font-size:14px;margin-bottom:3px;">200 Gold</div>
<div style="font-size:10px;color:#a08040;">Verfügbar: ${formatNum(hud.gold)}</div>
</button>`
: `<button disabled style="background:rgba(20,15,5,.6);border:2px solid rgba(140,100,20,.3);
border-radius:9px;color:rgba(180,140,40,.4);font-family:'Cinzel',serif;
font-size:12px;padding:12px 20px;flex:1;cursor:not-allowed;">
<div style="font-size:22px;margin-bottom:5px;">🪙</div>
<div style="font-weight:700;font-size:14px;margin-bottom:3px;">200 Gold</div>
<div style="font-size:10px;">Zu wenig Gold</div>
</button>`;
body = `
<div style="font-size:36px;margin-bottom:10px;">⚡</div>
<div style="font-family:'Cinzel',serif;font-size:16px;color:#f0d060;
letter-spacing:2px;margin-bottom:6px;">ENERGIE AUFFÜLLEN</div>
<p style="font-family:'Cinzel',serif;font-size:11px;color:#a08060;
margin:0 0 18px;line-height:1.7;">
+10 Energie · max. 1× täglich
</p>
<div style="display:flex;gap:12px;width:100%;">${gemBtn}${goldBtn}</div>
${!canGems && !canGold
? `<p style="font-family:'Cinzel',serif;font-size:11px;color:#e74c3c;
margin:12px 0 0;">Weder genug Gems noch Gold vorhanden.</p>`
: ""}`;
}
overlay.innerHTML = `
<div style="
background:linear-gradient(135deg,#1a0f04,#0a0803);
border:2px solid #c8960c; border-radius:14px;
padding:28px 32px; max-width:360px; width:90%;
text-align:center;
box-shadow:0 20px 60px rgba(0,0,0,.9);
font-family:'Cinzel',serif;">
${body}
<button id="ebtn-close"
style="margin-top:16px;background:none;border:1px solid rgba(200,150,60,.35);
border-radius:7px;color:#806040;font-family:'Cinzel',serif;
font-size:11px;padding:7px 22px;cursor:pointer;transition:.15s;">
Schließen
</button>
</div>`;
document.body.appendChild(overlay);
/* Schließen */
document.getElementById("ebtn-close").addEventListener("click", () => overlay.remove());
overlay.addEventListener("click", e => { if (e.target === overlay) overlay.remove(); });
/* Kauf-Buttons */
overlay.querySelectorAll(".ebtn").forEach(btn => {
btn.addEventListener("mouseenter", () => { btn.style.transform = "translateY(-2px)"; btn.style.opacity = ".9"; });
btn.addEventListener("mouseleave", () => { btn.style.transform = ""; btn.style.opacity = ""; });
btn.addEventListener("click", () => buyEnergy(btn.dataset.pay, overlay));
});
}
async function buyEnergy(payWith, overlay) {
/* Buttons deaktivieren während Kauf */
overlay.querySelectorAll(".ebtn").forEach(b => { b.disabled = true; b.style.opacity = ".5"; });
try {
const res = await fetch("/api/energy/buy", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ currency: payWith }),
});
const data = await res.json();
if (!res.ok) {
overlay.querySelector("div > div:last-of-type").insertAdjacentHTML(
"beforebegin",
`<p style="font-family:'Cinzel',serif;font-size:11px;color:#e74c3c;
margin:8px 0 0;">${data.error || "Fehler beim Kauf."}</p>`
);
overlay.querySelectorAll(".ebtn").forEach(b => { b.disabled = false; b.style.opacity = ""; });
return;
}
/* Erfolg: HUD aktualisieren + Popup mit Bestätigung */
await refreshHud();
overlay.remove();
const confirm = document.createElement("div");
confirm.style.cssText = `
position:fixed; inset:0; z-index:99999;
background:rgba(0,0,0,.75);
display:flex; align-items:center; justify-content:center;`;
confirm.innerHTML = `
<div style="background:linear-gradient(135deg,#0a1a08,#041004);
border:2px solid #4a8a3c; border-radius:14px;
padding:28px 36px; text-align:center; max-width:300px;
box-shadow:0 20px 60px rgba(0,0,0,.9); font-family:'Cinzel',serif;">
<div style="font-size:44px;margin-bottom:10px;">⚡</div>
<div style="font-size:16px;color:#7de87d;letter-spacing:2px;margin-bottom:8px;">
+10 ENERGIE
</div>
<p style="font-size:12px;color:#a0c890;margin:0 0 18px;">
Energie aufgefüllt auf ${data.energy} / ${data.energy_max}
</p>
<button onclick="this.closest('div[style]').remove()"
style="background:linear-gradient(#1a4a18,#0f2a0e);border:2px solid #4a8a3c;
border-radius:7px;color:#a0e090;font-family:'Cinzel',serif;
font-size:12px;padding:8px 24px;cursor:pointer;">
✔ OK
</button>
</div>`;
document.body.appendChild(confirm);
setTimeout(() => confirm.remove(), 3000);
} catch {
overlay.querySelectorAll(".ebtn").forEach(b => { b.disabled = false; b.style.opacity = ""; });
}
}
function formatNum(n) {
return Number(n || 0).toLocaleString("de-DE");
}