// ==UserScript== // @name Moomoo.io Enemy location tracker // @author Murka // @description Shows approximate location of enemies on the map. Using this script you can easily find players in the game // @icon https://moomoo.io/img/favicon.png?v=1 // @version 0.5 // @match *://moomoo.io/* // @match *://*.moomoo.io/* // @run-at document-start // @grant none // @license MIT // @namespace https://greasyfork.org/users/919633 // @downloadURL https://update.greasyfork.icu/scripts/447589/Moomooio%20Enemy%20location%20tracker.user.js // @updateURL https://update.greasyfork.icu/scripts/447589/Moomooio%20Enemy%20location%20tracker.meta.js // ==/UserScript== /* jshint esversion:6 */ /* Author: Murka Github: https://github.com/Murka007 Discord: https://discord.gg/sG9cyfGPj5 Greasyfork: https://greasyfork.org/en/users/919633 MooMooForge: https://github.com/MooMooForge How does it work? - It shows a notification on the map when some player breaks an object - You should see the object at least once to make notifications work - It works even better on sandbox */ (function() { "use strict"; const log = console.log; const createRecursiveHook = (target, prop, condition, callback) => { (function recursiveHook() { Object.defineProperty(target, prop, { set(value) { delete target[prop]; this[prop] = value; if ( condition(this, value) && callback(this, value) ) return; recursiveHook(); }, configurable: true }) })(); } function createHook(target, prop, setter) { const symbol = Symbol(prop); Object.defineProperty(target, prop, { get() { return this[symbol]; }, set(value) { setter(this, symbol, value); }, configurable: true }) } const myPlayer = { id: null, alive: false, focused: true, playerObject: null }; // Get myPlayer object createHook(Object.prototype, "isPlayer", function(that, symbol, value) { that[symbol] = value; if (value === true && that.sid === myPlayer.id) { myPlayer.playerObject = that; } }); function map(value, start1, stop1, start2, stop2) { return (value - start1) / (stop1 - start1) * (stop2 - start2) + start2; } function lerp(start, stop, amt) { return amt * (stop - start) + start; } function inView(x, y, radius) { const { maxScreenWidth, maxScreenHeight } = window.config || {}; const visibleHorizontally = x + radius > 0 && x - radius < maxScreenWidth; const visibleVertically = y + radius > 0 && y - radius < maxScreenHeight; return true; return visibleHorizontally && visibleVertically; } const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); canvas.id = "notification-canvas"; canvas.width = 300; canvas.height = 300; window.addEventListener("load", function() { const CSS = ` #notification-canvas { display: inline-block; position: absolute; bottom: 20px; left: 20px; width: 130px; height: 130px; border-radius: 4px; } @media only screen and (max-width: 768px) { #notification-canvas { width: 66px; height: 66px; bottom: unset; top: 8px; left: 8px; } } `; const style = document.createElement("style"); style.innerHTML = CSS; document.head.appendChild(style); const mapDisplay = document.querySelector("#mapDisplay"); mapDisplay.parentNode.insertBefore(canvas, mapDisplay.nextSibling); }) const notifications = []; function clear() { notifications.splice(0, notifications.length); } window.addEventListener("focus", () => { myPlayer.focused = true; }) window.addEventListener("blur", () => { myPlayer.focused = false; clear(); }) class Notification { constructor(x, y) { this._x = x; this._y = y; this._radius = 1; this._opacity = 1; this._timeout = { value: 0, max: 75 }; } _animate() { if (this._timeout.value === this._timeout.max) { const index = notifications.indexOf(this); notifications.splice(index, 1); return; } this._radius = lerp(this._radius, 35, 0.03); this._opacity = map(this._timeout.value, 0, this._timeout.max, 1, 0.3); this._timeout.value += 1; } draw() { this._animate(); const { mapScale } = window.config || {}; if (!mapScale) return; const x = this._x / mapScale * canvas.width; const y = this._y / mapScale * canvas.height; ctx.save(); ctx.globalAlpha = this._opacity; ctx.strokeStyle = "#c93e3e"; ctx.lineWidth = 10; ctx.beginPath(); ctx.arc(x, y, this._radius, 0, 2 * Math.PI); ctx.stroke(); ctx.restore(); } } // Notification rendering loop function loop() { window.requestAnimationFrame(loop); ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i=0;i ( typeof _this === "object" && typeof _this.encode === "function" && _this.encode.length === 1 ), (_this) => { document.msgpack.Encoder = _this; return true; } ); // Intercept msgpack decoder createRecursiveHook( Object.prototype, "maxExtLength", (_this) => ( typeof _this === "object" && typeof _this.decode === "function" && _this.decode.length === 1 ), (_this) => { document.msgpack.Decoder = _this; return true; } ); // Handle WebSocket data function message(event) { try { const data = document.msgpack.Decoder.decode(new Uint8Array(event.data)); const temp = [data[0], ...data[1]]; PACKETS[temp[0]](temp); } catch(err){} } // Intercept WebSocket window.WebSocket = new Proxy(WebSocket, { construct(target, args) { const socket = new target(...args); socket.addEventListener("message", message); return socket; } }); })();