n-Nurbo client

Bot = P Shift=Insta, AutoBiomehat Autoheal, F=Trap, V=Spike, C=4Spikes, B=4Traps, N=Mill. Glotus AutoBreak for traps.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         n-Nurbo client
// @namespace    http://youtube.com
// @version      1.6.1.5
// @description  Bot =  P    Shift=Insta, AutoBiomehat Autoheal, F=Trap, V=Spike, C=4Spikes, B=4Traps, N=Mill. Glotus AutoBreak for traps.
// @icon         https://static.wikia.nocookie.net/moom/images/7/70/Cookie.png/revision/latest?cb=20190223141839
// @author       Nurbo Mod
// @match        *://moomoo.io/*
// @match        *://*.moomoo.io/*
// @match        *://sandbox.moomoo.io/*
// @match        *://dev.moomoo.io/*
// @grant        none
// @require      https://update.greasyfork.org/scripts/423602/1005014/msgpack.js
// @require      https://update.greasyfork.org/scripts/480301/1322984/CowJS.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/fontfaceobserver.standalone.min.js
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Радар система с треугольными указателями
    class Radar {
        constructor() {
            this.players = new Map();
            this.myId = null;
            this.myPosition = {x: 0, y: 0};
            this.myClan = null;
            this.radarLayer = null;
            this.canvas = null;
            this.ctx = null;
            this.enemyPointers = new Map();
            this.frameCount = 0;
            this.lastDrawTime = 0;
            this.targetFPS = 60;
            this.frameInterval = 1000 / this.targetFPS;
            this.init();
        }

        init() {
            // Создаем слой для радара
            this.radarLayer = document.createElement('div');
            this.radarLayer.id = 'radar-layer';
            this.radarLayer.style.cssText = `
                position: fixed;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                pointer-events: none;
                z-index: 9999;
            `;
            document.body.appendChild(this.radarLayer);

            // Создаем canvas для отрисовки
            this.canvas = document.createElement('canvas');
            this.canvas.id = 'radar-canvas';
            this.canvas.style.cssText = `
                position: fixed;
                left: 0;
                top: 0;
                width: 100%;
                height: 100%;
                pointer-events: none;
                z-index: 9999;
                background: transparent;
            `;
            this.radarLayer.appendChild(this.canvas);

            this.ctx = this.canvas.getContext('2d');
            this.resizeCanvas();

            // Перехват WebSocket
            this.interceptWebSocket();

            // Запускаем анимацию
            this.animate();
        }

        resizeCanvas() {
            this.canvas.width = window.innerWidth;
            this.canvas.height = window.innerHeight;
        }

        interceptWebSocket() {
            const originalWebSocket = window.WebSocket;
            const self = this;

            window.WebSocket = new Proxy(originalWebSocket, {
                construct(target, args) {
                    const ws = new target(...args);
                    ws.binaryType = 'arraybuffer';

                    ws.addEventListener('message', function(e) {
                        try {
                            if (e.data instanceof ArrayBuffer) {
                                const data = msgpack.decode(new Uint8Array(e.data));
                                self.handleMessage(data);
                            }
                        } catch (err) {}
                    });

                    return ws;
                }
            });
        }

        handleMessage(data) {
            if (!Array.isArray(data) || data.length < 2) return;

            const type = data[0];
            const content = data[1];

            if (type === 'C') {
                this.myId = content[0];
            } else if (type === 'a' && Array.isArray(content) && content.length > 0) {
                const playerData = content[0];
                if (!Array.isArray(playerData)) return;

                const newEnemyIds = new Set();

                for (let i = 0; i < playerData.length; i += 13) {
                    const id = playerData[i];
                    const x = playerData[i + 1];
                    const y = playerData[i + 2];
                    const clan = playerData[i + 7];

                    if (x !== undefined && y !== undefined) {
                        if (id === this.myId) {
                            this.myPosition = {x: x, y: y};
                            this.myClan = clan;
                        } else {
                            const isEnemy = !this.myClan || clan !== this.myClan;
                            if (isEnemy) {
                                newEnemyIds.add(id);

                                let pointer = this.enemyPointers.get(id);

                                if (!pointer) {
                                    pointer = {
                                        id: id,
                                        x: x,
                                        y: y,
                                        targetX: x,
                                        targetY: y,
                                        clan: clan,
                                        lastUpdate: Date.now(),
                                        size: 1
                                    };
                                    this.enemyPointers.set(id, pointer);
                                } else {
                                    pointer.targetX = x;
                                    pointer.targetY = y;
                                    pointer.lastUpdate = Date.now();
                                }
                            }
                        }
                    }
                }

                // Удаляем врагов, которых больше нет в данных
                for (const [id, pointer] of this.enemyPointers) {
                    if (!newEnemyIds.has(id)) {
                        this.enemyPointers.delete(id);
                    }
                }
            }
        }

        updatePointers() {
            const now = Date.now();

            for (const [id, pointer] of this.enemyPointers) {
                const smoothFactor = 0.1;
                pointer.x += (pointer.targetX - pointer.x) * smoothFactor;
                pointer.y += (pointer.targetY - pointer.y) * smoothFactor;

                // Обновляем время последнего обновления
                pointer.lastUpdate = now;
            }
        }

        drawPointer(x, y, angle, size, color) {
            this.ctx.save();
            this.ctx.translate(x, y);
            this.ctx.rotate(angle);

            // Размер указателя
            const pointerSize = 10 * size *1.5;

            // Рисуем стрелку (треугольник, смотрящий НА врага)
            this.ctx.beginPath();
            this.ctx.moveTo(pointerSize, 0); // Острие стрелки
            this.ctx.lineTo(-pointerSize/2, -pointerSize/1.5);
            this.ctx.lineTo(-pointerSize/2, pointerSize/1.5);
            this.ctx.closePath();

            // Заливка
            this.ctx.fillStyle = color;
            this.ctx.fill();

            this.ctx.restore();
        }

        draw() {
            const now = Date.now();
            const deltaTime = now - this.lastDrawTime;

            if (deltaTime < this.frameInterval) {
                requestAnimationFrame(() => this.animate());
                return;
            }

            this.lastDrawTime = now - (deltaTime % this.frameInterval);

            this.updatePointers();

            this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

            if (!this.myId) {
                requestAnimationFrame(() => this.animate());
                return;
            }

            const centerX = this.canvas.width / 2;
            const centerY = this.canvas.height / 2;

            // Радиус круга радара
            const radarRadius = 200;

            // Радиус для указателей (внешний круг)
            const pointerRadius = radarRadius + 8; // Указатели на 20px ВНЕ круга

            // Рисуем круг радара
            this.ctx.beginPath();
            this.ctx.arc(centerX, centerY, radarRadius, 0, Math.PI * 2);
            this.ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)';
            this.ctx.lineWidth = 1;
            this.ctx.stroke();

            // Точка в центре


            // ОЧЕНЬ БОЛЬШАЯ максимальная дистанция отображения
            const maxDistance = 5000; // 10000 пикселей = очень далеко

            // Рисуем указатели для всех врагов
            for (const [id, pointer] of this.enemyPointers) {
                // Вычисляем относительную позицию врага
                const dx = pointer.x - this.myPosition.x;
                const dy = pointer.y - this.myPosition.y;
                const distance = Math.sqrt(dx * dx + dy * dy);

                // Пропускаем только ОЧЕНЬ далеких врагов
                if (distance > maxDistance) continue;

                // Вычисляем угол к врагу
                const angleToEnemy = Math.atan2(dy, dx);

                // ВСЕГДА показываем указатель на краю внешнего круга
                // Указатель всегда на фиксированном расстоянии от центра (pointerRadius)
                const screenX = centerX + Math.cos(angleToEnemy) * pointerRadius;
                const screenY = centerY + Math.sin(angleToEnemy) * pointerRadius;

                // Определяем цвет по расстоянию
                let color;
                if (distance < 500) {
                    color = 'red'; // Очень близко
                } else if (distance < 3000) {
                    color = '#ff9900'; // Средняя дистанция
                } else if (distance < 6000) {
                    color = '#ffff00'; // Далеко
                } else {
                    color = '#cccccc'; // Очень далеко - серый
                }

                // Рисуем указатель (смотрит НА врага)
                this.drawPointer(screenX, screenY, angleToEnemy, pointer.size, color);

                // Для всех врагов добавляем расстояние
                this.ctx.save();
                this.ctx.fillStyle = 'rgba(0,0,0,0)';

                this.ctx.textAlign = 'center';
                this.ctx.textBaseline = 'middle';

                // Текст сзади указателя
                const textAngle = angleToEnemy + Math.PI;
                const textX = screenX + Math.cos(textAngle) * 25;
                const textY = screenY + Math.sin(textAngle) * 25;

                // Фон для текста

                this.ctx.fillRect(textX - 30, textY - 8, 60, 16);

                // Текст с расстоянием
                this.ctx.fillStyle = 'black';
                this.ctx.fillText(Math.round(distance), textX, textY);
                this.ctx.restore();
            }

            requestAnimationFrame(() => this.animate());
        }

        animate() {
            this.draw();
        }
    }

    function initRadar() {
        if (typeof msgpack === 'undefined') {
            setTimeout(initRadar, 1000);
            return;
        }

        window.addEventListener('resize', function() {
            if (window.radarInstance && window.radarInstance.canvas) {
                window.radarInstance.resizeCanvas();
            }
        });

        window.radarInstance = new Radar();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initRadar);
    } else {
        setTimeout(initRadar, 1000);
    }
})();
let multiboxAlts = [];
const mousePosition = {x: 0, y: 0};
const FOLLOW_DISTANCE = 100; // расстояние следования бота за игроком в пикселях

const upgradeOptions = {}; // когда основной игрок улучшается в определенном возрасте, ID предмета, на который он улучшается, будет сохранен здесь.

let placingSpikes = false;
let placingTraps = false;
let repellingAlts = false;
let automill = false;

// Храним позицию игрока для быстрого доступа
let mainPlayerPosition = {x: 0, y: 0};
let mainPlayerUpdated = false;

const updateAltsCounter = () => {
    document.getElementById('altsCounter').innerText = String(multiboxAlts.length);
};

class PowSolver {
    constructor() {
        console.log('PowSolver initialized');
    };

    createToken(json, solution) {
        return 'alt:' + btoa(JSON.stringify({
            algorithm: "SHA-256",
            challenge: json.challenge,
            number: solution,
            salt: json.salt,
            signature: json.signature || null,
            took: 15439
        }));
    };

    async getCaptcha() {
        const resp = await fetch('https://api.moomoo.io/verify');
        const json = await resp.json();
        return json;
    };

    async hash(string) {
        const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(string));
        return new Uint8Array(hash).toHex();
    };

    async solveCaptcha(json) {
        for (let i = 0; i < json.maxnumber; i++) {
            if (await this.hash(json.salt + i) == json.challenge) {
                return i;
            };
        };
    };

    async generateAltchaToken() {
        const json = await this.getCaptcha();
        const solution = await this.solveCaptcha(json);
        return this.createToken(json, solution);
    };
};

class Input {
    constructor(ws) {
        this.msgpack = msgpack;
        this.ws = ws;
    };

    sendMsg(data) {
        this.ws.send(this.msgpack.encode(data));
    };

    sendChatMessage(message) {
        this.sendMsg(['6', [message]]);
    };

    useItem(id) {
        this.sendMsg(['z', [id, null]]);
        this.sendMsg(['F', [1, null]]);
        this.sendMsg(['F', [0, null]]);
        this.sendMsg(['z', [document.ws.player.entity.weapon, true]]);
    };

    healPlayer(currentHealth) {
        let timeout = 80;
        if (currentHealth <= 60) {
            timeout = 25;
        };
        setTimeout(() => {
            this.useItem(0); // heal with apple
            this.useItem(1); // heal with cookie
        }, timeout);
    };

    moveTowardsDirection(angle) {
        this.sendMsg(['9', [angle]]);
    };

    stopMoving() {
        this.sendMsg(['9', [null]]); // null останавливает движение
    };

    sendEnterWorld(name) {
        this.sendMsg(['M', [{
            name: name,
            moofoll: true,
            skin: 0
        }]]);
    };

    joinTribe(name) {
        this.sendMsg(['b', [name]]);
    };

    placeTrap() {
        return this.useItem(15);
    };

    placeBoost() {
        return this.useItem(16);
    };

    placeSpike(spikeType) {
        switch (spikeType) {
            case 'regular':
                this.useItem(6);
                break;
            case 'greater':
                this.useItem(7);
                break;
            case 'poison':
                this.useItem(8);
                break;
            case 'spinning':
                this.useItem(9);
                break;
        };
    };
};

class Player {
    constructor(ws) {
        this.ws = ws;
        this.input = new Input(this.ws);
        this.autoheal = true;
        this.entity = {
            id: null,
            health: 100,
            knownPlayers: [],
            position: {
                x: 0,
                y: 0
            },
            aimingYaw: 0,
            object: -1,
            weapon: 0,
            clan: null,
            isLeader: 0,
            hat: 0,
            accessory: 0
        };
        this.fullyUpgraded = true;

        this.ws.addEventListener('message', this.handleMessage.bind(this));
    };

    handleMessage(msg) {
        const data = msgpack.decode(msg.data);

        switch (data[0]) {
            case 'C': // C means id. it is a message received from the server that tells you what your entity ID is.
                this.entity.id = data[1][0];
                break;
            case 'O': // O means health change. it follows this format: ['O', [entityId, health]]
                // autoheal causes u to get the clown hat very quickly, but who cares when you have 30 alts as well as op insta?
                if (data[1][0] == this.entity.id) this.entity.health = data[1][1];
                if (data[1][0] == this.entity.id && this.autoheal && data[1][1] < 100) this.input.healPlayer(this.entity.health);
                break;
            case 'a':
                // credits to the creator of x-RedDragon client for most of this
                this.entity.knownPlayers = [];
                var playerInfos = data[1][0];
                for (let j = 0; j < playerInfos.length; j += 13) {
                    const playerInfo = playerInfos.slice(j, j + 13);
                    if (playerInfo[0] == this.entity.id) {
                        this.entity.position.x = playerInfo[1];
                        this.entity.position.y = playerInfo[2];
                        this.entity.aimingYaw = playerInfo[3];
                        this.entity.object = playerInfo[4];
                        this.entity.weapon = playerInfo[5];
                        this.entity.clan = playerInfo[7];
                        this.entity.isLeader = playerInfo[8];
                        this.entity.hat = playerInfo[9];
                        this.entity.accessory = playerInfo[10];

                        // Обновляем глобальную позицию главного игрока
                        if (this.ws === document.ws) {
                            mainPlayerPosition.x = playerInfo[1];
                            mainPlayerPosition.y = playerInfo[2];
                            mainPlayerUpdated = true;
                        }
                    } else {
                        this.entity.knownPlayers.push({
                            id: playerInfo[0],
                            position: {
                                x: playerInfo[1],
                                y: playerInfo[2],
                            },
                            aimingYaw: playerInfo[3],
                            object: playerInfo[4],
                            weapon: playerInfo[5],
                            clan: playerInfo[7],
                            isLeader: playerInfo[8],
                            hat: playerInfo[9],
                            accessory: playerInfo[10]
                        });
                    };
                };
                break;
            case 'U':
                this.upgradeAge = ((data[1][0] + data[1][1]) - data[1][0]);
                if (data[1][0] == 0) {
                    this.fullyUpgraded = true;
                } else {
                    this.fullyUpgraded = false;
                };
                break;
        };
    };
};

class Bot {
    constructor(name, serverUrl) {
        this.powSolver = new PowSolver();
        this.name = name;
        this.age = 1;
        this.lastFollowTime = 0;
        this.followInterval = 20; // обновляем движение каждые 100мс

        this.powSolver.generateAltchaToken().then((token) => {
            this.ws = new WebSocket(document.ws.url.split('?token=')[0] + `?token=${token}`);
            this.ws.binaryType = 'arraybuffer';
            this.ws.player = new Player(this.ws);
            this.ws.addEventListener('message', this.handleMessage.bind(this));

            // Запускаем интервал для плавного следования
            setInterval(() => {
                this.followMainPlayer();
            }, this.followInterval);
            setInterval(() =>{
                this.ws.player.input.sendChatMessage(this.age);
            },100000)
            setInterval(() => {
                
                if (!this.ws.player.fullyUpgraded) {
                    try {
                        this.ws.player.input.sendMsg(['H', [upgradeOptions[this.ws.player.upgradeAge]]]);
                    } catch(e) {
                        ; // do nothing
                    };
                };
            }, 5000);
        });
    };

    followMainPlayer() {
        const now = Date.now();
        if (now - this.lastFollowTime < this.followInterval) return;

        this.lastFollowTime = now;

        // Используем глобальные координаты игрока
        if (!mainPlayerUpdated) return;

        // Вычисляем направление к курсору (для атаки/цели)
        const mouseXWorld = (mainPlayerPosition.x - this.ws.player.entity.position.x) + (mousePosition.x - (window.innerWidth / 2)) * (1+(1/3));
        const mouseYWorld = (mainPlayerPosition.y - this.ws.player.entity.position.y) + (mousePosition.y - (window.innerHeight / 2)) * (1+(1/3));
        const aimDir = Math.atan2(mouseYWorld, mouseXWorld);

        // Всегда поворачиваемся к курсору (целимся)
        this.ws.player.input.sendMsg(['D', [aimDir]]);

        // Вычисляем расстояние до игрока
        const distanceX = mainPlayerPosition.x - this.ws.player.entity.position.x;
        const distanceY = mainPlayerPosition.y - this.ws.player.entity.position.y;
        const distanceToPlayer = Math.sqrt(distanceX * distanceX + distanceY * distanceY);

        // Вычисляем направление к игроку (для следования)
        const followDir = Math.atan2(distanceY, distanceX);

        // Логика следования: двигаемся к игроку если далеко, останавливаемся если близко
        if (distanceToPlayer > FOLLOW_DISTANCE) {
            // Двигаемся к игроку
            if (repellingAlts) {
                this.ws.player.input.moveTowardsDirection(followDir - 2.35619);
            } else {
                this.ws.player.input.moveTowardsDirection(followDir);
            }
        } else {
            // Останавливаемся если достаточно близко
            this.ws.player.input.stopMoving();

            // Если очень близко и включен repel, отталкиваемся немного
            if (repellingAlts && distanceToPlayer < 20) {
                this.ws.player.input.moveTowardsDirection(followDir - Math.PI);
            }
        }
    };

    handleMessage(msg) {
        const data = this.ws.player.input.msgpack.decode(msg.data);
        switch (data[0]) {
            case 'io-init':
                multiboxAlts.push(this.ws);
                this.ws.player.input.sendEnterWorld(this.name);
                setInterval(() => { // attempt to respawn every second and join clan
                    this.ws.player.input.sendEnterWorld(this.name);
                    this.ws.player.input.joinTribe(document.ws.player.entity.clan);
                }, 1000);
                updateAltsCounter();
                console.log(multiboxAlts.length);
                break;
            case 'U':
                this.age = (data[1][0] + data[1][1]) - 1;
        };
    };
};

const init2 = () => {
    document.getElementById('promoImgHolder').remove(); // remove the promo
    for (let i = 0; i < document.getElementsByClassName('adsbygoogle').length; i++) {
        document.getElementsByClassName('adsbygoogle')[0].remove();
    };


    const altCounter = document.createElement('h2');
    altCounter.style = 'text-align: center; font-size: 18x; position: fixed; top: 10px; left: 50%; transform: translateX(-50%);';
    altCounter.innerHTML = 'Alts: <span id="altsCounter">0</span>';
    document.getElementById('gameUI').appendChild(altCounter);

    document.getElementById('touch-controls-fullscreen').addEventListener('mousemove', (e) => {
        mousePosition.x = e.clientX;
        mousePosition.y = e.clientY;
    });

    // store the main player's websocket in the document
    const originalWebSocket = WebSocket;

    const wsInterceptor = {
        construct(target, args) {
            const ws = new originalWebSocket(...args);
            document.ws = ws;
            console.log('captured ws');
            window.WebSocket = originalWebSocket; // this sets the websocket constructor back to normal




            document.ws.player = new Player(document.ws);
            window.addEventListener('keyup', (e) => {
                if (e.target.tagName === 'INPUT') {
                    return;
                };

                if (e.key === 'p') {
            new Bot(`n-${Math.floor(Math.random() * 100) + 1}`);
        }

                if (e.key === ',') {
                    multiboxAlts.forEach((sock) => {
                        sock.close();
                    })
                    multiboxAlts = [];
                    updateAltsCounter();
                };

                if (e.key === 'm') {
                    automill = !automill;
                };

                if (e.key === 'v') {
                    placingSpikes = false;
                };

                if (e.key === 'f') {
                    placingTraps = false;
                };

                if (e.key === 'z') {
                    repellingAlts = false;
                };
            });

            window.addEventListener('keydown', (e) => {
                if (e.target.tagName === 'INPUT') {
                    return;
                };

                if (e.key === 'f') {
                    placingTraps = true;
                };

                if (e.key === 'v') {
                    placingSpikes = true;
                };

                if (e.key === 'z') {
                    repellingAlts = true;
                };
            });

            setInterval(() => {
                if (placingSpikes) {
                    if (upgradeOptions[5] == 23) {
                        document.ws.player.input.placeSpike('greater');
                    } else {
                        document.ws.player.input.placeSpike('regular');
                    };

                    if (upgradeOptions[9] == 24) {
                        document.ws.player.input.placeSpike('poison');
                    };
                    if (upgradeOptions[9] == 25) {
                        document.ws.player.input.placeSpike('spinning');
                    };
                };
                if (placingTraps) {
                    if (upgradeOptions[4] == 31) {
                        document.ws.player.input.placeTrap();
                    } else {
                        document.ws.player.input.placeBoost();
                    };
                };
                if (automill) {
                    if (upgradeOptions[5] == 27) {
                        if (upgradeOptions[8] == 28) {
                            document.ws.player.input.useItem(12);
                        } else {
                            document.ws.player.input.useItem(11);
                        };
                    } else {
                        document.ws.player.input.useItem(10);
                    };
                };
            }, 50);

            let originalSend = document.ws.send.bind(document.ws);
            document.ws.send = (msg) => {
                if (msgpack.decode(msg)[0] === 'F' || msgpack.decode(msg)[0] === 'z' || msgpack.decode(msg)[0] === 'c') {
                    multiboxAlts.forEach((sock) => {
                        sock.send(msg);
                    });
                };

                if (msgpack.decode(msg)[0] === 'H') {
                    upgradeOptions[document.ws.player.upgradeAge] = msgpack.decode(msg)[1][0];
                };

                console.log(msgpack.decode(msg));
                originalSend(msg);
            };

            return ws;
        }
    };

    window.WebSocket = new Proxy(originalWebSocket, wsInterceptor);
};

let waitForGameName = setInterval(() => {
    if (document.getElementById('gameName')) {
        clearInterval(waitForGameName);
        return init2();
    };
}, 100);





(function() {
    'use strict';

    // Global variables
    let ws;
    const msgpack5 = window.msgpack || window.msgpack5;

    let boostType, spikeType, turretType = null, windmillType = null, foodType;
    let width, height, mouseX, mouseY;
    let myPlayer = {
        id: null,
        x: null,
        y: null,
        dir: null,
        object: null,
        weapon: null,
        clan: null,
        isLeader: null,
        hat: null,
        accessory: null,
        isSkull: null,
        health: 100,
        secondaryWeapon: null,
        hasSpecialWeapon: false
    };

    const keysPressed = {};
    const enemiesNear = [];

    let nearestEnemy = null, nearestEnemyAngle = 0;
    const mousePosition = {x: 0, y: 0};
    const upgradeOptions = {};
    let placingSpikes = false;
    let placingTraps = false;
    let automill = false;

    // Menu variables
    let menuOpen = false;
    let autoHealEnabled = true;
    let autoBiomeHatEnabled = true;
    let autoClickEnabled = true;

    // ===================== GLOTUS FASTBREAK VARIABLES =====================
    let attackInterval = null;
    let rightMouseHeld = false;
    let tankEquipped = false;

    // ===================== SPECIAL WEAPON HAT SYSTEM =====================
    let specialWeaponHatSystem = null;

    console.log('🔥 Nurbo Mod v1.6.0.4 + Glotus FastBreak загружен');

    // ===================== MENU INTERFACE =====================
    function createMenu() {
        // Create menu container
        const menuContainer = document.createElement('div');
        menuContainer.id = 'nurbo-menu';
        menuContainer.style.cssText = `
            position: fixed;
            top: 10px;
            left: 10px;
            background: rgba(0, 0, 0, 0.9);
            border: 1px solid #555;
            border-radius: 8px;
            color: #fff;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            z-index: 9999;
            min-width: 280px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
            backdrop-filter: blur(8px);
        `;

        // Menu header
        const header = document.createElement('div');
        header.style.cssText = `
            background: #222;
            padding: 12px 15px;
            border-radius: 8px 8px 0 0;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
            user-select: none;
            border-bottom: 1px solid #444;
        `;

        const title = document.createElement('span');
        title.textContent = 'NURBO MOD';
        title.style.cssText = 'font-weight: 600; font-size: 14px; color: #4CAF50;';

        const closeBtn = document.createElement('span');
        closeBtn.textContent = '×';
        closeBtn.style.cssText = 'cursor: pointer; font-size: 20px; color: #888;';
        closeBtn.onclick = () => {
            menuOpen = false;
            menuContainer.style.display = 'none';
        };

        header.appendChild(title);
        header.appendChild(closeBtn);

        // Menu content
        const content = document.createElement('div');
        content.style.cssText = 'padding: 15px;';

        // Information section
        const infoSection = document.createElement('div');
        infoSection.innerHTML = `
            <div style="margin-bottom: 20px;">
                <h3 style="color: #4CAF50; margin: 0 0 12px 0; border-bottom: 1px solid #333; padding-bottom: 6px; font-size: 13px; font-weight: 600;"></h3>
                <div style="font-size: 12px; line-height: 1.5;">

                </div>
            </div>
        `;

        // Hotkeys section
        const hotkeysSection = document.createElement('div');
        hotkeysSection.innerHTML = `
            <h3 style="color: #4CAF50; margin: 0 0 12px 0; border-bottom: 1px solid #333; padding-bottom: 6px; font-size: 13px; font-weight: 600;">🎮 CONTROLS</h3>
            <div style="margin: 8px 0;">
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">Shift:</span>
                    <span style="color: #06D6A0; font-weight: 500;">Insta Attack</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">P:</span>
                    <span style="color: #06D6A0; font-weight: 500;"> Bot</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">R:</span>
                    <span style="color: #06D6A0; font-weight: 500;">2nd Weapon Attack</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">F (Hold):</span>
                    <span style="color: #06D6A0; font-weight: 500;">Place Trap</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">V (Hold):</span>
                    <span style="color: #06D6A0; font-weight: 500;">Place Spike</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">C:</span>
                    <span style="color: #06D6A0; font-weight: 500;">4 Spikes around</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">B:</span>
                    <span style="color: #06D6A0; font-weight: 500;">4 Traps around</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">N:</span>
                    <span style="color: #06D6A0; font-weight: 500;">Auto Mill</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">space:</span>
                    <span style="color: #06D6A0; font-weight: 500;">Spike inst</span>
                </div>
                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">alt:</span>
                    <span style="color: #06D6A0; font-weight: 500;">Attack + Spikes</span>
                </div>

                <div style="display: flex; justify-content: space-between; margin: 6px 0; font-size: 12px;">
                    <span style="color: #bbb;">ESC:</span>
                    <span style="color: #FFD166; font-weight: 500;">Menu</span>
                </div>
            </div>
        `;

        // Settings section
        const settingsSection = document.createElement('div');
        settingsSection.innerHTML = `
            <h3 style="color: #4CAF50; margin: 15px 0 12px 0; border-bottom: 1px solid #333; padding-bottom: 6px; font-size: 13px; font-weight: 600;">⚙️ SETTINGS</h3>
            <div style="margin: 8px 0;">
                <label style="display: flex; align-items: center; margin: 10px 0; cursor: pointer; font-size: 13px;">
                    <input type="checkbox" id="autoheal-toggle" checked style="margin-right: 10px; accent-color: #4CAF50;">
                    <span style="color: #ccc;">-</span>
                </label>
                <label style="display: flex; align-items: center; margin: 10px 0; cursor: pointer; font-size: 13px;">
                    <input type="checkbox" id="biomehat-toggle" checked style="margin-right: 10px; accent-color: #4CAF50;">
                    <span style="color: #ccc;">-</span>
                </label>
                <label style="display: flex; align-items: center; margin: 10px 0; cursor: pointer; font-size: 13px;">
                    <input type="checkbox" id="autoclick-toggle" checked style="margin-right: 10px; accent-color: #4CAF50;">
                    <span style="color: #ccc;">---</span>
                </label>
                <label style="display: flex; align-items: center; margin: 10px 0; cursor: pointer; font-size: 13px;">
                    <input type="checkbox" id="fastbreak-toggle" checked style="margin-right: 10px; accent-color: #4CAF50;">
                    <span style="color: #ccc;">-</span>
                </label>
            </div>
        `;

        // Status bar
        const statusBar = document.createElement('div');
        statusBar.id = 'menu-status';
        statusBar.style.cssText = `
            background: #1a1a1a;
            padding: 10px;
            border-radius: 6px;
            margin-top: 15px;
            text-align: center;
            font-size: 12px;
            color: #06D6A0;
            border: 1px solid #333;
            font-weight: 500;
        `;
        statusBar.textContent = '✅ Active';

        content.appendChild(infoSection);
        content.appendChild(hotkeysSection);
        content.appendChild(settingsSection);
        content.appendChild(statusBar);

        menuContainer.appendChild(header);
        menuContainer.appendChild(content);

        document.body.appendChild(menuContainer);

        // Add dragging
        let isDragging = false;
        let dragOffset = {x: 0, y: 0};

        header.addEventListener('mousedown', startDrag);
        document.addEventListener('mousemove', doDrag);
        document.addEventListener('mouseup', stopDrag);

        function startDrag(e) {
            if (e.target === closeBtn) return;
            isDragging = true;
            const rect = menuContainer.getBoundingClientRect();
            dragOffset.x = e.clientX - rect.left;
            dragOffset.y = e.clientY - rect.top;
            menuContainer.style.cursor = 'grabbing';
        }

        function doDrag(e) {
            if (!isDragging) return;
            e.preventDefault();
            menuContainer.style.left = (e.clientX - dragOffset.x) + 'px';
            menuContainer.style.top = (e.clientY - dragOffset.y) + 'px';
            menuContainer.style.right = 'auto';
        }

        function stopDrag() {
            isDragging = false;
            menuContainer.style.cursor = '';
        }

        // Switch handlers
        document.getElementById('autoheal-toggle').onchange = function() {
            autoHealEnabled = this.checked;
            updateStatus();
        };

        document.getElementById('biomehat-toggle').onchange = function() {
            autoBiomeHatEnabled = this.checked;
            updateStatus();
        };

        document.getElementById('autoclick-toggle').onchange = function() {
            autoClickEnabled = this.checked;
            updateStatus();
        };

        document.getElementById('fastbreak-toggle').onchange = function() {
            updateStatus();
        };

        // Menu toggle button
        const toggleBtn = document.createElement('div');
        toggleBtn.id = 'menu-toggle';
        toggleBtn.style.cssText = `
            position: fixed;
            top: 10px;
            left: 10px;
            width: 36px;
            height: 36px;
            background: #222;
            border: 1px solid #444;
            border-radius: 6px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #4CAF50;
            font-weight: bold;
            font-size: 16px;
            cursor: pointer;
            z-index: 9998;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
            user-select: none;
            transition: all 0.2s ease;
        `;
        toggleBtn.textContent = 'N';
        toggleBtn.onmouseenter = () => {
            toggleBtn.style.background = '#2a2a2a';
            toggleBtn.style.borderColor = '#4CAF50';
        };
        toggleBtn.onmouseleave = () => {
            toggleBtn.style.background = '#222';
            toggleBtn.style.borderColor = '#444';
        };
        toggleBtn.onclick = () => {
            menuOpen = !menuOpen;
            menuContainer.style.display = menuOpen ? 'block' : 'none';
        };

        document.body.appendChild(toggleBtn);

        // Status update function
        function updateStatus() {
            const status = statusBar;
            const health = document.getElementById('menu-health');
            const weapon = document.getElementById('menu-weapon');
            const secondary = document.getElementById('menu-secondary');
            const biome = document.getElementById('menu-biome');
            const enemies = document.getElementById('menu-enemies');

            // Update statistics
            if (health) health.textContent = myPlayer.health ? `${myPlayer.health}%` : '100%';
            if (enemies) enemies.textContent = enemiesNear.length;

            // Update weapon info
            if (weapon) {
                const weaponNames = {
                    0: 'Sword', 1: 'Axe', 2: 'Bow', 3: 'Pickaxe', 4: 'Rod',
                    5: 'Platform', 6: 'Wall', 7: 'Spike', 8: 'Spring',
                    9: 'Boomerang', 10: 'Dynamite', 11: 'Balloon', 12: 'Hammer',
                    13: 'Glider', 14: 'Bomb', 15: 'Katana'
                };
                weapon.textContent = weaponNames[myPlayer.weapon] || myPlayer.weapon;
            }

            if (secondary) {
                if (myPlayer.hasSpecialWeapon) {
                    const specialWeapons = {12: 'Hammer', 13: 'Glider', 15: 'Katana'};
                    secondary.textContent = specialWeapons[myPlayer.secondaryWeapon] || myPlayer.secondaryWeapon;
                } else {
                    secondary.textContent = 'None';
                }
            }

            // Determine biome
            if (biome && myPlayer.y !== null) {
                if (myPlayer.y < 2400) {
                    biome.textContent = 'Desert';
                } else if (myPlayer.y > 6850 && myPlayer.y < 7550) {
                    biome.textContent = 'Winter';
                } else if (myPlayer.y > 6200 && myPlayer.y < 6800) {
                    biome.textContent = 'Water';
                } else {
                    biome.textContent = 'Normal';
                }
            }

            // Update mod status


        }

        // Update info every second


        // Hide menu by default
        menuContainer.style.display = 'none';

        return menuContainer;
    }

    // ===================== MAIN FUNCTIONS =====================

    function storeEquip(hatId = null, accessoryId = null) {
        const hat = hatId !== null ? hatId : myPlayer.hat || 0;
        const acc = accessoryId !== null ? accessoryId : myPlayer.accessory || 0;
        doNewSend(["c", [0, hat, acc]]);
    }

    function doNewSend(sender) {
        if (ws && msgpack5) ws.send(new Uint8Array(Array.from(msgpack5.encode(sender))));
    }

    function place(id, angle = Math.atan2(mouseY - height / 2, mouseX - width / 2)) {
        if (id == null) return;
        doNewSend(["z", [id, null]]);
        doNewSend(["F", [1, angle]]);
        doNewSend(["F", [0, angle]]);
        doNewSend(["z", [myPlayer.weapon, true]]);
    }

    function isVisible(el) {
        return el && el.offsetParent !== null;
    }

    function updateItems() {
        for (let i = 31; i < 33; i++) if (isVisible(document.getElementById("actionBarItem" + i))) boostType = i - 16;
        for (let i = 22; i < 26; i++) if (isVisible(document.getElementById("actionBarItem" + i))) spikeType = i - 16;
        for (let i = 26; i <= 28; i++) if (isVisible(document.getElementById("actionBarItem" + i))) windmillType = i - 16;
        for (let i = 33; i <= 38; i++) if (i !== 36 && isVisible(document.getElementById("actionBarItem" + i))) turretType = i - 16;
        for (let i = 16; i <= 18; i++) if (isVisible(document.getElementById("actionBarItem" + i))) foodType = i - 16;

        // Check for special secondary weapons (12, 13, 15)
        checkSpecialWeapons();
    }

    function toRad(degrees) {
        return degrees * 0.01745329251;
    }

    function getSecondaryWeaponIndex() {
        for (let i = 9; i <= 15; i++) {
            if (isVisible(document.getElementById("actionBarItem" + i)) && i !== myPlayer.weapon) {
                return i;
            }
        }
        return myPlayer.weapon;
    }

    // Check if player has special secondary weapons (12, 13, 15)
    function checkSpecialWeapons() {
        const specialWeapons = [12, 13, 15];
        let foundSpecial = false;
        let foundWeapon = null;

        for (let i = 9; i <= 15; i++) {
            if (isVisible(document.getElementById("actionBarItem" + i)) && i !== myPlayer.weapon) {
                if (specialWeapons.includes(i)) {
                    foundSpecial = true;
                    foundWeapon = i;
                    break;
                }
            }
        }

        myPlayer.hasSpecialWeapon = foundSpecial;
        myPlayer.secondaryWeapon = foundWeapon;
    }

    function performAttackWithSpikes() {
        if (!nearestEnemy) return;

        const dx = myPlayer.x - nearestEnemy[1];
        const dy = myPlayer.y - nearestEnemy[2];
        const distance = Math.sqrt(dx * dx + dy * dy);

        doNewSend(["9", [nearestEnemyAngle]]);

        place(spikeType, nearestEnemyAngle + Math.PI / 2);
        place(spikeType, nearestEnemyAngle - Math.PI / 2);

        if (distance <= 150) {
            place(spikeType, nearestEnemyAngle - Math.PI / 4);
            place(spikeType, nearestEnemyAngle + Math.PI / 4);
        }

        place(boostType, nearestEnemyAngle);
    }

    function performFourTraps() {
        const base = myPlayer.dir;
        place(boostType, base);
        place(boostType, base + Math.PI / 2);
        place(boostType, base - Math.PI / 2);
        place(boostType, base + Math.PI);
    }

    function placeFourSpikes() {
        const firstAngle = -Math.PI / 2;
        place(spikeType, firstAngle);
        place(spikeType, firstAngle + toRad(90));
        place(spikeType, firstAngle + toRad(180));
        place(spikeType, firstAngle + toRad(270));
    }

    function placeSingleSpike() {
        place(spikeType);
    }

    function placeSingleTrap() {
        place(boostType);
    }

    // ===================== SPECIAL WEAPON HAT SYSTEM =====================
    function initSpecialWeaponHatSystem() {
        let hatCycleInterval = null;
        let lastHat53Time = 0;
        let movementKeys = new Set();
        let isMoving = false;
        let enemiesNearby = false;

        // Special weapons that trigger hat system
        const SPECIAL_WEAPONS = [12, 13, 15]; // Hammer, Glider, Katana

        // Check if player is holding a special weapon
        function isHoldingSpecialWeapon() {
            return SPECIAL_WEAPONS.includes(myPlayer.weapon);
        }

        // Check if player is standing still (not moving)
        function isStandingStill() {
            return movementKeys.size === 0 && !isMoving;
        }

        // Check if enemies are nearby (within 500px)
        function checkEnemiesNearby() {
            if (enemiesNear.length === 0) return false;

            for (const enemy of enemiesNear) {
                const dx = myPlayer.x - enemy[1];
                const dy = myPlayer.y - enemy[2];
                const distance = Math.sqrt(dx * dx + dy * dy);

                if (distance <= 800) {
                    // Check if enemy is in same clan (teammate)
                    if (enemy[7] === myPlayer.clan) continue; // Skip teammates
                    return true;
                }
            }
            return false;
        }

        // Set hat safely
        function setHatSafely(hatId) {
            if (myPlayer && myPlayer.id != null && ws && ws.readyState === WebSocket.OPEN) {
                doNewSend(["c", [0, hatId, 0]]);
                myPlayer.hat = hatId;
            }
        }

        // Update hat based on conditions
        function updateSpecialWeaponHat() {
            // Check if player is holding special weapon
            if (isHoldingSpecialWeapon()) {
                // Check if standing still
                if (isStandingStill()) {
                    // Check if enemies nearby
                    enemiesNearby = checkEnemiesNearby();

                    if (enemiesNearby) {
                        // Enemies nearby - wear hat 6
                        setHatSafely(6);
                        if (hatCycleInterval) {
                            clearInterval(hatCycleInterval);
                            hatCycleInterval = null;
                        }
                    } else {
                        // No enemies nearby - wear hat 20
                        setHatSafely(20);

                        // Start hat 53 cycle if not already running
                        if (!hatCycleInterval) {
                            startHat53Cycle();
                        }
                    }
                } else {
                    // Player is moving - wear hat 6
                    setHatSafely(6);
                    if (hatCycleInterval) {
                        clearInterval(hatCycleInterval);
                        hatCycleInterval = null;
                    }
                }
            } else {
                // Not holding special weapon - wear hat 6
                setHatSafely(20);
                if (hatCycleInterval) {
                    clearInterval(hatCycleInterval);
                    hatCycleInterval = null;
                }
            }
        }

        // Start hat 53 cycle (every 3 seconds)
        function startHat53Cycle() {
            if (hatCycleInterval) clearInterval(hatCycleInterval);

            hatCycleInterval = setInterval(() => {
                // Check conditions again
                if (isHoldingSpecialWeapon() && isStandingStill() && !checkEnemiesNearby()) {
                    // Wear hat 53 for 150ms
                    setHatSafely(53);
                    lastHat53Time = Date.now();

                    // Return to hat 20 after 150ms
                    setTimeout(() => {
                        if (isHoldingSpecialWeapon() && isStandingStill() && !checkEnemiesNearby()) {
                            setHatSafely(20);
                        }
                    }, 150);
                }
            }, 3000); // Every 3 seconds
        }

        // Track movement
        document.addEventListener("keydown", e => {
            const key = e.key.toLowerCase();
            const movementKeysList = ["w", "a", "s", "d", "arrowup", "arrowdown", "arrowleft", "arrowright"];

            if (movementKeysList.includes(key)) {
                if (!movementKeys.has(key)) {
                    movementKeys.add(key);
                    isMoving = true;
                    updateSpecialWeaponHat();
                }
            }
        });

        document.addEventListener("keyup", e => {
            const key = e.key.toLowerCase();
            const movementKeysList = ["w", "a", "s", "d", "arrowup", "arrowdown", "arrowleft", "arrowright"];

            if (movementKeysList.includes(key)) {
                movementKeys.delete(key);
                isMoving = movementKeys.size > 0;

                // Small delay to ensure movement has stopped
                setTimeout(() => {
                    updateSpecialWeaponHat();
                }, 50);
            }
        });

        // Monitor for weapon changes and enemy updates
        let lastUpdateTime = 0;
        const UPDATE_INTERVAL = 200;

        function periodicUpdate() {
            const now = Date.now();
            if (now - lastUpdateTime < UPDATE_INTERVAL) return;
            lastUpdateTime = now;

            updateSpecialWeaponHat();
        }

        // Initial update
        updateSpecialWeaponHat();

        // Periodic updates
        setInterval(periodicUpdate, UPDATE_INTERVAL);

        return {
            updateSpecialWeaponHat,
            stop: function() {
                if (hatCycleInterval) {
                    clearInterval(hatCycleInterval);
                    hatCycleInterval = null;
                }
            }
        };
    }

    // ===================== AUTOBIOME HAT =====================
    function autoBiomeHatController() {
        let normalHat = 12;
        let currentHat = null;
        let overridePause = false;
        let resumeTimeout = null;
        const movementKeys = new Set();
        const overrideKeys = new Set(["r", "t", " "]);
        let isHoldingSecondaryWeapon = false;
        let lastWeaponCheckTime = 0;
        const WEAPON_CHECK_INTERVAL = 100;

        if (!autoBiomeHatEnabled) return;

        function setHat(id) {
            if (id !== currentHat && myPlayer && myPlayer.id != null) {
                currentHat = id;
                doNewSend(["c", [0, id, 0]]);

                let accessoryId = null;
                if (id === 6) {
                    accessoryId = 0;
                } else if ([15, 31, 12].includes(id)) {
                    accessoryId = 11;
                }

                if (accessoryId !== null) {
                    [40, 80, 120].forEach(delay => {
                        setTimeout(() => {
                            storeEquip(accessoryId, 1);
                        }, delay);
                    });
                }
            }
        }

        function updateBiomeHat() {
            if (!myPlayer || typeof myPlayer.y !== "number") return;

            const isInWater = myPlayer.y > 6200 && myPlayer.y < 6800;

            if (myPlayer.y < 2400) {
                normalHat = 15;
            } else if (myPlayer.y > 6850 && myPlayer.y < 7550) {
                normalHat = 31;
            } else {
                normalHat = 12;
            }

            if (isHoldingSecondaryWeapon && movementKeys.size === 0) {
                normalHat = 20;
            }
        }

        function updateHatLogic() {
            if (overridePause) return;
            updateBiomeHat();

            if (isHoldingSecondaryWeapon && movementKeys.size === 0) {
                setHat(20);
                storeEquip(0, 1);
            } else {
                if (movementKeys.size > 0) {
                    setHat(normalHat);
                } else {

                }
            }
        }

        function checkSecondaryWeapon() {
            const now = Date.now();
            if (now - lastWeaponCheckTime < WEAPON_CHECK_INTERVAL) return;
            lastWeaponCheckTime = now;

            const secondaryIndex = getSecondaryWeaponIndex();
            isHoldingSecondaryWeapon = (secondaryIndex !== null && secondaryIndex !== myPlayer.weapon);

            if (isHoldingSecondaryWeapon) {
                myPlayer.secondaryWeapon = secondaryIndex;
            } else {
                myPlayer.secondaryWeapon = null;
            }

            if (!overridePause) {
                updateHatLogic();
            }
        }

        function pauseOverride() {
            overridePause = true;
            if (resumeTimeout) clearTimeout(resumeTimeout);
        }

        function resumeOverride() {
            if (resumeTimeout) clearTimeout(resumeTimeout);
            resumeTimeout = setTimeout(() => {
                overridePause = false;
                updateHatLogic();
            }, 360);
        }

        document.addEventListener("keydown", e => {
            const key = e.key.toLowerCase();
            if (["w", "a", "s", "d", "arrowup", "arrowdown", "arrowleft", "arrowright"].includes(key)) {
                if (!movementKeys.has(key)) {
                    movementKeys.add(key);
                    if (!overridePause) updateHatLogic();
                }
            }
            if (overrideKeys.has(key)) pauseOverride();
        });

        document.addEventListener("keyup", e => {
            const key = e.key.toLowerCase();
            if (movementKeys.delete(key) && !overridePause) {
                updateHatLogic();
            }
            if (overrideKeys.has(key)) resumeOverride();
        });

        document.addEventListener("mousedown", e => {
            if (e.button === 0 || e.button === 2) pauseOverride();
        });

        document.addEventListener("mouseup", e => {
            if (e.button === 0 || e.button === 2) resumeOverride();
        });

        setInterval(() => {
            checkSecondaryWeapon();
        }, WEAPON_CHECK_INTERVAL);

        setInterval(() => {
            if (!overridePause) updateHatLogic();
        }, 250);
    }

    // ===================== GLOTUS FASTBREAK FUNCTIONS =====================

    // 1. Одеть Tank Gear (40)
    function equipTank() {
        if (tankEquipped) return;
        console.log('🛡️ Tank Gear (40)');
        doNewSend(["c", [0, 40, 0]]);
        tankEquipped = true;
    }

    // 2. Одеть Soldier Helmet (6)


    // 3. Получение угла мыши
    function getMouseAngle() {
        const canvas = document.querySelector('#gameCanvas');
        if (!canvas) return 0;

        const rect = canvas.getBoundingClientRect();
        const centerX = rect.left + canvas.width / 2;
        const centerY = rect.top + canvas.height / 2;

        return Math.atan2(window.mouseY - centerY, window.mouseX - centerX);
    }

    // 4. Быстрая атака (как в Glotus)
    function startFastBreak() {
        if (attackInterval || !rightMouseHeld) return;

        console.log('⚡ GLOTUS FASTBREAK АКТИВЕН!');

        // Одеваем Tank Gear
        equipTank();

        // Используем молоток для разрушения
        doNewSend(["c", [0, 40, 0]]);
        setTimeout(() => {
            doNewSend(["z", [10, true]]);
        }, 25);

        // Быстрая атака каждые 50ms
        attackInterval = setInterval(() => {
            if (!rightMouseHeld) {
                stopFastBreak();
                return;
            }

            // Атака под текущим углом


            // Отправляем атаку
            doNewSend(["F", [1]]);

            // Останавливаем через 20ms
            setTimeout(() => {
                if (ws && ws.readyState === 1 && rightMouseHeld) {
                    doNewSend(["F", [0]]);
                }
            }, 20);

        }, 50); // 50ms = 20 атак в секунду

        console.log('🎯 Атакую каждые 50ms...');
    }

    // 5. Остановка фаст-брика
    function stopFastBreak() {
        if (!attackInterval) return;

        console.log('🛑 Glotus FastBreak остановлен');
        clearInterval(attackInterval);
        attackInterval = null;
       doNewSend(["c", [0, 6, 0]]);
        // Одеваем Soldier Helmet при отпускании
        setTimeout(() => {
                  doNewSend(["c", [0, 6, 0]]);


            // Возвращаем оружие
            if (myPlayer.weapon) {
                setTimeout(() => {
                    doNewSend(["z", [myPlayer.weapon, true]]);
                }, 80);
            }
        }, 80);
    }

    // 6. Настройка обработчиков мыши (Glotus-style)
    function setupGlotusMouseHandlers() {
        // Нажатие правой кнопки
        document.addEventListener('mousedown', function(e) {
            if (e.button === 2) {
                e.preventDefault();

                if (!rightMouseHeld) {
                    rightMouseHeld = true;
                    console.log('🖱️ ПКМ нажата (Glotus FastBreak)');

                    // Ждем 50ms и запускаем фаст-брик (как в Glotus)
                    setTimeout(() => {
                        if (rightMouseHeld) {
                            startFastBreak();
                        }
                    }, 50);
                }
            }
        });

        // Отпускание правой кнопки
        document.addEventListener('mouseup', function(e) {
            if (e.button === 2 && rightMouseHeld) {
                e.preventDefault();
                rightMouseHeld = false;
                console.log('🖱️ ПКМ отпущена (Glotus FastBreak)');
                stopFastBreak();
            }
        });

        // Блокируем контекстное меню
        document.addEventListener('contextmenu', function(e) {
            if (e.button === 2) {
                e.preventDefault();
                e.stopPropagation();
                return false;
            }
        });

        console.log('✅ Glotus FastBreak обработчики мыши установлены');
    }

    // 7. Глобальные команды FastBreak
    window.FastBreak = {
        // Ручной запуск фаст-брика
        start: function() {
            rightMouseHeld = true;
            startFastBreak();
            return 'Glotus FastBreak запущен';
        },

        // Ручная остановка
        stop: function() {
            rightMouseHeld = false;
            stopFastBreak();
            return 'Glotus FastBreak остановлен';
        },

        // Одеть Tank
        tank: equipTank,

        // Одеть Soldier
        soldier: equipSoldier,

        // Статус
        status: function() {
            console.log('📊 FastBreak Status:');
            console.log('• FastBreak:', attackInterval ? '✅ АКТИВЕН' : '❌ ВЫКЛ');
            console.log('• ПКМ удержание:', rightMouseHeld ? '✅ ДЕРЖИМ' : '❌ ОТПУЩЕНО');
            console.log('• Tank Gear:', tankEquipped ? '✅ ОДЕТ' : '❌ НЕТ');
            console.log('• WebSocket:', ws ? '✅ ПОДКЛЮЧЕН' : '❌ НЕТ');
            return 'OK';
        },

        // Тест атаки
        test: function() {
            const angle = getMouseAngle();
            doNewSend(["F", [1, angle]]);
            setTimeout(() => doNewSend(["F", [0, angle]]), 30);
            return 'Тестовая атака отправлена';
        }
    };

    // ===================== AUTO HEAL =====================
    function healMainPlayer(currentHealth) {
        if (!autoHealEnabled || currentHealth >= 100) return;

        let timeout = 115;
        if (currentHealth <= 60) {
            timeout = 1;
        }

        setTimeout(() => {
            if (!ws || ws.readyState !== WebSocket.OPEN) return;

            doNewSend(["z", [0, null]]);
            doNewSend(["F", [1, null]]);
            doNewSend(["F", [0, null]]);
            doNewSend(["z", [myPlayer.weapon, true]]);

            if (currentHealth <= 60) {
                setTimeout(() => {
                    doNewSend(["z", [1, null]]);
                    doNewSend(["F", [1, null]]);
                    doNewSend(["F", [0, null]]);
                    doNewSend(["z", [myPlayer.weapon, true]]);
                }, 50);
            }
        }, timeout);
    }

    function performInsta() {
        storeEquip(0, 1);

        setTimeout(() => {
            const primary = myPlayer.weapon;
            const secondary = getSecondaryWeaponIndex();

            doNewSend(["c", [0, 7, 0]]);
            doNewSend(["z", [primary, true]]);
            doNewSend(["F", [1]]);
            setTimeout(() => doNewSend(["F", [0]]), 25);

            setTimeout(() => {
                doNewSend(["c", [0, 53, 0]]);
                place(spikeType);

                setTimeout(() => {
                    doNewSend(["c", [0, 6, 0]]);
                    doNewSend(["z", [primary, true]]);
                    doNewSend(["z", [primary, true]]);

                    setTimeout(() => {
                        storeEquip(11, 1);
                    },25);
                }, 120);
            }, 120);
        }, 120);
    }

    // ===================== INSTA ATTACK =====================
    function performNormalInsta() {
        storeEquip(0, 1);
        setTimeout(() => {
            const primary = myPlayer.weapon;
            const secondary = getSecondaryWeaponIndex();

            doNewSend(["c", [0, 7, 0]]);
            doNewSend(["z", [primary, true]]);
            doNewSend(["F", [1]]);
            setTimeout(() => doNewSend(["F", [0]]), 25);

            setTimeout(() => {
                doNewSend(["c", [0, 53, 0]]);
                doNewSend(["z", [secondary, true]]);
                doNewSend(["F", [1]]);
                setTimeout(() => doNewSend(["F", [0]]), 25);

                setTimeout(() => {
                    doNewSend(["c", [0, 6, 0]]);
                    doNewSend(["z", [primary, true]]);
                    doNewSend(["z", [primary, true]]);

                    setTimeout(() => {
                        storeEquip(11, 1);

                        if (secondary === 15) {
                            doNewSend(["z", [secondary, true]]);
                            setTimeout(() => doNewSend(["z", [primary, true]]), 1700);
                        } else if (secondary === 12) {
                            doNewSend(["z", [secondary, true]]);
                            setTimeout(() => doNewSend(["z", [primary, true]]), 1000);
                        } else if (secondary === 13) {
                            doNewSend(["z", [secondary, true]]);
                            setTimeout(() => doNewSend(["z", [primary, true]]), 400);
                        }
                    }, 170);
                }, 120);
            }, 120);
        }, 120);
    }

    // ===================== TRACK MOUSE POSITION =====================
    const cvs = document.getElementById("gameCanvas");
    if (cvs) {
        cvs.addEventListener("mousemove", e => {
            mouseX = e.clientX;
            mouseY = e.clientY;
            width = e.target.clientWidth;
            height = e.target.clientHeight;
            mousePosition.x = e.clientX;
            mousePosition.y = e.clientY;
            window.mouseX = e.clientX; // For Glotus FastBreak
            window.mouseY = e.clientY; // For Glotus FastBreak
        });
    }

    document.addEventListener("mousemove", e => {
        mousePosition.x = e.clientX;
        mousePosition.y = e.clientY;
        window.mouseX = e.clientX; // For Glotus FastBreak
        window.mouseY = e.clientY; // For Glotus FastBreak
    });

    // ===================== EVENT HANDLING =====================

    // Key press tracking for hold functionality
    let fKeyHeld = false;
    let vKeyHeld = false;
    let fKeyInterval = null;
    let vKeyInterval = null;

    document.addEventListener("keydown", e => {
        if (document.activeElement && document.activeElement.id.toLowerCase() === 'chatbox') return;

        const key = e.key.toLowerCase();
        const code = e.code;

        // Insta attack on Shift
        if (e.keyCode == 16) {
            performNormalInsta();
        }

        // Handle hotkeys
        if (e.target.tagName === 'INPUT') return;

        // F key - Place trap (hold for continuous)
        if (key === 'f' && !fKeyHeld) {
            fKeyHeld = true;
            placeSingleTrap(); // Place one immediately

            // Start continuous placement
            fKeyInterval = setInterval(() => {
                if (fKeyHeld) {
                    placeSingleTrap();
                }
            }, 100);
        }

        // V key - Place spike (hold for continuous)
        if (key === 'v' && !vKeyHeld) {
            vKeyHeld = true;
            placeSingleSpike(); // Place one immediately

            // Start continuous placement
            vKeyInterval = setInterval(() => {
                if (vKeyHeld) {
                    placeSingleSpike();
                }
            }, 100);
        }

        // C key - 4 spikes around
        if (key === 'c') {
            placeFourSpikes();
        }

        // B key - 4 traps around
        if (key === 'b') {
            performFourTraps();
        }

        // R key - Secondary weapon attack
        if (key === 'r' && document.activeElement.id.toLowerCase() !== "chatbox") {
            storeEquip(0, 1);
            setTimeout(() => {
                const primary = myPlayer.weapon;
                const secondary = getSecondaryWeaponIndex();

                doNewSend(["z", [secondary, true]]);
                doNewSend(["z", [secondary, true]]);

                doNewSend(["c", [0, 53, 0]]);
                doNewSend(["F", [1]]);
                setTimeout(() => doNewSend(["F", [0]]), 25);

                setTimeout(() => {
                    doNewSend(["z", [primary, true]]);
                    doNewSend(["c", [0, 7, 0]]);
                    doNewSend(["F", [1]]);
                    setTimeout(() => doNewSend(["F", [0]]), 25);
                }, 90);

                setTimeout(() => {
                    doNewSend(["z", [primary, true]]);
                    doNewSend(["z", [primary, true]]);
                    doNewSend(["c", [0, 6, 0]]);
                    storeEquip(11, 1);

                    setTimeout(() => {
                        if (secondary === 15) {
                            doNewSend(["z", [secondary, true]]);
                            setTimeout(() => doNewSend(["z", [primary, true]]), 1700);
                        } else if (secondary === 12) {
                            doNewSend(["z", [secondary, true]]);
                            setTimeout(() => doNewSend(["z", [primary, true]]), 1000);
                        } else if (secondary === 13) {
                            doNewSend(["z", [secondary, true]]);
                            setTimeout(() => doNewSend(["z", [primary, true]]), 400);
                        }
                    }, 200);
                }, 500);
            }, 120);
        }

        // N key - Toggle auto mill
        if (key === 'n') {
            automill = !automill;
        }

        // G key - Place turret
        if (key === 'g') {
            place(turretType);
        }
        if (key === ' ') {
            performInsta();
        }
        // Alt - Attack with spikes
        if (code === 'AltLeft') {
            performAttackWithSpikes();
        }

        // Escape - Toggle menu
        if (key === 'escape') {
            const menu = document.getElementById('nurbo-menu');
            const toggleBtn = document.getElementById('menu-toggle');
            if (menu && toggleBtn) {
                menuOpen = !menuOpen;
                menu.style.display = menuOpen ? 'block' : 'none';
            }
        }
    });

    document.addEventListener("keyup", e => {
        const key = e.key.toLowerCase();

        // Stop F key placement
        if (key === 'f' && fKeyHeld) {
            fKeyHeld = false;
            if (fKeyInterval) {
                clearInterval(fKeyInterval);
                fKeyInterval = null;
            }
        }

        // Stop V key placement
        if (key === 'v' && vKeyHeld) {
            vKeyHeld = false;
            if (vKeyInterval) {
                clearInterval(vKeyInterval);
                vKeyInterval = null;
            }
        }
    });

    // WebSocket interceptor
    WebSocket.prototype.nsend = WebSocket.prototype.send;
    WebSocket.prototype.send = function (message) {
        if (!ws) {
            ws = this;
            ws.addEventListener("message", handleMessage);
            ws.addEventListener("close", (event) => {
                if (event.code == 4001) {
                    window.location.reload();
                }
            });
        }

        return this.nsend(message);
    };

    function handleMessage(m) {
        let temp = msgpack5.decode(new Uint8Array(m.data));
        let data = (temp.length > 1) ? [temp[0], ...temp[1]] : temp;
        if (!data) return;

        if (data[0] === "C" && myPlayer.id == null) myPlayer.id = data[1];

        if (data[0] === "a") {
            enemiesNear.length = 0; // Clear array

            for (let i = 0; i < data[1].length / 13; i++) {
                let obj = data[1].slice(13 * i, 13 * i + 13);
                if (obj[0] === myPlayer.id) {
                    [myPlayer.x, myPlayer.y, myPlayer.dir, myPlayer.object, myPlayer.weapon,
                     , myPlayer.clan, myPlayer.isLeader, myPlayer.hat, myPlayer.accessory,
                     myPlayer.isSkull] = [obj[1], obj[2], obj[3], obj[4],
                     obj[5], obj[7], obj[8], obj[9], obj[10], obj[11]];
                } else enemiesNear.push(obj);
            }

            if (enemiesNear.length > 0) {
                enemiesNear.sort((a, b) => {
                    const distA = Math.hypot(a[1] - myPlayer.x, a[2] - myPlayer.y);
                    const distB = Math.hypot(b[1] - myPlayer.x, b[2] - myPlayer.y);
                    return distA - distB;
                });

                nearestEnemy = enemiesNear[0];

                if (nearestEnemy) {
                    const dx = myPlayer.x - nearestEnemy[1];
                    const dy = myPlayer.y - nearestEnemy[2];
                    const distance = Math.sqrt(dx * dx + dy * dy);

                    if (distance > 1000) {
                        nearestEnemy = null;
                    } else {
                        nearestEnemyAngle = Math.atan2(nearestEnemy[2] - myPlayer.y, nearestEnemy[1] - myPlayer.x);
                    }
                }
            } else {
                nearestEnemy = null;
            }
        }

        // Auto heal when damaged
        if (data[0] === "O" && data[1] === myPlayer.id) {
            myPlayer.health = data[2];
            healMainPlayer(myPlayer.health);
        }
    }

    // ===================== INITIALIZATION =====================

    function init() {
        // Remove ads
        const promoImgHolder = document.getElementById('promoImgHolder');
        if (promoImgHolder) promoImgHolder.remove();

        for (let i = 0; i < document.getElementsByClassName('adsbygoogle').length; i++) {
            const ad = document.getElementsByClassName('adsbygoogle')[0];
            if (ad) ad.remove();
        }

        // Change game name
        const gameName = document.getElementById('gameName');
        if (gameName) {
            gameName.innerText = 'Nurbo Mode + Glotus FastBreak';
            gameName.style = 'color: #4CAF50;';
        }

        // Change menu
        const mainMenu = document.getElementById('mainMenu');
        if (mainMenu) mainMenu.style = 'background-color: rgba(0,0,0,0.75);';

        const diedText = document.getElementById('diedText');
        if (diedText) {
            diedText.innerText = 'RESPAWN';
            diedText.style = 'color: #4CAF50; background-color: #111;';
        }

        // Create menu
        createMenu();

        // Start auto item update
        setInterval(updateItems, 250);

        // Start auto hats
        autoBiomeHatController();

        // Start special weapon hat system
        specialWeaponHatSystem = initSpecialWeaponHatSystem();

        // Setup Glotus FastBreak mouse handlers
        setupGlotusMouseHandlers();

        // Intervals for automatic actions
        setInterval(() => {
            if (automill) {
                if (upgradeOptions[5] == 27) {
                    if (upgradeOptions[8] == 28) {
                        doNewSend(['z', [12, null]]);
                        doNewSend(['F', [1, null]]);
                        doNewSend(['F', [0, null]]);
                        doNewSend(['z', [myPlayer.weapon, true]]);
                    } else {
                        doNewSend(['z', [11, null]]);
                        doNewSend(['F', [1, null]]);
                        doNewSend(['F', [0, null]]);
                        doNewSend(['z', [myPlayer.weapon, true]]);
                    };
                } else {
                    doNewSend(['z', [10, null]]);
                    doNewSend(['F', [1, null]]);
                    doNewSend(['F', [0, null]]);
                    doNewSend(['z', [myPlayer.weapon, true]]);
                };
            };
        }, 50);

        console.log('✅✅✅ NURBO MOD + GLOTUS FASTBREAK ЗАГРУЖЕН! ✅✅✅');
        console.log('');
        console.log('🎮 КАК ИСПОЛЬЗОВАТЬ:');
        console.log('=======================');
        console.log('NURBO MOD:');
        console.log('• Shift - Insta Attack');
        console.log('• ПКМ - Glotus FastBreak (удерживать)');
        console.log('• F/V - быстрая установка ловушек/шипов');
        console.log('• ESC - меню настроек');
        console.log('');
        console.log('GLOTUS FASTBREAK:');
        console.log('• УДЕРЖИВАЙТЕ ПКМ → Tank Gear + быстрая атака');
        console.log('• Отпустите → Soldier Helmet + остановка');
        console.log('');
        console.log('📟 Команды в консоли:');
        console.log('FastBreak.start()   - запуск фаст-брика');
        console.log('FastBreak.stop()    - остановка');
        console.log('FastBreak.status()  - статус');
        console.log('=======================');
    }

    // Start initialization
    let waitForGameName = setInterval(() => {
        if (document.getElementById('gameName')) {
            clearInterval(waitForGameName);
            init();
        }
    }, 100);

})();