Gats.io - Vaakir's hack pack GUI V4

The almighty one

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Gats.io - Vaakir's hack pack GUI V4
// @namespace    http://tampermonkey.net/
// @version      4.8
// @description  The almighty one
// @author       PureVaakir (88%) & Freehuntx (12%)
// @match        https://gats.io/
// @grant        none
// ==/UserScript==

class GUI {
    constructor(parent) {
        this.parent = parent;
        this.init();
    }
    init() {
        this.gui = GUI.createCustomElement('div', this.parent, '', '', '', 'myFUNNYGUY');
        this.guiHead = GUI.createCustomElement('div', this.gui, 'Vaakir Hack pack V4.6', '', 'headDiv', '');

        // Drag functionality on (gui head div)
        let offsetX, offsetY, isDragging = false, gui = this.gui;
        this.guiHead.addEventListener('mousedown', function(event) {
            isDragging = true;
            offsetX = event.clientX - gui.getBoundingClientRect().left;
            offsetY = event.clientY - gui.getBoundingClientRect().top;
        });
        document.addEventListener('mousemove', function(event) {
            if (isDragging) {
                const newLeft = event.clientX - offsetX;
                const newTop = event.clientY - offsetY;
                gui.style.left = newLeft + 'px';
                gui.style.top = newTop + 'px';
            }
        });
        document.addEventListener('mouseup', function() {
            isDragging = false;
        });

        let styleSheet = document.createElement('style');
        let css = `
        .myFUNNYGUY {
            --name-width: 100px;
            --gui-width: 245px;
            --row-height: 30px;
            --elm-height: 20px;
            --inp-width: 80px;
            --bg-color: rgba(85, 85, 85, 0.5);

            -webkit-touch-callout: none; /* iOS Safari */
            -webkit-user-select: none; /* Safari */
             -khtml-user-select: none; /* Konqueror HTML */
               -moz-user-select: none; /* Old versions of Firefox */
                -ms-user-select: none; /* Internet Explorer/Edge */
                    user-select: none; /* Non-prefixed version, currently supported by Chrome, Edge, Opera and Firefox */

            background-color: var(--bg-color);
            position: fixed;
            top: 25vh;
            z-index: 9999;
            width: 245px;
            padding-bottom: 10px;

            font-size: var(--elm-height);
            font-weight: bold;
            font-family: Orbitron;
            color: white;
        }
        .myFUNNYGUY #headDiv {
            cursor: grab;
            text-align: center;
            height: var(--row-height);
        }
        .myFUNNNYGUY .myBox {
            width: var(--gui-width);
            padding: 0px;
            margin: 0;
        }
        .myFUNNYGUY .title {
            display: inline-block;
            cursor: pointer;
            transition: max-height 0.3s ease-in-out;
            width: 100%;
            max-height: 2em;
            border-bottom: 1px solid black;
        }

        .myFUNNYGUY .title:before {
            content: '▾';
            display: inline-block;
            transition: transform 0.3s ease-in-out;
        }
        .myFUNNYGUY .title.closed:before {
            content: '▸';
        }

        .myFUNNYGUY .row {
            display: flex;
            flex-direction: row;
            align-items: center;

            margin-left: 10px;
            height: var(--elm-height);
        }
        .myFUNNYGUY .label {
            width: var(--name-width);
            line-height: var(--elm-height);
            text-overflow: clip;
            text-align: left;

            margin-right: 10px;
            padding: 0;
            margin: 0;
            font-size: large;
        }
        .myFUNNYGUY .subOptionOpen {
            vertical-align: top;
            overflow: hidden;
            transition: max-height 0.3s ease-in-out;
        }
        .myFUNNYGUY .subOptionClosed {
            vertical-align: top;
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease-in-out;
        }

        .myFUNNYGUY input {
            margin-top: auto;
            margin-bottom: auto;
        }
        .myFUNNYGUY .select,
        .myFUNNYGUY .button,
        .myFUNNYGUY .inputColor,
        .myFUNNYGUY input {
            color: black;
        }

        .myFUNNYGUY input[type=checkbox] { margin: 0; outline: none; height: var(--elm-height); width: var(--elm-height);}
        .myFUNNYGUY input:hover { cursor: pointer;}
        .myFUNNYGUY input:focus { box-shadow: 0 0 10px #9ecaed;}
        .myFUNNYGUY input[type=radio] { border-top: auto; height: 20px;}
        .myFUNNYGUY input[type=color] { width: 50px;}
        .myFUNNYGUY .checked {
            height: var(--elm-height);
            width: var(--elm-height);
            box-shadow: -1px -2px 5px gray;
            border: 3px solid gray;
            background-color: green;
            margin-top: auto;
            margin-bottom: auto;
            box-shadow: 2 2 2px #a0a0a0;
        }
        .subOptionOpen .input,
        .subOptionOpen .inputText,
        .subOptionOpen .select {
            width: var(--inp-width);
            height: var(--elm-height);
        }
        .myFUNNYGUY .select {
            font-size: calc(var(--elm-height) * 0.75);
        }
        .myFUNNYGUY .colGreen {
            background-color: green;
        }
        .myFUNNYGUY .colRed {
            background-color: red;
        }
        .myFUNNYGUY .button {
            width: var(--elm-height);
            height: var(--elm-height);
            padding: 0;
            margin: 0;
            font-size: small;
        }

        .myFUNNYGUY .inputColor {
            height: var(--elm-height);
            font-size: small;
        }

        `;
        styleSheet.innerText = css;
        document.head.appendChild(styleSheet);
    }
    addMainFolder(name, id = '', className = '') {
        return new Folder(this.gui, name, id, className);
    }
    static createCustomElement(HTMLTag, parent, innerTxt, type, id, className) {
        let htmlTag = document.createElement(HTMLTag);
        parent.appendChild(htmlTag);


        if (innerTxt && type != "select")   htmlTag.innerText   = innerTxt;
        if (type && type != "select")       htmlTag.type        = type;
        if (id)         htmlTag.id          = id;
        if (className)  htmlTag.className   = className;

        if (type == "number") {
            htmlTag.value = innerTxt;
        } else if (type == "select") {

            // innerTxt in this example is : ["a","b", ..]
            for (let o of innerTxt) {
                // console.log(o);
                GUI.createCustomElement("option", htmlTag, o, "", "", "option")
            }
        }

        return htmlTag;
    }
}

class Folder {
    constructor(parent, name, id, className, guiInstance) {
        this.parent = parent;
        this.name = name;
        this.id = id;
        this.className = className;
        this.gui = guiInstance;
        this.init();
    }
    init() {
        let _this = this;
        let container   = GUI.createCustomElement('div', this.parent, '', '', '', 'myBox');
        let button      = GUI.createCustomElement('div', container, this.name, '', this.id, this.className);
        let subOptions  = GUI.createCustomElement('div', container, '', '', '', 'subOptionClosed');
        this.subOptions = subOptions;

        button.onclick = function() {
            if      (subOptions.className === 'subOptionOpen')   { subOptions.className = 'subOptionClosed'; button.className = _this.className + ' closed'}
            else if (subOptions.className === 'subOptionClosed') { subOptions.className = 'subOptionOpen'; button.className = _this.className + ' open'}
        }
        return subOptions;
    }
    addFolder(name, id = '', className = '', guiInstance) {
        return new Folder(this.subOptions, name, id, className, guiInstance);
    }
    add(options, name, type = "checkbox", val = 32) {
        // Each GUI row has a label and an input
        let row     = GUI.createCustomElement('div', this.subOptions, '', '', '', 'row');
        let label   = GUI.createCustomElement('label', row, name, '', '', 'label');
        let input;
        if (type == "checkbox") {
            input   = GUI.createCustomElement('input', row, '', type, '', 'input');

            const updateCheckboxState = () => { input.checked = options[name]; };
            const updateOptionsState = () => { options[name] = input.checked; };
            updateCheckboxState();

            input.addEventListener('change', () => { updateOptionsState(); opts.saveOptions(); });
            options[name] = input.checked;

            // Watch for changes in the options object and update the checkbox accordingly
            Object.defineProperty(options, name, {
                set(value) {
                    input.checked = value;
                },
                get() {
                    return input.checked;
                },
            });
        }
        else if (type == "number") {
            input = GUI.createCustomElement('input', row, val, type, '', 'input');

            const updateCheckboxState = () => { input.value = options[name]; };
            const updateOptionsState = () => { options[name] = parseInt(input.value) || 32; };
            updateCheckboxState();

            input.addEventListener('change', () => {updateOptionsState(); opts.saveOptions();});
            options[name] = parseInt(input.value) || 32;

            // Watch for changes in the options object and update the checkbox accordingly
            Object.defineProperty(options, name, {
                set(value) {
                    input.value = value;
                },
                get() {
                    return parseInt(input.value) || 32;
                },
            });
        } else if (type == "select") {
            input = GUI.createCustomElement('select', row, val, type, '', 'select');

            const updateCheckboxState = () => { input.value = options[name]; };
            const updateOptionsState = () => { options[name] = input.value; };
            updateCheckboxState();

            input.addEventListener('change', () => { updateOptionsState(); opts.saveOptions();});
            options[name] = input.value;

            // Watch for changes in the options object and update the checkbox accordingly

            Object.defineProperty(options, name, {
                set(value) {
                    input.value = value;
                },
                get() {
                    return name=="allies" ? input : input.value;
                },
            });

            if (name == "allies") {
                let buttonAdd = GUI.createCustomElement('button', row, '+', '', '', 'button');
                let buttonRemove = GUI.createCustomElement('button', row, '-', '', '', 'button');
                buttonAdd.onclick = function() {
                    options.alliesList.push(input.value);
                }
                buttonRemove.onclick = function() {
                    options.alliesList = options.alliesList.filter(item => item !== input.value);
                }
            }

        } else if (type == "input") {
            input = GUI.createCustomElement('input', row, val, type, '', 'inputText');
            // let buttonAdd = GUI.createCustomElement('button', row, '+', '', '', 'button');
            // let buttonRemove = GUI.createCustomElement('button', row, '-', '', '', 'button');

            input.onfocus = function() {
                if (typeof j46 !== 'undefined') {
                    j46 = true; // disables movement in the game until other game input is recieved
                }
            }
            input.onblur = function() {
                if (typeof j46 !== 'undefined') {
                    j46 = false; // disables movement in the game until other game input is recieved
                }
            }

            const updateCheckboxState = () => { input.value = options[name]; };
            const updateOptionsState = () => { options[name] = input.value; };
            updateCheckboxState();

            input.addEventListener('change', () => { updateOptionsState(); opts.saveOptions();});
            options[name] = input.value;

            // Watch for changes in the options object and update the checkbox accordingly

            Object.defineProperty(options, name, {
                set(value) {
                    input.value = value;
                },
                get() {
                    return input.value;
                },
            });

            // buttonAdd.onclick = function() {
            //     options.alliesList.push(input.value);
            // }
            // buttonRemove.onclick = function() {
            //     options.alliesList = options.alliesList.filter(item => item !== input.value);
            // }
        } else if (type == "color") {
            input   = GUI.createCustomElement('input', row, '', 'color', '', 'inputColor');

            const updateCheckboxState = () => { input.value = options[name]; };
            const updateOptionsState = () => { options[name] = input.value; opts.colorChange = true; };
            updateCheckboxState();

            input.addEventListener('change', () => { updateOptionsState(); opts.saveOptions(); });
            options[name] = input.value;

            // Watch for changes in the options object and update the checkbox accordingly
            Object.defineProperty(options, name, {
                set(value) {
                    input.value = value;
                },
                get() {
                    return input.value;
                },
            });
        } else if (type == "button") {
            let resetButton   = GUI.createCustomElement('button', row, 'Reset', '', '', 'inputColor');
            let randomButton   = GUI.createCustomElement('button', row, 'Random', '', '', 'inputColor');

            resetButton.onclick = function() {
                window.longCrate[0][1][1][3] = opts.texturePack.longCrate = opts.defaultColors[1];
                window.crate[0][1][1][3]     = opts.texturePack.squareCrate = opts.defaultColors[0];
                window.longCrate[0][0][1][3] = opts.texturePack.longCrateBorder = opts.defaultColors[2];
                window.crate[0][0][1][3]     = opts.texturePack.squareCrateBorder = opts.defaultColors[2];
                opts.colorChange = true;
                opts.saveOptions();
            }
            randomButton.onclick = function() {
                window.longCrate[0][1][1][3] = opts.texturePack.longCrate = calc.rColor();
                window.crate[0][1][1][3]     = opts.texturePack.squareCrate = calc.rColor();
                window.longCrate[0][0][1][3] = opts.texturePack.longCrateBorder = calc.rColor();
                window.crate[0][0][1][3]     = opts.texturePack.squareCrateBorder = calc.rColor();
                opts.colorChange = true;
                opts.saveOptions();
            }
            /*const updateCheckboxState = () => { input.value = options[name]; };
            const updateOptionsState = () => { options[name] = input.value; opts.colorChange = true; };
            updateCheckboxState();

            input.addEventListener('change', () => { updateOptionsState(); opts.saveOptions(); });
            options[name] = input.value;

            // Watch for changes in the options object and update the checkbox accordingly
            Object.defineProperty(options, name, {
                set(value) {
                    input.value = value;
                },
                get() {
                    return input.value;
                },
            });*/
        }
    }
}

class calc {
    static round_to(n, dec) {
        return Math.round( n * (10**dec)) / (10**dec);
    }
    static multiply(vectorA, vectorB) {
        return vectorA.x*vectorB.x + vectorA.y*vectorB.y;
    }
    static length(vector) {
        return Math.sqrt(vector.x**2+vector.y**2);
    }
    static angle180(vectorA, vectorB) {
        return Math.acos( calc.multiply(vectorA,vectorB) / (calc.length(vectorA)*calc.length(vectorB)) );
    }
    static angle360(vectorA, vectorB) {
        // GETS THE ANGLE IN [0,360] DEGREES AND NOT JUST [0,180], WHICH IS PRETTY USEFULL..
        let dot = vectorA.x * vectorB.x + vectorA.y * vectorB.y;      //# dot product
        let det = vectorA.x * vectorB.y - vectorA.y * vectorB.x;      //# determinant
        return Math.atan2(det, dot); //# atan2(y, x) or atan2(sin, cos)
    }
    static vectorAB(a,b) {
        return {x: a.x - b.x, y: a.y - b.y}
    }
    static rotateVector(vector, angle) {
        let v1 = [vector.x, vector.y];
        let v2 = {x: 0,y: 0};
        v2.x = v1[0] * Math.cos(angle) - v1[1]*Math.sin(angle);
        v2.y = v1[0] * Math.sin(angle) + v1[1]*Math.cos(angle);
        return v2;
    }
    static radiansToDegrees(piFraction) {
        return (piFraction / Math.PI) * 180;
    }
    static collisionCheck(obstacles, t) {

        let p = 20; // 1/2 of playerWidth..
        // let maxChecks = Math.min(5, obstacles.length); // the walls should be somewhat sorted
        for (let i = 0; i < obstacles.length; i++) {
            let wall = obstacles[i];

            // let TL = (wall.x1 < t.x && wall.y)
            // let betweenX = (wall.x1 > t.x && t.x > wall.x2);
            // let betweenY = (wall.y1 < t.y && t.y > wall.y2);
            // if (betweenX && betweenY) return true;
            // (wall.x1 > t.x && t.x > wall.x2)
            // (wall.y1 > t.y && t.y > wall.y2)
            if (t.x < (wall.x1 - p) || t.y < (wall.y1 - p) ) continue
            if (t.x > (wall.x2 + p) || t.y > (wall.y2 + p) ) continue
            return true;
            //if (betweenX && betweenY) return true;
        }

        //const closestAcceptableDistance = 50**2;
        //for (let i = 0; i < obstacles.length; i++) {
        //    const w = obstacles[i];
        //    const d1 = calc.distanceSquared(t, {x: w.x1, y: w.y1});
        //    const d2 = calc.distanceSquared(t, {x: w.x2, y: w.y1});
        //    const d3 = calc.distanceSquared(t, {x: w.x1, y: w.y2});
        //    const d4 = calc.distanceSquared(t, {x: w.x2, y: w.y2});
        //    const closestDistanceSquared = Math.min(d1,d2,d3,d4);
        //    if (closestDistanceSquared < closestAcceptableDistance) return true;
        //}
        return false;
    }
    static combine(vectorA, vectorB) {
        return {x: vectorA.x + vectorB.x, y: vectorA.y + vectorB.y}
    }
    static distance(coor1, coor2) {
        return Math.hypot(coor2.x - coor1.x, coor2.y - coor1.y);
    };
    static distanceSquared(coor1, coor2) {
        // I was told sqrt is slow okay
        return (coor2.x - coor1.x)**2 + (coor2.y - coor1.y)**2;
    };
    static entitiesDistance(entities, point) {
        entities.forEach(e => {
            e.distance = calc.distance(e, point)
        });
    }
    static sortByDistance(entities, point) {
        calc.entitiesDistance(entities, point);
        entities.sort((a, b) => a.distance - b.distance);
    };
    static normalize(v, length) {
        const currLength = calc.length(v);
        const scaleRatio = length / currLength;
        return {x: v.x * scaleRatio, y: v.y * scaleRatio }
    }
    static sortByDistanceFaster(entities, point) {
        // I think it is faster, :eyes: laze, with e okay, just deal with it
        entities.forEach(e => {
            e.distanceSquared = calc.distanceSquared(e, point)
        });
        entities.sort((a, b) => a.distanceSquared - b.distanceSquared);
        return entities;
    }
    static sortByDistanceFilter(entities, point, maxDistance) {
        let remainingEnemies = [];
        for (let i = 0; i < entities.length; i++) {
            let e = entities[i];
            e.distanceSquared = calc.distanceSquared(e, point);
            if (e.distanceSquared <= maxDistance * maxDistance) remainingEnemies.push(e);
        }
        remainingEnemies.sort((a, b) => a.distanceSquared - b.distanceSquared); // may make it slower, (hence removed)
        return remainingEnemies;
    }
    static collisionCoord(a,b) {

        // let a = {x:2,y:6,dx:3,dy:-4}; // let a = {x:2,y:6,dx:3,dy:-4};
        // let b = {x:0,y:-3,dx:2,dy:2}; // let b = {x:0,y:-3,dx:1,dy:1};

        if (b.dx ==0) { b.dx = 0.0001; }
        const s1 = (a.x-b.x)/b.dx;
        const s2 = a.dx/b.dx;
        const t = (a.y-b.y-b.dy*s1)/(-a.dy+b.dy*s2);
        const s = (a.x+a.dx*t-b.x)/b.dx;

        const x = a.x+a.dx*t;
        const y = a.y+a.dy*t;

        if (0 < s && s < 1 && 0 < t && t < 1) return {x: x, y: y};
        return false;
    }

    static aheadNess(e, distance) {
        return {
            x: e.x + e.spdX * distance / (window.fac ?? options.aimbot.calibrate - GameInterface.currentPing/10), // ping part is under tests (not used)
            y: e.y + e.spdY * distance / (window.fac ?? options.aimbot.calibrate - GameInterface.currentPing/10),
        }
    }

    static gunToEnemy(player, enemy, distance, getGunLengthMultiplier = 1) {
        const end = calc.aheadNess(enemy, distance);

        const angle = Math.atan2(player.y - end.y, player.x - end.x) + Math.PI; // 0 - 6
        const deltaX = Math.cos(angle - Math.PI / game.getGunOffset(player.class));
        const deltaY = Math.sin(angle - Math.PI / game.getGunOffset(player.class));

        const start = {
            x: player.x + deltaX * game.getGunLength(player.class) * getGunLengthMultiplier,
            y: player.y + deltaY * game.getGunLength(player.class) * getGunLengthMultiplier
        }

        return {start: start, end: end}
    }

    static lineCollidesWithWalls(gunLine, walls, ctx, doCollisionDrawing = options.aimbot.espCollisions) {
        let visible = true;
        let i = 0;
        while (visible && i < walls.length) {
            const w = walls[i];
            let wallTop = {x: w.x1, y: w.y1, dx: w.width, dy: 0}
            let wallBot = {x: w.x1, y: w.y2, dx: w.width, dy: 0}
            let wallLef = {x: w.x1, y: w.y1, dx: 0, dy: w.height}
            let wallRig = {x: w.x2, y: w.y1, dx: 0, dy: w.height}

            let col1 = calc.collisionCoord(gunLine, wallTop);
            let col2 = calc.collisionCoord(gunLine, wallBot);
            let col3 = calc.collisionCoord(gunLine, wallLef);
            let col4 = calc.collisionCoord(gunLine, wallRig);

            if (col1 || col2 || col3 || col4) {
                visible = false;

                // Aimbot espCollisions
                if (doCollisionDrawing) {
                    if (col1) { col1 = game.getScreenPos(col1); Draw.circle(ctx, col1.x, col1.y, 5, "red"); }
                    if (col2) { col2 = game.getScreenPos(col2); Draw.circle(ctx, col2.x, col2.y, 5, "red"); }
                    if (col3) { col3 = game.getScreenPos(col3); Draw.circle(ctx, col3.x, col3.y, 5, "red"); }
                    if (col4) { col4 = game.getScreenPos(col4); Draw.circle(ctx, col4.x, col4.y, 5, "red"); }
                }
            }
            i++;
        }
        return visible;
    }

    // From vaakirhack v3
    static rColor()          {
        return calc.rgbToHex(Math.floor(Math.random()*255),Math.floor(Math.random()*255),Math.floor(Math.random()*255));
    }
    static componentToHex(c) {
        var hex = c.toString(16);
        return hex.length == 1 ? "0" + hex : hex;
    }
    static rgbToHex(r, g, b) {
        return "#" + calc.componentToHex(r) + calc.componentToHex(g) + calc.componentToHex(b);
    }
}

const options = new class OptionsMenu {
    aimbot = {
        active: false,
        alwaysAim: false,
        calibrate: 30,
        moveFrequency: 10,
        autoShoot: true,
        espLine: true,
        espCollisions: true,
        allies: "",
        alliesList: ["[1337] PureVaakir","PureVaakir","Hacker0","VaakTradeBot"]
    }
    AI = {
        autoRespawn: false,
        autoTalk: false,
        tactReload: true,
        pathFinding: false,
        autoRetreat: true,
        cautious: true,
        espVector: true,
        algorithm: "terminator",
        followLeader: "PureVaakir",
        updateFrequency: 5,
    }
    esp = {
        active: true,
        playerLine: false,
        shootRange: true,
        walls: false,
        showAllies: false
    }
    autoUpgrade = {
        active: true,
        perk1: "longRange",
        perk2: "dash",
        perk3: "thickSkin"
    }
    misc = {
        zoom: true,
        antiSilencer: true,
        anitCamo: true,
        anitMines: true,
    }
    chatScroller = {
        active: false,
        activatedOnce: false,
        speed: 10,
        message: "Vaakir hax",
    }
    options = {
        textWidth: 20
    }
    data = {
        active: false,
        x: 0,
        y: 0,
        test1: false,
        test2: false,
    }
    texturePack = {
        longCrate: "#303030",
        squareCrate: "#b1e9f9",
        longCrateBorder: "#646464",
        squareCrateBorder: "#4dd8f0",
        wtf: false,
        texture: "ok"
    }
    futureUpdates = {}

    // these need only to be gathered every now and then, not for every animation frame #tick,
    // therefore I am saving them here together with the rest
    walls = [];

    // AI pathfinding variables
    searchLength = 30;
    rotating = Math.PI/24; // Rotating check by 0.2r = 11.5degrees
    mapCenterVector = {x: 3500, y: 3500}
    goalVector = {x: 3500, y: 3500}
    goal = {x: 3500, y: 3500}
    movementVector = {x: this.searchLength, y: 0} // silk touch
    perkBotActive = false;
    defaultColors = ["#dfbf9f","#bec8dd","#808080"];
    colorChange = true;
    changeRotatitingDirection = false;

    lastChat = Date.now();

    wtfOn = false; // darkmode is wtf

    // goalVector = {x:this.goal.x - this.x, y:this.goal.y - this.y};

    constructor() {
        this.init();
    }
    init() {
        this.loadOptions();

        this.gui = new GUI(document.body);

        const hackpack = this.gui.addMainFolder('Hacks', '', 'title');
        const aimbot = hackpack.addFolder('Aimbot (right click)', '', 'title', this.gui);
        aimbot.add(this.aimbot, 'active');
        aimbot.add(this.aimbot, 'alwaysAim');
        aimbot.add(this.aimbot, 'calibrate', 'number', this.aimbot.calibrate);
        aimbot.add(this.aimbot, 'moveFrequency', 'number', this.aimbot.moveFrequency);
        aimbot.add(this.aimbot, 'allies', 'select','');
        aimbot.add(this.aimbot, 'autoShoot');
        aimbot.add(this.aimbot, 'espLine');
        aimbot.add(this.aimbot, 'espCollisions');

        const AI = hackpack.addFolder('AI', '', 'title', this.gui);
        AI.add(this.AI, 'autoRespawn');
        AI.add(this.AI, 'autoTalk');
        AI.add(this.AI, 'tactReload');
        AI.add(this.AI, 'pathFinding');
        AI.add(this.AI, 'autoRetreat');
        AI.add(this.AI, 'cautious');
        AI.add(this.AI, 'espVector');
        AI.add(this.AI, 'algorithm', 'select', 'terminator follow'.split(" "));
        AI.add(this.AI, 'followLeader', 'input', 'PureVaakir');
        AI.add(this.AI, 'updateFrequency', 'number', this.AI.updateFrequency);

        const esp = hackpack.addFolder('Esp', '', 'title');
        esp.add(this.esp, 'active');
        esp.add(this.esp, 'playerLine');
        esp.add(this.esp, 'shootRange');
        esp.add(this.esp, 'showAllies');
        esp.add(this.esp, 'walls');

        const autoUpgrade = hackpack.addFolder('AutoUpgrade', '', 'title');
        autoUpgrade.add(this.autoUpgrade, 'active');
        autoUpgrade.add(this.autoUpgrade, 'perk1', 'select', 'bipod optics thermal armorPiercing extended grip silencer lightweight longRange thickSkin'.split(' '));
        autoUpgrade.add(this.autoUpgrade, 'perk2', 'select', 'shield firstAid grenade knife engineer ghillie dash gasGrenade landMine fragGrenade'.split(' '));
        autoUpgrade.add(this.autoUpgrade, 'perk3', 'select', 'bipod optics thermal armorPiercing extended grip silencer lightweight longRange thickSkin'.split(' '));

        const misc = hackpack.addFolder('Misc', '', 'title');
        misc.add(this.misc, 'zoom');
        misc.add(this.misc, 'antiSilencer');
        misc.add(this.misc, 'anitCamo');
        misc.add(this.misc, 'anitMines');

        const chatScroller = hackpack.addFolder('ChatScroller', '', 'title');
        chatScroller.add(this.chatScroller, 'active');
        chatScroller.add(this.chatScroller, 'speed', 'number', this.chatScroller.speed);
        chatScroller.add(this.chatScroller, 'message', 'input', 'Try Vaakir hack!');

        const data = hackpack.addFolder('data', '', 'title');
        data.add(this.data, 'active');
        data.add(this.data, 'x', 'input', '');
        data.add(this.data, 'y', 'input', '');
        data.add(this.data, 'test1');
        data.add(this.data, 'test2');

        const texturePack = hackpack.addFolder('texturePack', '', 'title');
        texturePack.add(this.texturePack, 'longCrate', 'color', '');
        texturePack.add(this.texturePack, 'squareCrate', 'color', '', );
        texturePack.add(this.texturePack, 'longCrateBorder', 'color', '', );
        texturePack.add(this.texturePack, 'squareCrateBorder', 'color', '', );
        texturePack.add(this.texturePack, 'wtf');
        texturePack.add(this.texturePack, 'texture', 'button', '');

        const future = hackpack.addFolder('FutureUpdates', '', 'title');
        future.add(this.AI, 'AI');
        future.add(this.AI, 'autoCalibrate');
        future.add(this.AI, 'multiboxing');
        future.add(this.AI, 'knifebot');
        future.add(this.AI, 'shieldbot');
        future.add(this.AI, 'customstuff');



        // const AI = hackpack.addFolder('AI', '', 'title');
        // AI.add(this.AI, 'comingSoonTM');

        // const chatScroller = hackpack.addFolder('ChatScroller', '', 'title');
        // chatScroller.add(this.chatScroller, 'comingSoon');
        // chatScroller.add(this.chatScroller, 'message', 'input','ok?');

        //const options = hackpack.addFolder('Options', '', 'title');
        // options.add(this.options, 'textWidth', 'number', this.options.textWidth);

    }
    loadOptions() {
        try {
            const savedOptions = JSON.parse(localStorage.getItem('options'));
            if (savedOptions) {
                Object.assign(this, savedOptions);
                this.aimbot.active = false; // Because I haven't added in an activation, unless you right click yet.
                this.colorChange = true; // just because
                this.wtfOn = false; // also just because - experiment
                this.chatScroller.activatedOnce = false; // also is just just because, amen to that
                this.perkBotActive = false;
                changeRotatitingDirection = false;
            }
        } catch (error) {
            console.error('Error loading options from localStorage:', error);
        }
    }
    saveOptions() {
        try {
            let saveData = JSON.stringify(this);
            localStorage.setItem('options', saveData);
        } catch (error) {
            console.error('Error saving options to localStorage:', error);
        }
    }
}
window.opts = options;

class Draw {
    static line(ctx, start, end, color="black", lineWidth=1) {
        ctx.strokeStyle = color;
        ctx.lineWidth = lineWidth;
        ctx.beginPath();
        ctx.moveTo(start.x, start.y);
        ctx.lineTo(end.x, end.y);
        ctx.stroke();
    }
    static triangle(ctx,x,y,size,color) {
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.moveTo(x-size/2, y);
        ctx.lineTo(x, y-size);
        ctx.lineTo(x+size/2, y);
        ctx.closePath();
        ctx.fill();
    }
    static circle(ctx, x, y, radius, color="red", lineWidth=1) {
        ctx.fillStyle = color;
        ctx.lineWidth = lineWidth;
        ctx.beginPath();
        ctx.arc(x, y, radius, 0, 2 * Math.PI);
        ctx.fill();
    }
}

class GameInterface {
    static get localPlayerId() {
        return +(window.c3 ?? '-1')
    }

    static get camera() {
        return window.c2
    }

    static get playerPool() {
        return window.RD?.pool || {}
    }

    static get grenadePool() {
        return window.RA?.pool || {}
    }

    static get wallPool() {
        return window.RB?.pool || {}
    }

    static get projectilePool() {
        return window.RC?.pool || {}
    }

    static get scaleX() {
        return window.j6
    }

    static get scaleY() {
        return window.j5
    }

    static get upgrades() {
        return window.o3
    }

    static get conn() {
        return RF.list[0]
    }

    static get leaderBoard() {
        return j38.current
    }

    static get currentPlayers() {
        return parseInt(o4.currentPlayers);
    }

    static get currentPing() {
        return RF.list[0].currentPing;
    }

}

class Game {
    constructor() {}

    get isIngame() {
        return GameInterface.localPlayerId > -1
    }

    get localPlayer() {
        return GameInterface.playerPool[GameInterface.localPlayerId]
    }
    get isAlive() {
        return (this.isIngame && this.localPlayer?.hp > 0)
    }

    getGunLength(playerClass = this.localPlayer?.class) {
        return {
            pistol: 60,
            smg: 60,
            shotgun: 80,
            assault: 80,
            'bolt-action-rifle': 100,
            'machine-gun': 85
        }[playerClass] || 0
    }

    getGunRange(playerClass = this.localPlayer?.class) {
        let hasLongRange = false;
        if (playerClass == this.localPlayer?.class) hasLongRange = Object.values(GameInterface.upgrades).indexOf('longRange') > -1
        const range = {
            pistol: 400,
            smg: 280,
            shotgun: 235,
            assault: 370,
            'bolt-action-rifle': 620,
            'machine-gun': 365
        }[playerClass] || 0
        const rangeBonus = hasLongRange ? range * (window.fact ?? 0.5) : 0
        return range + rangeBonus
    }

    getGunOffset(playerClass = this.localPlayer?.class) {
        return {
            pistol: 10,
            smg: 10,
            shotgun: 12,
            assault: 12,
            'bolt-action-rifle': 16,
            'machine-gun': 14
        }[playerClass] || 0
    }

    getPlayers(includeMe=true, includeMates=true, includeEnemies=true) {
        if (!this.isIngame) return []
        const { id, teamCode } = this.localPlayer

        return Object.values(GameInterface.playerPool)
            .filter(player => {
                if (!player.activated) return false
                if (player.hp <= 0) return false
                if (!includeMe && player.id === id) return false
                if (!includeMates && ((player.teamCode !== 0 && player.teamCode === teamCode) || options.aimbot.alliesList.includes(player.username))) return false
                if (!includeEnemies && ((player.teamCode === 0 || player.teamCode !== teamCode) && !options.aimbot.alliesList.includes(player.username))) return false
                return true
            })
    }

    getEnemies() {
        return this.getPlayers(false, false, true)
    }

    getMates() {
        return this.getPlayers(false, true, false)
    }

    getGrenades() {
        return Object.values(GameInterface.grenadePool).filter(grenade => {
            if (!grenade.activated) return false
            return true
        })
    }

    getWalls() {
        return Object.values(GameInterface.wallPool)
            .filter(wall => {
                if (!wall.activated) return false
                return true
            })
            .map(wall => {
                const fixedWall = { ...wall }
                const size = wall.model[0][0][0][0]
                fixedWall.width = size * 2
                fixedWall.height = size * 2

                if (wall.type === 'longCrate') {
                    if (wall.angle / 90 % 2 === 0) { fixedWall.width /= 2 }
                    else { fixedWall.height /= 2 }
                }

                // Used for the aimbot wall detection check
                fixedWall.x1 = fixedWall.x - fixedWall.width / 2
                fixedWall.x2 = fixedWall.x + fixedWall.width / 2
                fixedWall.y1 = fixedWall.y - fixedWall.height / 2
                fixedWall.y2 = fixedWall.y + fixedWall.height / 2



                return fixedWall
            })
    }

    getProjectiles() {
        return Object.values(GameInterface.projectilePool).filter(projectile => {
            if (!projectile.activated) return false
            //if (projectile.x !== 0 && projectile.x !== 0) return false
            return true
        })
    }

    getScreenPos(pos) {
        return GameInterface.camera?.getRelPos(pos) || { x: 0, y: 0 }
    }

    resizeCamera(width, height) {
        if (!this.isIngame) return
        window.width = width;
        window.height = width / (16 / 9);

        window.a1({
            width: width,
            height: typeof height !== 'undefined' ? height : width / (16 / 9)
        });
    }

    isOutsideMap(entity) {
        /*
            0  -> 3500-1000 = 1000
            11 -> 3500-2000 = 1500
            10 -> 3500-2000 = 1500
            17 -> 3500-1500 = 2000
            24 -> 3500-1500 = 2000
            25 -> 3500-1000 = 2500
        */
        const borderLevel = {
            0: 0,
            5: 0,
            10: 1,
            15: 1,
            20: 2,
            25: 3,
            30: 3
        }[Math.floor((GameInterface.currentPlayers) / 5)*5]
        const borderGrowth = 500;
        const defaultSize = 1000;

        const mapFogVertices = {
            x1: 3500 - (defaultSize + borderGrowth * borderLevel),
            x2: 3500 + (defaultSize + borderGrowth * borderLevel),
            y1: 3500 - (defaultSize + borderGrowth * borderLevel),
            y2: 3500 + (defaultSize + borderGrowth * borderLevel),
        }
        const outsideX =  (mapFogVertices.x1 > entity.x || entity.x > mapFogVertices.x2);
        const outsideY =  (mapFogVertices.y1 > entity.y || entity.y > mapFogVertices.y2);
        if (outsideX || outsideY) return true;
        return false;
    }

    /*setMouse(x, y) {
      if (!document.onmousemove) return
      const clientX = x * GameInterface.scaleX
      const clientY = y * GameInterface.scaleY
      document.onmousemove(new MouseEvent('mousemove', { clientX, clientY }))
    }*/
}
const game = new Game()
window.game = game

class Hack {
    #canvas = document.querySelector('canvas');
    #ctx = this.#canvas.getContext('2d');
    #aimbot = { active: false, target: null, x: 0, y: 0 }
    iter = 1;
    iterMax = 50;
    alliesWalls = [];

    constructor() {
        this.#initRender();
        this.#initAimbot();
        this.#initZoom();
        //this.#hookMouse()
    }

    /*fixConsole() {
        let _this = this
        setTimeout(() => {
            const iframe = document.createElement('iframe')
            iframe.style.display = 'block'
            document.body.appendChild(iframe)
            window.console = iframe.contentWindow.console
            _this.er = iframe
        })
    }*/

    #initRender() {
        this.#ctx.oclearRect = this.#ctx.oclearRect || this.#ctx.clearRect
        this.#ctx.clearRect = (...args) => {
            this.#ctx.oclearRect(...args)
            requestAnimationFrame(() => {
                if (game.isIngame) this.#tick()
            })
        }
    }

    #initAimbot() {
        const mouseMoveHook = evt => {
            if (!mouseMoveHook.original) return

            const clientX = (options.aimbot.active && this.#aimbot.active && this.#aimbot.target?.visible && game.isAlive) ? this.#aimbot.x * GameInterface.scaleX : evt.clientX;
            const clientY = (options.aimbot.active && this.#aimbot.active && this.#aimbot.target?.visible && game.isAlive) ? this.#aimbot.y * GameInterface.scaleY : evt.clientY;

            return mouseMoveHook.original(
              new MouseEvent('mousemove', {
                view: evt.view,
                bubbles: evt.bubbles,
                cancelable: evt.cancelable,
                clientX: clientX,
                clientY: clientY
              })
            );
        }

        mouseMoveHook.original = document.onmousemove;
        document.onmousemove = mouseMoveHook;

        Object.defineProperty(document, 'onmousemove', {
            set: val => {
                mouseMoveHook.original = val;
            }
        })

        let interval;
        window.onmousedown = ({ button, clientX, clientY }) => {
            if (button == 2) {
                options.aimbot.active = !options.aimbot.active;
                this.#aimbot.active = true;
                interval = setInterval(() => {
                    if (options.aimbot.active && this.#aimbot.active && game.isAlive) {
                        if (this.#aimbot.target) {
                            if (this.#aimbot.target.activated && this.#aimbot.target.hp > 0) {
                                const clientX = this.#aimbot.x * GameInterface.scaleX;
                                const clientY = this.#aimbot.y * GameInterface.scaleY;

                                if (this.#aimbot.target.visible || options.aimbot.alwaysAim) {
                                    mouseMoveHook.original(new MouseEvent('mousemove', { clientX, clientY }));
                                }
                                if (this.#aimbot.target.visible && options.aimbot.autoShoot) {
                                    document.onmousedown?.(new MouseEvent('mousedown', { clientX, clientY }));

                                    setTimeout(() => document.onmouseup?.(new MouseEvent('mouseup', { clientX, clientY })), 15);
                                }
                            }
                            else {
                                this.#aimbot.target = null;
                            }
                        }
                    }
                }, options.aimbot.moveFrequency);
            }
        }

        window.onmouseup = ({ button, clientX, clientY }) => {
            if (button === 2 && !options.aimbot.active) {
                clearInterval(interval)
                this.#aimbot.active = false;
                this.#aimbot.target = null;
                // options.aimbot.active = !options.aimbot.active;
                // options.aimbot.active = false;
            }
        }
    }

    #initZoom() {
        window.addEventListener("wheel", function(e) {
            if (options.misc.zoom) {
                // game.resizeCamera(window.width, window.height); soo.. window.width is max 2000, editing j7, j8 allows for further zoom which is fun..
                let dir = Math.sign(e.deltaY);
                if (dir== 1) {
                    a1({width: j7 * 1.1, height: j8 * 1.1});
                }
                if (dir==-1) {
                    a1({width: j7 * 0.9, height: j8 * 0.9});
                }
            }
        });
    }

    #tick() {
        const me = game.localPlayer;
        const enemies = game.getEnemies();
        const mates = game.getMates(); // mates :) best word usage
        // Remove enemies which are too close
        /*.filter(enemy => {
            if (enemy.x > me.x + 50) return true
            if (enemy.x < me.x - 50) return true
            if (enemy.y > me.y + 50) return true
            if (enemy.y < me.y - 50) return true
            return false
        })*/
        const myScreenPos = game.getScreenPos(me);
        const myAngle = me.playerAngle * (Math.PI / 180);
        let walls = options.walls; // game.getWalls();

        // calc.sortByDistance(enemies, me);

        // Gun range helper
        if (options.esp.active && options.esp.shootRange) {
            const gunStart = {
                x: me.x + Math.cos(myAngle - Math.PI / game.getGunOffset()) * game.getGunLength(),
                y: me.y + Math.sin(myAngle - Math.PI / game.getGunOffset()) * game.getGunLength()
            }
            const gunEnd = {
                x: gunStart.x + Math.cos(myAngle) * game.getGunRange(),
                y: gunStart.y + Math.sin(myAngle) * game.getGunRange()
            }
            // const gunStartScreenPos = game.getScreenPos(gunStart);
            // const gunEndScreenPos = game.getScreenPos(gunEnd);

            const fakeEnemy = {fake: true, x: gunEnd.x, y: gunEnd.y, spdX: 0, spdY: 0, distance: game.getGunRange()}
            enemies.unshift(fakeEnemy);

            // Draw.line(this.#ctx, gunStartScreenPos, gunEndScreenPos, "black", 1);
        }

        let fallBack = {dist: Infinity, x: undefined, newX: undefined, y: undefined, newY: undefined, visible: false}
        let visibleEnemies = [];

        // we do not want to shoot our allies.. so we pretend that they are moving square walls :>
        if (options.aimbot.alliesList.length > 0) {
            walls = walls.filter(item => !this.alliesWalls.includes(item));

            this.alliesWalls = [];
            for (const a of mates) {
                const dist = calc.distance(a, me);
                const aX = a.x + a.spdX * dist / (window.fac ?? options.aimbot.calibrate);
                const aY = a.y + a.spdY * dist / (window.fac ?? options.aimbot.calibrate);

                const newWallMove = {
                    x1: aX - 40, x2: aX + 40,
                    y1: aY - 40, y2: aY + 40,
                    width: 80, height: 80
                }
                const newWallPos = {
                    x1: a.x - 25, x2: a.x + 25,
                    y1: a.y - 25, y2: a.y + 25,
                    width: 50, height: 50
                }
                walls.push(newWallMove);
                walls.push(newWallPos);
                this.alliesWalls.push(newWallMove);
                this.alliesWalls.push(newWallPos);

                // enemy block esp
                if (options.esp.active && options.esp.showAllies) {
                    const topLeft0 = game.getScreenPos({x: newWallMove.x1, y: newWallMove.y1});
                    const topLeft1 = game.getScreenPos({x: newWallPos.x1, y: newWallPos.y1});

                    this.#ctx.strokeStyle = 'red';
                    this.#ctx.lineWidth = 1;
                    this.#ctx.strokeRect(topLeft0.x, topLeft0.y, newWallMove.width, newWallMove.height);
                    this.#ctx.strokeRect(topLeft1.x, topLeft1.y, newWallPos.width, newWallPos.height);
                }
            }
        }

        // Wall esp
        if (options.esp.active && options.esp.walls) {
            for (const w of walls) {
                const topLeft = game.getScreenPos({x: w.x1, y: w.y1});
                const bottomRight = game.getScreenPos({x: w.x2, y: w.y2});

                this.#ctx.strokeStyle = 'red';
                this.#ctx.lineWidth = 1;
                this.#ctx.strokeRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
            }
        }

        for (const enemy of enemies) {
            enemy.distance = calc.distance(enemy, me);
            const dist = enemy.distance - game.getGunLength();

            if (dist < game.getGunRange()) {

                const gunToEnemy = calc.gunToEnemy(me, enemy, dist);
                const startX = gunToEnemy.start.x;
                const startY = gunToEnemy.start.y;
                const endX = gunToEnemy.end.x;
                const endY = gunToEnemy.end.y;

                const startScreenPos = game.getScreenPos({ x: startX, y: startY });
                const endScreenPos = game.getScreenPos({ x: endX, y: endY });

                // Visibility check + no need to calculate the line collisions for the other enemies
                let visible = true;
                if (visibleEnemies.length == 0) {

                    // We do not want to shoot at walls.. so we check if our hypothetical gun line crosses each corner of every wall (close by)
                    const gunLine = {x: startX, y: startY, dx: endX - startX, dy: endY - startY}
                    visible = calc.lineCollidesWithWalls(gunLine, walls, this.#ctx);
                }

                // ESP playerLine
                if (options.esp.active && options.esp.playerLine && !enemy.fake) {
                    const enemyScreenPos = game.getScreenPos(enemy);
                    Draw.line(this.#ctx, myScreenPos, enemyScreenPos, enemy.color.a, 2);
                }

                // Aim target logic
                if (this.#aimbot.active && !enemy.fake) {
                    if (fallBack.newX == undefined) {
                        fallBack = {distance: enemy.distance, x: undefined, newX: endScreenPos.x, y: undefined, newY: endScreenPos.y, visible: visible, ...enemy}
                    } else if (visible && !fallBack.visible || !fallBack.visible && enemy.distance < fallBack.distance || fallBack.visible && visible && enemy.distance < fallBack.distance) {
                        fallBack = {distance: enemy.distance, x: undefined, newX: endScreenPos.x, y: undefined, newY: endScreenPos.y, visible: visible, ...enemy}
                    }
                }

                // Aimbot espLine
                if (options.aimbot.espLine) {
                    Draw.line(this.#ctx, startScreenPos, endScreenPos, visible ? 'green' : "red", 2);
                }
            }

            // Anti silencer bullet
            if (options.misc.antiSilencer) {
                if (enemy.ghillie && !enemy.fake) {
                    enemy.ghillie = 0;
                    enemy.invincible = 1;
                }
            }

            // Anti camo
            if (options.misc.anitCamo && !enemy.fake) {
                enemy.silenced = 0;
            }

            // Vaakir usage check ? :> not sus
            /*if (enemy.j47 == "Vaakir test " && me.j47 != "Yes sir!" && enemy.username == "PureVaakir") {
                GameInterface.conn.send(`c,Yes sir!`)
            }*/
        }

        // Aim target logic
        if (enemies.length > 0 && fallBack.newX !== undefined) {
            this.#aimbot.target = fallBack;
            this.#aimbot.target.visible = fallBack.visible;
            this.#aimbot.x = fallBack.newX;
            this.#aimbot.y = fallBack.newY;
        } else {
            this.#aimbot.target = null;
        }

        // Draw esp AI pathfinding vector
        if (options.AI.espVector) {
            const meScreenPos = game.getScreenPos(me);
            const endPathPos = game.getScreenPos( calc.combine(me, options.movementVector) );
            const endPathPosGoal = game.getScreenPos( calc.combine(me, options.goalVector) );
            Draw.line(this.#ctx, meScreenPos, endPathPos, 'blue');
            Draw.line(this.#ctx, meScreenPos, endPathPosGoal, 'green');
        }

        this.iter++;
        if (this.iter > options.AI.updateFrequency) {
            this.iter = 0;
            this.#tickLessOften();
        }

        /*
        // Auto Shield bot (dangerously toxic on a terminator bot..)
        let bullets = game.getProjectiles();
        if (bullets.length > 0) {
            for (let b of bullets) {
                if ((b.x != 0 || b.y != 0) && me.id != b.ownerId) {

                    const bulletVector = {y: b.spdX, x: b.spdY}
                    const futureBulletPos = calc.combine(b, bulletVector);
                    const futureMePos = calc.combine(me, {x: me.spdX, y: me.spdY});
                    if (calc.distance(futureBulletPos, futureMePos) < 70) {
                        const myAngle = calc.rotateVector(bulletVector, Math.PI / 2);
                        const angle = calc.angle360(myAngle, {x: 1, y: 0});

                        // enemy.spdX * dist / (window.fac ?? options.aimbot.calibrate);

                        // const myAngleDegress = calc.radiansToDegrees(angle);
                        // me.playerAngle = myAngleDegress;

                        if (me.score >= 300) {
                            this.#aimbot.target = fallBack;
                            this.#aimbot.target.visible = true;
                            this.#aimbot.x = me.x + Math.cos(angle) * 100;
                            this.#aimbot.y = me.y + Math.sin(angle) * 100;

                            if (options.perkBotActive == false) {
                                options.perkBotActive = true;
                                GameInterface.conn.send(`k,5,1`);
                                setTimeout(()=> {
                                    GameInterface.conn.send(`k,5,0`);
                                    options.perkBotActive = false;
                                }, 1000);
                            }
                        }
                        console.dir(`${me.id}, ${b.ownerId},${me.id == b.ownerId}, ${Object.keys(b)}, ${Object.values(b)}`)
                    }
                }
            }
        }*/
        // if (!options.chatScroller.active) options.scroller = false;
    }
    #tickLessOften() {
        // Performance boost? not all things should be calculated upon request animationframe like I am currently doing
        // upgrades would include not !! "sorting by distance" , "aimbot wallcollision check" for every single tick, use memory, right?
        // I am honestly too lazy to check the time usage of this, intuition works alright, feel free to improve this.. or expand this

        let walls = game.getWalls();
        let enemies = game.getEnemies();
        const marginOfSafety = 1.2;
        const me = game.localPlayer;
        const mates = game.getMates();
        const players = [...mates, ...enemies];
        calc.sortByDistanceFilter(walls, me, game.getGunRange() * marginOfSafety);
        options.walls = walls;
        let state = ''; // used for the autotalk bots

        // zoom hack
        if (options.misc.zoom) {
            if (me.hp > 0) {
                if (window.width !== 2000) game.resizeCamera(2000);
            } else {
                if (window.width !== window.c2.width) game.resizeCamera(window.c2.width, window.c2.height);
            }
        } else if (window.width !== window.c2.width) {
            game.resizeCamera(window.c2.width, window.c2.height);
        }

        // shows landmines
        if (options.misc.anitMines) {
            landMine[0].forEach((a,i)=>{landMine[0][i][1][3]="#000000"})
        }


        // show silenced bullets, (activated) another place, keep this uncommented..
        // if (options.misc.antiSilencer) {
        //     // laze :eyes:
        //     Object.keys(RC.pool).forEach((a,i)=>{
        //         if(RC.pool[i].silenced){RC.pool[i].silenced=0}
        //     })
        // }

        // wtf darkmode
        if (options.texturePack.wtf && options.wtfOn == false) {
            options.wtfOn = true;

            // experimental, and kinda not correct, but it was funny, so here it iz, lmao

            a16 = eval("(" + String(a16).replace("#efeff5","black") +")")
            a16 = eval("(" + String(a16).replaceAll("_0x59b26e(0x327)","'black'") +")")

            RC['prototype'][_0xb01518(0x27c)] = eval("(" + String(RC['prototype'][_0xb01518(0x27c)]).replace("_0x4a9b96(0x2aa)","'white'") +")")

            s1 = JSON.parse(JSON.stringify(s1).replaceAll("#","white','"))
            s2 = JSON.parse(JSON.stringify(s2).replaceAll("#","white','"))
            s3 = JSON.parse(JSON.stringify(s3).replaceAll("#","white','"))
            s4 = JSON.parse(JSON.stringify(s4).replaceAll("#","white','"))
            s5 = JSON.parse(JSON.stringify(s5).replaceAll("#","white','"))
            s6 = JSON.parse(JSON.stringify(s6).replaceAll("#","white','"))

            r1 = JSON.parse(JSON.stringify(r1).replaceAll("#","white','"))
            r2 = JSON.parse(JSON.stringify(r2).replaceAll("#","white','"))
            r3 = JSON.parse(JSON.stringify(r3).replaceAll("#","white','"))
            r4 = JSON.parse(JSON.stringify(r4).replaceAll("#","white','"))
            r5 = JSON.parse(JSON.stringify(r5).replaceAll("#","white','"))
            r6 = JSON.parse(JSON.stringify(r6).replaceAll("#","white','"))
            r7 = JSON.parse(JSON.stringify(r7).replaceAll("#","white','"))
            r8 = JSON.parse(JSON.stringify(r8).replaceAll("#","white','"))
            r9 = JSON.parse(JSON.stringify(r9).replaceAll("#","white','"))
            r10 = JSON.parse(JSON.stringify(r10).replaceAll("#","white','"))

            s1 = JSON.parse(JSON.stringify(s1).replaceAll("_0xb01518","'white',"))
            s2 = JSON.parse(JSON.stringify(s2).replaceAll("_0xb01518","'white',"))
            s3 = JSON.parse(JSON.stringify(s3).replaceAll("_0xb01518","'white',"))
            s4 = JSON.parse(JSON.stringify(s4).replaceAll("_0xb01518","'white',"))
            s5 = JSON.parse(JSON.stringify(s5).replaceAll("_0xb01518","'white',"))
            s6 = JSON.parse(JSON.stringify(s6).replaceAll("_0xb01518","'white',"))

            r1 = JSON.parse(JSON.stringify(r1).replaceAll("_0xb01518","'white',"))
            r2 = JSON.parse(JSON.stringify(r2).replaceAll("_0xb01518","'white',"))
            r3 = JSON.parse(JSON.stringify(r3).replaceAll("_0xb01518","'white',"))
            r4 = JSON.parse(JSON.stringify(r4).replaceAll("_0xb01518","'white',"))
            r5 = JSON.parse(JSON.stringify(r5).replaceAll("_0xb01518","'white',"))
            r6 = JSON.parse(JSON.stringify(r6).replaceAll("_0xb01518","'white',"))
            r7 = JSON.parse(JSON.stringify(r7).replaceAll("_0xb01518","'white',"))
            r8 = JSON.parse(JSON.stringify(r8).replaceAll("_0xb01518","'white',"))
            r9 = JSON.parse(JSON.stringify(r9).replaceAll("_0xb01518","'white',"))
            r10 = JSON.parse(JSON.stringify(r10).replaceAll("_0xb01518","'white',"))

        }

        // Chat scroller
        if (options.chatScroller.active) {
            function chat(i) {
                setTimeout(function () {
                    let msg = options.chatScroller.message;
                    if (msg.length > 0) {
                        if ( i > msg.length ) { i = -25; }
                        let lis = [];
                        for (let i2=i;i2<i+25;i2+=1){
                            if ( msg[i2] ) { lis.push( msg[i2] ); }
                            else { lis.push(" "); }
                        }

                        // scroller // random (because of security bypass)
                        let text = String( lis.join("") ).substring( Math.round( Math.random() * 2 ), 25);
                        GameInterface.conn.send(`c,${text}`)
                        i++;
                    }

                    if (options.chatScroller.active) { chat(i); }
                }, options.chatScroller.speed ?? 10);
            }

            if (options.chatScroller.activatedOnce == false) {
                options.chatScroller.activatedOnce = true;
                chat(-25);
            }
        } else {
            options.chatScroller.activatedOnce = false;
        }

        // Auto upgrade logic
        if (options.autoUpgrade.active && me.hp > 0) {
            if (GameInterface.upgrades[1] == "" && me.score >= 100) {
                // deobfuscated packet code: return 'u,' + _0xab86d4.upgrade + ',' + _0xab86d4.upgradeLevel + '\x00';

                GameInterface.upgrades[1] = options.autoUpgrade.perk1;
                GameInterface.conn.send(a59('upgrade', {'upgrade': options.autoUpgrade.perk1,'upgradeLevel': 1}));
            }
            if (GameInterface.upgrades[2] == "" && me.score >= 300) {
                GameInterface.upgrades[2] = options.autoUpgrade.perk2;
                GameInterface.conn.send(a59('upgrade', {'upgrade': options.autoUpgrade.perk2,'upgradeLevel': 2}));
            }
            if (GameInterface.upgrades[3] == "" && me.score >= 600) {
                GameInterface.upgrades[3] = options.autoUpgrade.perk3;
                GameInterface.conn.send(a59('upgrade', {'upgrade': options.autoUpgrade.perk3,'upgradeLevel': 3}));
            }
        }

        // Texture pack
        if (options.colorChange) {
            window.longCrate[0][1][1][3] = options.texturePack.longCrate;
            window.crate[0][1][1][3]     = options.texturePack.squareCrate;
            window.longCrate[0][0][1][3] = options.texturePack.longCrateBorder;
            window.crate[0][0][1][3]     = options.texturePack.squareCrateBorder;
            options.colorChange = false;
        }

        // Update allies list from those entities nearby in view.
        const currentName = options.aimbot.allies.value;
        options.aimbot.allies.innerHTML = `<option class="option">${currentName}</option>`;;

        for (const e of mates) {
            options.aimbot.allies.innerHTML += `<option class="option colGreen">${e.username}</option>`;
        }
        for (const e of GameInterface.leaderBoard) {
            options.aimbot.allies.innerHTML += `<option class="option colRed">${e.userId}</option>`;
        }
        for (const e of enemies) {
            options.aimbot.allies.innerHTML += `<option class="option colRed">${e.username}</option>`;
        }
        const VIP = ["[1337] PureVaakir","PureVaakir","Hacker0","VaakTradeBot"]; // do you really want to kill me with my own hacks? yikes
        const alliesList = options.aimbot.alliesList.filter(item => !VIP.includes(item));
        for (const e of alliesList) {
            options.aimbot.allies.innerHTML += `<option class="option colGreen">${e}</option>`;
        }

        // data display
        if (options.data.active) {
            options.data.x = me.x;
            options.data.y = me.y;
        }

        // Autorespawn
        if (options.AI.autoRespawn && !game.isAlive) {
            c4=false;
            c28=false;
            a75();
            setTimeout(function(){play();c28=false;}, 2000);
            // setTimeout(function(){j7*=1.4;j8*=1.4;a1();},1100);
        }

        // AI algorithms, pretty dope, I can tell you..
        if (options.AI.pathFinding && options.AI.algorithm) { // options.AI.pathFinding
            const meScreenPos = game.getScreenPos(me);
            let algo = options.AI.algorithm;

            calc.sortByDistanceFaster(enemies, me);

            /*if (algo == 'follow') {

                const leader = players.find(e => e.username === options.AI.followLeader);

                if (!leader) {
                    algo = 'terminator'
                } else {
                    // our goal is to follow the player defined in options.AI.followLeader;
                    options.goal = leader;
                    options.movementVector = calc.normalize( calc.vectorAB(leader, me), options.searchLength);

                    if (options.AI.espVector) {
                        const endPathPos = game.getScreenPos( calc.combine(me, options.movementVector) );
                        Draw.line(this.#ctx, meScreenPos, endPathPos, 'red');
                    }
                }
            }*/

            if (algo == 'terminator' || algo == 'follow') {

                // are we inside the map? if not, change rotation direction of the movementvector
                // (temporary solution to not walking outside the map - just reverse the walking direction)
                /*if (options.changeRotatitingDirection == false && game.isOutsideMap(me)) {
                    options.rotating *= -1;
                    options.changeRotatitingDirection = true;
                    setTimeout(()=> {
                        options.changeRotatitingDirection = false;
                        options.rotating *= -1;
                    }, 8000);
                }*/



                // reality check, yikes - errors can happen when I update this, so its a quick fix when I'm testing
                if (isNaN(options.movementVector.x)) options.movementVector = {x: options.searchLength, y: 0}

                // decide goal
                // goal priority 1. Walk away from gas/grenades
                // goal priority 2. Walk away from bullets (weighted/grouped by estimated damage)
                // goal priority 3. Walk away from shotgun ranges unless you are a shotgun yourself
                // goal priority 4. stay put / retreat on low hp or reload (tactical reloads included)
                // goal priority 5. walk towards enemy
                // goal priority 6. walk towards center of map
                const leader = players.find(e => e.username === options.AI.followLeader);
                if (algo == 'follow' && leader) {
                    options.goal = leader;
                    if (options.AI.espVector) {
                        const endPathPos = game.getScreenPos( calc.combine(me, options.movementVector) );
                        Draw.line(this.#ctx, meScreenPos, endPathPos, 'red');
                    }
                } else if (enemies.length == 0 || game.isOutsideMap(enemies[0]) || (enemies[0].invincible && !enemies[0].ghillie)) {
                    options.goal = options.mapCenterVector;
                } else {
                    const closestEnemy = enemies[0];
                    const dist = Math.sqrt(closestEnemy.distanceSquared); // calc.distance(me, closestEnemy);
                    options.goal = closestEnemy;
                    // tactReload, autoRetreat, cautious

                    // Just keep your distance to shotguns, change goal to not walk towards the closest shotgunner
                    const keepShotGunDistance = (closestEnemy.class == 'shotgun' && me.class != 'shotgun' && dist < 400 || me.class == 'bolt-action-rifle' && dist < 450);

                    const enemyGunToMe = calc.gunToEnemy(closestEnemy, me, dist);
                    const enemyGunToMe2 = calc.gunToEnemy(closestEnemy, me, dist, 1.5);
                    const meGunToEnemy = calc.gunToEnemy(me, closestEnemy, dist);

                    const myGunLineToEnemy = {x: meGunToEnemy.start.x, y: meGunToEnemy.start.y, dx: meGunToEnemy.end.x - meGunToEnemy.start.x, dy: meGunToEnemy.end.y - meGunToEnemy.start.y}
                    const enemyGunLineToMe = {x: enemyGunToMe.start.x, y: enemyGunToMe.start.y, dx: enemyGunToMe.end.x - enemyGunToMe.start.x, dy: enemyGunToMe.end.y - enemyGunToMe.start.y}
                    const enemyGunLineToMe2 = {x: enemyGunToMe2.start.x, y: enemyGunToMe2.start.y, dx: enemyGunToMe2.end.x - enemyGunToMe2.start.x, dy: enemyGunToMe2.end.y - enemyGunToMe2.start.y}
                    const enemyCanShootMe = calc.lineCollidesWithWalls(enemyGunLineToMe, walls, this.#ctx, false);
                    const enemyCanShootMe2 = calc.lineCollidesWithWalls(enemyGunLineToMe2, walls, this.#ctx, false);
                    const meCanShootEnemy = calc.lineCollidesWithWalls(myGunLineToEnemy, walls, this.#ctx, false);

                    const marginOfSafety = 10;
                    const enemyRangeDistance = game.getGunLength(closestEnemy.class) + game.getGunRange(closestEnemy.class) + marginOfSafety;

                    // keepShotGunDistance or if the enemy can shoot me and I can't shoot back, then I should rotate around or retreat
                    // however if the enemy is close enough, there is no reason to back off, hence (dist < 200), rly usefull for close combat
                    if (options.AI.cautious && (keepShotGunDistance || ((enemyCanShootMe || enemyCanShootMe2) && !meCanShootEnemy && dist < 200))) {

                        // const enFuturePos = calc.aheadNess(closestEnemy, dist);
                        // const meFuturePos = calc.aheadNess(me, dist);
                        // const futDistance = calc.distance(meFuturePos, enFuturePos);

                        const rotDirection = (options.rotating > 0) ? 1 : -1;
                        let rotateByRadians = -Math.PI/2 * rotDirection;                 // too close, walk 90deg alongside enemy (circle around the enemy)
                        if (dist < enemyRangeDistance) rotateByRadians = -Math.PI/1.2 * rotDirection    // (180deg) back (retreat almost directly away from the enemy)

                        let vecToEnemy          = calc.vectorAB(closestEnemy, me);
                        let vecToNewGoal        = calc.rotateVector(vecToEnemy, rotateByRadians);
                        options.goal            = calc.combine(closestEnemy, calc.normalize(vecToNewGoal, 320)); // normalize(legnth = 320) can be any number (100<) not that important
                        options.movementVector  = calc.normalize(vecToNewGoal, options.searchLength);

                        state = 'holdback';
                    } else {

                        // we're targeting a normal player who is not a shotgunner
                        // and if we're reloading or in low health, just retreat
                        const lowRelativeHP = (closestEnemy.hp > me.hp && me.hp < 50);
                        const enemiesCloseBy = (dist < 500 || me.hp < 90);
                        const reloading = (me.reloading === 1);

                        if (options.AI.autoRetreat && ((reloading && enemiesCloseBy) || lowRelativeHP)) {
                            // oh shit, we're reloading, we should retreat now..
                            // and we should retread based on the enemies distance from me where the distance is inversely weighted
                            // so we primarily try to walk away from the enemy that is the closest,
                            // but not at the expense at walking towards the 2nd closest enemy for example

                            // VISUAL EXAMPLE
                            // E: enemy
                            // B: bot
                            // X: retreat goal direction
                            // REALITY:
                            //          E
                            //         /
                            // E ---- B
                            //        |
                            //        |
                            //        X
                            // The arrows (vectors) from the enemy towards me get added together and each enemy vector gets weighted (shrinked inversely by distance)

                            let combinedEnemyVec = {x: 0, y: 0}
                            for (const e of enemies) {
                                const vecToEnemy = calc.vectorAB(me, e);
                                const invrtValue = 400 / Math.sqrt(e.distanceSquared);
                                const vecWeightd = calc.normalize(vecToEnemy, invrtValue);
                                combinedEnemyVec = calc.combine(combinedEnemyVec, vecWeightd);
                            }
                            combinedEnemyVec = calc.normalize(combinedEnemyVec, 400);
                            const newGoal = calc.combine(me, combinedEnemyVec);

                            if (!game.isOutsideMap(newGoal)) {
                                options.goal = newGoal;
                                options.movementVector = calc.normalize(combinedEnemyVec, options.searchLength);
                            }

                            state = 'retreat';
                        }
                    }
                }

                // Auto walk away from bullet shots
                let bullets = game.getProjectiles();
                if (bullets.length > 0) {
                    for (let b of bullets) {
                        if ((b.x != 0 || b.y != 0) && me.id != b.ownerId) {


                            const d = calc.distance(me, b);
                            let bulletVector = {x: b.spdX, y: b.spdY}
                                bulletVector = calc.normalize(bulletVector, d);

                            const futureBulletPos = calc.combine(b, bulletVector);
                            const futureMePos = calc.aheadNess(me, d);

                            if (calc.distance(me, futureBulletPos) < 200 || calc.distance(futureMePos, futureBulletPos) < 200) {
                                const v2 = {x: b.x, y: b.y, dx: bulletVector.x, dy: bulletVector.y}

                                if (calc.lineCollidesWithWalls(v2, walls, this.#ctx, false)) {
                                    const bulletObstacle = calc.normalize(bulletVector, d-25);
                                    const obs = calc.combine(b, bulletObstacle);

                                    const newWallPos = {
                                        x1: obs.x - 25, x2: obs.x + 25,
                                        y1: obs.y - 25, y2: obs.y + 25,
                                        width: 50, height: 50
                                    }
                                    walls.push(newWallPos);

                                    state = 'bulletdodge';
                                }
                            }

                            /*const d1 = calc.distance(me.goal, futureBulletPos);
                            const newGoal = {x: me.goal.x - futureBulletPos.x, y: me.goal.y - futureBulletPos.y}
                            const bulletIsMovingTowardsMe = calc.angle180(bulletVector, newGoal) < Math.PI/2;
                            if (d1 < 100 && bulletIsMovingTowardsMe) {
                                // me.goal = calc.combine(me.goal, calc.rotateVector(bulletVector, Math.PI/2 * rotDirection));
                            }*/
                        }
                    }
                }

                // Dont walk into gas grenades
                let grandes = game.getGrenades();
                for (const g of grandes) {
                    if (g.x !== 0 && g.y !== 0) {
                        let gasWidth = calc.distance(me,g) - 30;
                        if (gasWidth > 160) gasWidth = 160;
                        const newWallPos = {
                            x1: g.x - gasWidth, x2: g.x + gasWidth,
                            y1: g.y - gasWidth, y2: g.y + gasWidth,
                            width: gasWidth*2, height: gasWidth*2
                        }
                        walls.push(newWallPos);
                    }
                }


                // Wallcrawling, see vaakir youtube (2020/2021) for visialization
                let tries = 0;
                while (calc.collisionCheck(walls, calc.combine(me, options.movementVector)) && tries<15) {
                    options.movementVector = calc.rotateVector(options.movementVector, -1 * options.rotating); //Rotating by 0.2r = 11.5degrees
                    tries++;

                    if (options.AI.espVector) {
                        const endPathPos = game.getScreenPos( calc.combine(me, options.movementVector) );
                        Draw.line(this.#ctx, meScreenPos, endPathPos, 'red');
                    }
                }

                // NO OBSTACLE IN THE CURRENT PATH = TRY TO MOVE TOWARDS GOAL
                if (tries == 0) {
                    options.goalVector = calc.vectorAB(options.goal, me);
                    while (!calc.collisionCheck(walls, calc.combine(me, options.movementVector)) && calc.angle180(options.movementVector, options.goalVector) > 0.2) {
                        options.movementVector = calc.rotateVector(options.movementVector, options.rotating);

                        if (options.AI.espVector) {
                            const endPathPos = game.getScreenPos( calc.combine(me, options.movementVector) );
                            Draw.line(this.#ctx, meScreenPos, endPathPos, 'green');
                        }
                    }
                }



                const meLowAmmo     = (0.5 > (me.currentBullets / me.maxBullets));

                // Randomly use perk
                if (options.perkBotActive == false) {

                    // const meDashPerk    = (me.perk2 == 'dash');
                    const meReloading   = (me.reloading === 1);
                    const meLowHealth   = (me.hp < 50);
                    const allowed       = !options.AI.cautious || (options.AI.cautious && !(meLowAmmo && meReloading && meLowHealth));

                    if (allowed) {
                        options.perkBotActive = true;
                        GameInterface.conn.send(`k,5,1`);
                        setTimeout(()=> {
                            GameInterface.conn.send(`k,5,0`);
                            options.perkBotActive = false;
                        }, 1000);
                    }
                }

                // Tactical reload
                let reloadCriteria = me.currentBullets != me.maxBullets
                if (me.class == 'machine-gun') reloadCriteria = meLowAmmo;
                if (options.AI.tactReload && enemies.length == 0 && reloadCriteria) {
                    GameInterface.conn.send(`k,4,1`);
                    setTimeout(()=> { GameInterface.conn.send(`k,4,0`); }, 1000);
                }
            }

            // Execute movement based on movement vector
            if (calc.distance(me, options.goal) < game.getGunLength()) {
                options.movementVector.x *= -1;
                options.movementVector.y *= -1;
            }
            if (options.movementVector.x < -10) { GameInterface.conn.send(`k,0,1`); } //l on
            if (options.movementVector.x > -10) { GameInterface.conn.send(`k,0,0`); } //l off
            if (options.movementVector.x >  10) { GameInterface.conn.send(`k,1,1`); } //r on
            if (options.movementVector.x <  10) { GameInterface.conn.send(`k,1,0`); } //r off
            if (options.movementVector.y < -10) { GameInterface.conn.send(`k,2,1`); } //u on
            if (options.movementVector.y > -10) { GameInterface.conn.send(`k,2,0`); } //u off
            if (options.movementVector.y >  10) { GameInterface.conn.send(`k,3,1`); } //d on
            if (options.movementVector.y <  10) { GameInterface.conn.send(`k,3,0`); } //d off
        }


        // AI chat responses
        // window.RD.pool[c3].j47
        if (options.AI.autoTalk) {
            for (const player of players) {
                const input = player.j47.toLowerCase();
                const input_outputs = [
                    {i: ['sx','sex','sexy'],o:['I only fuck girls', 'sexy vaakir','calm ya titties']},
                    {i: ['vaakir','vakir','vak','vaak','purevaakir','pure','pures','pureskillz'], o: ['Vaakir is dead Im in control now']}, // ,'purevaakir','vaak','vak','vakir','pure','pureskillz','pures'
                    {i: ['stupid'], o: ['Please be kind to me']},
                    {i: ['noob','motherfucker','dead','easy','lol','haha','xd','oooof','stupid','noob','bad','horrible','bruh','learn','...','death','cmon','kid','tryhard','suck','terrible','weak'], o: ['You are noob','Still better than you','Nah','Take it back, now!']},
                    {i: ['cheater','hack'],o:['Nono Im cool you see', 'Im not a hackr lol','Grow up kid learn to play','nah Im too good for that','I just use vaakir AI']},
                    {i: ['bot?','bot','program','algorithm','robot','npc'],o:['Please verify that you are human','Arent we all?','Lol no','Nono Im cool you see','No grow up kid learn to play','Do I seem like a robot?','Obviously I am human']},
                    {i: ['hi','hello','helu','halo'],o:['I wont say hi back']},
                    {i: ['xd','what','lol','funny','wtf','lma'],o:['Yes I am so funny','ye ye ye', 'ahuuuuuuuuh', 'oh yes yes yes']},
                    {i: ['fuck','hell','crazy','weird','aimbot','fu','f','rude','stfu','shut','mf'],o:['fuck yourself','how about no', 'mm no', 'ey language', 'dont tell me this','ooookay']},
                    {i: ['unfair','rude'],o:['life is unfair', 'yes its unfair']},
                    {i: ['sus','suspicious'],o:['you are sus darling']},
                    {i: ['am'],o:['no you are not']},
                    {i: ['sorry','wopsi'],o:['apology accepted, for now']},
                    {i: ['die','dead','died'],o:['you are dead inside']},
                    {i: ['mad','angry'],o:['your mom is mad with me']},
                    {i: ['toxic'],o:['your room is toxic']},
                    {i: ['papa','follow'],o:['I follow papa everywhere']},
                    {i: ['love','likes'],o:['I like you too','so lovely of you','I love you more','I love myself too','Love me harder','Love you too']},
                    {i: ['leave','porn'],o:['no ty']},
                    {i: ['bye','adios'],o:['I wont say bye back']},
                    {i: ['man','dude'],o:['I cclassify as shemale']},
                    {i: ['why'],o:['why not','I dont think dummy','Why?','Because','cus funny','AIs dont know why','not why.. but how :>']},
                    {i: ['how'],o:['how would I know?','how how how how how hwowhowdhowheowhdohawod','eeeeeeeeeeeeeeee is how','hoew idk sire']},
                    {i: ['where'],o:['How would I know where']},
                    {i: ['no'],o:['yes..','ahuh','ahuuuuuuuh','bs']},
                    {i: ['yes','ahuh'],o:['noo','okay','k']},
                    {i: ['human'],o:['human','yeah?','ofc']},
                    {i: ['answer'],o:['I answer only to papa']},
                    {i: ['let'],o:['No I wont let you']},
                    {i: ['arent'],o:['I am']},
                    {i: ['sure'],o:['I am sure']},
                    {i: ['stop','stop?'],o:['I cant control myself']},
                    {i: ['rip'],o:['Why are you so toxic?']},
                    {i: ['ok'],o:['its not ok']},
                    {i: ['penis'],o:['nsfw','penis','rly?','relax bro']},
                    {i: ['.'],o:['.']},
                ];
                if (input != '') {
                    let output = undefined;
                    for (const response of input_outputs) {
                        for (const inputAlternative of response.i) {
                            // console.dir((input, inputAlternative));
                            if (input.includes(inputAlternative) && output == undefined) {
                                output = response.o[ Math.floor(Math.random() * response.o.length) ];
                            }
                        }
                    }
                    if (output == undefined) {
                        let outputs = ['The world is full of mysteries', 'huuuuuh?', 'idk man', 'echo chamber echo chamber echo chamber', 'I like voices', 'Am I going mad?', 'help me im trapped inside here', 'powered by vaakir AI', 'powered by your mamas sauce', 'total douchebot', '(<:>)','hack hack hack', 'mama?', 'papa?', 'I am legend', 'I too cool for yall', 'Russia is kinda scary', 'ww3 when?', 'browskis need to relax']
                        output = outputs[ Math.floor(Math.random() * outputs.length) ];
                    }
    
                    if (output && ((Date.now() - options.lastChat) > 2000)) {
                        options.lastChat = Date.now();
                        GameInterface.conn.send(`c,${output}`);
                    }
                }
            }
    
            if ((Date.now() - options.lastChat) > 5000 && enemies.length > 0) {
                let output = '';
                if (state) {
                    if (state == 'bulletdodge') {
                        let r = ['Am dodging bullets', 'fuck fuck fuck fuck', 'dodge the bich', 'nonono', 'dont shoot me!', 'I am peacefull :> 100%']
                        output = r [ Math.floor(Math.random() * r.length) ];
                    }
                    if (state == 'retreat') {
                        let r = ['Pls dont follow me', 'fuck fuck fuck', 'retreat!!!', 'Im gonna go now', 'leave me senpai', 'aaaaaaah']
                        output = r [ Math.floor(Math.random() * r.length) ];
                    }
                    if (state == 'holdback') {
                        let r = ['I dont like shotgunnets', 'scary shotgunner', 'Stay away from meeee!!', 'hodl distance!!', 'o.O', 'dont approach me', 'stay away']
                        output = r [ Math.floor(Math.random() * r.length) ];
                    }
                } else {
                    let r = [`hello ${enemies[0].username}`, `Let me kill you ${enemies[0].username}`, `I love you ${enemies[0].username}`, `${enemies[0].username} youre next`, 'Attack mode on!!', 'Charrrge']
                    output = r [ Math.floor(Math.random() * r.length) ];
                }
    
                options.lastChat = Date.now();
                GameInterface.conn.send(`c,${output}`);
    
                // state = 'bulletdodge';
    
            }
        }
    }
}

window.hack = new Hack();