// ==UserScript==
// @name         Free Agario.in Bots (Vanilla Version) updated
// @namespace    Free and Real agario bots updated
// @version      2.0.5
// @description  Free and Real open source agario bots (tags: ogario, legend mod, vanilla, free bots, unlimited, hacks, infinity, agar.io, cheat, miniclip, agar)
// @author       chance
// @grant        none
// @run-at       document-start
// @match        *://*/*
// @downloadURL none
// ==/UserScript==
 
/* START OF USER SETTINGS */
window.options = {
    settings: {
        "EXTENDED_ZOOM": {
           "text": "Extended Zoom",
          "type": "checkbox",
          "value": true
        },
        "DRAW_MAP_GRID": {
           "text": "Grid",
          "type": "checkbox",
          "value": false
        },
        "SHOW_ALL_PLAYERS_MASS": {
           "text": "Show Mass (All players)",
          "type": "checkbox",
          "value": true
        },
    },
    hotkeys: {
        "BOTS_SPLIT_KEY": {
            "text": "Bot Split Key",
            "key": "T",
            "keycode": 84,
        },
        "BOTS_FEED_KEY": {
            "text": "Bot Feed Key",
            "key": "A",
            "keycode": 65,
        },
        "BOTS_AI_KEY": {
            "text": "Bot AI Key",
            "key": "F",
            "keycode": 70,
        },
        "MACRO_FEED_KEY": {
            "text": "Macro Feed Key",
            "key": "E",
            "keycode": 69,
        },
        "DOUBLE_SPLIT_KEY": {
            "text": "Double Split Key",
            "key": "Q",
            "keycode": 81,
        },
        "SIXTEEN_SPLIT_KEY": {
            "text": "Sixteen Split Key",
            "key": "R",
            "keycode": 82,
        },
    }
}
if(localStorage.getItem('nebula-hotkeys')) window.options.hotkeys =JSON.parse(localStorage.getItem('nebula-hotkeys'));
if(localStorage.getItem('nebula-settings')) window.options.settings =JSON.parse(localStorage.getItem('nebula-settings'));
window.changeKey = (name, event) => {
    event.preventDefault();
    $(`#${name}`).val(event.key.toLocaleUpperCase())
    let key = window.options.hotkeys[name];
    key["key"] = event.key.toLocaleUpperCase();
    key["keycode"] = event.keyCode;
    checkDuplicates(name, event.keyCode);
    localStorage.setItem('nebula-hotkeys', JSON.stringify(window.options.hotkeys));
}
window.checkboxChange = (name) => {
    let setting = window.options.settings[name];
    setting["value"] = document.getElementById(name).checked;
    localStorage.setItem('nebula-settings', JSON.stringify(window.options.settings));
};
window.checkDuplicates = (keyname, keycode) => {
for (var name in window.options.hotkeys) {
        var key = window.options.hotkeys[name];
        if(name == keyname) continue;
    if(keycode == key.keycode) key.keycode = 0, key.key = "", $(`#${name}`).val("");
    }
}
window.setUpHotkeys = () => {
    for (var name in window.options.hotkeys) {
        var key = window.options.hotkeys[name];
        let html =
            `
                        ${key.text}
                        
                    
`
        $("#hotkeys").append(html);
    }
}
window.getOption = (name) => {
    if(document.getElementById(name))return document.getElementById(name).checked
    else return false
}
window.setUpOptions = () => {
    for (var name in window.options.settings) {
        var option = window.options.settings[name];
        let html =
            `
                        ${option.text}
                        
                    
`
        $("#settings").append(html);
        if(option["value"] == true) $(`#${name}`).click();
    }
}
 
window.SERVER_HOST = 'ws://localhost:8083' // Hostname/IP of the server where the bots are running [Default = localhost (your own pc)]
 
window.ZOOM_SPEED = 0.85 // Numerical value that indicates the speed of the mouse wheel when zooming, value must be between 0.01-0.99 [Default = 0.85]
 
window.EXTENDED_ZOOM = true // Boolean value that indicates whether to extend the zoom or not, possible values are true and false [Default = true]
 
window.DRAW_MAP_GRID = false // Boolean value that indicates whether to draw the map grid or not, possible values are true and false [Default = false]
 
window.SHOW_ALL_PLAYERS_MASS = true // Boolean value that indicates whether to show all players mass or not, possible values are true and false [Default = true]
 
/* END OF USER SETTINGS */
 
class Writer {
    constructor(size) {
        this.dataView = new DataView(new ArrayBuffer(size))
        this.byteOffset = 0
    }
    writeUint8(value) {
        this.dataView.setUint8(this.byteOffset++, value)
    }
    writeInt32(value) {
        this.dataView.setInt32(this.byteOffset, value, true)
        this.byteOffset += 4
    }
    writeUint32(value) {
        this.dataView.setUint32(this.byteOffset, value, true)
        this.byteOffset += 4
    }
    writeString(string) {
        for (let i = 0; i < string.length; i++) this.writeUint8(string.charCodeAt(i))
        this.writeUint8(0)
    }
}
 
window.buffers = {
    startBots(url, protocolVersion, clientVersion, userStatus, botsName, botsAmount) {
            const writer = new Writer(13 + url.length + botsName.length)
            writer.writeUint8(0)
            writer.writeString(url)
            writer.writeUint32(protocolVersion)
            writer.writeUint32(clientVersion)
            writer.writeUint8(Number(userStatus))
            writer.writeString(botsName)
            writer.writeUint8(botsAmount)
            return writer.dataView.buffer
        },
        mousePosition(x, y) {
            const writer = new Writer(9)
            writer.writeUint8(6)
            writer.writeInt32(x)
            writer.writeInt32(y)
            return writer.dataView.buffer
        }
}
 
window.connection = {
    ws: null,
    connect() {
        this.ws = new WebSocket(`${window.SERVER_HOST}`)
        this.ws.binaryType = 'arraybuffer'
        this.ws.onopen = this.onopen.bind(this)
        this.ws.onmessage = this.onmessage.bind(this)
        this.ws.onclose = this.onclose.bind(this)
    },
    send(buffer) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.send(buffer)
    },
    onopen() {
        document.getElementById('userStatus').style.color = '#00C02E'
        document.getElementById('userStatus').innerText = 'Connected'
        document.getElementById('connect').disabled = true
        document.getElementById('startBots').disabled = false
        document.getElementById('stopBots').disabled = false
    },
    onmessage(message) {
        const dataView = new DataView(message.data)
        switch (dataView.getUint8(0)) {
            case 0:
                document.getElementById('startBots').disabled = true
                document.getElementById('stopBots').disabled = false
                document.getElementById('startBots').style.display = 'none'
                document.getElementById('stopBots').style.display = 'inline'
                document.getElementById('stopBots').innerText = 'Stop Bots'
                window.user.startedBots = true
                break
            case 1:
                document.getElementById('stopBots').disabled = true
                document.getElementById('stopBots').innerText = 'Stopping Bots...'
                break
            case 2:
                document.getElementById('botsAI').style.color = '#DA0A00'
                document.getElementById('botsAI').innerText = 'Disabled'
                document.getElementById('startBots').disabled = false
                document.getElementById('stopBots').disabled = true
                document.getElementById('startBots').style.display = 'inline'
                document.getElementById('stopBots').style.display = 'none'
                document.getElementById('stopBots').innerText = 'Stop Bots'
                window.user.startedBots = false
                window.bots.ai = false
                break
            case 3:
                alert('Your IP has captcha and bots are unable to spawn, change your ip with a VPN or something to one that doesn\'t has captcha in order to use the bots')
                break
             case 4:
                 //Connected Bot count = getUint8(1)
                //Spawned Bot count = getUint8(2)
                //Server player amount = getUint8(3)
                $('#botCount').html(`${dataView.getUint8(1)}/${dataView.getUint8(2)}/${window.bots.amount}`)
               // $('#slots').html(dataView.getUint8(3) + "/200")
                break;
            case 5:
                $('#slots').html(dataView.getUint8(1) + "/200")
                break;
        }
    },
    onclose() {
        document.getElementById('userStatus').style.color = '#DA0A00'
        document.getElementById('userStatus').innerText = 'Disconnected'
        document.getElementById('botsAI').style.color = '#DA0A00'
        document.getElementById('botsAI').innerText = 'Disabled'
        document.getElementById('connect').disabled = false
        document.getElementById('startBots').disabled = true
        document.getElementById('stopBots').disabled = true
        document.getElementById('startBots').style.display = 'inline'
        document.getElementById('stopBots').style.display = 'none'
        window.user.startedBots = false
        window.bots.ai = false
    }
}
 
window.game = {
    url: '',
    protocolVersion: 0,
    clientVersion: 0
}
 
window.user = {
    startedBots: false,
    isAlive: false,
    mouseX: 0,
    mouseY: 0,
    offsetX: 0,
    offsetY: 0,
    macroFeedInterval: null
}
 
window.bots = {
    name: '',
    amount: 0,
    ai: false
}
 
function modifyCore(core) {
    return core
        .replace(/if\(\w+\.MC&&\w+\.MC\.onPlayerSpawn\)/, `
            $&
            window.user.isAlive = true
            if(window.user.startedBots) window.connection.send(new Uint8Array([5, Number(window.user.isAlive)]).buffer)
        `)
        .replace(/if\(\w+\.MC&&\w+\.MC\.onPlayerDeath\)/, `
            $&
            window.user.isAlive = false
            if(window.user.startedBots) window.connection.send(new Uint8Array([5, Number(window.user.isAlive)]).buffer)
        `)
        .replace(/new\s+WebSocket\((\w+\(\w+\))\)/, `
            $&
            if(window.user.startedBots) window.connection.send(new Uint8Array([1]).buffer)
            window.game.url = $1
            window.user.isAlive = false
            window.user.macroFeedInterval = null
        `).replace(/(\w+)=~~\(\+\w+\[\w+\+\d+>>3]\+\s+\+\(\(\w+\[\w+\+\d+>>2]\|0\)-\(\(\w+\[\d+]\|0\)\/2\|0\)\|0\)\/\w+\);(\w+)=~~\(\+\w+\[\w+\+\d+>>3]\+\s+\+\(\(\w+\[\w+\+\d+>>2]\|0\)-\(\(\w+\[\d+]\|0\)\/2\|0\)\|0\)\/\w+\)/, `
            $&
            window.user.mouseX = $1 - window.user.offsetX
            window.user.mouseY = $2 - window.user.offsetY
            if(window.user.startedBots && window.user.isAlive) window.connection.send(window.buffers.mousePosition(window.user.mouseX, window.user.mouseY))
        `)
        .replace(/\w+\[\w+\+272>>3]=(\w+);\w+\[\w+\+280>>3]=(\w+);\w+\[\w+\+288>>3]=(\w+);\w+\[\w+\+296>>3]=(\w+)/, `
            $&
            if(~~($3 - $1) === 14142 && ~~($4 - $2) === 14142){
                window.user.offsetX = ($1 + $3) / 2
                window.user.offsetY = ($2 + $4) / 2
            }
        `)
        .replace(/\(\.9,/, '(window.ZOOM_SPEED,')
        .replace(/;if\((\w+)<1\.0\)/, ';if($1 < (!getOption("EXTENDED_ZOOM")))')
        .replace(/(\w+\(\d+,\w+\|0,\.5,\.5\)\|0);(\w+\(\d+,\w+\|0,\.5,50\.5\)\|0);(\w+\(\d+,\w+\|0,\.5,\.5\)\|0);(\w+\(\d+,\w+\|0,50\.5,\.5\)\|0)/, `
            $1
            if(window.getOption("DRAW_MAP_GRID")) $2
            $3
            if(window.getOption("DRAW_MAP_GRID")) $4
        `)
        .replace(/while\(0\);(\w+)=\(\w+\|0\)!=\(\w+\|0\);/, `
            $&
            if(window.getOption("SHOW_ALL_PLAYERS_MASS")) $1 = true
        `)
}
 
function setKeysEvents() {
    document.addEventListener('keydown', e => {
        if (!document.getElementById('overlays')) {
            switch (e.keyCode) {
                case options.hotkeys["BOTS_SPLIT_KEY"].keycode:
                    if (window.user.startedBots && window.user.isAlive) window.connection.send(new Uint8Array([2]).buffer)
                    break
                case options.hotkeys["BOTS_FEED_KEY"].keycode:
                    if (window.user.startedBots && window.user.isAlive) window.connection.send(new Uint8Array([3]).buffer)
                    break
                case options.hotkeys["BOTS_AI_KEY"].keycode:
                    if (window.user.startedBots && window.user.isAlive) {
                        if (!window.bots.ai) {
                            document.getElementById('botsAI').style.color = '#00C02E'
                            document.getElementById('botsAI').innerText = 'Enabled'
                            window.bots.ai = true
                            window.connection.send(new Uint8Array([4, Number(window.bots.ai)]).buffer)
                        } else {
                            document.getElementById('botsAI').style.color = '#DA0A00'
                            document.getElementById('botsAI').innerText = 'Disabled'
                            window.bots.ai = false
                            window.connection.send(new Uint8Array([4, Number(window.bots.ai)]).buffer)
                        }
                    }
                    break
                case options.hotkeys["MACRO_FEED_KEY"].keycode:
                    if (!window.user.macroFeedInterval) {
                        window.core.eject()
                        window.user.macroFeedInterval = setInterval(window.core.eject, 80)
                    }
                    break
                case options.hotkeys["DOUBLE_SPLIT_KEY"].keycode:
                    window.core.split()
                    setTimeout(window.core.split, 40)
                    break
                case options.hotkeys["SIXTEEN_SPLIT_KEY"].keycode:
                    window.core.split()
                    setTimeout(window.core.split, 40)
                    setTimeout(window.core.split, 80)
                    setTimeout(window.core.split, 120)
                    break
            }
        }
    })
    document.addEventListener('keyup', e => {
        if (!document.getElementById('overlays') && e.keyCode === options.hotkeys["MACRO_FEED_KEY"].keycode && window.user.macroFeedInterval) {
            clearInterval(window.user.macroFeedInterval)
            window.user.macroFeedInterval = null
        }
    })
}
 
function setGUI() {
    let menuhtml = `