Greasy Fork

Alguien Client - Surve.io Client

A client to enhance the survev.io in-game experience with many features, as well as future features.

目前为 2024-12-06 提交的版本。查看 最新版本

// ==UserScript==
// @name         Alguien Client - Surve.io Client
// @namespace    https://github.com/SoyAlguien0/AlguienClient
// @version      0.0.1
// @description  A client to enhance the survev.io in-game experience with many features, as well as future features.
// @author       SoyAlguien
// @license      AGPL-3.0
// @require      https://update.greasyfork.org/scripts/391611/743919/WSHook.js
// @run-at       document-end
// @match        *://survev.io/*
// @match        *://66.179.254.36/*
// @match        *://expandedwater.online/*
// @grant        none
// ==/UserScript==
class GameMod {
  constructor() {
    this.lastFrameTime = performance.now();
    this.frameCount = 0;
    this.fps = 0;
    this.isFpsUncapped = false;
    this.isFpsVisible = true;
    this.isMenuVisible = true;

    this.animationFrameCallback = window.requestAnimationFrame.bind(window);

    this.initFpsCounter();
    this.initMenu();
    this.startUpdateLoop();
    this.setupWeaponBorderHandler();
    this.setupKeyListeners();
  }

  initFpsCounter() {
    this.fpsCounter = document.createElement("div");
    this.fpsCounter.id = "fpsCounter";
    Object.assign(this.fpsCounter.style, {
      position: "fixed",
      top: "10px",
      left: "10px",
      color: "white",
      backgroundColor: "rgba(0, 0, 0, 0.2)",
      padding: "5px 10px",
      borderRadius: "5px",
      fontFamily: "Arial, sans-serif",
      fontSize: "14px",
      zIndex: "10000",
      pointerEvents: "none",
    });

    document.body.appendChild(this.fpsCounter);
    this.updateFpsVisibility();
  }

  updateFpsVisibility() {
    this.fpsCounter.style.display = this.isFpsVisible ? "block" : "none";
  }

  toggleFpsDisplay() {
    this.isFpsVisible = !this.isFpsVisible;
    this.updateFpsVisibility();
  }

  toggleFpsUncap() {
    this.isFpsUncapped = !this.isFpsUncapped;
    this.animationFrameCallback = this.isFpsUncapped
      ? (callback) => setTimeout(callback, 1)
      : window.requestAnimationFrame.bind(window);
  }

  updateHealthBars() {
    const healthBars = document.querySelectorAll("#ui-health-container");
    healthBars.forEach((container) => {
      const bar = container.querySelector("#ui-health-actual");
      if (bar) {
        const width = Math.round(parseFloat(bar.style.width));
        let percentageText = container.querySelector(".health-text");

        if (!percentageText) {
          percentageText = document.createElement("span");
          percentageText.classList.add("health-text");
          Object.assign(percentageText.style, {
            width: "100%",
            textAlign: "center",
            marginTop: "5px",
            color: "#333",
            fontSize: "20px",
            fontWeight: "bold",
            position: "absolute",
            zIndex: "10",
          });
          container.appendChild(percentageText);
        }

        percentageText.textContent = `${width}%`;
      }
    });
  }

  updateBoostBars() {
    const boostCounter = document.querySelector("#ui-boost-counter");
    if (boostCounter) {
      const boostBars = boostCounter.querySelectorAll(
        ".ui-boost-base .ui-bar-inner",
      );

      let totalBoost = 0;
      const weights = [25, 25, 40, 10];

      boostBars.forEach((bar, index) => {
        const width = parseFloat(bar.style.width);
        if (!isNaN(width)) {
          totalBoost += width * (weights[index] / 100);
        }
      });

      const averageBoost = Math.round(totalBoost);
      let boostDisplay = boostCounter.querySelector(".boost-display");

      if (!boostDisplay) {
        boostDisplay = document.createElement("div");
        boostDisplay.classList.add("boost-display");
        Object.assign(boostDisplay.style, {
          position: "absolute",
          bottom: "75px",
          right: "335px",
          color: "#FF901A",
          backgroundColor: "rgba(0, 0, 0, 0.4)",
          padding: "5px 10px",
          borderRadius: "5px",
          fontFamily: "Arial, sans-serif",
          fontSize: "14px",
          zIndex: "10",
          textAlign: "center",
        });

        boostCounter.appendChild(boostDisplay);
      }

      boostDisplay.textContent = `AD: ${averageBoost}%`;
    }
  }

  setupWeaponBorderHandler() {
    const weaponContainers = Array.from(
      document.getElementsByClassName("ui-weapon-switch"),
    );
    weaponContainers.forEach((container) => {
      if (container.id === "ui-weapon-id-4") {
        container.style.border = "3px solid #2f4032";
      } else {
        container.style.border = "3px solid #FFFFFF";
      }
    });

    const weaponNames = Array.from(
      document.getElementsByClassName("ui-weapon-name"),
    );
    weaponNames.forEach((weaponNameElement) => {
      const weaponContainer = weaponNameElement.closest(".ui-weapon-switch");
      const observer = new MutationObserver(() => {
        const weaponName = weaponNameElement.textContent.trim();
        let border = "#FFFFFF";

        switch (weaponName.toUpperCase()) {
          case "CZ-3A1":
          case "G18C":
          case "M9":
          case "M93R":
          case "MAC-10":
          case "MP5":
          case "P30L":
          case "DUAL P30L":
          case "UMP9":
          case "VECTOR (9MM)":
          case "VSS":
          case "FLAMETHROWER":
            border = "#FFAE00";
            break;

          case "AK-47":
          case "OT-38":
          case "OTS-38":
          case "M39 EMR":
          case "DP-28":
          case "MOSIN-NAGANT":
          case "SCAR-H":
          case "SV-98":
          case "M1 GARAND":
          case "PKP PECHENEG":
          case "AN-94":
          case "BAR M1918":
          case "BLR 81":
          case "SVD-63":
          case "M134":
          case "WATER GUN":
            border = "#007FFF";
            break;

          case "FAMAS":
          case "M416":
          case "M249":
          case "QBB-97":
          case "MK 12 SPR":
          case "M4A1-S":
          case "SCOUT ELITE":
          case "L86A2":
            border = "#0f690d";
            break;

          case "M870":
          case "MP220":
          case "SAIGA-12":
          case "SPAS-12":
          case "USAS-12":
          case "SUPER 90":
          case "LASR GUN":
          case "M1100":
            border = "#FF0000";
            break;

          case "DEAGLE 50":
          case "RAINBOW BLASTER":
            border = "#000000";
            break;

          case "AWM-S":
          case "MK 20 SSR":
            border = "#808000";
            break;

          case "FLARE GUN":
            border = "#FF4500";
            break;

          case "MODEL 94":
          case "PEACEMAKER":
          case "VECTOR (.45 ACP)":
          case "M1911":
          case "M1A1":
            border = "#800080";
            break;

          case "M79":
            border = "#008080";
            break;

          case "POTATO CANNON":
          case "SPUD GUN":
            border = "#A52A2A";
            break;

          case "HEART CANNON":
            border = "#FFC0CB";
            break;

          default:
            border = "#FFFFFF";
            break;
        }

        if (weaponContainer.id !== "ui-weapon-id-4") {
          weaponContainer.style.border = `3px solid ${border}`;
        }
      });

      observer.observe(weaponNameElement, {
        childList: true,
        characterData: true,
        subtree: true,
      });
    });
  }

  updateMenuButtonText() {
    const hideButton = document.getElementById("hideMenuButton");
    hideButton.textContent = this.isMenuVisible
      ? "Hide Menu [P]"
      : "Show Menu [P]";
  }

  setupKeyListeners() {
    document.addEventListener("keydown", (event) => {
      if (event.key.toLowerCase() === "p") {
        this.toggleMenuVisibility();
      }
    });
  }

  initMenu() {
    const menu = document.createElement("div");
    menu.id = "soyAlguienMenu";
    Object.assign(menu.style, {
      position: "fixed",
      top: "50px",
      left: "10px",
      backgroundColor: "rgba(0, 0, 0, 0.8)",
      padding: "15px",
      borderRadius: "10px",
      boxShadow: "0 4px 10px rgba(0, 0, 0, 0.6)",
      zIndex: "10001",
      width: "250px",
      fontFamily: "Arial, sans-serif",
      color: "#fff",
    });

    const title = document.createElement("h2");
    title.textContent = "SoyAlguien Mod Menu";
    title.style.margin = "0 0 10px";
    title.style.textAlign = "center";
    title.style.fontSize = "18px";
    title.style.color = "#FFAE00";
    menu.appendChild(title);

    const fpsToggle = document.createElement("button");
    fpsToggle.textContent = "Show FPS";
    Object.assign(fpsToggle.style, {
      backgroundColor: "#FFAE00",
      border: "none",
      color: "#fff",
      padding: "10px",
      borderRadius: "5px",
      width: "100%",
      marginBottom: "10px",
      fontSize: "14px",
      cursor: "pointer",
    });
    fpsToggle.onclick = () => this.toggleFpsDisplay();
    menu.appendChild(fpsToggle);

    const uncapFpsToggle = document.createElement("button");
    uncapFpsToggle.textContent = "Uncap FPS";
    Object.assign(uncapFpsToggle.style, {
      backgroundColor: "#FF4500",
      border: "none",
      color: "#fff",
      padding: "10px",
      borderRadius: "5px",
      width: "100%",
      marginBottom: "10px",
      fontSize: "14px",
      cursor: "pointer",
    });
    uncapFpsToggle.onclick = () => this.toggleFpsUncap();
    menu.appendChild(uncapFpsToggle);

    const hideShowToggle = document.createElement("button");
    hideShowToggle.textContent = "Hide/Show Menu [P]";
    Object.assign(hideShowToggle.style, {
      backgroundColor: "#808080",
      border: "none",
      color: "#fff",
      padding: "10px",
      borderRadius: "5px",
      width: "100%",
      marginBottom: "10px",
      fontSize: "14px",
      cursor: "pointer",
    });
    hideShowToggle.onclick = () => this.toggleMenuVisibility();
    menu.appendChild(hideShowToggle);

    document.body.appendChild(menu);

    this.menu = menu;
  }

  toggleMenuVisibility() {
    const isVisible = this.menu.style.display !== "none";
    this.menu.style.display = isVisible ? "none" : "block";
  }

  startUpdateLoop() {
    const now = performance.now();
    const delta = now - this.lastFrameTime;

    this.frameCount++;

    if (delta >= 1000) {
      this.fps = Math.round((this.frameCount * 1000) / delta);
      this.frameCount = 0;
      this.lastFrameTime = now;

      if (this.isFpsVisible) {
        this.fpsCounter.textContent = `FPS: ${this.fps}`;
      }
    }

    this.updateHealthBars();
    this.updateBoostBars();
    this.animationFrameCallback(() => this.startUpdateLoop());
  }
}

const gameMod = new GameMod();