Moomoo.io Enemy location tracker

Shows approximate location of enemies on the map. Using this script you can easily find players in the game

Nainštalovať tento skript?
Autor skriptu navrhuje

Tiež sa vám môže páčiť Dsync Client [Sploop.io].

Nainštalovať tento skript
// ==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
// ==/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<notifications.length;i++) {
            notifications[i].draw();
        }
    }
    window.requestAnimationFrame(loop);

    const objects = new Map();
    function createNotification(id) {
        const object = objects.get(id);
        const { x, y } = myPlayer.playerObject;

        // Object must exist and it shouldn't be in player view
        if (!object) return;
        //if (!object || inView(object.x-x, object.y-y, object.radius)) return;

        const notify = new Notification(object.x, object.y);
        notifications.push(notify);
    }

    function playerSetup(temp) {
        myPlayer.id = temp[1];
        myPlayer.alive = true;
    }

    function playerDied(temp) {
        myPlayer.alive = false;
        clear();
    }

    function formatObject(object) {
        const [ id, x, y, angle, radius, resourceType, objectType, ownerID ] = object;
        return {
            id,
            x,
            y,
            angle,
            radius,
            resourceType,
            objectType,
            ownerID
        };
    }

    function createObject(temp) {
        for (let i=0;i<temp[1].length;i+=8) {
            const object = formatObject(temp[1].slice(i, i+8));
            objects.set(object.id, object);
        }
    }

    function deleteObject(temp) {

        // Make sure we don't create a new notification when player is afk, otherwise it will cause lag issues
        if (myPlayer.alive && myPlayer.focused) createNotification(temp[1]);
        objects.delete(temp[1]);
    }

    const PACKETS = {
        1: playerSetup,
        11: playerDied,
        6: createObject,
        12: deleteObject,
    };

    document.msgpack = {
        Encoder: null,
        Decoder: null
    };

    // Intercept msgpack encoder
    createRecursiveHook(
        Object.prototype, "initialBufferSize",
        (_this) => (
            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;
        }
    });

})();