Upload files to "html"
This commit is contained in:
parent
d2e5303901
commit
8b1c54a95d
349
html/atm.css
Normal file
349
html/atm.css
Normal file
|
|
@ -0,0 +1,349 @@
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden { display: none !important; }
|
||||||
|
|
||||||
|
/* Stop clicks leaking into the game world */
|
||||||
|
body { pointer-events: none; }
|
||||||
|
#wrap, #wrap * { pointer-events: auto; }
|
||||||
|
|
||||||
|
/* Fullscreen wrapper that centers the ATM card */
|
||||||
|
/* Fullscreen wrapper that centers the ATM card */
|
||||||
|
#wrap {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ✅ Only draw the ATM vignette when ATM is visible (not hidden) */
|
||||||
|
#wrap:not(.hidden)::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: radial-gradient(
|
||||||
|
circle at 50% 40%,
|
||||||
|
rgba(0,0,0,0.25),
|
||||||
|
rgba(0,0,0,0.65)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATM card */
|
||||||
|
.card {
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
width: 420px;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 18px;
|
||||||
|
|
||||||
|
color: #e7f2ff;
|
||||||
|
|
||||||
|
background: linear-gradient(180deg, rgba(28, 40, 52, 0.95), rgba(10, 14, 18, 0.92));
|
||||||
|
border: 1px solid rgba(130, 190, 255, 0.25);
|
||||||
|
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(255,255,255,0.06),
|
||||||
|
0 25px 70px rgba(0,0,0,0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: rgba(160, 210, 255, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LCD balance panel */
|
||||||
|
.balances {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
|
||||||
|
background: linear-gradient(180deg, rgba(10, 18, 26, 0.88), rgba(6, 10, 14, 0.78));
|
||||||
|
border: 1px solid rgba(120, 200, 255, 0.18);
|
||||||
|
box-shadow: inset 0 0 22px rgba(0,0,0,0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row strong {
|
||||||
|
color: rgba(155, 231, 255, 0.95);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actions area */
|
||||||
|
.actions {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input */
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
border: 1px solid rgba(120, 200, 255, 0.14);
|
||||||
|
background: rgba(0,0,0,0.38);
|
||||||
|
|
||||||
|
color: #eaf4ff;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
box-shadow: inset 0 0 10px rgba(0,0,0,0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border-color: rgba(140, 220, 255, 0.35);
|
||||||
|
box-shadow: inset 0 0 10px rgba(0,0,0,0.55), 0 0 0 2px rgba(100, 180, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.btnrow {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 0 rgba(255,255,255,0.18),
|
||||||
|
0 8px 18px rgba(0,0,0,0.55);
|
||||||
|
|
||||||
|
transition: transform 0.06s ease, filter 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover { filter: brightness(1.12); }
|
||||||
|
button:active { transform: translateY(2px); }
|
||||||
|
|
||||||
|
/* Primary actions */
|
||||||
|
#btnDeposit { background: linear-gradient(180deg, rgba(0, 180, 90, 0.95), rgba(0, 120, 60, 0.9)); }
|
||||||
|
#btnWithdraw { background: linear-gradient(180deg, rgba(0, 130, 255, 0.95), rgba(0, 90, 180, 0.9)); }
|
||||||
|
|
||||||
|
/* Exit / secondary */
|
||||||
|
.secondary {
|
||||||
|
background: rgba(255,255,255,0.12);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hint text */
|
||||||
|
.hint {
|
||||||
|
margin-top: 10px;
|
||||||
|
opacity: 0.8;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Gang Bank HUD (overlay)
|
||||||
|
========================= */
|
||||||
|
#gangbankHud {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 170px; /* pushed up ~2x HUD height */
|
||||||
|
right: 22px;
|
||||||
|
z-index: 2;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gb-hidden { display: none !important; }
|
||||||
|
|
||||||
|
.gb-card {
|
||||||
|
min-width: 190px;
|
||||||
|
padding: 6px 0; /* tighter HUD feel */
|
||||||
|
background: none; /* ✅ no background */
|
||||||
|
border: none; /* ✅ no border */
|
||||||
|
box-shadow: none; /* ✅ no shadow */
|
||||||
|
backdrop-filter: none;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* GTA-ish font stack (safe fallback) */
|
||||||
|
.gb-card, .gb-card * {
|
||||||
|
font-family: "ChaletLondonNineteenSixty", "Chalet Comprime Cologne", "ChaletComprime Cologne", Arial, Helvetica, sans-serif;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gb-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: var(--gang-color, #ffffff); /* ✅ dynamic gang color */
|
||||||
|
text-shadow: 0 0 6px rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gb-value {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #3cff57; /* cash green */
|
||||||
|
text-shadow: 0 2px 10px rgba(0,0,0,0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pulse animation */
|
||||||
|
@keyframes gbPulse {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.06); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
.gb-pulse { animation: gbPulse 0.25s ease; }
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
Turf Capture HUD (overlay)
|
||||||
|
========================= */
|
||||||
|
#captureHud {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 10%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 3;
|
||||||
|
pointer-events: none; /* never blocks input */
|
||||||
|
}
|
||||||
|
|
||||||
|
.cap-hidden { display: none !important; }
|
||||||
|
|
||||||
|
.cap-card {
|
||||||
|
width: 360px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border-radius: 14px;
|
||||||
|
|
||||||
|
background: rgba(10, 10, 10, 0.60);
|
||||||
|
border: 1px solid rgba(255,255,255,0.12);
|
||||||
|
color: #fff;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cap-card, .cap-card * {
|
||||||
|
font-family: "ChaletLondonNineteenSixty", "Chalet Comprime Cologne", "ChaletComprime Cologne", Arial, Helvetica, sans-serif;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cap-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 800;
|
||||||
|
opacity: 0.95;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cap-bar {
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.14);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cap-fill {
|
||||||
|
height: 100%;
|
||||||
|
width: 0%;
|
||||||
|
background: rgba(255,255,255,0.85);
|
||||||
|
transition: width 80ms linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cap-sub {
|
||||||
|
margin-top: 7px;
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
✅ Leaderboard HUD (overlay)
|
||||||
|
========================= */
|
||||||
|
#leaderboardHud {
|
||||||
|
position: absolute;
|
||||||
|
top: 12%;
|
||||||
|
right: 22px;
|
||||||
|
z-index: 4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lb-hidden { display: none !important; }
|
||||||
|
|
||||||
|
.lb-card {
|
||||||
|
width: 260px;
|
||||||
|
padding: 6px 0; /* tighter, cleaner */
|
||||||
|
background: none; /* ✅ fully transparent */
|
||||||
|
border: none; /* ✅ no border */
|
||||||
|
box-shadow: none; /* ✅ no shadow */
|
||||||
|
backdrop-filter: none;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.lb-card, .lb-card * {
|
||||||
|
font-family: "ChaletLondonNineteenSixty", "Chalet Comprime Cologne", "ChaletComprime Cologne", Arial, Helvetica, sans-serif;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lb-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 900;
|
||||||
|
opacity: 0.95;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lb-error {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(255, 80, 80, 0.18);
|
||||||
|
border: 1px solid rgba(255, 80, 80, 0.35);
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lb-rows .lb-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 6px 0;
|
||||||
|
border-bottom: none; /* cleaner HUD look */
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.lb-rows .lb-row:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lb-rank {
|
||||||
|
opacity: 0.8;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lb-name {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-right: 10px;
|
||||||
|
text-shadow: 0 0 6px rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lb-val {
|
||||||
|
font-weight: 900;
|
||||||
|
opacity: 0.95;
|
||||||
|
text-shadow: 0 0 6px rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
127
html/atm.html
Normal file
127
html/atm.html
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
|
<title>turfwar ui</title>
|
||||||
|
|
||||||
|
<!-- Existing styles -->
|
||||||
|
<link rel="stylesheet" href="atm.css" />
|
||||||
|
<link rel="stylesheet" href="killfeed.css" />
|
||||||
|
|
||||||
|
<!-- NEW shop styles -->
|
||||||
|
<link rel="stylesheet" href="shop.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- =========================
|
||||||
|
ATM UI
|
||||||
|
========================= -->
|
||||||
|
<div id="wrap" class="hidden">
|
||||||
|
<div class="card">
|
||||||
|
<div class="title">Fleeca Bank</div>
|
||||||
|
|
||||||
|
<div class="balances">
|
||||||
|
<div class="row">
|
||||||
|
<span>Cash</span>
|
||||||
|
<strong id="cash">$0</strong>
|
||||||
|
</div>
|
||||||
|
<div class="row" style="border-bottom:0;">
|
||||||
|
<span>Bank</span>
|
||||||
|
<strong id="bank">$0</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<input id="amount" type="number" inputmode="numeric" min="0" step="1" placeholder="Amount" />
|
||||||
|
|
||||||
|
<div class="btnrow">
|
||||||
|
<button id="btnDeposit" type="button">Deposit</button>
|
||||||
|
<button id="btnWithdraw" type="button">Withdraw</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="btnClose" type="button" class="secondary">Exit</button>
|
||||||
|
<div class="hint">Press <b>ESC</b> to close</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- =========================
|
||||||
|
Gang Bank HUD
|
||||||
|
========================= -->
|
||||||
|
<div id="gangbankHud" class="gb-hidden">
|
||||||
|
<div class="gb-card">
|
||||||
|
<div class="gb-label" id="gbLabel">Gang Bank</div>
|
||||||
|
<div class="gb-value" id="gbAmount">$0</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- =========================
|
||||||
|
Turf Capture HUD
|
||||||
|
========================= -->
|
||||||
|
<div id="captureHud" class="cap-hidden">
|
||||||
|
<div class="cap-card">
|
||||||
|
<div class="cap-title" id="capTitle">Capturing…</div>
|
||||||
|
|
||||||
|
<div class="cap-bar" id="capBar">
|
||||||
|
<div class="cap-fill" id="capFill"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cap-sub" id="capSub">0%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- =========================
|
||||||
|
Leaderboard HUD
|
||||||
|
========================= -->
|
||||||
|
<div id="leaderboardHud" class="lb-hidden">
|
||||||
|
<div class="lb-card">
|
||||||
|
<div class="lb-title" id="lbTitle">Most influence</div>
|
||||||
|
<div class="lb-error lb-hidden" id="lbError"></div>
|
||||||
|
<div class="lb-rows" id="lbRows"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- =========================
|
||||||
|
Killfeed container
|
||||||
|
========================= -->
|
||||||
|
<div id="killfeed"></div>
|
||||||
|
|
||||||
|
<!-- =========================
|
||||||
|
GANG SHOP UI (NEW)
|
||||||
|
========================= -->
|
||||||
|
<div id="shop-wrap" class="hidden">
|
||||||
|
<div class="shop-card">
|
||||||
|
<div class="shop-top">
|
||||||
|
<div>
|
||||||
|
<h1 id="shop-title" class="shop-title">Gang Shop</h1>
|
||||||
|
<div class="shop-sub">Weapons • Ammo • Vehicles</div>
|
||||||
|
</div>
|
||||||
|
<div class="shop-balance">
|
||||||
|
<div>Gang Bank</div>
|
||||||
|
<div id="shop-balance-num" class="num">$0</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="shop-tabs">
|
||||||
|
<div id="shop-tab-weapons" class="shop-tab active">Weapons</div>
|
||||||
|
<div id="shop-tab-ammo" class="shop-tab">Ammo</div>
|
||||||
|
<div id="shop-tab-vehicles" class="shop-tab">Vehicles</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="shop-list" class="shop-list"></div>
|
||||||
|
|
||||||
|
<div class="shop-footer">
|
||||||
|
<div>Tip: Press ESC to close</div>
|
||||||
|
<div id="shop-close" class="shop-close">Close</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scripts (order matters a bit) -->
|
||||||
|
<script src="killfeed.js"></script>
|
||||||
|
<script src="atm.js"></script>
|
||||||
|
<script src="shop.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
384
html/atm.js
Normal file
384
html/atm.js
Normal file
|
|
@ -0,0 +1,384 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
108
html/killfeed.css
Normal file
108
html/killfeed.css
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/* =========================================================
|
||||||
|
Killfeed (NUI)
|
||||||
|
- Bottom-left (above minimap)
|
||||||
|
- Stacks UP
|
||||||
|
- Bigger, bold, white text
|
||||||
|
- FULLY TRANSPARENT (no background)
|
||||||
|
========================================================= */
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: transparent;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Killfeed container */
|
||||||
|
#killfeed {
|
||||||
|
position: absolute;
|
||||||
|
left: 18px;
|
||||||
|
bottom: 250px; /* tweak if needed */
|
||||||
|
|
||||||
|
z-index: 50;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse; /* stack upward */
|
||||||
|
|
||||||
|
gap: 8px;
|
||||||
|
max-width: 460px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each line */
|
||||||
|
.kf-line {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
padding: 0; /* no box feel */
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
/* ❌ NO BACKGROUND */
|
||||||
|
background: none;
|
||||||
|
backdrop-filter: none;
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
font-family: "ChaletLondonNineteenSixty",
|
||||||
|
"Chalet Comprime Cologne",
|
||||||
|
"ChaletComprime Cologne",
|
||||||
|
Arial, Helvetica, sans-serif;
|
||||||
|
|
||||||
|
font-size: 16px; /* bigger */
|
||||||
|
font-weight: 800; /* bold */
|
||||||
|
line-height: 1.25;
|
||||||
|
|
||||||
|
color: #ffffff; /* white text */
|
||||||
|
|
||||||
|
/* Strong GTA-style shadow for readability */
|
||||||
|
text-shadow:
|
||||||
|
0 1px 2px rgba(0,0,0,0.95),
|
||||||
|
0 0 8px rgba(0,0,0,0.85);
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-6px);
|
||||||
|
animation: kfIn 140ms ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force all spans to white unless overridden inline */
|
||||||
|
.kf-line span {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Killer & victim names (still gang-colored inline) */
|
||||||
|
.kf-killer,
|
||||||
|
.kf-victim {
|
||||||
|
font-weight: 900;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 180px;
|
||||||
|
|
||||||
|
text-shadow:
|
||||||
|
0 1px 2px rgba(0,0,0,0.98),
|
||||||
|
0 0 8px rgba(0,0,0,0.90);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Weapon + extras */
|
||||||
|
.kf-weapon,
|
||||||
|
.kf-extra {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 800;
|
||||||
|
opacity: 0.98;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fade out */
|
||||||
|
.kf-out {
|
||||||
|
animation: kfOut 260ms ease forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes kfIn {
|
||||||
|
to { opacity: 1; transform: translateX(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes kfOut {
|
||||||
|
to { opacity: 0; transform: translateX(-8px); }
|
||||||
|
}
|
||||||
102
html/killfeed.js
Normal file
102
html/killfeed.js
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
console.log("[KILLFEED] killfeed.js loaded");
|
||||||
|
|
||||||
|
const kfWrap = document.getElementById("killfeed");
|
||||||
|
|
||||||
|
function safeText(s) {
|
||||||
|
return String(s ?? "").replace(/[&<>"']/g, (c) => ({
|
||||||
|
"&":"&", "<":"<", ">":">", '"':""", "'":"'"
|
||||||
|
}[c]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgbCss(rgb) {
|
||||||
|
const a = Array.isArray(rgb) ? rgb : [255,255,255];
|
||||||
|
let r = Number(a[0]), g = Number(a[1]), b = Number(a[2]);
|
||||||
|
if (!Number.isFinite(r)) r = 255;
|
||||||
|
if (!Number.isFinite(g)) g = 255;
|
||||||
|
if (!Number.isFinite(b)) b = 255;
|
||||||
|
|
||||||
|
// brighten very dark colors slightly
|
||||||
|
if (r < 70 && g < 70 && b < 70) {
|
||||||
|
r = Math.min(255, r + 80);
|
||||||
|
g = Math.min(255, g + 80);
|
||||||
|
b = Math.min(255, b + 80);
|
||||||
|
}
|
||||||
|
return `rgb(${r},${g},${b})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addKillfeedLine(payload) {
|
||||||
|
if (!kfWrap) return;
|
||||||
|
payload = payload || {};
|
||||||
|
|
||||||
|
// ✅ Death without a killer
|
||||||
|
if (payload.isDeathOnly) {
|
||||||
|
const victim = safeText(payload.victimName || "Unknown");
|
||||||
|
const vCss = rgbCss(payload.victimRgb);
|
||||||
|
|
||||||
|
const line = document.createElement("div");
|
||||||
|
line.className = "kf-line";
|
||||||
|
line.innerHTML = `
|
||||||
|
<span class="kf-victim" style="color:${vCss}">${victim}</span>
|
||||||
|
<span class="kf-weapon">has died</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
kfWrap.prepend(line);
|
||||||
|
|
||||||
|
const maxLines = 6;
|
||||||
|
while (kfWrap.children.length > maxLines) {
|
||||||
|
kfWrap.lastElementChild?.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
line.classList.add("kf-out");
|
||||||
|
setTimeout(() => line.remove(), 300);
|
||||||
|
}, 6000);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Normal PvP line
|
||||||
|
const killer = safeText(payload.killerName || "Unknown");
|
||||||
|
const victim = safeText(payload.victimName || "Unknown");
|
||||||
|
const weapon = safeText(payload.weapon || "Unknown");
|
||||||
|
|
||||||
|
const extras = [];
|
||||||
|
if (payload.headshot) extras.push("HS");
|
||||||
|
const dist = Number(payload.distance || 0);
|
||||||
|
if (Number.isFinite(dist) && dist > 0.1) extras.push(`${Math.round(dist)}m`);
|
||||||
|
const extraText = extras.length ? `(${extras.join(" · ")})` : "";
|
||||||
|
|
||||||
|
const line = document.createElement("div");
|
||||||
|
line.className = "kf-line";
|
||||||
|
|
||||||
|
const kCss = rgbCss(payload.killerRgb);
|
||||||
|
const vCss = rgbCss(payload.victimRgb);
|
||||||
|
|
||||||
|
line.innerHTML = `
|
||||||
|
<span class="kf-killer" style="color:${kCss}">${killer}</span>
|
||||||
|
<span class="kf-weapon">[${weapon}]</span>
|
||||||
|
<span class="kf-victim" style="color:${vCss}">${victim}</span>
|
||||||
|
${extraText ? `<span class="kf-extra">${safeText(extraText)}</span>` : ""}
|
||||||
|
`;
|
||||||
|
|
||||||
|
kfWrap.prepend(line);
|
||||||
|
|
||||||
|
const maxLines = 6;
|
||||||
|
while (kfWrap.children.length > maxLines) {
|
||||||
|
kfWrap.lastElementChild?.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
line.classList.add("kf-out");
|
||||||
|
setTimeout(() => line.remove(), 300);
|
||||||
|
}, 7000);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("message", (event) => {
|
||||||
|
const data = event.data || {};
|
||||||
|
if (data.type === "killfeed:add") {
|
||||||
|
addKillfeedLine(data);
|
||||||
|
} else if (data.type === "killfeed:clear") {
|
||||||
|
if (kfWrap) kfWrap.innerHTML = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user