// html/shop.js (() => { const wrap = document.getElementById("shop-wrap"); const title = document.getElementById("shop-title"); const balanceNum = document.getElementById("shop-balance-num"); const tabBtns = { weapons: document.getElementById("shop-tab-weapons"), ammo: document.getElementById("shop-tab-ammo"), vehicles: document.getElementById("shop-tab-vehicles"), }; const list = document.getElementById("shop-list"); const closeBtn = document.getElementById("shop-close"); console.log("[turfwar] shop.js loaded"); // Guard against missing markup (prevents silent failures) if ( !wrap || !title || !balanceNum || !list || !closeBtn || !tabBtns.weapons || !tabBtns.ammo || !tabBtns.vehicles ) { console.error("[turfwar] shop.js missing DOM elements. Check atm.html includes #shop-wrap and tab ids."); return; } const ALL_TABS = ["weapons", "ammo", "vehicles"]; let state = { open: false, tab: "weapons", gangId: 0, balance: 0, payload: { weapons: {}, ammo: {}, vehicles: {}, gangs: {} }, // NEW: which tabs are allowed for this open instance allowedTabs: ["weapons", "ammo", "vehicles"], }; function fmtMoney(n) { n = Number(n) || 0; return "$" + n.toLocaleString(); } function normalizeAllowedTabs(arr) { // If not provided, allow all if (!Array.isArray(arr) || arr.length === 0) return [...ALL_TABS]; // Keep only known tabs, unique, in ALL_TABS order const set = new Set(arr.map(String)); return ALL_TABS.filter(t => set.has(t)); } function firstAllowedTab() { return (state.allowedTabs && state.allowedTabs[0]) ? state.allowedTabs[0] : "weapons"; } function applyAllowedTabsUI() { // Hide / show tab buttons for (const t of ALL_TABS) { const btn = tabBtns[t]; const ok = state.allowedTabs.includes(t); btn.classList.toggle("hidden", !ok); // Also prevent focusing hidden buttons btn.tabIndex = ok ? 0 : -1; } // If current tab not allowed, force to first allowed if (!state.allowedTabs.includes(state.tab)) { state.tab = firstAllowedTab(); } } function setActiveTab(tab) { tab = String(tab || ""); // Enforce allowed tabs if (!state.allowedTabs.includes(tab)) { tab = firstAllowedTab(); } state.tab = tab; Object.keys(tabBtns).forEach((k) => tabBtns[k].classList.toggle("active", k === tab)); renderList(); } function renderList() { list.innerHTML = ""; // Enforce allowed tab again (in case something changed) if (!state.allowedTabs.includes(state.tab)) { setActiveTab(firstAllowedTab()); return; } const catalog = state.payload[state.tab] || {}; const entries = Object.entries(catalog); if (entries.length === 0) { const empty = document.createElement("div"); empty.style.padding = "10px 0"; empty.style.opacity = "0.8"; empty.textContent = "No items configured."; list.appendChild(empty); return; } for (const [itemId, it] of entries) { const row = document.createElement("div"); row.className = "shop-row"; const left = document.createElement("div"); const name = document.createElement("div"); name.className = "shop-item-name"; name.textContent = it.label || itemId; const sub = document.createElement("div"); sub.className = "shop-item-sub"; if (state.tab === "vehicles" && itemId === "gangveh") { const gName = (state.payload.gangs && state.payload.gangs[state.gangId] && state.payload.gangs[state.gangId].name) || "Your Gang"; sub.textContent = `Free gang vehicle for ${gName}`; } else { sub.textContent = `ID: ${itemId}`; } left.appendChild(name); left.appendChild(sub); const right = document.createElement("div"); right.style.display = "flex"; right.style.alignItems = "center"; const price = document.createElement("div"); price.className = "shop-price"; price.textContent = fmtMoney(it.price || 0); const buy = document.createElement("button"); buy.className = "shop-buy"; buy.type = "button"; buy.textContent = "Buy"; buy.onclick = () => doBuy(state.tab, itemId); right.appendChild(price); right.appendChild(buy); row.appendChild(left); row.appendChild(right); list.appendChild(row); } } function post(name, data) { return fetch(`https://${GetParentResourceName()}/${name}`, { method: "POST", headers: { "Content-Type": "application/json; charset=UTF-8" }, body: JSON.stringify(data || {}), }).catch((err) => { console.error("[turfwar] NUI post failed:", name, err); }); } function doBuy(tab, itemId) { // Hard enforce allowed tabs (security-ish) if (!state.allowedTabs.includes(tab)) { console.warn("[turfwar] blocked buy from disallowed tab:", tab, itemId); return; } if (tab === "weapons") return post("shop:buyWeapon", { itemId }); if (tab === "ammo") return post("shop:buyAmmo", { itemId }); if (tab === "vehicles") return post("shop:buyVehicle", { itemId }); } function open(msg) { state.open = true; state.gangId = Number(msg.gangId) || 0; state.payload = msg.payload || state.payload; // NEW: allowed tabs state.allowedTabs = normalizeAllowedTabs(msg.allowedTabs); // requested tab (will be forced to allowed) state.tab = String(msg.tab || "weapons"); title.textContent = "Gang Shop"; balanceNum.textContent = fmtMoney(state.balance); // apply allowed tab UI before setting active tab applyAllowedTabsUI(); wrap.classList.remove("hidden"); setActiveTab(state.tab); } function close() { state.open = false; wrap.classList.add("hidden"); post("shop:close", {}); } // Buttons (they’ll be hidden when not allowed) tabBtns.weapons.addEventListener("click", () => setActiveTab("weapons")); tabBtns.ammo.addEventListener("click", () => setActiveTab("ammo")); tabBtns.vehicles.addEventListener("click", () => setActiveTab("vehicles")); closeBtn.addEventListener("click", close); // ESC closes window.addEventListener("message", (e) => { if (e.data.type === "shop:open") { const rgb = e.data.gangRGB || { r: 10, g: 10, b: 10 } document.documentElement.style.setProperty( "--gang-rgb", `${rgb.r}, ${rgb.g}, ${rgb.b}` ) } }) // Receive messages from Lua window.addEventListener("message", (event) => { const msg = event.data; if (!msg || !msg.type) return; if (msg.type === "shop:open") { console.log("[turfwar] shop:open", msg); open(msg); } else if (msg.type === "shop:close") { wrap.classList.add("hidden"); state.open = false; } else if (msg.type === "shop:balance") { state.balance = Number(msg.balance) || 0; state.gangId = Number(msg.gangId) || state.gangId; balanceNum.textContent = fmtMoney(state.balance); } else if (msg.type === "shop:gang") { state.gangId = Number(msg.gangId) || 0; renderList(); } }); })();