370 lines
14 KiB
JavaScript
370 lines
14 KiB
JavaScript
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() +
|
||
renderResourceSelector(data, buildingId) +
|
||
(data.selected_resource ? renderProductionSection(data) : "") +
|
||
(data.selected_resource ? renderDivider() : "") +
|
||
(data.selected_resource ? renderLoopSection(data, buildingId) : "") +
|
||
(data.selected_resource ? renderDivider() : "") +
|
||
(data.selected_resource ? renderCollectSection(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>";
|
||
}
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────
|
||
Abschnitt: Header (Level + Zyklusinfo)
|
||
───────────────────────────────────────────────── */
|
||
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>"
|
||
);
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────
|
||
Abschnitt: Ressourcen-Auswahl
|
||
───────────────────────────────────────────────── */
|
||
function renderResourceSelector(data, buildingId) {
|
||
const resources = data.production; // [{resource, amount}]
|
||
|
||
const buttons = resources.map(r => {
|
||
const isSelected = data.selected_resource === r.resource;
|
||
return (
|
||
"<button class='mine-res-btn" +
|
||
(isSelected ? " mine-res-btn-active" : "") +
|
||
"' data-resource='" + r.resource +
|
||
"' data-building='" + buildingId + "'>" +
|
||
"<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 (
|
||
"<p class='mine-section-title'>Ressource wählen</p>" +
|
||
"<div class='mine-res-selector'>" +
|
||
buttons +
|
||
"</div>"
|
||
);
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────
|
||
Abschnitt: Aktuelle Produktion
|
||
───────────────────────────────────────────────── */
|
||
function renderProductionSection(data) {
|
||
const minutesLeft = Math.floor(data.next_cycle_in_seconds / 60);
|
||
const secondsLeft = data.next_cycle_in_seconds % 60;
|
||
|
||
const maxHoursDisplay = data.max_hours + "h";
|
||
const progressPercent = Math.min(
|
||
100,
|
||
Math.round((data.cycles / data.max_cycles) * 100)
|
||
);
|
||
|
||
return (
|
||
renderDivider() +
|
||
"<p class='mine-section-title'>Aktuelle Produktion</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:" + progressPercent + "%'></div>" +
|
||
"</div>" +
|
||
"<div class='mine-timer-row'>" +
|
||
"<span class='mine-timer-label'>" +
|
||
(data.is_full
|
||
? "Maximale Zeit erreicht (" + maxHoursDisplay + ")"
|
||
: "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>"
|
||
);
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────
|
||
Abschnitt: Schleifen (Loops)
|
||
───────────────────────────────────────────────── */
|
||
function renderLoopSection(data, buildingId) {
|
||
const loopDots = [];
|
||
for (let i = 0; i < 4; i++) {
|
||
loopDots.push(
|
||
"<span class='mine-loop-dot" +
|
||
(i < data.loops_purchased ? " mine-loop-dot-active" : "") +
|
||
"'></span>"
|
||
);
|
||
}
|
||
|
||
const canBuy = data.loops_available > 0 && data.can_afford_loop;
|
||
const maxReached = data.loops_available === 0;
|
||
|
||
return (
|
||
"<p class='mine-section-title'>Schleifen</p>" +
|
||
"<div class='mine-loop-row'>" +
|
||
"<div class='mine-loop-dots'>" + loopDots.join("") + "</div>" +
|
||
"<span class='mine-loop-info'>" +
|
||
data.max_hours + "h max" +
|
||
(maxReached ? " <span class='mine-loop-maxed'>(Max)</span>" : "") +
|
||
"</span>" +
|
||
"</div>" +
|
||
(!maxReached
|
||
? "<button class='mine-btn-loop" + (canBuy ? "" : " mine-btn-disabled") + "'" +
|
||
" id='mine-loop-btn'" +
|
||
" data-building='" + buildingId + "'" +
|
||
(canBuy ? "" : " disabled") + ">" +
|
||
"<span class='mine-loop-gem-icon'>💎</span>" +
|
||
"10 Juwelen → +5h" +
|
||
(data.can_afford_loop ? "" : " <span class='mine-loop-no-gems'>(" + data.player_gems + " verfügbar)</span>") +
|
||
"</button>"
|
||
: ""
|
||
)
|
||
);
|
||
}
|
||
|
||
/* ─────────────────────────────────────────────────
|
||
Abschnitt: Abholen-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-Delegation: Ressource wählen
|
||
───────────────────────────────────────────────── */
|
||
document.addEventListener("click", async (e) => {
|
||
const btn = e.target.closest(".mine-res-btn");
|
||
if (!btn || btn.classList.contains("mine-res-btn-active")) return;
|
||
|
||
const resource = btn.dataset.resource;
|
||
const buildingId = btn.dataset.building;
|
||
|
||
// Alle Buttons kurz deaktivieren
|
||
document.querySelectorAll(".mine-res-btn").forEach(b => b.disabled = true);
|
||
|
||
try {
|
||
const res = await fetch("/api/mine/" + buildingId + "/select", {
|
||
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;
|
||
}
|
||
|
||
showNotification(
|
||
resourceLabel(resource) + " ausgewählt.\nTimer wurde zurückgesetzt.",
|
||
"Mine", "⛏️"
|
||
);
|
||
await renderMineStatus(buildingId);
|
||
} catch (err) {
|
||
console.error("Ressource wählen Fehler:", err);
|
||
await renderMineStatus(buildingId);
|
||
}
|
||
});
|
||
|
||
/* ─────────────────────────────────────────────────
|
||
Event-Delegation: Schleife kaufen
|
||
───────────────────────────────────────────────── */
|
||
document.addEventListener("click", async (e) => {
|
||
const btn = e.target.closest("#mine-loop-btn");
|
||
if (!btn || btn.disabled) return;
|
||
|
||
const buildingId = btn.dataset.building;
|
||
btn.disabled = true;
|
||
btn.textContent = "Kaufe...";
|
||
|
||
try {
|
||
const res = await fetch("/api/mine/" + buildingId + "/loop", {
|
||
method: "POST",
|
||
});
|
||
if (!res.ok) throw new Error("API Fehler");
|
||
const data = await res.json();
|
||
|
||
if (data.error) {
|
||
showNotification(data.error, "Mine", "⛏️");
|
||
await renderMineStatus(buildingId);
|
||
return;
|
||
}
|
||
|
||
showNotification(
|
||
"Schleife aktiviert! +" + 5 + "h Abbauzeit.\n" +
|
||
"Noch " + data.loops_available + " Schleife(n) verfügbar.",
|
||
"Mine", "💎"
|
||
);
|
||
await renderMineStatus(buildingId);
|
||
await refreshHud();
|
||
} catch (err) {
|
||
console.error("Schleife kaufen Fehler:", err);
|
||
await renderMineStatus(buildingId);
|
||
}
|
||
});
|
||
|
||
/* ─────────────────────────────────────────────────
|
||
Event-Delegation: Ressource 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;
|
||
showNotification(
|
||
"Abgeholt!\n" + resourceLabel(c.resource) + ": +" + c.amount,
|
||
"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;
|
||
}
|