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))