From e1e0128e0e852dd1722bcb6ecf1da5eb2e9e366e Mon Sep 17 00:00:00 2001 From: cay Date: Tue, 14 Apr 2026 09:30:27 +0100 Subject: [PATCH] ghkghc --- app.js | 56 ++++++++++++++++++++---- public/js/buildings/arena.js | 71 +++++++++++++++++++++++++++++- public/js/hud.js | 15 +++++++ sockets/arena.socket.js | 83 ++++++++++++++++++++++++++++++++++-- 4 files changed, 212 insertions(+), 13 deletions(-) diff --git a/app.js b/app.js index 34ea516..5c84cd0 100644 --- a/app.js +++ b/app.js @@ -334,23 +334,45 @@ app.post("/api/building/:id/upgrade", requireLogin, async (req, res) => { ======================== */ app.get("/api/hud", requireLogin, async (req, res) => { - const userId = req.session.user.id; + const userId = req.session.user.id; + const ENERGY_MAX = 40; try { const [[account]] = await db.query( "SELECT ingame_name FROM accounts WHERE id = ?", [userId], ); const [[currency]] = await db.query( - "SELECT silver, gold, gems, wood, stone FROM account_currency WHERE account_id = ?", + "SELECT silver, gold, gems, wood, stone, energy, energy_reset FROM account_currency WHERE account_id = ?", [userId], ); + + /* ── Täglicher Energie-Reset ──────────────────────────── + Wenn energy_reset < heute (oder NULL) → Energie auf Max + ────────────────────────────────────────────────────────── */ + const today = new Date().toISOString().slice(0, 10); + const lastReset = currency?.energy_reset + ? new Date(currency.energy_reset).toISOString().slice(0, 10) + : null; + + let currentEnergy = currency?.energy ?? ENERGY_MAX; + + if (lastReset !== today) { + currentEnergy = ENERGY_MAX; + await db.query( + "UPDATE account_currency SET energy = ?, energy_reset = ? WHERE account_id = ?", + [ENERGY_MAX, today, userId], + ); + } + res.json({ - name: account?.ingame_name || "Held", - silver: currency?.silver || 0, - gold: currency?.gold || 0, - gems: currency?.gems || 0, - wood: currency?.wood || 0, - stone: currency?.stone || 0, + name: account?.ingame_name || "Held", + silver: currency?.silver || 0, + gold: currency?.gold || 0, + gems: currency?.gems || 0, + wood: currency?.wood || 0, + stone: currency?.stone || 0, + energy: currentEnergy, + energy_max: ENERGY_MAX, }); } catch (err) { console.error(err); @@ -412,6 +434,24 @@ app.use("/api", bazaarRoutes); app.use("/himmelstor", himmelstorRoutes); app.use("/api/himmelstor/daily", himmelstorDailyRoutes); +/* ======================== + Energie abfragen +======================== */ + +app.get("/api/energy", requireLogin, async (req, res) => { + const userId = req.session.user.id; + const ENERGY_MAX = 40; + try { + const [[row]] = await db.query( + "SELECT energy FROM account_currency WHERE account_id = ?", + [userId], + ); + res.json({ energy: row?.energy ?? ENERGY_MAX, energy_max: ENERGY_MAX }); + } catch (err) { + res.status(500).json({ error: "DB Fehler" }); + } +}); + /* ======================== 404 Handler ======================== */ diff --git a/public/js/buildings/arena.js b/public/js/buildings/arena.js index b365d2c..865673d 100644 --- a/public/js/buildings/arena.js +++ b/public/js/buildings/arena.js @@ -387,11 +387,72 @@ function initArenaModes() { /* ── 1v1 ───────────────────────────────────────────────────*/ +/* ── Energie prüfen ──────────────────────────────────────── + Gibt true zurück wenn Energie vorhanden, sonst false + Hinweis +────────────────────────────────────────────────────────────── */ +async function checkEnergy() { + try { + const res = await fetch("/api/energy"); + if (!res.ok) return true; // bei Fehler nicht blockieren + const { energy } = await res.json(); + if (energy <= 0) { + showEnergyError(); + return false; + } + return true; + } catch { + return true; // bei Netzwerkfehler nicht blockieren + } +} + +function showEnergyError() { + /* Bestehende Fehlermeldung entfernen */ + document.getElementById("arena-energy-notice")?.remove(); + + const box = document.createElement("div"); + box.id = "arena-energy-notice"; + box.style.cssText = ` + position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); + z-index:20000; + background:linear-gradient(135deg,#1a0f04,#2a1808); + border:2px solid rgba(231,76,60,.6); + border-radius:14px; padding:28px 36px; + text-align:center; max-width:340px; + box-shadow:0 20px 60px rgba(0,0,0,.85); + font-family:'Cinzel',serif; + animation:arenaFadeIn .25s ease;`; + box.innerHTML = ` +
+
KEINE ENERGIE
+

+ Du hast heute keine Energie mehr für Arena-Kämpfe.
+ Deine Energie wird täglich um Mitternacht erneuert. +

+ `; + + document.body.appendChild(box); + document.getElementById("arena-energy-ok").addEventListener("click", () => box.remove()); + + /* Klick außerhalb schließt auch */ + setTimeout(() => { + const close = (e) => { if (!box.contains(e.target)) { box.remove(); document.removeEventListener("click", close); } }; + document.addEventListener("click", close); + }, 100); +} + async function handle1v1Click(card) { const socket = getSocket(); if (!socket) { showArenaError("Keine Verbindung zum Server."); return; } if (card.classList.contains("searching")) return; + /* Energie prüfen bevor Queue beigetreten wird */ + if (!await checkEnergy()) return; + let me; try { const res = await fetch("/arena/me"); @@ -412,8 +473,13 @@ async function handle1v1Click(card) { socket.off("queue_status"); socket.on("queue_status", data => { - if (data.status === "waiting") showQueueStatus(me.level, levelRange, data.poolSize); + if (data.status === "waiting") showQueueStatus(me.level, levelRange, data.poolSize); else if (data.status === "left") { setCardSearching(card, false); hideQueueStatus(); } + else if (data.status === "error") { + setCardSearching(card, false); + hideQueueStatus(); + showEnergyError(); + } }); socket.once("match_found", data => { @@ -442,6 +508,9 @@ function cancelQueue(card) { async function openTeamLobby(mode) { const socket = getSocket(); + /* Energie prüfen bevor Lobby geöffnet wird */ + if (!await checkEnergy()) return; + document.getElementById("arena-mode-screen").style.display = "none"; document.getElementById(`arena-${mode}-screen`).style.display = "flex"; diff --git a/public/js/hud.js b/public/js/hud.js index 891f4ff..84ef916 100644 --- a/public/js/hud.js +++ b/public/js/hud.js @@ -21,6 +21,21 @@ function applyHudData(data) { 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() { diff --git a/sockets/arena.socket.js b/sockets/arena.socket.js index 3c29f31..6d62f27 100644 --- a/sockets/arena.socket.js +++ b/sockets/arena.socket.js @@ -5,6 +5,7 @@ ============================================================ */ const { runCombatPhase } = require('./combat'); +const db = require('../database/database'); const { htAiMatchIds } = require('./1vKI_daily.socket'); const db = require('../database/database'); const pointsRoute = require('../routes/points.route'); @@ -156,8 +157,49 @@ function leaveAllTeams_nvn(socketId, io, mode) { } } +/* ── Energie prüfen und abziehen ───────────────────────────── + Gibt true zurück wenn genug Energie vorhanden, sonst false. + Zieht ENERGY_COST ab und setzt energy_reset auf heute. +────────────────────────────────────────────────────────────── */ +const ENERGY_COST = 2; +const ENERGY_MAX = 40; + +async function deductEnergy(accountId) { + if (!accountId) return true; // kein Account → erlauben (Fallback) + try { + const today = new Date().toISOString().slice(0, 10); + + // Energie laden + ggf. tagesreset + const [[row]] = await db.query( + "SELECT energy, energy_reset FROM account_currency WHERE account_id = ?", + [accountId] + ); + if (!row) return true; // kein Eintrag → erlauben + + const lastReset = row.energy_reset + ? new Date(row.energy_reset).toISOString().slice(0, 10) + : null; + const current = lastReset !== today ? ENERGY_MAX : (row.energy ?? ENERGY_MAX); + + if (current < ENERGY_COST) return false; // nicht genug Energie + + // Energie abziehen + await db.query( + `UPDATE account_currency + SET energy = ?, + energy_reset = ? + WHERE account_id = ?`, + [current - ENERGY_COST, today, accountId] + ); + return true; + } catch (err) { + console.error('[Energy] deductEnergy Fehler:', err); + return true; // bei DB-Fehler nicht blockieren + } +} + /* ── 1v1 Matchmaking ── */ -function tryMatchmaking(io, newSocketId) { +async function tryMatchmaking(io, newSocketId) { const challenger = waitingPool.get(newSocketId); if (!challenger) return; @@ -170,6 +212,38 @@ function tryMatchmaking(io, newSocketId) { waitingPool.delete(id); const matchId = `match_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`; + + // Energie beider Spieler abziehen (2 pro Match) + const [energyOk1, energyOk2] = await Promise.all([ + deductEnergy(challenger.player.accountId), + deductEnergy(entry.player.accountId), + ]); + + if (!energyOk1) { + challenger.socket.emit("queue_status", { + status: "error", + message: "Nicht genug Energie für einen Kampf. Warte bis morgen!", + }); + waitingPool.set(newSocketId, challenger); // zurück in Pool + waitingPool.set(id, entry); + return; + } + if (!energyOk2) { + // Energie von Spieler 1 zurückgeben + if (challenger.player.accountId) { + db.query( + "UPDATE account_currency SET energy = LEAST(energy + ?, ?) WHERE account_id = ?", + [ENERGY_COST, ENERGY_MAX, challenger.player.accountId] + ).catch(() => {}); + } + entry.socket.emit("queue_status", { + status: "error", + message: "Nicht genug Energie für einen Kampf. Warte bis morgen!", + }); + waitingPool.set(newSocketId, challenger); + return; + } + challenger.socket.emit("match_found", { matchId, opponent: entry.player, @@ -564,9 +638,10 @@ function registerArenaHandlers(io, socket) { socket.on("join_1v1", (playerData) => { if (waitingPool.has(socket.id)) return; const player = { - id: playerData.id, - name: playerData.name, - level: Number(playerData.level) || 1, + id: playerData.id, + name: playerData.name, + level: Number(playerData.level) || 1, + accountId: playerData.id || null, // für Energie-Abzug }; // levelRange vom Client übernehmen (5 oder 10), Standard = 5 const levelRange = [5, 10].includes(Number(playerData.levelRange))