This commit is contained in:
cay 2026-04-14 09:30:27 +01:00
parent b61220425a
commit e1e0128e0e
4 changed files with 212 additions and 13 deletions

56
app.js
View File

@ -334,23 +334,45 @@ app.post("/api/building/:id/upgrade", requireLogin, async (req, res) => {
======================== */ ======================== */
app.get("/api/hud", 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 { try {
const [[account]] = await db.query( const [[account]] = await db.query(
"SELECT ingame_name FROM accounts WHERE id = ?", "SELECT ingame_name FROM accounts WHERE id = ?",
[userId], [userId],
); );
const [[currency]] = await db.query( 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], [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({ res.json({
name: account?.ingame_name || "Held", name: account?.ingame_name || "Held",
silver: currency?.silver || 0, silver: currency?.silver || 0,
gold: currency?.gold || 0, gold: currency?.gold || 0,
gems: currency?.gems || 0, gems: currency?.gems || 0,
wood: currency?.wood || 0, wood: currency?.wood || 0,
stone: currency?.stone || 0, stone: currency?.stone || 0,
energy: currentEnergy,
energy_max: ENERGY_MAX,
}); });
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -412,6 +434,24 @@ app.use("/api", bazaarRoutes);
app.use("/himmelstor", himmelstorRoutes); app.use("/himmelstor", himmelstorRoutes);
app.use("/api/himmelstor/daily", himmelstorDailyRoutes); 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 404 Handler
======================== */ ======================== */

View File

@ -387,11 +387,72 @@ function initArenaModes() {
/* ── 1v1 ───────────────────────────────────────────────────*/ /* ── 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 = `
<div style="font-size:44px;margin-bottom:12px;"></div>
<div style="font-size:17px;color:#e74c3c;letter-spacing:2px;margin-bottom:10px;">KEINE ENERGIE</div>
<p style="font-size:12px;color:#a08060;line-height:1.7;margin-bottom:20px;">
Du hast heute keine Energie mehr für Arena-Kämpfe.<br>
<strong style="color:#f0d060;">Deine Energie wird täglich um Mitternacht erneuert.</strong>
</p>
<button id="arena-energy-ok"
style="background:linear-gradient(#4a1010,#2a0808);border:2px solid #8a3030;
border-radius:8px;color:#e07070;font-family:'Cinzel',serif;
font-size:12px;padding:9px 28px;cursor:pointer;transition:.15s;">
Schließen
</button>`;
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) { async function handle1v1Click(card) {
const socket = getSocket(); const socket = getSocket();
if (!socket) { showArenaError("Keine Verbindung zum Server."); return; } if (!socket) { showArenaError("Keine Verbindung zum Server."); return; }
if (card.classList.contains("searching")) return; if (card.classList.contains("searching")) return;
/* Energie prüfen bevor Queue beigetreten wird */
if (!await checkEnergy()) return;
let me; let me;
try { try {
const res = await fetch("/arena/me"); const res = await fetch("/arena/me");
@ -412,8 +473,13 @@ async function handle1v1Click(card) {
socket.off("queue_status"); socket.off("queue_status");
socket.on("queue_status", data => { 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 === "left") { setCardSearching(card, false); hideQueueStatus(); }
else if (data.status === "error") {
setCardSearching(card, false);
hideQueueStatus();
showEnergyError();
}
}); });
socket.once("match_found", data => { socket.once("match_found", data => {
@ -442,6 +508,9 @@ function cancelQueue(card) {
async function openTeamLobby(mode) { async function openTeamLobby(mode) {
const socket = getSocket(); 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 = "none";
document.getElementById(`arena-${mode}-screen`).style.display = "flex"; document.getElementById(`arena-${mode}-screen`).style.display = "flex";

View File

@ -21,6 +21,21 @@ function applyHudData(data) {
set("hud-gold", data.gold); set("hud-gold", data.gold);
set("hud-wood", data.wood); set("hud-wood", data.wood);
set("hud-stone", data.stone); 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() { export async function loadHud() {

View File

@ -5,6 +5,7 @@
============================================================ */ ============================================================ */
const { runCombatPhase } = require('./combat'); const { runCombatPhase } = require('./combat');
const db = require('../database/database');
const { htAiMatchIds } = require('./1vKI_daily.socket'); const { htAiMatchIds } = require('./1vKI_daily.socket');
const db = require('../database/database'); const db = require('../database/database');
const pointsRoute = require('../routes/points.route'); 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 ── */ /* ── 1v1 Matchmaking ── */
function tryMatchmaking(io, newSocketId) { async function tryMatchmaking(io, newSocketId) {
const challenger = waitingPool.get(newSocketId); const challenger = waitingPool.get(newSocketId);
if (!challenger) return; if (!challenger) return;
@ -170,6 +212,38 @@ function tryMatchmaking(io, newSocketId) {
waitingPool.delete(id); waitingPool.delete(id);
const matchId = `match_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`; 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", { challenger.socket.emit("match_found", {
matchId, matchId,
opponent: entry.player, opponent: entry.player,
@ -564,9 +638,10 @@ function registerArenaHandlers(io, socket) {
socket.on("join_1v1", (playerData) => { socket.on("join_1v1", (playerData) => {
if (waitingPool.has(socket.id)) return; if (waitingPool.has(socket.id)) return;
const player = { const player = {
id: playerData.id, id: playerData.id,
name: playerData.name, name: playerData.name,
level: Number(playerData.level) || 1, level: Number(playerData.level) || 1,
accountId: playerData.id || null, // für Energie-Abzug
}; };
// levelRange vom Client übernehmen (5 oder 10), Standard = 5 // levelRange vom Client übernehmen (5 oder 10), Standard = 5
const levelRange = [5, 10].includes(Number(playerData.levelRange)) const levelRange = [5, 10].includes(Number(playerData.levelRange))