Taming.io Zoom hack

Allows to change zoom of the game using mouse wheel

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

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

(I already have a user script manager, let me install it!)

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.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==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();
        })
    }

})();