diff --git a/app.js b/app.js index 5c84cd0..a399f79 100644 --- a/app.js +++ b/app.js @@ -443,15 +443,90 @@ app.get("/api/energy", requireLogin, async (req, res) => { const ENERGY_MAX = 40; try { const [[row]] = await db.query( - "SELECT energy FROM account_currency WHERE account_id = ?", + "SELECT energy, energy_bought FROM account_currency WHERE account_id = ?", [userId], ); - res.json({ energy: row?.energy ?? ENERGY_MAX, energy_max: ENERGY_MAX }); + const today = new Date().toISOString().slice(0, 10); + const boughtDate = row?.energy_bought + ? new Date(row.energy_bought).toISOString().slice(0, 10) + : null; + res.json({ + energy: row?.energy ?? ENERGY_MAX, + energy_max: ENERGY_MAX, + bought_today: boughtDate === today, + }); } catch (err) { res.status(500).json({ error: "DB Fehler" }); } }); +/* ======================== + Energie kaufen +======================== */ + +app.post("/api/energy/buy", requireLogin, async (req, res) => { + const userId = req.session.user.id; + const ENERGY_MAX = 40; + const ENERGY_BUY = 10; // Energie die dazugekauft wird + const COST_GEMS = 10; + const COST_GOLD = 200; + const { currency: payWith } = req.body; // "gems" oder "gold" + + if (!["gems", "gold"].includes(payWith)) { + return res.status(400).json({ error: "Ungültige Zahlungsmethode." }); + } + + try { + const today = new Date().toISOString().slice(0, 10); + + const [[row]] = await db.query( + "SELECT energy, gems, gold, energy_bought FROM account_currency WHERE account_id = ?", + [userId], + ); + if (!row) return res.status(404).json({ error: "Account nicht gefunden." }); + + /* Bereits heute gekauft? */ + const boughtDate = row.energy_bought + ? new Date(row.energy_bought).toISOString().slice(0, 10) + : null; + if (boughtDate === today) { + return res.status(400).json({ error: "Du hast heute bereits Energie aufgefüllt." }); + } + + /* Genug Währung? */ + if (payWith === "gems" && row.gems < COST_GEMS) { + return res.status(400).json({ error: `Nicht genug Gems. Benötigt: ${COST_GEMS}.` }); + } + if (payWith === "gold" && row.gold < COST_GOLD) { + return res.status(400).json({ error: `Nicht genug Gold. Benötigt: ${COST_GOLD}.` }); + } + + /* Energie darf max auf ENERGY_MAX steigen */ + const newEnergy = Math.min((row.energy ?? ENERGY_MAX) + ENERGY_BUY, ENERGY_MAX); + + if (payWith === "gems") { + await db.query( + `UPDATE account_currency + SET energy = ?, gems = gems - ?, energy_bought = ? + WHERE account_id = ?`, + [newEnergy, COST_GEMS, today, userId], + ); + } else { + await db.query( + `UPDATE account_currency + SET energy = ?, gold = gold - ?, energy_bought = ? + WHERE account_id = ?`, + [newEnergy, COST_GOLD, today, userId], + ); + } + + res.json({ success: true, energy: newEnergy, energy_max: ENERGY_MAX }); + } catch (err) { + console.error("[Energy Buy]", err); + res.status(500).json({ error: "DB Fehler" }); + } +}); + /* ======================== 404 Handler ======================== */ diff --git a/public/js/hud.js b/public/js/hud.js index 84ef916..cd0e117 100644 --- a/public/js/hud.js +++ b/public/js/hud.js @@ -67,3 +67,201 @@ 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 = ` +
+
BEREITS AUFGEFÜLLT
+

+ Du hast heute bereits Energie dazugekauft.
+ Morgen wieder verfügbar. +

`; + } else if (energyFull) { + body = ` +
+
ENERGIE VOLL
+

+ Deine Energie ist bereits auf dem Maximum. +

`; + } else { + const gemBtn = canGems + ? `` + : ``; + + const goldBtn = canGold + ? `` + : ``; + + body = ` +
+
ENERGIE AUFFÜLLEN
+

+ +10 Energie · max. 1× täglich +

+
${gemBtn}${goldBtn}
+ ${!canGems && !canGold + ? `

Weder genug Gems noch Gold vorhanden.

` + : ""}`; + } + + overlay.innerHTML = ` +
+ ${body} + +
`; + + 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", + `

${data.error || "Fehler beim Kauf."}

` + ); + 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 = ` +
+
+
+ +10 ENERGIE +
+

+ Energie aufgefüllt auf ${data.energy} / ${data.energy_max} +

+ +
`; + 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"); +}