Zeph Menu

Speed Hack, Set KP (Work In Progress) Invisibility

目前為 2025-02-09 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Zeph Menu
// @match        *://kour.io/*
// @version      1.0.2
// @author       Happyjeffery
// @icon         https://i.imgur.com/11sYWVM.png
// @description  Speed Hack, Set KP (Work In Progress) Invisibility
// @run-at       document-start
// @grant        unsafeWindow
// @namespace    https://greasyfork.org/users/1369586
// ==/UserScript==

(function() {
    'use strict';

    /***************************************
     * Performance.now Speed Hack
     ***************************************/
    const originalPerfNow = performance.now.bind(performance);
    function updatePerformanceNow(multiplier) {
        if (multiplier === 1) {
            performance.now = originalPerfNow;
        } else {
            performance.now = new Proxy(originalPerfNow, {
                apply: function(target, thisArg, argArray) {
                    try {
                        throw new Error();
                    } catch (e) {
                        if (!e.stack.includes("invoke_")) {
                            return target.apply(thisArg, argArray) * multiplier;
                        }
                    }
                    return target.apply(thisArg, argArray);
                }
            });
        }
    }
    updatePerformanceNow(1);

    /***************************************
     * Invisibility WebSocket Hook
     *
     * Wraps both addEventListener and the onmessage setter so that incoming
     * messages with the damage signature are blocked when invisibility is enabled.
     ***************************************/
    const wsInstances = [];  // Store all open WebSocket instances.
    const OriginalWebSocket = unsafeWindow.WebSocket;
    function hookOnMessage(ws) {
        // Wrap addEventListener("message", ...)
        const originalAddEventListener = ws.addEventListener;
        ws.addEventListener = function(type, listener, options) {
            if (type === "message") {
                const wrappedListener = function(event) {
                    try {
                        if (event.data && typeof event.data !== "string") {
                            const data = new Uint8Array(event.data);
                            const hexString = Array.from(data)
                                .map(b => b.toString(16).padStart(2, '0'))
                                .join(" ");
                            const damageSignature = "f3 04 c8 02 f5 15 04";
                            if (hexString.startsWith(damageSignature) && kourInstance.config.Invisible) {
                                return; // Block this damage packet.
                            }
                        }
                    } catch (e) { /* ignore errors */ }
                    listener.call(this, event);
                };
                return originalAddEventListener.call(this, type, wrappedListener, options);
            } else {
                return originalAddEventListener.call(this, type, listener, options);
            }
        };

        // Hook the onmessage property setter.
        const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(ws), 'onmessage');
        Object.defineProperty(ws, 'onmessage', {
            set: function(fn) {
                const wrapped = function(event) {
                    try {
                        if (event.data && typeof event.data !== "string") {
                            const data = new Uint8Array(event.data);
                            const hexString = Array.from(data)
                                .map(b => b.toString(16).padStart(2, '0'))
                                .join(" ");
                            const damageSignature = "f3 04 c8 02 f5 15 04";
                            if (hexString.startsWith(damageSignature) && kourInstance.config.Invisible) {
                                return;
                            }
                        }
                    } catch (e) { }
                    fn(event);
                };
                descriptor.set.call(this, wrapped);
            },
            get: function() {
                return descriptor.get.call(this);
            }
        });
    }
    unsafeWindow.WebSocket = function(...args) {
        const ws = new OriginalWebSocket(...args);
        wsInstances.push(ws);
        hookOnMessage(ws);
        return ws;
    };
    unsafeWindow.WebSocket.prototype = OriginalWebSocket.prototype;

    /***************************************
     * Minimal Kour Config Storage
     ***************************************/
    class Kour {
        constructor() {
            this.config = {
                Invisible: true  // When true, damage packets are blocked.
            };
        }
    }
    const kourInstance = new Kour();
    unsafeWindow.kourInstance = kourInstance;

    /***************************************
     * Admin Panel: Set KP (Work in progress)
     ***************************************/
    function setKP() {
        let kpValue = prompt("Enter KP value:", "35");
        if (!kpValue) return;
        const numKP = Number(kpValue);
        if (isNaN(numKP)) {
            console.error("Invalid KP value entered.");
            return;
        }
        if (typeof unityInstance !== 'undefined' && typeof unityInstance.SendMessage === 'function') {
            try {
                // Send numeric KP value – adjust the receiver method if needed.
                unityInstance.SendMessage('MainManager', 'OnReceivedIsAdmin', numKP);
                console.log(`[Zeph Menu] Sent KP value ${numKP} via SendMessage.`);
            } catch (e) {
                console.error("[Zeph Menu] SendMessage failed:", e);
            }
        } else {
            console.error("[Zeph Menu] unityInstance not found.");
        }
        unsafeWindow.kpValue = numKP;
    }

    /***************************************
     * Force Update of WebSocket Handlers on Invisibility Toggle
     ***************************************/
    function refreshWebSocketHandlers() {
        wsInstances.forEach(ws => {
            try {
                let current = ws.onmessage;
                ws.onmessage = current; // Reassign to trigger the setter.
            } catch (e) { /* ignore errors */ }
        });
    }

    /***************************************
     * Create UI (toggled with the "o" key)
     ***************************************/
    function createUI() {
        const menu = document.createElement('div');
        menu.id = "zephMenu";
        Object.assign(menu.style, {
            position: "fixed",
            top: "50px",
            right: "50px",
            width: "250px",
            backgroundColor: "#8b008b",
            color: "#fff",
            padding: "10px",
            zIndex: "10000",
            fontFamily: "Arial, sans-serif",
            fontSize: "14px",
            borderRadius: "5px",
            display: "none"
        });

        // Header
        const header = document.createElement("div");
        header.textContent = "Zeph Menu";
        header.style.textAlign = "center";
        header.style.fontWeight = "bold";
        header.style.marginBottom = "10px";
        menu.appendChild(header);

        // Set KP Button with "(Work in progress)"
        const adminBtn = document.createElement("button");
        adminBtn.textContent = "Set KP (Work in progress)";
        Object.assign(adminBtn.style, {
            width: "100%",
            margin: "5px 0",
            padding: "5px",
            cursor: "pointer"
        });
        adminBtn.addEventListener("click", setKP);
        menu.appendChild(adminBtn);

        // Speed Hack Slider (max 6×)
        const speedContainer = document.createElement("div");
        speedContainer.style.margin = "10px 0";
        const speedLabel = document.createElement("label");
        speedLabel.textContent = "Speed Hack Multiplier: ";
        speedContainer.appendChild(speedLabel);
        const speedValue = document.createElement("span");
        speedValue.textContent = "1x";
        speedContainer.appendChild(speedValue);
        const speedSlider = document.createElement("input");
        speedSlider.type = "range";
        speedSlider.min = "1";
        speedSlider.max = "6"; // Maximum 6×
        speedSlider.step = "0.5";
        speedSlider.value = "1";
        speedSlider.style.width = "100%";
        speedSlider.addEventListener("input", function() {
            let multiplier = parseFloat(speedSlider.value);
            speedValue.textContent = multiplier.toFixed(1) + "x";
            updatePerformanceNow(multiplier);
        });
        speedContainer.appendChild(speedSlider);
        menu.appendChild(speedContainer);

        // Invisibility Toggle
        const invisContainer = document.createElement("div");
        const invisCheckbox = document.createElement("input");
        invisCheckbox.type = "checkbox";
        invisCheckbox.id = "invisToggle";
        invisCheckbox.checked = kourInstance.config.Invisible;
        invisCheckbox.addEventListener("change", function() {
            kourInstance.config.Invisible = this.checked;
            console.log("Invisibility set to " + this.checked);
            refreshWebSocketHandlers();
        });
        const invisLabel = document.createElement("label");
        invisLabel.htmlFor = "invisToggle";
        invisLabel.textContent = " Invisible";
        invisContainer.appendChild(invisCheckbox);
        invisContainer.appendChild(invisLabel);
        menu.appendChild(invisContainer);

        document.body.appendChild(menu);
    }

    // Toggle the UI when pressing the "o" key (unless typing in an input/textarea)
    document.addEventListener("keydown", function(e) {
        if (e.key === "o" && !e.target.matches("input, textarea")) {
            const menu = document.getElementById("zephMenu");
            if (menu) {
                menu.style.display = (menu.style.display === "none" ? "block" : "none");
            }
        }
    });

    window.addEventListener("load", createUI);
})();