// ==UserScript==
// @name Kour.io 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);
})();