// ==UserScript== // @name Nova Client // @namespace https://github.com/karizzmaa/nova-client/ // @version 2.1.5 // @description Customizable Mod menu for Survev.io. // @author karizzmaa // @match *://survev.io/* // @match *://66.179.254.36/* // @match *://185.126.158.61/* // @match *://resurviv.biz/* // @match *://survev.github.io/survev/* // @match *://survivx.org/* // @match *://localhost:3000/* // @match *://eu-comp.net/* // @match *://cursev.io/* // @match *://uno.cheap/* // @exclude https://survev.io/stats/ // @exclude https://survev.io/changelog // @exclude https://survev.io/privacy // @exclude https://survev.io/changelogRec // @exclude https://survev.io/hof // @exclude https://survev.io/attribution.txt // @grant GM_addStyle // @icon https://raw.githubusercontent.com/karizzmaa/nova-client/refs/heads/main/icon.png // ==/UserScript== (function() { "use strict"; function updateHelpMenu(userList) { const btn = document.getElementById("btn-help"); const helpBox = document.getElementById("start-help"); if (!btn || !helpBox) return; btn.textContent = "Client Users: (" + userList.length + ")"; helpBox.innerHTML = ""; const title = document.createElement("h1"); title.textContent = "Active Nova Users"; helpBox.appendChild(title); userList.forEach(name => { const p = document.createElement("p"); p.textContent = name; if (name !== "Guest") { p.style.cursor = "pointer"; p.style.color = "#60cdff"; p.onclick = () => { const encoded = encodeURIComponent(name); window.open(`https://survev.io/stats/?slug=${encoded}`, "_blank"); }; } helpBox.appendChild(p); }); } // FIREBASE CONFIG DONT TOUCH function loadFirebase(callback) { const appScript = document.createElement("script"); appScript.src = "https://www.gstatic.com/firebasejs/10.12.2/firebase-app-compat.js"; document.head.appendChild(appScript); const dbScript = document.createElement("script"); dbScript.src = "https://www.gstatic.com/firebasejs/10.12.2/firebase-database-compat.js"; document.head.appendChild(dbScript); dbScript.onload = () => callback(); } const firebaseConfig = { apiKey: "AIzaSyB6m3lBLHx4mE-kd7jmHBi5DCZmEVxsNFk", authDomain: "nova-client-users.firebaseapp.com", databaseURL: "https://nova-client-users-default-rtdb.asia-southeast1.firebasedatabase.app", projectId: "nova-client-users", storageBucket: "nova-client-users.firebasestorage.app", messagingSenderId: "359512037474", appId: "1:359512037474:web:af122f8cf0ff2de4de993b" }; loadFirebase(() => { firebase.initializeApp(firebaseConfig); const db = firebase.database(); function getUsername() { const el = document.getElementById("account-player-name"); if (!el) return "Guest"; const name = el.textContent.trim(); if (!name || name === "Log In / Create Account") { return "Guest"; } return name; } let deviceId = localStorage.getItem("nova_device_id"); if (!deviceId) { deviceId = crypto.randomUUID(); localStorage.setItem("nova_device_id", deviceId); } const userRef = db.ref("users/" + deviceId); function heartbeat() { userRef.set({ name: getUsername(), lastSeen: Date.now() }); } heartbeat(); setInterval(heartbeat, 10000); window.addEventListener("beforeunload", () => { userRef.remove(); }); setInterval(() => { db.ref("users").once("value", snapshot => { snapshot.forEach(child => { const data = child.val(); if (Date.now() - data.lastSeen > 30000) { db.ref("users/" + child.key).remove(); } }); }); }, 15000); db.ref("users").on("value", snapshot => { const users = []; snapshot.forEach(child => { const data = child.val(); users.push(data.name); }); updateHelpMenu(users); }); }); const defaultBackgrounds = [{ id: "b1", name: "Turkey", data: "https://raw.githubusercontent.com/survev/survev/refs/heads/master/client/public/img/main_splash_turkey_01.png", builtIn: true, }, { id: "b2", name: "Easter", data: "https://github.com/survev/survev/blob/master/client/public/img/main_splash_easter.png?raw=true", builtIn: true, }, { id: "b3", name: "Desert", data: "https://github.com/survev/survev/blob/master/client/public/img/main_splash_desert_01.png?raw=true", builtIn: true, }, { id: "b4", name: "Halloween", data: "https://raw.githubusercontent.com/survev/survev/refs/heads/master/client/public/img/main_splash_halloween.png", builtIn: true, }, { id: "b5", name: "Cobalt", data: "https://github.com/survev/survev/blob/master/client/public/img/main_splash_cobalt.png?raw=true", builtIn: true, }, { id: "b10", name: "Main", data: "https://raw.githubusercontent.com/survev/survev/refs/heads/master/client/public/img/main_splash.png", builtIn: true, }, ]; const defaultConfig = { fps: false, ping: false, hpAd: false, uncap: false, fpsLimit: 0, glass: true, fastMenu: false, cleanMenu: false, hideAccountBlock: false, useClassicLogo: false, autoFS: false, activeCrosshair: null, customCrosshairs: [], activeBackground: "b10", customBackgrounds: [], activeKeybindId: null, customKeybinds: [], shuffleEnabled: false, fpsPos: { top: "60%", left: "10px" }, pingPos: { top: "65%", left: "10px" }, hpAdPos: { top: "70%", left: "10px" }, customLabels: [], onlyShowLabelsIngame: false, nameRandomizer: false, nameInterval: 0.1, randomNames: ["Player", "NovaUser", "Pro"], autoHideMinimap: false, killCounter: false, killCounterPos: { top: "75%", left: "10px" }, }; let config = JSON.parse(localStorage.getItem("nova_config")) || defaultConfig; config = { ...defaultConfig, ...config }; if (!config.fpsPos) config.fpsPos = defaultConfig.fpsPos; if (!config.pingPos) config.pingPos = defaultConfig.pingPos; if (!config.hpAdPos) config.hpAdPos = defaultConfig.hpAdPos; if (!config.customLabels) config.customLabels = []; if (!config.customKeybinds) config.customKeybinds = []; if (!config.killCounterPos) config.killCounterPos = defaultConfig.killCounterPos; let shuffleInterval = null; function saveConfig() { localStorage.setItem("nova_config", JSON.stringify(config)); } function updateGameKeybinds(bindString) { let gameConfig = JSON.parse(localStorage.getItem("surviv_config")) || {}; gameConfig.binds = bindString; localStorage.setItem("surviv_config", JSON.stringify(gameConfig)); } let fpsDisplay = null; let fpsAnimationId = null; let pingDisplay = null; let hpAdDisplay = null; let hpAdInterval = null; let killCounterDisplay = null; let killCounterInterval = null; let ws = null; const originalRAF = window.requestAnimationFrame; function applyFPSLimiter() { if (!config.uncap) { window.requestAnimationFrame = originalRAF; return; } const limit = parseInt(config.fpsLimit) || 0; if (limit <= 0) { window.requestAnimationFrame = (cb) => setTimeout(cb, 1); return; } const frameTime = 1000 / limit; let last = 0; window.requestAnimationFrame = (cb) => { const now = performance.now(); const delta = now - last; if (delta >= frameTime) { last = now; cb(now); } else { setTimeout(() => window.requestAnimationFrame(cb), frameTime - delta); } }; } GM_addStyle(` #nova-menu { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.7); width: 580px; height: 500px; background: rgba(20, 20, 20, 0.85); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; color: white; font-family: 'Segoe UI', system-ui, sans-serif; display: none; flex-direction: column; overflow: hidden; z-index: 10000; opacity: 0; box-shadow: 0 25px 50px rgba(0,0,0,0.5); user-select: none; } .nova-animate { transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease; } .nova-glass { backdrop-filter: blur(20px) saturate(180%); background: rgba(20, 20, 20, 0.6) !important; } #nova-menu.active { display: flex; opacity: 1; transform: translate(-50%, -50%) scale(1); } #nova-header { padding: 14px 20px; background: rgba(255, 255, 255, 0.05); display: flex; justify-content: space-between; align-items: center; cursor: move; } #nova-nav { display: flex; gap: 8px; padding: 10px 15px; background: rgba(0, 0, 0, 0.2); border-bottom: 1px solid rgba(255, 255, 255, 0.05); overflow-x: auto; } .nav-item { padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 12px; white-space: nowrap; color: rgba(255, 255, 255, 0.6); transition: 0.2s; } .nav-item.active { background: rgba(255, 255, 255, 0.12); color: #60cdff; font-weight: 600; } #nova-content { padding: 20px; flex-grow: 1; overflow-y: auto; position: relative; } .cat-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .cat-title { font-size: 20px; font-weight: 700; } .item-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; } .item-card { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; aspect-ratio: 1/1; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; cursor: pointer; transition: 0.2s; overflow: hidden; } .item-card:hover { background: rgba(255,255,255,0.1); border-color: #60cdff; } .item-card.active { border: 2px solid #60cdff; background: rgba(96, 205, 255, 0.1); } .preview-img { width: 100%; height: 70%; object-fit: cover; pointer-events: none; opacity: 0.8; } .xhair-preview { width: 42px; height: 42px; object-fit: contain; margin-bottom: 10px; } .item-name { font-size: 11px; margin-top: 5px; opacity: 0.9; text-align: center; padding: 0 5px; } .shuffle-btn { width: 32px; height: 32px; cursor: pointer; border-radius: 6px; padding: 6px; transition: 0.2s; background: rgba(255,255,255,0.05); } .shuffle-btn:hover { background: rgba(96, 205, 255, 0.2); } .shuffle-btn.active { background: #60cdff; } .shuffle-icon { width: 100%; height: 100%; transition: filter 0.3s; } .inverted-icon { filter: invert(1); } .add-btn { border: 2px dashed rgba(255,255,255,0.2); background: transparent; } .item-actions { position: absolute; top: 5px; right: 5px; display: flex; gap: 4px; opacity: 0; transition: 0.2s; } .item-card:hover .item-actions { opacity: 1; } .action-btn { background: rgba(0,0,0,0.6); border-radius: 4px; padding: 2px 5px; font-size: 10px; color: white; } .action-btn:hover { background: #60cdff; color: black; } .tweak-card { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background: rgba(255, 255, 255, 0.04); border-radius: 8px; margin-bottom: 8px; } .win-switch { position: relative; display: inline-block; width: 44px; height: 22px; } .win-switch input { opacity: 0; width: 0; height: 0; } .win-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; border: 2px solid rgba(255, 255, 255, 0.5); transition: .2s; border-radius: 22px; } .win-slider:before { position: absolute; content: ""; height: 12px; width: 12px; left: 4px; bottom: 3px; background-color: rgba(255, 255, 255, 0.8); transition: .2s; border-radius: 50%; } input:checked + .win-slider { background-color: #60cdff; border-color: #60cdff; } input:checked + .win-slider:before { transform: translateX(20px); background-color: #000; } .nova-hidden { display: none !important; } .nova-aligned-bar { position: relative !important; margin-top: 60px !important; display: flex !important; justify-content: center !important; } .move-btn { margin-right: 10px; background: rgba(255,255,255,0.1); border:none; color:white; border-radius:4px; padding: 4px 8px; cursor: pointer; transition:0.2s; font-size:11px;} .move-btn:hover { background: #60cdff; color:black; } #edit-hud { position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%); background: rgba(20,20,20,0.9); border: 1px solid rgba(255,255,255,0.2); padding: 10px 20px; border-radius: 30px; display: none; gap: 10px; z-index: 10002; backdrop-filter: blur(10px); } .hud-btn { padding: 8px 20px; border-radius: 20px; border:none; cursor:pointer; font-weight:600; } .hud-btn.reset { background: rgba(255,50,50,0.2); color: #ff6b6b; } .hud-btn.done { background: #60cdff; color: black; } .nova-label { position: fixed; color: white; font-size: 14px; text-shadow: 1px 1px 2px black; background: rgba(0,0,0,0.3); padding: 3px 8px; border-radius: 5px; z-index: 10001; pointer-events: none; user-select: none; } .draggable { pointer-events: auto !important; cursor: grab; border: 2px dashed #60cdff; background: rgba(96,205,255,0.2) !important; } .draggable:active { cursor: grabbing; } .nova-modal { position: absolute; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); display:flex; justify-content:center; align-items:center; z-index: 10; } .modal-box { background: #1a1a1a; padding: 20px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.1); width: 300px; display:flex; flex-direction:column; gap:10px; } .modal-input { background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: white; padding: 8px; border-radius: 4px; outline: none; } .modal-row { display: flex; justify-content: space-between; align-items: center; } .color-picker { width: 50px; height: 30px; border: none; cursor: pointer; } .bind-icon { width: 40px; height: 40px; margin-bottom: 10px; opacity: 0.7; } .nova-clean-centered { left: 50% !important; right: auto !important; transform: translateX(-50%) !important; display: flex !important; justify-content: center !important; align-items: center !important; } `); const glassStyleId = "glassmorphism-start-menu-bg-only"; const glassCSS = ` #start-menu{ background:rgba(25,25,25,.45)!important; backdrop-filter:blur(14px)saturate(130%); -webkit-backdrop-filter:blur(14px)saturate(130%); border-radius:18px; border:1px solid rgba(255,255,255,.15); box-shadow:0 8px 30px rgba(0,0,0,.5),inset 0 0 0 1px rgba(255,255,255,.04) } #start-menu *{ backdrop-filter:none!important; -webkit-backdrop-filter:none!important }`; function toggleGlassStyle(enabled) { let existing = document.getElementById(glassStyleId); if (enabled) { if (!existing) { const s = document.createElement("style"); s.id = glassStyleId; s.textContent = glassCSS; document.head.appendChild(s); } } else { if (existing) existing.remove(); } } new MutationObserver(() => { if (config.glass && document.querySelector("#start-menu")) { toggleGlassStyle(true); } }).observe(document.documentElement, { childList: true, subtree: true }); function applyCrosshair(base64) { const target = document.querySelector("#game-area-wrapper") || document.querySelector("canvas"); if (target) target.style.cursor = `url(${base64}) 16 16, auto`; } function applyBackground(url) { const bg = document.querySelector("#background"); if (!bg) return; bg.style.backgroundImage = `url("${url}")`; } function doShuffle() { const pool = [...defaultBackgrounds, ...config.customBackgrounds]; const randomBg = pool[Math.floor(Math.random() * pool.length)]; config.activeBackground = randomBg.id; saveConfig(); applyBackground(randomBg.data); } function toggleShuffle(enabled) { config.shuffleEnabled = enabled; saveConfig(); if (enabled) { if (!shuffleInterval) shuffleInterval = setInterval(doShuffle, 600000); } else { clearInterval(shuffleInterval); shuffleInterval = null; } } function extractBase64(input) { const match = input.match(/data:image\/[a-zA-Z]+;base64,[^'")\s]+/); return match ? match[0] : input; } const editHud = document.createElement("div"); editHud.id = "edit-hud"; editHud.innerHTML = ``; document.body.appendChild(editHud); function enterEditMode(element, configKey, defaultPos, onDone) { if (!element) return; toggleMenu(false); editHud.style.display = "flex"; element.classList.add("draggable"); let isDragging = false; let startX, startY, initialLeft, initialTop; const onMouseDown = (e) => { isDragging = true; startX = e.clientX; startY = e.clientY; initialLeft = element.offsetLeft; initialTop = element.offsetTop; e.preventDefault(); }; const onMouseMove = (e) => { if (!isDragging) return; const dx = e.clientX - startX; const dy = e.clientY - startY; element.style.left = `${initialLeft + dx}px`; element.style.top = `${initialTop + dy}px`; element.style.transform = "none"; }; const onMouseUp = () => { isDragging = false; }; element.addEventListener("mousedown", onMouseDown); window.addEventListener("mousemove", onMouseMove); window.addEventListener("mouseup", onMouseUp); editHud.querySelector(".reset").onclick = () => { element.style.top = defaultPos.top; element.style.left = defaultPos.left; if (defaultPos.top.includes("%")) element.style.transform = "translateY(-50%)"; }; editHud.querySelector(".done").onclick = () => { if (configKey) { if (typeof configKey === "string") { config[configKey] = { top: element.style.top, left: element.style.left, }; } else if ( typeof configKey === "object" && configKey.type === "label" ) { const lbl = config.customLabels.find((l) => l.id === configKey.id); if (lbl) { lbl.top = element.style.top; lbl.left = element.style.left; } } saveConfig(); } element.classList.remove("draggable"); element.removeEventListener("mousedown", onMouseDown); window.removeEventListener("mousemove", onMouseMove); window.removeEventListener("mouseup", onMouseUp); editHud.style.display = "none"; toggleMenu(true); if (onDone) onDone(); }; } function toggleFPS(enabled) { config.fps = enabled; saveConfig(); if (enabled && !fpsDisplay) { fpsDisplay = document.createElement("div"); fpsDisplay.className = "nova-label"; fpsDisplay.style.top = config.fpsPos.top; fpsDisplay.style.left = config.fpsPos.left; if (config.fpsPos.top.includes("%")) fpsDisplay.style.transform = "translateY(-50%)"; document.body.appendChild(fpsDisplay); applyLabelContainerMode(); let times = []; const run = () => { fpsAnimationId = requestAnimationFrame(() => { const now = performance.now(); while (times.length > 0 && times[0] <= now - 1000) times.shift(); times.push(now); if (fpsDisplay) { fpsDisplay.innerHTML = `${times.length} FPS`; run(); } }); }; run(); } else if (!enabled && fpsDisplay) { fpsDisplay.remove(); fpsDisplay = null; cancelAnimationFrame(fpsAnimationId); } } function togglePing(enabled) { config.ping = enabled; saveConfig(); if (enabled && !pingDisplay) { pingDisplay = document.createElement("div"); pingDisplay.className = "nova-label"; pingDisplay.innerHTML = "Ping: -- ms"; pingDisplay.style.top = config.pingPos.top; pingDisplay.style.left = config.pingPos.left; if (config.pingPos.top.includes("%")) pingDisplay.style.transform = "translateY(-50%)"; document.body.appendChild(pingDisplay); applyLabelContainerMode(); initPingSocket(); } else if (!enabled && pingDisplay) { if (ws) ws.close(); if (pingDisplay) pingDisplay.remove(); pingDisplay = null; } } function toggleHPAD(enabled) { config.hpAd = enabled; saveConfig(); if (enabled && !hpAdDisplay) { hpAdDisplay = document.createElement("div"); hpAdDisplay.className = "nova-label"; hpAdDisplay.innerHTML = "HP: -- | AD: --"; hpAdDisplay.style.top = config.hpAdPos.top; hpAdDisplay.style.left = config.hpAdPos.left; if (config.hpAdPos.top.includes("%")) hpAdDisplay.style.transform = "translateY(-50%)"; document.body.appendChild(hpAdDisplay); applyLabelContainerMode(); hpAdInterval = setInterval(() => { const hpEl = document.getElementById("ui-health-actual"); let hp = 0; if (hpEl) hp = parseFloat(hpEl.style.width) || 0; const getBoost = (id) => { const el = document.querySelector(`#${id} .ui-bar-inner`); return el ? parseFloat(el.style.width) || 0 : 0; }; const boost0 = getBoost("ui-boost-counter-0"); const boost1 = getBoost("ui-boost-counter-1"); const boost2 = getBoost("ui-boost-counter-2"); const boost3 = getBoost("ui-boost-counter-3"); const adr = boost0 * 0.25 + boost1 * 0.25 + boost2 * 0.375 + boost3 * 0.125; hpAdDisplay.innerHTML = `HP: ${Math.round(hp)} | AD: ${Math.round(adr)}`; }, 100); } else if (!enabled && hpAdDisplay) { clearInterval(hpAdInterval); hpAdDisplay.remove(); hpAdDisplay = null; } } function toggleKillCounter(enabled) { config.killCounter = enabled; saveConfig(); if (enabled && !killCounterDisplay) { killCounterDisplay = document.createElement("div"); killCounterDisplay.className = "nova-label"; killCounterDisplay.innerHTML = "Kills: 0"; killCounterDisplay.style.top = config.killCounterPos.top; killCounterDisplay.style.left = config.killCounterPos.left; if (config.killCounterPos.top.includes("%")) killCounterDisplay.style.transform = "translateY(-50%)"; document.body.appendChild(killCounterDisplay); applyLabelContainerMode(); killCounterInterval = setInterval(() => { const killEl = document.querySelector(".ui-player-kills"); if (killEl && killCounterDisplay) { const kills = killEl.textContent.trim(); killCounterDisplay.innerHTML = `Kills: ${kills}`; } }, 50); } else if (!enabled && killCounterDisplay) { clearInterval(killCounterInterval); killCounterDisplay.remove(); killCounterDisplay = null; } } function initPingSocket() { const getWsUrl = () => { const reg = document.getElementById("server-select-main")?.value || "na"; const map = { na: "usr", eu: "eur", asia: "asr", sa: "sa" }; return `wss://${map[reg] || "usr"}.mathsiscoolfun.com:8001/ptc`; }; const startPing = () => { if (ws) ws.close(); ws = new WebSocket(getWsUrl()); let sendTime; ws.onopen = () => { ws.send(new ArrayBuffer(1)); sendTime = Date.now(); }; ws.onmessage = () => { const diff = Date.now() - sendTime; if (pingDisplay) { pingDisplay.innerHTML = `Ping: ${diff} ms`; pingDisplay.style.color = diff > 120 ? "#ff4d4d" : diff > 80 ? "#ffa500" : "white"; } setTimeout(() => { if (ws && ws.readyState === 1) { sendTime = Date.now(); ws.send(new ArrayBuffer(1)); } }, 1500); }; ws.onclose = () => { if (config.ping && pingDisplay) pingDisplay.innerHTML = "Ping: Offline"; }; ws.onerror = () => { if (pingDisplay) pingDisplay.innerHTML = "Ping: Error"; }; }; document.addEventListener("click", (e) => { if ( e.target.classList?.contains("btn-green") || e.target.id === "btn-start-team" ) { setTimeout(startPing, 1000); } }); } function renderCustomLabels() { document.querySelectorAll(".nova-custom-lbl").forEach((e) => e.remove()); config.customLabels.forEach((lbl) => { const el = document.createElement("div"); el.className = "nova-label nova-custom-lbl"; el.id = `lbl-${lbl.id}`; el.innerText = lbl.text; el.style.color = lbl.color; if (lbl.bold) el.style.fontWeight = "bold"; if (lbl.italic) el.style.fontStyle = "italic"; el.style.top = lbl.top; el.style.left = lbl.left; document.body.appendChild(el); }); } function openLabelCreator() { const modal = document.createElement("div"); modal.className = "nova-modal"; modal.innerHTML = ` `; menu.appendChild(modal); modal.querySelector("#lbl-cancel").onclick = () => modal.remove(); modal.querySelector("#lbl-save").onclick = () => { const text = modal.querySelector("#lbl-text").value; if (!text) return; const newLabel = { id: Date.now(), text, color: modal.querySelector("#lbl-color").value, bold: modal.querySelector("#lbl-bold").checked, italic: modal.querySelector("#lbl-italic").checked, top: "50%", left: "50%", }; config.customLabels.push(newLabel); saveConfig(); renderCustomLabels(); setTimeout(applyLabelContainerMode, 1000); modal.remove(); const el = document.getElementById(`lbl-${newLabel.id}`); enterEditMode( el, { type: "label", id: newLabel.id }, { top: "50%", left: "50%" }, () => loadCategory("Labels"), ); }; } function toggleAutoFS(enabled) { config.autoFS = enabled; saveConfig(); if (enabled) { const fs = () => { if (!document.fullscreenElement) document.documentElement.requestFullscreen().catch(() => {}); window.removeEventListener("mousedown", fs); }; window.addEventListener("mousedown", fs); } } function toggleCleanMenu(enabled) { config.cleanMenu = enabled; saveConfig(); const targets = [ "#news-block", "#left-column", "#social-share-block", 'a[href*="privacy"]', 'a[href*="changelog"]', ".language-select-wrap", ]; targets.forEach((s) => document .querySelectorAll(s) .forEach((el) => enabled ? el.classList.add("nova-hidden") : el.classList.remove("nova-hidden"), ), ); const bar = document.getElementById("start-bottom-right"); const menuEl = document.getElementById("start-menu"); if (bar && menuEl) { if (enabled) { bar.classList.add("nova-aligned-bar"); bar.classList.add("nova-clean-centered"); menuEl.appendChild(bar); } else { bar.classList.remove("nova-aligned-bar"); bar.classList.remove("nova-clean-centered"); document.body.appendChild(bar); } } } function toggleAccountBlock(enabled) { config.hideAccountBlock = enabled; saveConfig(); const selector = ".account-block"; document.querySelectorAll(selector).forEach((el) => { if (enabled) { el.classList.add("nova-hidden"); } else { el.classList.remove("nova-hidden"); } }); } function toggleClassicLogo(enabled) { config.useClassicLogo = enabled; saveConfig(); const OLD_LOGO = "https://survev.io/img/survev_logo_full.png"; const NEW_LOGO = "https://survev.io/img/surviv_logo_full.png"; const targetSrc = enabled ? OLD_LOGO : NEW_LOGO; const replacementSrc = enabled ? NEW_LOGO : OLD_LOGO; document.querySelectorAll("img").forEach((img) => { if (img.src === targetSrc) { img.src = replacementSrc; } }); document.querySelectorAll("*").forEach((el) => { const bg = getComputedStyle(el).backgroundImage; if (bg.includes(targetSrc)) { el.style.backgroundImage = bg.replace(targetSrc, replacementSrc); } }); } let nameIntervalId = null; function applyRandomName() { const input = document.querySelector("#player-name-input-solo"); if (!input || !config.randomNames.length) return; const randomName = config.randomNames[Math.floor(Math.random() * config.randomNames.length)]; input.value = randomName; input.dispatchEvent(new Event("input", { bubbles: true })); } function toggleNameRandomizer(enabled) { config.nameRandomizer = enabled; saveConfig(); if (enabled) { if (nameIntervalId) clearInterval(nameIntervalId); nameIntervalId = setInterval( () => { applyRandomName(); }, (parseFloat(config.nameInterval) || 0.1) * 60 * 1000, ); setTimeout(applyRandomName, 1500); } else { clearInterval(nameIntervalId); nameIntervalId = null; } } let autoHideMinimapInterval = null; let minimapHiddenThisMatch = false; function toggleAutoHideMinimap(enabled) { config.autoHideMinimap = enabled; saveConfig(); if (enabled) { autoHideMinimapInterval = setInterval(() => { const btn = document.getElementById("ui-map-minimize"); if (btn && btn.offsetParent !== null) { if (!minimapHiddenThisMatch) { btn.click(); minimapHiddenThisMatch = true; } } else { minimapHiddenThisMatch = false; } }, 50); } else { clearInterval(autoHideMinimapInterval); autoHideMinimapInterval = null; minimapHiddenThisMatch = false; } } function loadCategory(cat) { contentArea.innerHTML = `
${cat}
${ cat === "Backgrounds" ? `
` : "" }
`; const grid = document.createElement("div"); grid.className = "item-grid"; if (cat === "Crosshairs") { const addBtn = document.createElement("div"); addBtn.className = "item-card add-btn"; addBtn.innerHTML = `+Add Crosshair`; addBtn.onclick = () => { const name = prompt("Name:"); if (!name) return; const input = prompt("Paste Bookmarklet or Base64:"); if (!input) return; config.customCrosshairs.push({ id: Date.now(), name, data: extractBase64(input), }); saveConfig(); loadCategory("Crosshairs"); }; grid.appendChild(addBtn); config.customCrosshairs.forEach((xh) => { const card = document.createElement("div"); card.className = `item-card ${config.activeCrosshair === xh.id ? "active" : ""}`; card.innerHTML = `${xh.name}
Edit
Del
`; card.onclick = () => { config.activeCrosshair = xh.id; saveConfig(); applyCrosshair(xh.data); loadCategory("Crosshairs"); }; card.querySelector(".edit").onclick = (e) => { e.stopPropagation(); const n = prompt("New Name:", xh.name); if (n) xh.name = n; saveConfig(); loadCategory("Crosshairs"); }; card.querySelector(".del").onclick = (e) => { e.stopPropagation(); config.customCrosshairs = config.customCrosshairs.filter( (i) => i.id !== xh.id, ); saveConfig(); loadCategory("Crosshairs"); }; grid.appendChild(card); }); contentArea.appendChild(grid); } else if (cat === "Keybinds") { const addBtn = document.createElement("div"); addBtn.className = "item-card add-btn"; addBtn.innerHTML = `+Add Profile`; addBtn.onclick = () => { const name = prompt("Profile Name:"); if (!name) return; const bindStr = prompt("Paste Keybind String"); if (!bindStr) return; config.customKeybinds.push({ id: Date.now(), name, data: bindStr }); saveConfig(); loadCategory("Keybinds"); }; grid.appendChild(addBtn); config.customKeybinds.forEach((kb) => { const card = document.createElement("div"); card.className = `item-card ${config.activeKeybindId === kb.id ? "active" : ""}`; card.innerHTML = `${kb.name}
Edit
Del
`; card.onclick = () => { config.activeKeybindId = kb.id; saveConfig(); updateGameKeybinds(kb.data); alert(`Applied Keybind Profile: ${kb.name}`); loadCategory("Keybinds"); }; card.querySelector(".edit").onclick = (e) => { e.stopPropagation(); const n = prompt("New Name:", kb.name); if (n) kb.name = n; const d = prompt("New Bind String:", kb.data); if (d) kb.data = d; saveConfig(); loadCategory("Keybinds"); }; card.querySelector(".del").onclick = (e) => { e.stopPropagation(); config.customKeybinds = config.customKeybinds.filter( (i) => i.id !== kb.id, ); saveConfig(); loadCategory("Keybinds"); }; grid.appendChild(card); }); contentArea.appendChild(grid); } else if (cat === "Backgrounds") { const shuffleBtn = contentArea.querySelector("#shuffle-trigger"); shuffleBtn.onclick = () => { const newState = !config.shuffleEnabled; toggleShuffle(newState); loadCategory("Backgrounds"); }; const addBtn = document.createElement("div"); addBtn.className = "item-card add-btn"; addBtn.innerHTML = `+Add Background`; addBtn.onclick = () => { const choice = confirm("Press OK for URL or Cancel for File Upload"); const name = prompt("Name:"); if (!name) return; if (choice) { const url = prompt("Paste Image URL:"); if (url) { config.customBackgrounds.push({ id: Date.now(), name, data: url }); saveConfig(); loadCategory("Backgrounds"); } } else { const input = document.createElement("input"); input.type = "file"; input.accept = "image/*"; input.onchange = (e) => { const reader = new FileReader(); reader.onload = () => { config.customBackgrounds.push({ id: Date.now(), name, data: reader.result, }); saveConfig(); loadCategory("Backgrounds"); }; reader.readAsDataURL(e.target.files[0]); }; input.click(); } }; grid.appendChild(addBtn); [...defaultBackgrounds, ...config.customBackgrounds].forEach((bg) => { const card = document.createElement("div"); card.className = `item-card ${config.activeBackground === bg.id ? "active" : ""}`; card.innerHTML = `${bg.name}`; if (!bg.builtIn) { card.innerHTML += `
Edit
Del
`; card.querySelector(".edit").onclick = (e) => { e.stopPropagation(); const n = prompt("New Name:", bg.name); if (n) bg.name = n; saveConfig(); loadCategory("Backgrounds"); }; card.querySelector(".del").onclick = (e) => { e.stopPropagation(); config.customBackgrounds = config.customBackgrounds.filter( (i) => i.id !== bg.id, ); saveConfig(); loadCategory("Backgrounds"); }; } card.onclick = () => { config.activeBackground = bg.id; saveConfig(); location.reload(); applyBackground(bg.data); loadCategory("Backgrounds"); }; grid.appendChild(card); }); contentArea.appendChild(grid); } else if (cat === "Labels") { contentArea.appendChild( createTweak("FPS Counter", "fps", config.fps, toggleFPS, { text: "Move", action: () => { if (!config.fps) { toggleFPS(true); config.fps = true; saveConfig(); loadCategory("Labels"); } enterEditMode(fpsDisplay, "fpsPos", defaultConfig.fpsPos, () => loadCategory("Labels"), ); }, }), ); contentArea.appendChild( createTweak("Ping / LAT Counter", "ping", config.ping, togglePing, { text: "Move", action: () => { if (!config.ping) { togglePing(true); config.ping = true; saveConfig(); loadCategory("Labels"); } enterEditMode(pingDisplay, "pingPos", defaultConfig.pingPos, () => loadCategory("Labels"), ); }, }), ); contentArea.appendChild( createTweak("HP + AD Counter", "hpAd", config.hpAd, toggleHPAD, { text: "Move", action: () => { if (!config.hpAd) { toggleHPAD(true); config.hpAd = true; saveConfig(); loadCategory("Labels"); } enterEditMode(hpAdDisplay, "hpAdPos", defaultConfig.hpAdPos, () => loadCategory("Labels"), ); }, }), ); contentArea.appendChild( createTweak( "Kill Counter", "killCounter", config.killCounter, toggleKillCounter, { text: "Move", action: () => { if (!config.killCounter) { toggleKillCounter(true); config.killCounter = true; saveConfig(); loadCategory("Labels"); } enterEditMode( killCounterDisplay, "killCounterPos", defaultConfig.killCounterPos, () => loadCategory("Labels"), ); }, }, ), ); const clHeader = document.createElement("div"); clHeader.className = "cat-header"; clHeader.style.marginTop = "20px"; clHeader.innerHTML = `
Custom Labels
`; const addLbl = document.createElement("button"); addLbl.className = "move-btn"; addLbl.style.background = "#60cdff"; addLbl.style.color = "black"; addLbl.style.fontWeight = "bold"; addLbl.innerText = "+ Add New"; addLbl.onclick = openLabelCreator; clHeader.appendChild(addLbl); contentArea.appendChild(clHeader); config.customLabels.forEach((lbl) => { const row = document.createElement("div"); row.className = "tweak-card"; row.innerHTML = `${lbl.text}
`; row.querySelector(".move-btn").onclick = () => { const el = document.getElementById(`lbl-${lbl.id}`); enterEditMode( el, { type: "label", id: lbl.id }, { top: "50%", left: "50%" }, () => loadCategory("Labels"), ); }; row.querySelectorAll(".move-btn")[1].onclick = () => { config.customLabels = config.customLabels.filter( (l) => l.id !== lbl.id, ); saveConfig(); renderCustomLabels(); loadCategory("Labels"); }; contentArea.appendChild(row); }); } else if (cat === "Gameplay") { contentArea.appendChild( createTweak("Uncap FPS", "uncap", config.uncap, (v) => { config.uncap = v; saveConfig(); if (!v) { window.requestAnimationFrame = originalRAF; } else { applyFPSLimiter(); } loadCategory("Gameplay"); }), ); if (config.uncap) { const limitCard = document.createElement("div"); limitCard.className = "tweak-card"; limitCard.style.flexDirection = "column"; limitCard.style.alignItems = "flex-start"; limitCard.innerHTML = ` FPS Limiter leave at 0 for unlimited `; const input = limitCard.querySelector("input"); input.addEventListener("input", () => { config.fpsLimit = parseInt(input.value) || 0; saveConfig(); applyFPSLimiter(); }); contentArea.appendChild(limitCard); } contentArea.appendChild( createTweak( "Name Randomizer", "nameRandomizer", config.nameRandomizer, (v) => { toggleNameRandomizer(v); loadCategory("Gameplay"); }, ), ); if (config.nameRandomizer) { const intervalCard = document.createElement("div"); intervalCard.className = "tweak-card"; intervalCard.style.flexDirection = "column"; intervalCard.style.alignItems = "flex-start"; intervalCard.innerHTML = ` Change Interval (minutes) `; const intervalInput = intervalCard.querySelector("input"); intervalInput.addEventListener("input", () => { config.nameInterval = parseFloat(intervalInput.value) || 0.1; saveConfig(); if (config.nameRandomizer) toggleNameRandomizer(true); }); contentArea.appendChild(intervalCard); const namesHeader = document.createElement("div"); namesHeader.className = "cat-header"; namesHeader.style.marginTop = "15px"; namesHeader.innerHTML = `
Name Pool
`; const addBtn = document.createElement("button"); addBtn.className = "move-btn"; addBtn.style.background = "#60cdff"; addBtn.style.color = "black"; addBtn.style.fontWeight = "bold"; addBtn.innerText = "+ Add Name"; addBtn.onclick = () => { const name = prompt("Enter Name:"); if (!name) return; config.randomNames.push(name); saveConfig(); loadCategory("Gameplay"); }; namesHeader.appendChild(addBtn); contentArea.appendChild(namesHeader); config.randomNames.forEach((name, index) => { const row = document.createElement("div"); row.className = "tweak-card"; row.innerHTML = ` ${name} `; row.querySelector(".move-btn").onclick = () => { config.randomNames.splice(index, 1); saveConfig(); loadCategory("Gameplay"); }; contentArea.appendChild(row); }); } contentArea.appendChild( createTweak("Auto Fullscreen", "afs", config.autoFS, toggleAutoFS), ); contentArea.appendChild( createTweak( "Only Show Labels Ingame", "onlyShowLabelsIngame", config.onlyShowLabelsIngame, (v) => { config.onlyShowLabelsIngame = v; saveConfig(); applyLabelContainerMode(); }, ), ); contentArea.appendChild( createTweak( "AutoHide Minimap", "autoHideMinimap", config.autoHideMinimap, toggleAutoHideMinimap, ), ); } else if (cat === "Client") { contentArea.appendChild( createTweak("Glassmorphism", "glass", config.glass, (v) => { config.glass = v; saveConfig(); menu.classList.toggle("nova-glass", v); toggleGlassStyle(v); // Toggles the provided Start Menu style if ( document.querySelector(".nav-item.active").dataset.cat === "Backgrounds" ) loadCategory("Backgrounds"); }), ); contentArea.appendChild( createTweak("Fast Menu", "fast", config.fastMenu, (v) => { config.fastMenu = v; saveConfig(); menu.classList.toggle("nova-animate", !v); }), ); } else if (cat === "Misc") { contentArea.appendChild( createTweak("Clean Menu", "clean", config.cleanMenu, toggleCleanMenu), ); contentArea.appendChild( createTweak( "Hide Account Block", "hideAccountBlock", config.hideAccountBlock, toggleAccountBlock, ), ); contentArea.appendChild( createTweak( "Use Classic Logo", "useClassicLogo", config.useClassicLogo, toggleClassicLogo, ), ); } } function createTweak(name, id, checked, callback, extraBtn = null) { const card = document.createElement("div"); card.className = "tweak-card"; let html = `${name}
`; if (extraBtn) html += ``; html += `
`; card.innerHTML = html; card.querySelector("input").onchange = (e) => callback(e.target.checked); if (extraBtn) card.querySelector(".move-btn").onclick = extraBtn.action; return card; } const menu = document.createElement("div"); menu.id = "nova-menu"; menu.innerHTML = `
Nova Client
×
`; const contentArea = document.createElement("div"); contentArea.id = "nova-content"; menu.appendChild(contentArea); document.body.appendChild(menu); const toggleMenu = (open) => { if (open) { menu.style.display = "flex"; setTimeout(() => menu.classList.add("active"), 10); } else { menu.classList.remove("active"); setTimeout( () => { if (!menu.classList.contains("active")) menu.style.display = "none"; }, config.fastMenu ? 0 : 400, ); } }; window.onkeydown = (e) => { if (e.code === "ShiftRight") toggleMenu(!menu.classList.contains("active")); }; document.getElementById("nova-close").onclick = () => toggleMenu(false); menu.querySelectorAll(".nav-item").forEach((item) => { item.onclick = () => { menu .querySelectorAll(".nav-item") .forEach((i) => i.classList.remove("active")); item.classList.add("active"); loadCategory(item.dataset.cat); }; }); let drag = false, ox, oy; document.getElementById("nova-header").onmousedown = (e) => { drag = true; ox = e.clientX - menu.offsetLeft; oy = e.clientY - menu.offsetTop; }; document.onmousemove = (e) => { if (drag) { menu.style.left = e.clientX - ox + menu.offsetWidth / 2 + "px"; menu.style.top = e.clientY - oy + menu.offsetHeight / 2 + "px"; } }; document.onmouseup = () => (drag = false); function isInGame() { const gameArea = document.getElementById("game-touch-area"); if (!gameArea) return false; const rect = gameArea.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth ); } function applyLabelContainerMode() { const container = document.querySelector("#ui-health-container"); if (!container) return; const labels = document.querySelectorAll(".nova-label"); labels.forEach((label) => { if (config.onlyShowLabelsIngame) { if (label.parentNode !== container) { container.appendChild(label); } } else { if (label.parentNode !== document.body) { document.body.appendChild(label); } } }); } loadCategory("Labels"); if (config.fps) toggleFPS(true); if (config.ping) togglePing(true); if (config.hpAd) toggleHPAD(true); if (config.uncap) applyFPSLimiter(); if (config.cleanMenu) toggleCleanMenu(true); if (config.hideAccountBlock) toggleAccountBlock(true); if (config.useClassicLogo) toggleClassicLogo(true); if (config.autoFS) toggleAutoFS(true); if (config.shuffleEnabled) toggleShuffle(true); toggleGlassStyle(config.glass); renderCustomLabels(); if (config.nameRandomizer) { toggleNameRandomizer(true); if (config.autoHideMinimap) toggleAutoHideMinimap(true); if (config.killCounter) { toggleKillCounter(true); } } if (config.activeCrosshair) { const x = config.customCrosshairs.find( (i) => i.id === config.activeCrosshair, ); if (x) applyCrosshair(x.data); } function forceBackground() { const bgEl = document.querySelector("#background"); if (!bgEl) { requestAnimationFrame(forceBackground); return; } const b = [...defaultBackgrounds, ...config.customBackgrounds].find( (i) => i.id === config.activeBackground, ); if (!b) return; bgEl.style.backgroundImage = `url("${b.data}")`; new MutationObserver(() => { if (bgEl.style.backgroundImage !== `url("${b.data}")`) { bgEl.style.backgroundImage = `url("${b.data}")`; } }).observe(bgEl, { attributes: true, attributeFilter: ["style"] }); } if (config.activeBackground) { forceBackground(); } menu.classList.toggle("nova-glass", config.glass); menu.classList.toggle("nova-animate", !config.fastMenu); })();