dok/public/js/buildings/mine.js
2026-04-08 14:15:17 +01:00

390 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { showNotification } from "../notification.js";
import { refreshHud } from "../hud.js";
/* ── Einstiegspunkt ─────────────────────────────── */
export async function loadMine(buildingId) {
const actionsTab = document.getElementById("tab-actions");
if (!actionsTab) return;
actionsTab.innerHTML = `<div class="mine-loading">Lade Mineninfo...</div>`;
await renderMineStatus(buildingId);
}
/* ─────────────────────────────────────────────────
Haupt-Render-Funktion
───────────────────────────────────────────────── */
async function renderMineStatus(buildingId) {
const actionsTab = document.getElementById("tab-actions");
try {
const res = await fetch("/api/mine/" + buildingId + "/status");
if (!res.ok) throw new Error("API Fehler");
const data = await res.json();
if (data.error) {
actionsTab.innerHTML = "<p class='mine-error'>" + data.error + "</p>";
return;
}
actionsTab.innerHTML =
"<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);
}
} catch (err) {
console.error("Mine Fehler:", err);
actionsTab.innerHTML =
"<p class='mine-error'>Fehler beim Laden der Mineninfo.</p>";
}
}
/* ─────────────────────────────────────────────────
Header
───────────────────────────────────────────────── */
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";
else cycleText = "Läuft...";
}
return (
"<div class='mine-header-row'>" +
"<span class='mine-level-badge'>Level " + data.level + "</span>" +
"<span class='mine-cycles'>" + cycleText + "</span>" +
"</div>"
);
}
/* ─────────────────────────────────────────────────
Ressourcen-Auswahl (Startscreen ohne aktive Session)
───────────────────────────────────────────────── */
function renderResourceSelector(data, buildingId) {
return (
"<p class='mine-section-title'>Ressource wählen</p>" +
"<p class='mine-hint'>Wähle eine Ressource für die nächsten " + data.session_hours + "h.</p>" +
renderResourceGrid(data.production, null, buildingId, "select")
);
}
/* ─────────────────────────────────────────────────
Aktive Session (mit Timer, Queue, Collect-Button)
───────────────────────────────────────────────── */
function renderActiveSession(data, buildingId) {
return (
// Aktuelle Produktion
renderCurrentProduction(data) +
renderDivider() +
// Warteschlangen-Bereich
renderQueueSection(data, buildingId) +
renderDivider() +
// Abholen-Button
renderCollectSection(data, buildingId)
);
}
/* ─────────────────────────────────────────────────
Aktuelle Produktion
───────────────────────────────────────────────── */
function renderCurrentProduction(data) {
const minutesLeft = Math.floor(data.next_cycle_in_seconds / 60);
const secondsLeft = data.next_cycle_in_seconds % 60;
const progressPct = Math.min(100, Math.round((data.cycles / data.max_cycles) * 100));
return (
"<p class='mine-section-title'>Aktuelle Session " + data.session_hours + "h</p>" +
"<div class='mine-resource-row" + (data.ready ? " mine-resource-ready" : "") + "'>" +
"<span class='mine-resource-icon'>" + resourceIcon(data.selected_resource) + "</span>" +
"<span class='mine-resource-label'>" + resourceLabel(data.selected_resource) + "</span>" +
"<span class='mine-resource-amount'>" + data.available_amount + "</span>" +
"</div>" +
"<div class='mine-progress-wrap'>" +
"<div class='mine-progress-bar' style='width:" + progressPct + "%'></div>" +
"</div>" +
"<div class='mine-timer-row'>" +
"<span class='mine-timer-label'>" +
(data.is_full ? "Zeit abgelaufen!" : "Nächster Zyklus 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>"
) +
"</div>"
);
}
/* ─────────────────────────────────────────────────
Warteschlangen-Bereich
zeigt belegte Slots als Ressourcen-Badges
zeigt freie Slots als aufklappbare Auswahl
───────────────────────────────────────────────── */
function renderQueueSection(data, buildingId) {
const { loop_queue, loop_slots_free, loop_slots_used,
loop_cost_gems, can_afford_loop, player_gems } = data;
let html =
"<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'>" +
"<span class='mine-queue-pos'>" + (i + 1) + ".</span>" +
"<span class='mine-queue-icon'>" + resourceIcon(res) + "</span>" +
"<span class='mine-queue-label'>" + resourceLabel(res) + "</span>" +
"<span class='mine-queue-dur'>" + data.session_hours + "h</span>" +
"</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'>" +
"<button class='mine-btn-add-loop" + (can_afford_loop ? "" : " mine-btn-disabled") + "'" +
" id='mine-queue-toggle'" +
" data-building='" + buildingId + "'" +
(can_afford_loop ? "" : " disabled") + ">" +
"<span class='mine-loop-gem-icon'>💎</span>" +
(can_afford_loop
? "Schleife " + nextPos + " hinzufügen (" + loop_cost_gems + " Juwelen)"
: "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") +
"</div>" +
"</div>";
}
if (loop_slots_free === 0) {
html += "<p class='mine-hint mine-hint-center'>Warteschlange voll 4 Schleifen aktiv.</p>";
}
return html;
}
/* ─────────────────────────────────────────────────
Ressourcen-Raster (wiederverwendbar für select + loop)
mode: "select" | "loop"
───────────────────────────────────────────────── */
function renderResourceGrid(production, activeResource, buildingId, mode) {
const buttons = production.map(r => {
const isActive = r.resource === activeResource;
return (
"<button class='mine-res-btn" + (isActive ? " mine-res-btn-active" : "") + "'" +
" data-resource='" + r.resource + "'" +
" data-building='" + buildingId + "'" +
" data-mode='" + mode + "'" +
(isActive ? " disabled" : "") + ">" +
"<span class='mine-res-btn-icon'>" + resourceIcon(r.resource) + "</span>" +
"<span class='mine-res-btn-name'>" + resourceLabel(r.resource) + "</span>" +
"<span class='mine-res-btn-amount'>+" + r.amount + "/Zyklus</span>" +
"</button>"
);
}).join("");
return "<div class='mine-res-selector'>" + buttons + "</div>";
}
/* ─────────────────────────────────────────────────
Collect-Button
───────────────────────────────────────────────── */
function renderCollectSection(data, buildingId) {
return (
"<div class='mine-actions'>" +
"<button class='mine-btn-collect" + (data.ready ? "" : " mine-btn-disabled") + "'" +
" id='mine-collect-btn'" +
" data-building='" + buildingId + "'" +
(data.ready ? "" : " disabled") + ">" +
(data.ready ? "Abholen" : "Noch nicht bereit") +
"</button>" +
"</div>"
);
}
function renderDivider() {
return "<div class='mine-divider'></div>";
}
/* ─────────────────────────────────────────────────
Event: Toggle Schleifenpicker
───────────────────────────────────────────────── */
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)
───────────────────────────────────────────────── */
document.addEventListener("click", async (e) => {
const btn = e.target.closest(".mine-res-btn");
if (!btn || btn.disabled || btn.classList.contains("mine-res-btn-active")) return;
const resource = btn.dataset.resource;
const buildingId = btn.dataset.building;
const mode = btn.dataset.mode; // "select" | "loop"
document.querySelectorAll(".mine-res-btn").forEach(b => (b.disabled = true));
const endpoint = mode === "loop"
? "/api/mine/" + buildingId + "/loop"
: "/api/mine/" + buildingId + "/select";
try {
const res = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ resource }),
});
if (!res.ok) throw new Error("API Fehler");
const data = await res.json();
if (data.error) {
showNotification(data.error, "Mine", "⛏️");
await renderMineStatus(buildingId);
return;
}
if (mode === "loop") {
showNotification(
"Schleife hinzugefügt!\n" + resourceLabel(resource) + " 5h\n" +
"Noch " + data.loop_slots_free + " Slot(s) frei.",
"Mine", "💎"
);
} else {
showNotification(
resourceLabel(resource) + " ausgewählt.\n5h Session startet jetzt.",
"Mine", "⛏️"
);
}
await renderMineStatus(buildingId);
if (mode === "loop") await refreshHud();
} catch (err) {
console.error("Mine Fehler:", err);
await renderMineStatus(buildingId);
}
});
/* ─────────────────────────────────────────────────
Event: Abholen
───────────────────────────────────────────────── */
document.addEventListener("click", async (e) => {
const btn = e.target.closest("#mine-collect-btn");
if (!btn || btn.disabled) return;
const buildingId = btn.dataset.building;
btn.disabled = true;
btn.textContent = "Wird abgeholt...";
try {
const res = await fetch("/api/mine/" + buildingId + "/collect", {
method: "POST",
});
if (!res.ok) throw new Error("API Fehler");
const data = await res.json();
if (data.error) {
showNotification(
data.ready_in_display
? "Noch nicht bereit.\nBereit in: " + data.ready_in_display
: data.error,
"Mine", "⛏️"
);
await renderMineStatus(buildingId);
return;
}
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.";
}
showNotification(msg, "Mine", "⛏️");
await renderMineStatus(buildingId);
await refreshHud();
} catch (err) {
console.error("Abholen Fehler:", err);
showNotification("Fehler beim Abholen. Bitte erneut versuchen.", "Fehler", "⚠️");
await renderMineStatus(buildingId);
}
});
/* ─────────────────────────────────────────────────
Countdown-Timer
───────────────────────────────────────────────── */
let countdownInterval = null;
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; }
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(countdownInterval);
renderMineStatus(buildingId);
}
}, 1000);
}
/* ─────────────────────────────────────────────────
Icons & Labels
───────────────────────────────────────────────── */
function resourceIcon(resource) {
const imgMap = {
iron: "/images/items/eisen.png",
gold: "/images/items/goldmuenze.png",
wood: "/images/items/holz.png",
stone: "/images/items/stein.png",
};
if (imgMap[resource]) {
return (
"<span class='mine-resource-icon-" + resource + "'>" +
"<img src='" + imgMap[resource] + "' alt=''>" +
"</span>"
);
}
return "📦";
}
function resourceLabel(resource) {
const map = { gold: "Gold", iron: "Eisen", stone: "Stein", wood: "Holz" };
return map[resource] || resource;
}