// ==UserScript==
// @name Moomoo.io - 1.8.0 Holder Macro & Interactive Menu
// @author Seryo
// @description This can act as a key rebinder. Pressing 'o' transforms it into a hold macro. use 'Esc' to toggle the menu and adjust the interval between holds. Holds: V=Spike, F=Trap, N=Mill, M=Food."
// @version 1
// @match *://*.moomoo.io/*
// @namespace https://gf.qytechs.cn/users/1190411
// @icon https://cdn.glitch.com/82ae8945-dcc6-4276-98a9-665381b4cd2b/cursor12.png
// @require https://cdnjs.cloudflare.com/ajax/libs/msgpack-lite/0.1.26/msgpack.min.js
// @require https://gf.qytechs.cn/scripts/478839-moomoo-io-packet-code/code/MooMooio%20Packet%20Code.js?version=1274028
// @run-at document-start
// @grant unsafeWindow
// @license MIT
// ==/UserScript==
function iso() {
return document.activeElement.id.toLowerCase() === 'chatbox';
}
function iaia() {
return document.activeElement.id.toLowerCase() === 'allianceinput';
}
function shhk() {
return !iso() && !iaia();
}
function saveInputValue() {
const inputValue = document.getElementById("speed").value;
localStorage.setItem("speedValue", inputValue);
}
function loadInputValue() {
const savedValue = localStorage.getItem("speedValue");
if (savedValue) {
document.getElementById("speed").value = savedValue;
}
const speedInput = document.getElementById("speed");
speedInput.addEventListener("blur", handleBlur);
speedInput.addEventListener("input", handleInput);
}
function handleBlur() {
const speedInput = document.getElementById("speed");
const inputValue = speedInput.value;
if (inputValue === "" || inputValue === "110") {
speedInput.value = "110";
speedInput.style.color = "gray";
}
}
function handleInput() {
const speedInput = document.getElementById("speed");
const inputValue = speedInput.value;
if (inputValue !== "110") {
speedInput.style.color = "black";
}
}
let menuVisible = false;
let initialInterval = true;
let multipleTimes = false;
document.body.innerHTML += `
<div id="menu" style="
display: none;
background: rgba(0, 0, 0, 0.8);
font-family: 'Hammersmith One, sans-serif';
position: fixed;
top: 20px;
left: 20px;
border: 1px solid #000;
border-radius: 8px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25), 5px 5px 10px rgba(0, 0, 0, 0.4);
width: 200px;
color: #fff;
font-size: 17px;
z-index: 1000;
overflow-y: auto;
padding: 10px;
text-align: center;
">
<h2 style="font-size: 28px; color: white; margin-bottom: 8px;">Holder</h2>
<hr style="border-color: white; margin: 5px 0;">
<p style="font-size: 22px; color: gray; margin: 8px 0; display: inline-block; vertical-align: middle;"><span id="onOffIndicator">${multipleTimes ? 'On' : 'Off'}</span></p>
<label for="speed" style="display: inline-block; margin-bottom: 5px; margin-left: 10px;"></label>
<input type="number" id="speed" value="110" min="0" maxlength="5" style="width: 50px; margin: 0; display: inline-block; border-radius: 8px; border: 1px solid white; padding: 5px; vertical-align: middle;">
<span style="margin-left: 5px; color: white;">ms</span>
<div id="controls" style="margin-top: 10px; position: relative; color: white; opacity: 0; transition: opacity 0.3s ease-in-out;">
<span id="controlsHeader" style="cursor: pointer; border: 1px solid white; padding: 8px; border-radius: 8px; background-color: #000; display: block;">Controls</span>
<div id="controlsOptions" style="display: none; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); background-color: #000; border: 1px solid white; border-radius: 8px; padding: 8px; width: 100px;">
<p class="fade" style="font-size: 22px; color: gray; margin: 8px 0;">Spike = V</p>
<p class="fade" style="font-size: 22px; color: gray; margin: 8px 0;">Trap = F</p>
<p class="fade" style="font-size: 22px; color: gray; margin: 8px 0;">Mill = N</p>
<p class="fade" style="font-size: 22px; color: gray; margin: 8px 0;">Food = M</p>
</div>
</div>
</div>
`;
const controlsElement = document.getElementById('controls');
const controlsOptionsElement = document.getElementById('controlsOptions');
const fadeElements = document.querySelectorAll('.fade');
document.getElementById('controlsHeader').addEventListener('click', () => {
controlsOptionsElement.style.display = controlsOptionsElement.style.display === 'none' ? 'block' : 'none';
fadeElements.forEach(element => {
element.style.opacity = controlsOptionsElement.style.display === 'none' ? 0 : 1;
});
controlsElement.style.opacity = controlsOptionsElement.style.display === 'none' ? 0 : 1;
});
(async () => {
unsafeWindow.keyRebinder = true;
let items = [],
weapons = [],
inGame = false,
keys = {},
intervalIds = {},
ws = await new Promise(async (resolve) => {
let { send } = WebSocket.prototype;
WebSocket.prototype.send = function (...x) {
send.apply(this, x);
this.send = send;
this.iosend = function (...datas) {
const [packet, data] = datas;
this.send(msgpack.encode([packet, data]));
};
this.addEventListener("message", (e) => {
if (!e.origin.includes("moomoo.io") && unsafeWindow.privateServer) return;
const [packet, data] = msgpack.decode(new Uint8Array(e.data));
switch (packet) {
case OLDPACKETCODE.RECEIVE["1"]:
inGame = true;
items = [0, 3, 6, 10];
weapons = [0];
break;
case OLDPACKETCODE.RECEIVE["11"]:
inGame = false;
break;
case OLDPACKETCODE.RECEIVE["17"]:
if (data[0]) {
if (data[1]) weapons = data[0];
else items = data[0];
}
break;
}
});
resolve(this);
};
});
loadInputValue();
document.getElementById("speed").addEventListener("input", saveInputValue);
unsafeWindow.addEventListener("keydown", handleKeydown);
unsafeWindow.addEventListener("keyup", (event) => {
if (inGame && keys[event.code] && shhk()) {
keys[event.code] = false;
clearInterval(intervalIds[event.code]);
}
});
async function handleKeydown(event) {
if (event.code === "Escape") {
menuVisible = !menuVisible;
const menu = document.getElementById("menu");
menu.style.display = menuVisible ? "block" : "none";
}
const speed = parseInt(document.getElementById("speed").value) || 110;
if (event.keyCode === 79 && shhk()) {
multipleTimes = !multipleTimes;
document.getElementById('onOffIndicator').textContent = multipleTimes ? 'On' : 'Off';
document.title = `𝙷𝚘𝚕𝚍 𝙼𝚊𝚌𝚛𝚘 ${multipleTimes ? '𝙾𝙽' : '𝙾𝙵𝙵'}`;
console.log("Multiple Times: " + multipleTimes);
}
if (inGame && shhk() && !keys[event.code]) {
keys[event.code] = true;
if (event.keyCode === 70) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[4], null]);
} else if (event.keyCode === 86) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[2], null]);
} else if (event.keyCode === 78) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[3], null]);
} else if (event.keyCode === 77) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[0], null]);
}
if (multipleTimes) {
intervalIds[event.code] = setInterval(() => {
if (event.keyCode === 70) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[4], null]);
} else if (event.keyCode === 86) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[2], null]);
} else if (event.keyCode === 78) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[3], null]);
} else if (event.keyCode === 77) {
ws.iosend(OLDPACKETCODE.SEND["5"], [items[0], null]);
}
}, speed);
}
}
}
})();