Taming.io Zoom hack

Allows to change zoom of the game using mouse wheel

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name Taming.io Zoom hack
// @author Murka
// @description Allows to change zoom of the game using mouse wheel
// @icon https://taming.io/img/interface/favicon.png
// @version 0.1
// @match *://taming.io/*
// @run-at document-start
// @grant none
// @license MIT
// @namespace https://greatest.deepsurf.us/users/919633
// ==/UserScript==
/* jshint esversion:8 */

/*
    DISCORD: https://discord.gg/cPRFdcZkeD
    GITHUB: https://github.com/murka007

    THIS SCRIPT MAY GET YOUR ACCOUNT BANNED
    TO SAFELY TEST IT, USE A TOR BROWSER!!! 
*/

(function() {
    "use strict";

    // Fix console.log
    const log = console.log;

    class Regex {
        constructor(code, unicode) {
            this.code = code;
            this.COPY_CODE = this.code;
            this._unicode = unicode || false;
            this._ANY_LETTER = "(?:[^\\x00-\\x7F-]|\\$|\\w)";
            this.hooks = {};
            this._NumberSystem = [
                { type: "binary", radix: 2, template: "0b" },
                { type: "octal", radix: 8, template: "0+" },
                { type: "decimal", radix: 10, template: "" },
                { type: "hexadecimal", radix: 16, template: "0x" },
            ];
            this._REPLACE_TYPE = {
                APPEND: 0,
                PREPEND: 1
            };
        }

        static parseData(value) {
            try {
                return Function(`return (${value});`)();
            } catch(err) {
                return null;
            }
        }

        _isRegexp(regex) {
            return Object.prototype.toString.call(regex) === "[object RegExp]";
        }

        // Used to replace number into it's representation in different number systems
        _generateNumberSystem(integer) {
            const copy = [...this._NumberSystem];
            const template = copy.map((system) => system.template + integer.toString(system.radix));
            return `(?:${ template.join("|") })`;
        }

        // Used to replace `{VAR}` into `(?:let|var|const)`
        _parseVariable(regex) {
            return regex.replace(/\{VAR\}/g, "(?:let|var|const)");
        }

        _parseNumber(regex) {
            return regex.replace(/NUMBER\{(.+?)\}/g, (...args) => {
                const number = Number(args[1]);
                return this._generateNumberSystem(number);
            })
        }

        // Used to format regular expressions, can support following types [RegExp, RegExp], [string, string], RegExp, string
        _format(name, regex, flags) {
            if (Array.isArray(regex)) {
                regex = regex.map(exp => this._isRegexp(exp) ? exp.source : exp).join("\\s*");
            }
            if (this._unicode) {
                regex = this._isRegexp(regex) ? regex.source : regex;
                regex = regex.replace(/\\w/g, this._ANY_LETTER);
            }
            regex = this._parseVariable(regex);
            regex = this._parseNumber(regex);
            const expression = new RegExp(regex, flags);
            if (!expression.test(this.code)) throw new Error("Failed to find: " + name);
            return expression;
        }

        _template(type, name, regex, substr) {
            const expression = new RegExp(`(${this._format(name, regex).source})`);
            this.code = this.code.replace(expression, type === this._REPLACE_TYPE.APPEND ? ("$1" + substr) : (substr + "$1"));
        }

        // Used to find some strings, variables etc and use it somewhere else
        match(name, regex, flags, debug) {
            const expression = this._format(name, regex, flags);
            const match = this.code.match(expression);
            this.hooks[name] = {
                expression: expression,
                match: match
            };
            if (debug) log(name, this.hooks[name]);
            return match;
        }

        matchAll(name, regex, flags, debug) {
            const expression = this._format(name, regex, flags);
            const matches = this.code.matchAll(expression);
            this.hooks[name] = {
                expression: expression,
                match: matches
            };
            if (debug) log(name, this.hooks[name]);
            return matches;
        }

        replace(name, regex, substr, flags) {
            const expression = this._format(name, regex, flags);
            this.code = this.code.replace(expression, substr);
        }

        // Used to add something to a string
        append(name, regex, substr) {
            this._template(this._REPLACE_TYPE.APPEND, name, regex, substr);
        }

        // Used to add something before a string
        prepend(name, regex, substr) {
            this._template(this._REPLACE_TYPE.PREPEND, name, regex, substr);
        }
    }

    function defineProperty(target, name, value) {
        Object.defineProperty(target, name, {
            value: value
        })
    }

    function linker(value) {
        const hook = {
            0: value,
            toString: (radix) => hook[0].toString(radix),
            valueOf: () => hook[0].valueOf(),
            get length() {
                return hook[0].length;
            }
        };
        return hook;
    }

    window.max_width = linker(1920 + 192 * 6);
    window.max_height = linker(1080 + 108 * 6);

    function applyRegex(code) {
        // return code;

        const Hook = new Regex(code, true);
        const matches = Hook.matchAll("max_scale", [/const /, /(\w+)/, /=/, /(?=[\d(-+])(.+?);/], "g", true);
        const scale = {
            max_width: false,
            max_height: false
        };

        for (const [match, name, value] of matches) {
            if (scale.max_width && scale.max_height) break;

            const integer = Regex.parseData(value);
            if (Number.isInteger(integer)) {
                if (integer === 1920) {
                    Hook.replace("max_width_var", [`const `, `${name}`, `=.+?;`], `const ${name} = window.max_width;`);
                    scale.max_width = true;
                }
                if (integer === 1080) {
                    Hook.replace("max_height_var", [`const `, `${name}`, `=.+?;`], `const ${name} = window.max_height;`);
                    scale.max_height = true;
                }
            }
        }

        Hook.replace("max_width", [/NUMBER{1920}/, /\//], `window.max_width/`, "gi");
        Hook.replace("max_height", [/NUMBER{1080}/, /\//], `window.max_height/`, "gi");

        return Hook.code;
    }

    async function loadScript(target, script) {

        const response = await fetch(script.src);
        let code = await response.text();
        code = applyRegex(code);

        const blob = new Blob([code], { type: "text/plain" });
        const element = document.createElement("script");
        element.src = URL.createObjectURL(blob);

        // lol
        defineProperty(element, "src", script.src);

        target.appendChild(element);
        URL.revokeObjectURL(element.src);

        START();
    }

    let count = 0;
    const observer = new MutationObserver(function(mutations) {
        for (const mutation of mutations) {
            for (const node of mutation.addedNodes) {
                if (node.tagName === "SCRIPT" && /^https?:\/\/taming\.io\/js\/.+?\.js$/.test(node.src)) {
                    if (count !== 2) {
                        count += 1;
                        return;
                    }
                    observer.disconnect();
                    loadScript(mutation.target, node);

                    // Firefox support
                    function scriptExecuteHandler(event) {
                        event.preventDefault();
                        node.removeEventListener("beforescriptexecute", scriptExecuteHandler);
                    }
                    node.addEventListener("beforescriptexecute", scriptExecuteHandler);
                    node.remove();
                }
            }
        }
    })
    observer.observe(document, { childList: true, subtree: true });

    function START() {

        let resize = null;
        window.addEventListener = new Proxy(window.addEventListener, {
            apply(target, _this, args) {
                const [type, listener, ...options] = args;
                if (type === "resize" && /max_(?:width|height)/.test(listener.toString())) {
                    resize = listener;
                    window.addEventListener = target;
                }
                return target.apply(_this, args);
            }
        })

        let wheels = 0;
        const scaleFactor = {
            width: 192,
            height: 108
        };

        window.addEventListener("wheel", function(event) {
            if (!(event.target instanceof HTMLCanvasElement) || event.ctrlKey) return;
            const { max_width, max_height } = window;

            // Used to create a small gap, so users could easily find the default scale
            if (max_width[0] === 1920 && max_height[0] === 1080) wheels += 1;
            if (wheels % 5 !== 0) return;

            const { width, height } = scaleFactor;
            max_width[0] = Math.max(width, max_width[0] + (event.deltaY > 0 ? -width : width));
            max_height[0] = Math.max(height, max_height[0] + (event.deltaY > 0 ? -height : height));
            if (typeof resize === "function") resize();
        })
    }

})();