Survev.io X-Ray + ping/fps/health display

Always enable visual X-Ray mode (see through houses, no ads)

目前為 2025-11-08 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Survev.io X-Ray + ping/fps/health display
// @namespace    https://github.com/ashcool/survevio-xray
// @version      5.0
// @description  Always enable visual X-Ray mode (see through houses, no ads)
// @author       ashcool (Credits to Zetraious)
// @license      MIT
// @match        *://survev.io/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

let xrayEnabled = true;

// ---- X-Ray effect ----
Object.defineProperty(Object.prototype, 'textureCacheIds', {
	set(value) {
		this._textureCacheIds = value;
		if (Array.isArray(value)) {
			const scope = this;
			value.push = new Proxy(value.push, {
				apply(target, thisArgs, args) {
					if (args[0].indexOf('ceiling') > -1) {
						Object.defineProperty(scope, 'valid', {
							set(v) { this._valid = v; },
							get() { return false; }
						});
					}
					return Reflect.apply(target, thisArgs, args);
				}
			});
		}
	},
	get() {
		return this._textureCacheIds;
	}
});

// ---- Prevent WebGL detection ----
const params = { get() { return null; } };
Object.defineProperty(window, 'WebGLRenderingContext', params);
Object.defineProperty(window, 'WebGL2RenderingContext', params);

// ---- Notice ----
window.addEventListener('DOMContentLoaded', () => {
	const el = document.createElement('div');
	el.innerHTML = `
	<style>
	.my-dialog {
		position: absolute;left: 50%;top: 50%;
		padding: 20px;background: rgba(0, 0, 0, 0.9);
		box-shadow: 0 0 0 1000vw rgba(0, 0, 0, 0.5);
		border-radius: 5px;color: #fff;
		transform: translate(-50%, -50%);
		text-align: center;z-index: 999999;
	}
	.my-close {
		position: absolute;right: 5px;top: 5px;width: 20px;height: 20px;
		opacity: 0.5;cursor: pointer;
	}
	.my-close:before,.my-close:after{
		content:'';position:absolute;left:50%;top:50%;
		width:100%;height:20%;
		transform:translate(-50%,-50%) rotate(-45deg);
		background:#fff;
	}
	.my-close:after{transform:translate(-50%,-50%) rotate(45deg);}
	.my-close:hover{opacity:1;}
	</style>
	<div class="my-dialog">
		<div class="my-close" onclick="this.parentNode.style.display='none';"></div>
		<big style="font-size: 2em;">Survev.io X-Ray HUD</big>
		<br><br>X-Ray mode is <b>Always ON</b>.
		<br>Includes FPS, Ping, Color Health, and No Ads.
	</div>`;
	while (el.children.length > 0) document.body.appendChild(el.children[0]);
});

// ---- HUD ----
window.addEventListener('load', function () {
	(function() {
		const c = "position:absolute;left:10px;transform:translateY(-50%);color:white;font-size:14px;font-family:'roboto condensed',sans-serif;font-weight:bold;background-color:rgba(0,0,0,0.3);padding:3px 5px;border-radius:5px;z-index:10000;";

		// --- FPS ---
		const fpsDisplay = document.createElement('div');
		fpsDisplay.style.cssText = c + "top:60%;";
		fpsDisplay.innerHTML = `0 FPS`;
		document.body.appendChild(fpsDisplay);

		let frames = [];
		const updateFPS = () => {
			requestAnimationFrame(() => {
				const now = performance.now();
				while (frames.length > 0 && frames[0] <= now - 1000) frames.shift();
				frames.push(now);
				const fps = frames.length;
				fpsDisplay.innerHTML = `${fps} FPS`;
				fpsDisplay.style.color = fps < 50 ? "red" : fps < 80 ? "yellow" : "white";
				updateFPS();
			});
		};
		updateFPS();

		// --- Ping ---
		const pingDisplay = document.createElement('div');
		pingDisplay.style.cssText = c + "top:calc(60% + 25px);";
		pingDisplay.innerHTML = `Waiting...`;
		document.body.appendChild(pingDisplay);

		let ws, sendTime, region = "na";
		function wsUrl() {
			const map = { na: 'usr', eu: 'eur', asia: 'asr', sa: 'sa', ru: 'russia' };
			return `wss://${map[region] || 'usr'}.mathsiscoolfun.com:8001/ptc`;
		}

		function startPing() {
			if (ws && ws.readyState === 1) ws.close();
			try {
				ws = new WebSocket(wsUrl());
				ws.onopen = () => {
					sendTime = Date.now();
					ws.send(new ArrayBuffer(1));
				};
				ws.onmessage = () => {
					const ping = Date.now() - sendTime;
					pingDisplay.innerHTML = `${ping} ms`;
					pingDisplay.style.color =
						ping > 120 ? "red" :
						ping > 90 ? "orange" :
						ping > 60 ? "yellow" : "white";
					setTimeout(() => {
						if (ws.readyState === 1) {
							sendTime = Date.now();
							ws.send(new ArrayBuffer(1));
						}
					}, 1000);
				};
				ws.onerror = () => {
					pingDisplay.innerHTML = `Ping error`;
					pingDisplay.style.color = "gray";
				};
				ws.onclose = () => {
					setTimeout(startPing, 2000); // auto reconnect
				};
			} catch (err) {
				pingDisplay.innerHTML = "No WS";
				pingDisplay.style.color = "gray";
			}
		}

		// Wait until the in-game HUD loads, then start ping
		const waitForGame = setInterval(() => {
			if (document.getElementById("ui-health-container")) {
				clearInterval(waitForGame);
				startPing();
			}
		}, 2000);

		// --- Health / Boost HUD ---
		let lastHealth = 0;
		const healthText = document.createElement("span");
		healthText.style = "display:block;position:fixed;z-index:2;right:15px;margin:6px 0 0 0;mix-blend-mode:difference;font-weight:bold;font-size:large;";
		const boostText = document.createElement("span");
		boostText.style = "display:block;position:fixed;z-index:2;left:15px;margin:6px 0 0 0;mix-blend-mode:difference;font-weight:bold;font-size:large;";
		document.querySelector("#ui-health-container")?.appendChild(healthText);
		document.querySelector("#ui-health-container")?.appendChild(boostText);

		function getHealthColor(pct) {
			if (pct > 75) return "#00FF66";
			if (pct > 50) return "#CCFF33";
			if (pct > 25) return "#FF9933";
			return "#FF3333";
		}

		setInterval(() => {
			const hpBar = document.getElementById("ui-health-actual");
			if (!hpBar) return;
			const hp = parseFloat(hpBar.style.width) || 0;
			if (hp !== lastHealth) {
				lastHealth = hp;
				healthText.innerHTML = hp.toFixed(1) + "%";
				healthText.style.color = getHealthColor(hp);
			}

			const boosts = [0,1,2,3].map(i =>
				parseFloat(document.querySelector(`#ui-boost-counter-${i} .ui-bar-inner`)?.style.width || 0) / 100
			);
			const total = Math.round(25*boosts[0] + 25*boosts[1] + 37.5*boosts[2] + 12.5*boosts[3]);
			boostText.innerHTML = total + "%";
			boostText.style.color = total > 50 ? "#33CCFF" : "#66FFFF";
		}, 200);

		// --- Remove Ads ---
		function delAds(id) {
			const el = document.getElementById(id);
			if (el) el.remove();
		}
		setInterval(() => {
			delAds("leaderboard-front");
			delAds("adunit");
			delAds("ui-stats-ad-container-desktop");
			document.getElementsByClassName("ad-block-leaderboard-bottom")[0]?.remove();
		}, 1000);
	})();
});