ydfnd
This commit is contained in:
parent
a2ab6c6297
commit
d394657b18
@ -28,17 +28,14 @@ async function renderMineStatus(buildingId) {
|
||||
"<div class='mine-panel'>" +
|
||||
renderHeader(data) +
|
||||
renderDivider() +
|
||||
|
||||
// Ressource wählen (nur wenn noch keine aktive Session)
|
||||
(!data.selected_resource
|
||||
? renderResourceSelector(data, buildingId)
|
||||
: renderActiveSession(data, buildingId)
|
||||
) +
|
||||
|
||||
"</div>";
|
||||
|
||||
if (data.selected_resource) {
|
||||
startCountdown(buildingId, data);
|
||||
startSessionCountdown(buildingId, data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Mine Fehler:", err);
|
||||
@ -53,8 +50,8 @@ async function renderMineStatus(buildingId) {
|
||||
function renderHeader(data) {
|
||||
let cycleText = "Keine Ressource gewählt";
|
||||
if (data.selected_resource) {
|
||||
if (data.is_full) cycleText = "Voll – bitte abholen!";
|
||||
else if (data.cycles > 0) cycleText = data.cycles + "x Zyklus abgeschlossen";
|
||||
if (data.is_full) cycleText = "Session beendet – bitte abholen!";
|
||||
else if (data.cycles > 0) cycleText = data.cycles + "x Zyklus bereit";
|
||||
else cycleText = "Läuft...";
|
||||
}
|
||||
return (
|
||||
@ -66,7 +63,7 @@ function renderHeader(data) {
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Ressourcen-Auswahl (Startscreen ohne aktive Session)
|
||||
Startscreen (keine aktive Session)
|
||||
───────────────────────────────────────────────── */
|
||||
function renderResourceSelector(data, buildingId) {
|
||||
return (
|
||||
@ -77,29 +74,31 @@ function renderResourceSelector(data, buildingId) {
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Aktive Session (mit Timer, Queue, Collect-Button)
|
||||
Aktive Session
|
||||
───────────────────────────────────────────────── */
|
||||
function renderActiveSession(data, buildingId) {
|
||||
return (
|
||||
// Aktuelle Produktion
|
||||
renderCurrentProduction(data) +
|
||||
renderDivider() +
|
||||
|
||||
// Warteschlangen-Bereich
|
||||
renderQueueSection(data, buildingId) +
|
||||
renderDivider() +
|
||||
|
||||
// Abholen-Button
|
||||
renderCollectSection(data, buildingId)
|
||||
);
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Aktuelle Produktion
|
||||
Zwei Timer:
|
||||
1. Session-Countdown → wie lange läuft die 5h noch?
|
||||
2. Zyklus-Countdown → wann ist der nächste Zyklus fertig?
|
||||
───────────────────────────────────────────────── */
|
||||
function renderCurrentProduction(data) {
|
||||
const minutesLeft = Math.floor(data.next_cycle_in_seconds / 60);
|
||||
const secondsLeft = data.next_cycle_in_seconds % 60;
|
||||
const sessionMin = Math.floor(data.session_seconds_left / 60);
|
||||
const sessionSec = data.session_seconds_left % 60;
|
||||
const cycleMin = Math.floor(data.next_cycle_in_seconds / 60);
|
||||
const cycleSec = data.next_cycle_in_seconds % 60;
|
||||
|
||||
/* Fortschritt: wieviele Zyklen von max wurden in der Session erreicht */
|
||||
const progressPct = Math.min(100, Math.round((data.cycles / data.max_cycles) * 100));
|
||||
|
||||
return (
|
||||
@ -109,27 +108,38 @@ function renderCurrentProduction(data) {
|
||||
"<span class='mine-resource-label'>" + resourceLabel(data.selected_resource) + "</span>" +
|
||||
"<span class='mine-resource-amount'>" + data.available_amount + "</span>" +
|
||||
"</div>" +
|
||||
|
||||
/* Fortschrittsbalken */
|
||||
"<div class='mine-progress-wrap'>" +
|
||||
"<div class='mine-progress-bar' style='width:" + progressPct + "%'></div>" +
|
||||
"</div>" +
|
||||
|
||||
/* Session-Timer (läuft immer durch) */
|
||||
"<div class='mine-timer-row'>" +
|
||||
"<span class='mine-timer-label'>" +
|
||||
(data.is_full ? "Zeit abgelaufen!" : "Nächster Zyklus in") +
|
||||
"</span>" +
|
||||
"<span class='mine-timer-label'>Session endet in</span>" +
|
||||
(data.is_full
|
||||
? "<span class='mine-timer mine-timer-full'>VOLL</span>"
|
||||
: "<span class='mine-timer' id='mine-countdown' data-seconds='" +
|
||||
data.next_cycle_in_seconds + "'>" +
|
||||
minutesLeft + "m " + secondsLeft + "s</span>"
|
||||
? "<span class='mine-timer mine-timer-full'>FERTIG</span>"
|
||||
: "<span class='mine-timer' id='mine-session-countdown'" +
|
||||
" data-seconds='" + data.session_seconds_left + "'>" +
|
||||
sessionMin + "m " + sessionSec + "s</span>"
|
||||
) +
|
||||
"</div>" +
|
||||
|
||||
/* Zyklus-Timer (nur wenn Session noch läuft und noch Zyklen kommen) */
|
||||
(!data.is_full
|
||||
? "<div class='mine-timer-row mine-timer-row-sub'>" +
|
||||
"<span class='mine-timer-label'>Nächster Zyklus in</span>" +
|
||||
"<span class='mine-timer mine-timer-sub' id='mine-cycle-countdown'" +
|
||||
" data-seconds='" + data.next_cycle_in_seconds + "'>" +
|
||||
cycleMin + "m " + cycleSec + "s</span>" +
|
||||
"</div>"
|
||||
: ""
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Warteschlangen-Bereich
|
||||
– zeigt belegte Slots als Ressourcen-Badges
|
||||
– zeigt freie Slots als aufklappbare Auswahl
|
||||
Warteschlange
|
||||
───────────────────────────────────────────────── */
|
||||
function renderQueueSection(data, buildingId) {
|
||||
const { loop_queue, loop_slots_free, loop_slots_used,
|
||||
@ -139,7 +149,6 @@ function renderQueueSection(data, buildingId) {
|
||||
"<p class='mine-section-title'>Warteschlange" +
|
||||
" <span class='mine-queue-count'>(" + loop_slots_used + "/4)</span></p>";
|
||||
|
||||
// Belegte Slots
|
||||
loop_queue.forEach((res, i) => {
|
||||
html +=
|
||||
"<div class='mine-queue-slot mine-queue-slot-filled'>" +
|
||||
@ -150,11 +159,10 @@ function renderQueueSection(data, buildingId) {
|
||||
"</div>";
|
||||
});
|
||||
|
||||
// Freie Slots (kaufbar)
|
||||
if (loop_slots_free > 0) {
|
||||
const nextPos = loop_slots_used + 1;
|
||||
html +=
|
||||
"<div class='mine-queue-add' id='mine-queue-add'>" +
|
||||
"<div class='mine-queue-add'>" +
|
||||
"<button class='mine-btn-add-loop" + (can_afford_loop ? "" : " mine-btn-disabled") + "'" +
|
||||
" id='mine-queue-toggle'" +
|
||||
" data-building='" + buildingId + "'" +
|
||||
@ -165,7 +173,6 @@ function renderQueueSection(data, buildingId) {
|
||||
: "Schleife " + nextPos + " (" + loop_cost_gems + " 💎 – nur " + player_gems + " verfügbar)"
|
||||
) +
|
||||
"</button>" +
|
||||
// Resource-Picker (standardmäßig versteckt)
|
||||
"<div class='mine-loop-picker' id='mine-loop-picker' style='display:none'>" +
|
||||
"<p class='mine-hint'>Welche Ressource soll in Slot " + nextPos + " abgebaut werden?</p>" +
|
||||
renderResourceGrid(data.production, null, buildingId, "loop") +
|
||||
@ -181,8 +188,7 @@ function renderQueueSection(data, buildingId) {
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Ressourcen-Raster (wiederverwendbar für select + loop)
|
||||
mode: "select" | "loop"
|
||||
Ressourcen-Raster
|
||||
───────────────────────────────────────────────── */
|
||||
function renderResourceGrid(production, activeResource, buildingId, mode) {
|
||||
const buttons = production.map(r => {
|
||||
@ -199,7 +205,6 @@ function renderResourceGrid(production, activeResource, buildingId, mode) {
|
||||
"</button>"
|
||||
);
|
||||
}).join("");
|
||||
|
||||
return "<div class='mine-res-selector'>" + buttons + "</div>";
|
||||
}
|
||||
|
||||
@ -224,22 +229,20 @@ function renderDivider() {
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Event: Toggle Schleifenpicker
|
||||
Event: Loop-Picker togglen
|
||||
───────────────────────────────────────────────── */
|
||||
document.addEventListener("click", (e) => {
|
||||
const btn = e.target.closest("#mine-queue-toggle");
|
||||
if (!btn || btn.disabled) return;
|
||||
|
||||
const picker = document.getElementById("mine-loop-picker");
|
||||
if (!picker) return;
|
||||
|
||||
const isOpen = picker.style.display !== "none";
|
||||
picker.style.display = isOpen ? "none" : "block";
|
||||
btn.classList.toggle("mine-btn-add-loop-open", !isOpen);
|
||||
});
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Event: Ressource wählen (select) oder Schleife buchen (loop)
|
||||
Event: Ressource wählen / Schleife buchen
|
||||
───────────────────────────────────────────────── */
|
||||
document.addEventListener("click", async (e) => {
|
||||
const btn = e.target.closest(".mine-res-btn");
|
||||
@ -247,7 +250,7 @@ document.addEventListener("click", async (e) => {
|
||||
|
||||
const resource = btn.dataset.resource;
|
||||
const buildingId = btn.dataset.building;
|
||||
const mode = btn.dataset.mode; // "select" | "loop"
|
||||
const mode = btn.dataset.mode;
|
||||
|
||||
document.querySelectorAll(".mine-res-btn").forEach(b => (b.disabled = true));
|
||||
|
||||
@ -322,11 +325,13 @@ document.addEventListener("click", async (e) => {
|
||||
|
||||
const c = data.collected;
|
||||
let msg = "Abgeholt!\n" + resourceLabel(c.resource) + ": +" + c.amount;
|
||||
if (data.next_resource) {
|
||||
msg += "\n\nNächste Session: " + resourceLabel(data.next_resource) + " (5h)";
|
||||
} else {
|
||||
msg += "\n\nKeine weiteren Schleifen – neue Ressource wählen.";
|
||||
|
||||
if (data.session_ended) {
|
||||
msg += data.next_resource
|
||||
? "\n\nNächste Session: " + resourceLabel(data.next_resource) + " (5h)"
|
||||
: "\n\nKeine weiteren Schleifen – neue Ressource wählen.";
|
||||
}
|
||||
/* Session läuft noch → kein Hinweis nötig */
|
||||
|
||||
showNotification(msg, "Mine", "⛏️");
|
||||
await renderMineStatus(buildingId);
|
||||
@ -339,17 +344,23 @@ document.addEventListener("click", async (e) => {
|
||||
});
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
Countdown-Timer
|
||||
Zwei parallele Countdowns:
|
||||
1. Session-Countdown (mine-session-countdown)
|
||||
2. Zyklus-Countdown (mine-cycle-countdown)
|
||||
───────────────────────────────────────────────── */
|
||||
let countdownInterval = null;
|
||||
let sessionInterval = null;
|
||||
let cycleInterval = null;
|
||||
|
||||
function startSessionCountdown(buildingId, data) {
|
||||
if (sessionInterval) clearInterval(sessionInterval);
|
||||
if (cycleInterval) clearInterval(cycleInterval);
|
||||
|
||||
function startCountdown(buildingId, data) {
|
||||
if (countdownInterval) clearInterval(countdownInterval);
|
||||
if (data.is_full) return;
|
||||
|
||||
countdownInterval = setInterval(() => {
|
||||
const el = document.getElementById("mine-countdown");
|
||||
if (!el) { clearInterval(countdownInterval); return; }
|
||||
/* Session-Timer */
|
||||
sessionInterval = setInterval(() => {
|
||||
const el = document.getElementById("mine-session-countdown");
|
||||
if (!el) { clearInterval(sessionInterval); return; }
|
||||
|
||||
let secs = parseInt(el.dataset.seconds, 10) - 1;
|
||||
if (secs < 0) secs = 0;
|
||||
@ -357,8 +368,25 @@ function startCountdown(buildingId, data) {
|
||||
el.textContent = Math.floor(secs / 60) + "m " + (secs % 60) + "s";
|
||||
|
||||
if (secs === 0) {
|
||||
clearInterval(countdownInterval);
|
||||
renderMineStatus(buildingId);
|
||||
clearInterval(sessionInterval);
|
||||
clearInterval(cycleInterval);
|
||||
renderMineStatus(buildingId); // Session abgelaufen → neu rendern
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
/* Zyklus-Timer */
|
||||
cycleInterval = setInterval(() => {
|
||||
const el = document.getElementById("mine-cycle-countdown");
|
||||
if (!el) { clearInterval(cycleInterval); return; }
|
||||
|
||||
let secs = parseInt(el.dataset.seconds, 10) - 1;
|
||||
if (secs < 0) secs = 0;
|
||||
el.dataset.seconds = secs;
|
||||
el.textContent = Math.floor(secs / 60) + "m " + (secs % 60) + "s";
|
||||
|
||||
if (secs === 0) {
|
||||
clearInterval(cycleInterval);
|
||||
renderMineStatus(buildingId); // Neuer Zyklus → Menge aktualisieren
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@ -3,9 +3,9 @@ const router = require("express").Router();
|
||||
const db = require("../database/database");
|
||||
|
||||
/* ── Konstanten ─────────────────────────────────── */
|
||||
const SESSION_HOURS = 5; // Stunden pro Session / Slot
|
||||
const MAX_QUEUE_SLOTS = 4; // Maximale Schleifen in der Warteschlange
|
||||
const LOOP_COST_GEMS = 10; // Juwelen pro Schleife
|
||||
const SESSION_HOURS = 5;
|
||||
const MAX_QUEUE_SLOTS = 4;
|
||||
const LOOP_COST_GEMS = 10;
|
||||
const MINE_RESOURCES = ["gold", "iron", "stone", "wood"];
|
||||
|
||||
/* ── Auth-Guard ─────────────────────────────────── */
|
||||
@ -27,8 +27,8 @@ async function ensureTimer(userBuildingId) {
|
||||
if (!existing) {
|
||||
await db.query(
|
||||
`INSERT INTO building_collect_timer
|
||||
(user_building_id, last_collected, selected_resource, loop_queue)
|
||||
VALUES (?, NOW(), NULL, JSON_ARRAY())`,
|
||||
(user_building_id, last_collected, session_started, selected_resource, loop_queue)
|
||||
VALUES (?, NOW(), NOW(), NULL, JSON_ARRAY())`,
|
||||
[userBuildingId]
|
||||
);
|
||||
}
|
||||
@ -46,6 +46,7 @@ async function loadMineData(userId, buildingId) {
|
||||
bp.amount,
|
||||
bp.cycle_seconds,
|
||||
bct.last_collected,
|
||||
bct.session_started,
|
||||
bct.selected_resource,
|
||||
bct.loop_queue
|
||||
FROM user_buildings ub
|
||||
@ -77,9 +78,7 @@ function parseQueue(raw) {
|
||||
try {
|
||||
const q = typeof raw === "string" ? JSON.parse(raw) : raw;
|
||||
return Array.isArray(q) ? q : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
} catch { return []; }
|
||||
}
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
@ -105,18 +104,34 @@ router.get("/:buildingId/status", requireLogin, async (req, res) => {
|
||||
}
|
||||
|
||||
const {
|
||||
cycle_seconds, last_collected,
|
||||
cycle_seconds, last_collected, session_started,
|
||||
selected_resource, loop_queue, level
|
||||
} = rows[0];
|
||||
|
||||
const queue = parseQueue(loop_queue);
|
||||
const maxCycles = calcMaxCycles(cycle_seconds);
|
||||
const now = Date.now();
|
||||
|
||||
const elapsed = Math.floor((Date.now() - new Date(last_collected).getTime()) / 1000);
|
||||
const rawCycles = Math.floor(elapsed / cycle_seconds);
|
||||
const cycles = Math.min(rawCycles, maxCycles);
|
||||
const isFull = rawCycles >= maxCycles;
|
||||
const nextIn = isFull ? 0 : cycle_seconds - (elapsed % cycle_seconds);
|
||||
/* ── session_started: wie weit ist die 5h-Session? ── */
|
||||
const sessionElapsed = Math.floor((now - new Date(session_started).getTime()) / 1000);
|
||||
const sessionDuration = SESSION_HOURS * 3600;
|
||||
const isFull = sessionElapsed >= sessionDuration;
|
||||
|
||||
/* Verbleibende Sessionzeit für den Countdown */
|
||||
const sessionSecondsLeft = isFull ? 0 : sessionDuration - sessionElapsed;
|
||||
|
||||
/* ── last_collected: welche Zyklen noch nicht abgeholt? ── */
|
||||
const collectElapsed = Math.floor((now - new Date(last_collected).getTime()) / 1000);
|
||||
const rawCycles = Math.floor(collectElapsed / cycle_seconds);
|
||||
|
||||
/* Nicht mehr als max_cycles und nicht über das Session-Ende hinaus */
|
||||
const maxCyclesLeft = Math.floor(sessionElapsed / cycle_seconds);
|
||||
const cycles = Math.min(rawCycles, maxCycles, maxCyclesLeft);
|
||||
|
||||
/* Nächster Zyklus: basierend auf last_collected, aber nur wenn Session noch läuft */
|
||||
const nextCycleIn = isFull
|
||||
? 0
|
||||
: cycle_seconds - (collectElapsed % cycle_seconds);
|
||||
|
||||
const selectedRow = selected_resource
|
||||
? rows.find(r => r.resource === selected_resource)
|
||||
@ -136,10 +151,12 @@ router.get("/:buildingId/status", requireLogin, async (req, res) => {
|
||||
cycles,
|
||||
max_cycles: maxCycles,
|
||||
session_hours: SESSION_HOURS,
|
||||
session_seconds_left: sessionSecondsLeft, // ← komplette Session-Restzeit
|
||||
next_cycle_in_seconds: nextCycleIn, // ← bis zum nächsten Zyklus
|
||||
is_full: isFull,
|
||||
ready: cycles > 0 && !!selected_resource,
|
||||
selected_resource,
|
||||
loop_queue: queue, // z.B. ["wood", "iron"]
|
||||
loop_queue: queue,
|
||||
loop_slots_used: queue.length,
|
||||
loop_slots_free: MAX_QUEUE_SLOTS - queue.length,
|
||||
loop_cost_gems: LOOP_COST_GEMS,
|
||||
@ -148,7 +165,7 @@ router.get("/:buildingId/status", requireLogin, async (req, res) => {
|
||||
available_amount: availableAmount,
|
||||
production,
|
||||
last_collected,
|
||||
next_cycle_in_seconds: nextIn,
|
||||
session_started,
|
||||
cycle_seconds,
|
||||
});
|
||||
} catch (err) {
|
||||
@ -159,7 +176,7 @@ router.get("/:buildingId/status", requireLogin, async (req, res) => {
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
POST /api/mine/:buildingId/select
|
||||
Erste Ressource wählen – startet Session neu
|
||||
Neue Ressource wählen – setzt BEIDE Zeitstempel
|
||||
───────────────────────────────────────────────── */
|
||||
router.post("/:buildingId/select", requireLogin, async (req, res) => {
|
||||
const userId = req.session.user.id;
|
||||
@ -193,10 +210,13 @@ router.post("/:buildingId/select", requireLogin, async (req, res) => {
|
||||
return res.status(400).json({ error: "Ressource nicht verfügbar" });
|
||||
}
|
||||
|
||||
// Session neu starten, Warteschlange leeren
|
||||
/* Beide Zeitstempel auf NOW() – frische Session */
|
||||
await db.query(
|
||||
`UPDATE building_collect_timer
|
||||
SET selected_resource = ?, last_collected = NOW(), loop_queue = JSON_ARRAY()
|
||||
SET selected_resource = ?,
|
||||
last_collected = NOW(),
|
||||
session_started = NOW(),
|
||||
loop_queue = JSON_ARRAY()
|
||||
WHERE user_building_id = ?`,
|
||||
[resource, userBuilding.id]
|
||||
);
|
||||
@ -210,7 +230,7 @@ router.post("/:buildingId/select", requireLogin, async (req, res) => {
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
POST /api/mine/:buildingId/loop
|
||||
Schleife mit eigener Ressourcenwahl zur Queue hinzufügen
|
||||
Schleife zur Queue hinzufügen
|
||||
───────────────────────────────────────────────── */
|
||||
router.post("/:buildingId/loop", requireLogin, async (req, res) => {
|
||||
const userId = req.session.user.id;
|
||||
@ -234,10 +254,7 @@ router.post("/:buildingId/loop", requireLogin, async (req, res) => {
|
||||
"SELECT loop_queue, selected_resource FROM building_collect_timer WHERE user_building_id = ?",
|
||||
[userBuilding.id]
|
||||
);
|
||||
if (!timer) {
|
||||
return res.status(404).json({ error: "Timer nicht gefunden" });
|
||||
}
|
||||
if (!timer.selected_resource) {
|
||||
if (!timer?.selected_resource) {
|
||||
return res.status(400).json({ error: "Erst eine Ressource für die aktuelle Session wählen" });
|
||||
}
|
||||
|
||||
@ -256,7 +273,6 @@ router.post("/:buildingId/loop", requireLogin, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Juwelen abziehen & Ressource ans Ende der Queue hängen
|
||||
await db.query(
|
||||
"UPDATE account_currency SET gems = gems - ? WHERE account_id = ?",
|
||||
[LOOP_COST_GEMS, userId]
|
||||
@ -284,7 +300,9 @@ router.post("/:buildingId/loop", requireLogin, async (req, res) => {
|
||||
|
||||
/* ─────────────────────────────────────────────────
|
||||
POST /api/mine/:buildingId/collect
|
||||
Ressourcen gutschreiben + nächsten Queue-Slot starten
|
||||
Ressourcen gutschreiben.
|
||||
session_started wird NICHT verändert –
|
||||
die 5h laufen unabhängig vom Abholen weiter.
|
||||
───────────────────────────────────────────────── */
|
||||
router.post("/:buildingId/collect", requireLogin, async (req, res) => {
|
||||
const userId = req.session.user.id;
|
||||
@ -307,20 +325,24 @@ router.post("/:buildingId/collect", requireLogin, async (req, res) => {
|
||||
|
||||
const {
|
||||
user_building_id, cycle_seconds, last_collected,
|
||||
selected_resource, loop_queue
|
||||
session_started, selected_resource, loop_queue
|
||||
} = rows[0];
|
||||
|
||||
if (!selected_resource) {
|
||||
return res.status(400).json({ error: "Keine Ressource ausgewählt" });
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const maxCycles = calcMaxCycles(cycle_seconds);
|
||||
const elapsed = Math.floor((Date.now() - new Date(last_collected).getTime()) / 1000);
|
||||
const rawCycles = Math.floor(elapsed / cycle_seconds);
|
||||
const cycles = Math.min(rawCycles, maxCycles);
|
||||
const sessionElapsed = Math.floor((now - new Date(session_started).getTime()) / 1000);
|
||||
const collectElapsed = Math.floor((now - new Date(last_collected).getTime()) / 1000);
|
||||
|
||||
const maxCyclesLeft = Math.floor(sessionElapsed / cycle_seconds);
|
||||
const rawCycles = Math.floor(collectElapsed / cycle_seconds);
|
||||
const cycles = Math.min(rawCycles, maxCycles, maxCyclesLeft);
|
||||
|
||||
if (cycles < 1) {
|
||||
const waitSeconds = cycle_seconds - elapsed;
|
||||
const waitSeconds = cycle_seconds - (collectElapsed % cycle_seconds);
|
||||
return res.json({
|
||||
error: "Noch nichts bereit",
|
||||
ready_in_seconds: waitSeconds,
|
||||
@ -333,7 +355,7 @@ router.post("/:buildingId/collect", requireLogin, async (req, res) => {
|
||||
return res.status(400).json({ error: "Ressource nicht gefunden" });
|
||||
}
|
||||
|
||||
// Ressource gutschreiben
|
||||
/* Ressource gutschreiben */
|
||||
const toAdd = selectedRow.amount * cycles;
|
||||
await db.query(
|
||||
`UPDATE account_currency
|
||||
@ -342,30 +364,48 @@ router.post("/:buildingId/collect", requireLogin, async (req, res) => {
|
||||
[toAdd, userId]
|
||||
);
|
||||
|
||||
// Queue: erstes Element wird zur neuen aktiven Session
|
||||
const queue = parseQueue(loop_queue);
|
||||
const nextResource = queue.length > 0 ? queue.shift() : null;
|
||||
|
||||
// Timer exakt vorsetzen (Restsekunden bleiben erhalten)
|
||||
/* last_collected vorsetzen – session_started bleibt unangetastet */
|
||||
const newLastCollected = new Date(
|
||||
new Date(last_collected).getTime() + cycles * cycle_seconds * 1000
|
||||
);
|
||||
|
||||
/* Prüfen ob Session abgelaufen → Queue-Slot starten */
|
||||
const isFull = sessionElapsed >= SESSION_HOURS * 3600;
|
||||
const queue = parseQueue(loop_queue);
|
||||
let nextResource = selected_resource; // bleibt aktiv falls Session noch läuft
|
||||
let newQueue = queue;
|
||||
let newSessionStart = null; // wird nur gesetzt wenn Session wechselt
|
||||
|
||||
if (isFull) {
|
||||
/* Session ist vorbei → nächsten Queue-Slot holen */
|
||||
nextResource = queue.length > 0 ? queue.shift() : null;
|
||||
newQueue = queue;
|
||||
newSessionStart = new Date(); // neue Session startet jetzt
|
||||
}
|
||||
|
||||
await db.query(
|
||||
`UPDATE building_collect_timer
|
||||
SET last_collected = ?,
|
||||
session_started = COALESCE(?, session_started),
|
||||
selected_resource = ?,
|
||||
loop_queue = ?
|
||||
WHERE user_building_id = ?`,
|
||||
[newLastCollected, nextResource, JSON.stringify(queue), user_building_id]
|
||||
[
|
||||
newLastCollected,
|
||||
newSessionStart, // NULL wenn Session noch läuft → COALESCE behält alten Wert
|
||||
nextResource,
|
||||
JSON.stringify(newQueue),
|
||||
user_building_id,
|
||||
]
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
cycles,
|
||||
collected: { resource: selected_resource, amount: toAdd },
|
||||
next_resource: nextResource, // null wenn Queue leer war
|
||||
queue_remaining: queue.length,
|
||||
session_ended: isFull,
|
||||
next_resource: isFull ? nextResource : null,
|
||||
queue_remaining: newQueue.length,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
href="/images/favicon/dok_favicon_32px.ico"
|
||||
type="image/x-icon"
|
||||
/>
|
||||
<script src="/js/heartbeat.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Cinzel Decorative";
|
||||
@ -96,6 +95,7 @@
|
||||
<link rel="stylesheet" href="/css/events.css" />
|
||||
<link rel="stylesheet" href="/css/hud.css" />
|
||||
<link rel="stylesheet" href="/css/mine.css" />
|
||||
<script src="/js/heartbeat.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user