385 lines
9.7 KiB
JavaScript
385 lines
9.7 KiB
JavaScript
console.log("[ATM UI] atm.js loaded");
|
||
console.log("[NUI] loaded");
|
||
|
||
// =====================
|
||
// DOM refs
|
||
// =====================
|
||
const wrap = document.getElementById("wrap");
|
||
const cashEl = document.getElementById("cash");
|
||
const bankEl = document.getElementById("bank");
|
||
const amountEl = document.getElementById("amount");
|
||
|
||
const btnDeposit = document.getElementById("btnDeposit");
|
||
const btnWithdraw = document.getElementById("btnWithdraw");
|
||
const btnClose = document.getElementById("btnClose");
|
||
|
||
// Gang Bank HUD
|
||
const gbHud = document.getElementById("gangbankHud");
|
||
const gbAmount = document.getElementById("gbAmount");
|
||
const gbLabel = document.getElementById("gbLabel");
|
||
|
||
// Capture HUD
|
||
const capHud = document.getElementById("captureHud");
|
||
const capTitle = document.getElementById("capTitle");
|
||
const capFill = document.getElementById("capFill");
|
||
const capSub = document.getElementById("capSub");
|
||
const capBar = document.getElementById("capBar");
|
||
|
||
// Leaderboard HUD
|
||
const lbHud = document.getElementById("leaderboardHud");
|
||
const lbTitle = document.getElementById("lbTitle");
|
||
const lbRows = document.getElementById("lbRows");
|
||
const lbError = document.getElementById("lbError");
|
||
|
||
// =====================
|
||
// State
|
||
// =====================
|
||
let lastGangBank = null;
|
||
|
||
// =====================
|
||
// Helpers
|
||
// =====================
|
||
function fmt(n) {
|
||
n = Number(n || 0);
|
||
if (!Number.isFinite(n)) n = 0;
|
||
return "$" + Math.floor(n).toLocaleString();
|
||
}
|
||
|
||
function post(name, data = {}) {
|
||
try {
|
||
fetch(`https://${GetParentResourceName()}/${name}`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
||
body: JSON.stringify(data)
|
||
}).catch(() => {});
|
||
} catch (e) {
|
||
console.log("[ATM UI] post failed:", name, e);
|
||
}
|
||
}
|
||
|
||
function getAmount() {
|
||
const v = Number(amountEl?.value || 0);
|
||
if (!Number.isFinite(v)) return 0;
|
||
return Math.floor(v);
|
||
}
|
||
|
||
// =====================
|
||
// ATM UI
|
||
// =====================
|
||
function hideATM() {
|
||
wrap?.classList.add("hidden");
|
||
}
|
||
|
||
function showATM() {
|
||
wrap?.classList.remove("hidden");
|
||
setTimeout(() => amountEl?.focus(), 0);
|
||
}
|
||
|
||
// ========================
|
||
// Turf Capture HUD (fixed)
|
||
// ========================
|
||
function capShow() {
|
||
capHud?.classList.remove("cap-hidden");
|
||
}
|
||
|
||
function capHide() {
|
||
capHud?.classList.add("cap-hidden");
|
||
if (capFill) capFill.style.width = "0%";
|
||
if (capSub) capSub.textContent = "0%";
|
||
}
|
||
|
||
function capStart(titleText) {
|
||
capShow();
|
||
if (capTitle) capTitle.textContent = titleText || "Capturing…";
|
||
capSet(0);
|
||
}
|
||
|
||
function capSet(t) {
|
||
t = Math.max(0, Math.min(1, Number(t || 0)));
|
||
const pct = Math.round(t * 100);
|
||
if (capFill) capFill.style.width = pct + "%";
|
||
if (capSub) capSub.textContent = pct + "%";
|
||
}
|
||
|
||
/**
|
||
* Capture styling:
|
||
* - fillCss = contesting/capturing gang
|
||
* - bgCss = outgoing/current owner gang
|
||
*/
|
||
function capStyle(fillCss, bgCss) {
|
||
if (capFill && fillCss) capFill.style.background = fillCss;
|
||
if (capBar && bgCss) capBar.style.background = bgCss;
|
||
}
|
||
|
||
function capPaused(isPaused) {
|
||
if (!capSub) return;
|
||
if (isPaused) capSub.textContent = "PAUSED";
|
||
}
|
||
|
||
// ========================
|
||
// Leaderboard HUD
|
||
// ========================
|
||
function lbShow() {
|
||
lbHud?.classList.remove("lb-hidden");
|
||
}
|
||
|
||
function lbHide() {
|
||
lbHud?.classList.add("lb-hidden");
|
||
}
|
||
|
||
function lbRender(payload) {
|
||
if (!payload) return;
|
||
|
||
lbShow();
|
||
|
||
if (lbTitle) lbTitle.textContent = String(payload.title || "Most influence");
|
||
|
||
// Error display (optional)
|
||
if (payload.error) {
|
||
if (lbError) {
|
||
lbError.textContent = String(payload.error);
|
||
lbError.classList.remove("lb-hidden");
|
||
}
|
||
} else {
|
||
lbError?.classList.add("lb-hidden");
|
||
if (lbError) lbError.textContent = "";
|
||
}
|
||
|
||
if (!lbRows) return;
|
||
lbRows.innerHTML = "";
|
||
|
||
const rows = Array.isArray(payload.rows) ? payload.rows : [];
|
||
|
||
rows.forEach((r) => {
|
||
const gangId = Number(r.gangId);
|
||
const name = String(r.name ?? "Unknown");
|
||
const val = Number(r.value ?? 0);
|
||
|
||
// 🚫 Skip Police
|
||
if (gangId === 3) return;
|
||
|
||
// --- Color handling ---
|
||
const rgb = Array.isArray(r.rgb) ? r.rgb : [255, 255, 255];
|
||
|
||
let R = Number(rgb[0]);
|
||
let G = Number(rgb[1]);
|
||
let B = Number(rgb[2]);
|
||
|
||
// Fallback safety
|
||
if (!Number.isFinite(R)) R = 255;
|
||
if (!Number.isFinite(G)) G = 255;
|
||
if (!Number.isFinite(B)) B = 255;
|
||
|
||
// Brighten very dark colors only (Lost Gang)
|
||
if (R < 80 && G < 80 && B < 80) {
|
||
R = Math.min(255, R + 80);
|
||
G = Math.min(255, G + 80);
|
||
B = Math.min(255, B + 80);
|
||
}
|
||
|
||
const div = document.createElement("div");
|
||
div.className = "lb-row";
|
||
|
||
div.innerHTML = `
|
||
<span class="lb-name" style="color: rgb(${R}, ${G}, ${B});">
|
||
${escapeHtml(name)}
|
||
</span>
|
||
<span class="lb-val">${Number.isFinite(val) ? val : 0}</span>
|
||
`;
|
||
|
||
lbRows.appendChild(div);
|
||
});
|
||
}
|
||
|
||
function escapeHtml(s) {
|
||
return String(s)
|
||
.replaceAll("&", "&")
|
||
.replaceAll("<", "<")
|
||
.replaceAll(">", ">")
|
||
.replaceAll('"', """)
|
||
.replaceAll("'", "'");
|
||
}
|
||
|
||
function setGangBankColor(rgb) {
|
||
if (!gbLabel) return;
|
||
|
||
const arr = Array.isArray(rgb) ? rgb : [255, 255, 255];
|
||
|
||
let R = Number(arr[0]);
|
||
let G = Number(arr[1]);
|
||
let B = Number(arr[2]);
|
||
|
||
if (!Number.isFinite(R)) R = 255;
|
||
if (!Number.isFinite(G)) G = 255;
|
||
if (!Number.isFinite(B)) B = 255;
|
||
|
||
// Brighten very dark colors (e.g. Lost Gang)
|
||
if (R < 80 && G < 80 && B < 80) {
|
||
R = Math.min(255, R + 80);
|
||
G = Math.min(255, G + 80);
|
||
B = Math.min(255, B + 80);
|
||
}
|
||
|
||
const css = `rgb(${R}, ${G}, ${B})`;
|
||
|
||
// Apply to label (and optionally amount if you want later)
|
||
gbLabel.style.setProperty("--gang-color", css);
|
||
gbLabel.style.color = "var(--gang-color)"; // ensures it shows even if CSS missing
|
||
}
|
||
|
||
|
||
|
||
// =====================
|
||
// Boot
|
||
// =====================
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
hideATM();
|
||
capHide(); // ✅ was capStop() in your file (but didn’t exist)
|
||
lbHide(); // start hidden until first update
|
||
|
||
console.log("[ATM UI] DOMContentLoaded -> posting atm:ready");
|
||
post("atm:ready");
|
||
});
|
||
|
||
// =====================
|
||
// Button wiring
|
||
// =====================
|
||
btnDeposit?.addEventListener("click", () => {
|
||
const amount = getAmount();
|
||
if (amount > 0) post("atm:deposit", { amount });
|
||
});
|
||
|
||
btnWithdraw?.addEventListener("click", () => {
|
||
const amount = getAmount();
|
||
if (amount > 0) post("atm:withdraw", { amount });
|
||
});
|
||
|
||
btnClose?.addEventListener("click", () => post("atm:close"));
|
||
|
||
document.addEventListener("keydown", (e) => {
|
||
if (e.key === "Escape") post("atm:close");
|
||
});
|
||
|
||
// =====================
|
||
// NUI messages (single listener)
|
||
// =====================
|
||
window.addEventListener("message", (event) => {
|
||
const data = event.data || {};
|
||
const type = data.type;
|
||
if (!type) return;
|
||
|
||
// Capture debug (optional)
|
||
if (type.startsWith("capture:")) {
|
||
// console.log("[NUI] capture msg", data);
|
||
}
|
||
|
||
switch (type) {
|
||
// -------- ATM --------
|
||
case "atm:open":
|
||
showATM();
|
||
break;
|
||
|
||
case "atm:close":
|
||
hideATM();
|
||
break;
|
||
|
||
case "atm:balances":
|
||
if (cashEl) cashEl.textContent = fmt(data.cash);
|
||
if (bankEl) bankEl.textContent = fmt(data.bank);
|
||
break;
|
||
|
||
// ---- Gang Bank HUD ----
|
||
case "gangbank:show":
|
||
gbHud?.classList.remove("gb-hidden");
|
||
break;
|
||
|
||
case "gangbank:hide":
|
||
gbHud?.classList.add("gb-hidden");
|
||
break;
|
||
|
||
case "gangbank:label": {
|
||
if (gbLabel) gbLabel.textContent = String(data.label || "Gang Bank");
|
||
|
||
// ✅ set color if provided
|
||
if (data.rgb) setGangBankColor(data.rgb);
|
||
|
||
break;
|
||
}
|
||
|
||
case "gangbank:update": {
|
||
if (!gbAmount) break;
|
||
|
||
const amt = Number(data.balance ?? data.amount ?? 0);
|
||
gbAmount.textContent = fmt(amt);
|
||
|
||
// ✅ also accept rgb here (in case update is the only message carrying it)
|
||
if (data.rgb) setGangBankColor(data.rgb);
|
||
|
||
// pulse only if changed
|
||
if (lastGangBank !== null && amt !== lastGangBank) {
|
||
gbAmount.classList.remove("gb-pulse");
|
||
void gbAmount.offsetWidth; // restart animation
|
||
gbAmount.classList.add("gb-pulse");
|
||
}
|
||
lastGangBank = amt;
|
||
break;
|
||
}
|
||
|
||
// ---- Turf Capture HUD ----
|
||
case "capture:start":
|
||
capStart(data.turfName || data.title || "Capturing…");
|
||
if (data.t !== undefined) capSet(data.t);
|
||
if (data.fill || data.bg) capStyle(data.fill, data.bg);
|
||
break;
|
||
|
||
case "capture:set":
|
||
capShow();
|
||
capSet(data.t);
|
||
break;
|
||
|
||
case "capture:style":
|
||
capStyle(data.fill, data.bg);
|
||
break;
|
||
|
||
case "capture:paused":
|
||
capPaused(!!data.paused);
|
||
break;
|
||
|
||
case "capture:hint":
|
||
if (capSub && data.text) {
|
||
capSub.textContent = String(data.text);
|
||
setTimeout(() => {
|
||
// only clear if still showing the same hint-like text
|
||
if (capSub && capSub.textContent === String(data.text)) {
|
||
capSub.textContent = "";
|
||
}
|
||
}, 1500);
|
||
}
|
||
break;
|
||
|
||
case "capture:stop":
|
||
capHide();
|
||
break;
|
||
|
||
// ---- ✅ Leaderboard ----
|
||
// We accept either name:
|
||
// - "leaderboard:update" (client might send this)
|
||
// - "turfwar:leaderboard:update" (recommended)
|
||
case "leaderboard:update":
|
||
case "turfwar:leaderboard:update":
|
||
lbRender(data.payload || data);
|
||
break;
|
||
|
||
case "leaderboard:hide":
|
||
lbHide();
|
||
break;
|
||
|
||
case "leaderboard:show":
|
||
lbShow();
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
});
|