x-RedDragon Client

R=InstaNormal, T=BoostInsta, G=BoostSpike, B=4Traps/Boost, M=AutoMills, F=Trap/BoostPad, V=Spike, N=Mill, H=Teleport/Turret, P=AutoGrindsBETA, AutoBiomeHat, Esc=MenuMusic, ClickRight=FastBreak, AutoGG, AutoHeal, AntiInsta and visual mods.

目前為 2025-08-26 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         x-RedDragon Client
// @namespace    http://youtube.com/@x-RedDragonYT
// @version      1.0.7
// @description  R=InstaNormal, T=BoostInsta, G=BoostSpike, B=4Traps/Boost, M=AutoMills, F=Trap/BoostPad, V=Spike, N=Mill, H=Teleport/Turret, P=AutoGrindsBETA, AutoBiomeHat, Esc=MenuMusic, ClickRight=FastBreak, AutoGG, AutoHeal, AntiInsta and visual mods.
// @icon         https://i.imgur.com/AFJt4iq.png
// @author       x-RedDragonYT
// @match        *://moomoo.io/*
// @match        *://*.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';

    let ws;
    const msgpack5 = window.msgpack;

    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
    };
    let myPlayeroldx, myPlayeroldy;
    let automillx = 10, automilly = 10;
    let walkmillhaha = false;
    let healToggle = true;
    const keysPressed = {};
    const placementIntervals = {};
    let gInterval = null;
    let prevKillCount = 0;

    const ID_BullHelmet = 7;
    const ID_TurretGear = 53;
    const ID_SoldierHelmet = 6;
    const ID_TankGear = 40;

    const cvs = document.getElementById("gameCanvas");
    if (!cvs) return;

    cvs.addEventListener("mousemove", e => {
        mouseX = e.clientX;
        mouseY = e.clientY;
        width = e.target.clientWidth;
        height = e.target.clientHeight;
    });

    function emit(type, ...data) {
        if (ws && msgpack5) ws.send(Uint8Array.from(msgpack5.encode([type, data])));
    }

    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;
    }

    setInterval(updateItems, 250);

    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;
    }

    function sequenceInstaKill(includeBoost = false) {
        if (includeBoost) place(boostType);

        setTimeout(() => {
            doNewSend(["c", [0, ID_BullHelmet, 0]]);
            emit("z", myPlayer.weapon, true);
            emit("F", 1);
            emit("F", 0);

            setTimeout(() => {
                doNewSend(["c", [0, ID_TurretGear, 0]]);
                const secondary = getSecondaryWeaponIndex();
                emit("z", secondary, true);
                emit("F", 1);
                emit("F", 0);

                setTimeout(() => {
                    doNewSend(["c", [0, ID_SoldierHelmet, 0]]);
                }, 200);
            }, 120);
        }, 120);
    }

    function startPlacingStructure(key, itemId) {
        if (!placementIntervals[key]) {
            placementIntervals[key] = setInterval(() => place(itemId), 50);
        }
    }

    function stopPlacingStructure(key) {
        clearInterval(placementIntervals[key]);
        delete placementIntervals[key];
    }

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

    function performGReleaseSequence() {
        const dir = myPlayer.dir;
        place(spikeType, dir - Math.PI / 4);
        place(spikeType, dir + Math.PI / 4);
    }

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

    document.addEventListener("keydown", e => {
        if (document.activeElement.id.toLowerCase() === 'chatbox') return;
        const k = e.key.toLowerCase();
        if (keysPressed[k]) return;
        keysPressed[k] = true;

        if (k === 'r') sequenceInstaKill(false);
        if (k === 't') sequenceInstaKill(true);
        if (k === 'm') {
            walkmillhaha = !walkmillhaha;
            doNewSend(["6", ["Mills : " + walkmillhaha]]);
        }
        if (k === 'f') startPlacingStructure(k, boostType);
        if (k === 'v') startPlacingStructure(k, spikeType);
        if (k === 'n') startPlacingStructure(k, windmillType);
        if (k === 'h') startPlacingStructure(k, turretType);
        if (k === 'g' && !gInterval) {
            performGSequence();
            gInterval = setInterval(performGSequence, 80);
        }
        if (k === 'b') {
            performBPlacement();
        }
    });

    document.addEventListener("keyup", e => {
        const k = e.key.toLowerCase();
        keysPressed[k] = false;
        stopPlacingStructure(k);

        if (k === 'g' && gInterval) {
            clearInterval(gInterval);
            gInterval = null;
            setTimeout(performGReleaseSequence, 80);
        }
    });

    // ✅ Redefinición segura de WebSocket.send
    if (!WebSocket.prototype.__originalSend) {
        WebSocket.prototype.__originalSend = WebSocket.prototype.send;
        WebSocket.prototype.send = function (data) {
            if (!ws) {
                ws = this;
                document.ws = this;
                ws.addEventListener("message", handleMessage);
            }
            return this.__originalSend(data);
        };
    }

    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") {
            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]];
                }
            }

            if (automillx === false) automillx = myPlayer.x;
            if (automilly === false) automilly = myPlayer.y;

            if (myPlayeroldy !== myPlayer.y || myPlayeroldx !== myPlayer.x) {
                if (walkmillhaha) {
                    if (Math.sqrt(Math.pow(myPlayer.y - automilly, 2) + Math.pow(myPlayer.x - automillx, 2)) > 100) {
                        let angle = Math.atan2(myPlayeroldy - myPlayer.y, myPlayeroldx - myPlayer.x);
                        place(windmillType, angle + toRad(78));
                        place(windmillType, angle - toRad(78));
                        place(windmillType, angle);
                        doNewSend(["D", [Math.atan2(mouseY - height / 2, mouseX - width / 2)]]);
                        automillx = myPlayer.x;
                        automilly = myPlayer.y;
                    }
                }
                myPlayeroldx = myPlayer.x;
                myPlayeroldy = myPlayer.y;
            }
        }

        if (data[0] === 'O' && data[1] === myPlayer.id) {
            const hp = data[2];
            if (hp < 100 && hp > 0 && healToggle) {
                let delay = hp <= 60 ? 5 : 122;
                setTimeout(() => place(foodType), delay);
            }
        }
    }

    // 🧠 Anti-Rotación
    Object.defineProperty(Object.prototype, "turnSpeed", {
        get() { return 0; },
        set(_) {},
        configurable: true
    });

    // 🗨️ AutoGG al matar
    const observeKills = new MutationObserver(mutations => {
        for (const mutation of mutations) {
            if (mutation.target.id === "killCounter") {
                const count = parseInt(mutation.target.innerText, 10) || 0;
                if (count > prevKillCount) {
                    prevKillCount = count;
                    doNewSend(["6", ["<[GG]>x-RedDragon Client<[GG]>"]]);
                }
            }
        }
    });
    observeKills.observe(document, { subtree: true, childList: true });

// 🖱️ Click derecho: ataque automático + cambio de casco (sin storeEquip)
let rightClickAttackInterval = null;
let rightClickHeld = false;
let isPlacingStructure = false;
let attackPausedForPlacement = false;

function startAttackLoop() {
    if (rightClickAttackInterval) return;
    rightClickAttackInterval = setInterval(() => {
        doNewSend(["c", [0, ID_TankGear, 0]]); // Casco ofensivo
        doNewSend(["F", [1]]);
        setTimeout(() => doNewSend(["F", [0]]), 10);
    }, 60);
}

function stopAttackLoop() {
    clearInterval(rightClickAttackInterval);
    rightClickAttackInterval = null;
}

// Escucha el clic derecho y activa la lógica de ataque
document.addEventListener("mousedown", function (e) {
    if (e.button === 2 && !rightClickHeld) {
        rightClickHeld = true;
        doNewSend(["c", [0, ID_TankGear, 0]]); // Equipar casco ofensivo

        setTimeout(() => {
            if (rightClickHeld && !isPlacingStructure) {
                startAttackLoop();
            }
        }, 60);
    }
});

document.addEventListener("mouseup", function (e) {
    if (e.button === 2 && rightClickHeld) {
        rightClickHeld = false;
        stopAttackLoop();
        doNewSend(["F", [0]]);
        setTimeout(() => doNewSend(["c", [0, ID_SoldierHelmet, 0]]), 100); // Volver al casco base
    }
});

// Interceptamos la función `place(...)` para pausar el ataque si se colocan estructuras
const originalPlace = place;
place = function (...args) {
    isPlacingStructure = true;

    if (rightClickAttackInterval) {
        attackPausedForPlacement = true;
        stopAttackLoop();
    }

    originalPlace.apply(this, args);

    setTimeout(() => {
        isPlacingStructure = false;
        if (rightClickHeld && attackPausedForPlacement) {
            attackPausedForPlacement = false;
            startAttackLoop();
        }
    }, 100);
};

// 🔁 AutoGrind Beta-2
let autoGrindActive = false;
let autoGrindInterval = null;
const myStructures = [];
const STRUCTURE_LIFETIME = 1000;
const STRUCTURE_DISTANCE = 50;
const UP_DIR = -Math.PI / 2;

function customPlaceAndTrack(id, angle) {
    place(id, angle);
    const fakeX = myPlayer.x + Math.cos(angle) * STRUCTURE_DISTANCE;
    const fakeY = myPlayer.y + Math.sin(angle) * STRUCTURE_DISTANCE;
    myStructures.push({ type: id, angle, x: fakeX, y: fakeY, time: Date.now() });
}

function cleanupStructures() {
    const now = Date.now();
    while (myStructures.length > 0 && now - myStructures[0].time > STRUCTURE_LIFETIME) {
        myStructures.shift();
    }
}

function countRecentPlaced(type) {
    const now = Date.now();
    return myStructures.filter(obj =>
        obj.type === type && now - obj.time < STRUCTURE_LIFETIME
    ).length;
}

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

        if (autoGrindActive) {
            console.log("⚙️ AutoGrind ACTIVADO");
            doNewSend(["6", ["AutoGrind ACTIVADO"]]);

            // ⚔️ Equipar TankGear al iniciar
            doNewSend(["c", [0, ID_TankGear, 0]]);

            autoGrindInterval = setInterval(() => {
                if (!turretType || typeof myPlayer.x !== 'number' || typeof myPlayer.y !== 'number') return;

                cleanupStructures();
                emit("D", UP_DIR);

                if (countRecentPlaced(turretType) < 2) {
                    customPlaceAndTrack(turretType, UP_DIR);
                }

                doNewSend(["F", [1]]);
                setTimeout(() => doNewSend(["F", [0]]), 10);

                // ⚔️ Reequipar TankGear por seguridad
                doNewSend(["c", [0, ID_TankGear, 0]]);
                emit("D", UP_DIR);
            }, 120);
        } else {
            console.log("❌ AutoGrind DESACTIVADO");
            doNewSend(["6", ["AutoGrind DESACTIVADO"]]);
            clearInterval(autoGrindInterval);
            autoGrindInterval = null;

            // 🛡 Volver a SoldierHelmet después
            setTimeout(() => {
                doNewSend(["c", [0, ID_SoldierHelmet, 0]]);
            }, 300);
        }
    }
});
// 🎩 AutoBiomeHatController
function autoBiomeHatController() {
    const ID_SoldierHelmet = 6;
    const ID_SnowHelmet = 15;
    const ID_SandHelmet = 31;
    const ID_GrassHelmet = 12;

    let normalHat = ID_GrassHelmet;
    let currentHat = null;
    let overridePause = false;
    let resumeTimeout = null;
    const movementKeys = new Set();
    const overrideKeys = new Set(["r", "t", " "]);

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

    function updateBiomeHat() {
        if (!myPlayer || typeof myPlayer.y !== "number") return;
        if (myPlayer.y < 2400) normalHat = ID_SnowHelmet;
        else if (myPlayer.y > 6850 && myPlayer.y < 7550) normalHat = ID_SandHelmet;
        else normalHat = ID_GrassHelmet;
    }

    function updateHatLogic() {
        if (overridePause) return;
        updateBiomeHat();
        if (movementKeys.size > 0) {
            setHat(normalHat);
        } else {
            setHat(ID_SoldierHelmet);
        }
    }

    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);
                updateHatLogic();
            }
        }
        if (overrideKeys.has(key)) pauseOverride();
    });

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

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

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

    setInterval(() => {
        if (!overridePause) updateHatLogic();
    }, 250); // más reactivo y fluido

    console.log("✅ AutoBiomeHatController V2 activado.");
}

autoBiomeHatController();
})();

(function() {
    'use strict';

    const GRID_ENABLED = false;

    function waitForConfig(callback) {
        if (window.config && window.config.maxScreenWidth && window.config.maxScreenHeight) {
            callback();
        } else {
            setTimeout(() => waitForConfig(callback), 100);
        }
    }

    waitForConfig(() => {
        const maxWidth = window.config.maxScreenWidth;
        const maxHeight = window.config.maxScreenHeight;

        const CELL_SIZE = 50;
        const tolerance = 1.5;

        function isGridLinePair(x1, y1, x2, y2) {
            // Solo bloqueamos líneas completamente horizontales o verticales
            const isStraight = (x1 === x2 || y1 === y2);

            // Solo si ambas coordenadas están cerca de múltiplos del grid
            const isNearGrid = (coord) =>
                (coord % CELL_SIZE <= tolerance) || (CELL_SIZE - (coord % CELL_SIZE) <= tolerance);

            return isStraight && (isNearGrid(x1) || isNearGrid(y1));
        }

        let lastMoveTo = null;

        const originalMoveTo = CanvasRenderingContext2D.prototype.moveTo;
        const originalLineTo = CanvasRenderingContext2D.prototype.lineTo;

        CanvasRenderingContext2D.prototype.moveTo = function(x, y) {
            lastMoveTo = [x, y]; // Guardamos el punto anterior
            return originalMoveTo.call(this, x, y);
        };

        CanvasRenderingContext2D.prototype.lineTo = function(x, y) {
            if (!GRID_ENABLED && lastMoveTo) {
                const [x0, y0] = lastMoveTo;
                if (
                    x >= 0 && x <= maxWidth &&
                    y >= 0 && y <= maxHeight &&
                    isGridLinePair(x0, y0, x, y)
                ) {
                    return this;
                }
            }
            return originalLineTo.call(this, x, y);
        };
    });
})();

(function () {
    'use strict';

    console.log("x-RedDragon Client Visuals Loaded");

    const config = window.config || {};
    config.skinColors = [
        "#bf8f54", "#4c4c4c", "#896c4b",
        "#fadadc", "#ececec", "#c37373",
        "#000000", "#ecaff7", "#738cc3",
        "#8bc373", "#91b2db"
    ];
    window.config = config;

    const css = `
    body {
        background: linear-gradient(135deg, rgba(15, 15, 15, 0.2), rgba(28, 28, 28, 0.2));
        font-family: 'Orbitron', sans-serif;
        color: white;
    }

    #mainMenu {
        background-color: black !important;
        background-size: cover;
        background-position: center;
        background-repeat: no-repeat;
    }

    .menuButton {
        background-color: rgba(0, 0, 0, 0.6);
        border: 2px solid #ff3333;
        color: #ffffff;
        font-family: 'Orbitron', sans-serif;
        font-size: 18px;
        font-weight: bold;
        padding: 12px 28px;
        border-radius: 14px;
        cursor: pointer;
        box-shadow: 0 0 15px #ff0000;
        transition: all 0.4s ease-in-out;
        text-shadow: 0 0 6px #ff0000;
    }

    .menuButton.epic-hover:hover {
        background: linear-gradient(135deg, #ff1111, #ff0000, #cc0000);
        background-size: 300% 300%;
        animation: redPulse 3s infinite ease-in-out;
        box-shadow: 0 0 25px #ff0000, 0 0 50px #ff1111;
        color: #fff;
        transform: scale(1.1);
        border: 3px solid #ffffff;
    }

    @keyframes redPulse {
        0% { background-position: 0% 50%; }
        50% { background-position: 100% 50%; }
        100% { background-position: 0% 50%; }
    }

    #gameName {
        font-size: 80px !important;
        font-weight: bold;
        color: #ff2222 !important;
        text-shadow: 0 0 12px #ff0000, 0 0 30px #ff1111;
        animation: shimmer 3s infinite;
        font-family: 'Courier New', monospace !important;
        position: relative;
        top: -50px;
        text-align: center;
        transition: all 0.5s ease;
    }

    @keyframes shimmer {
        0% { text-shadow: 0 0 12px #ff0000, 0 0 30px #ff1111; }
        50% { text-shadow: 0 0 20px #ff3333, 0 0 40px #ff0000; }
        100% { text-shadow: 0 0 12px #ff0000, 0 0 30px #ff1111; }
    }

    #gameName::after {
        content: "𝕩-ℝ𝕖𝕕𝔻𝕣𝕒𝕘𝕠𝕟 ℂ𝕝𝕚𝕖𝕟𝕥";
        display: block;
        font-size: 1.1em;
        color: #ff4444;
        text-shadow: 0 0 25px #ff1111, 0 0 45px #ff0000;
        animation: glowtext 2s infinite alternate;
        position: relative;
        top: 10px;
        text-align: center;
    }

    @keyframes glowtext {
        0% { opacity: 1; }
        100% { opacity: 0.7; }
    }

    #loadingScreen {
        background: rgba(0, 0, 0, 0.2) !important;
        box-shadow: none !important;
        border: none !important;
    }

    #loadingScreen::before {
        content: "Cargando x-RedDragon Client...";
        font-size: 28px;
        color: #ff2222;
        text-shadow: 0 0 12px #ff0000;
        margin-bottom: 20px;
        animation: pulseText 2s infinite;
    }

    @keyframes pulseText {
        0% { opacity: 1; transform: scale(1); }
        50% { opacity: 0.8; transform: scale(1.05); }
        100% { opacity: 1; transform: scale(1); }
    }

    .menuCard, #bottomText, #storeHolder, #youtuberBtn, #adCard, .setNameContainer, .newsHolder {
        background-color: rgba(20, 20, 20, 0.2);
        padding: 20px;
        border: 2px solid #ff3333;
        box-shadow: 0 0 25px rgba(255, 0, 0, 0.8), 0 0 35px rgba(255, 50, 50, 0.6);
        color: #fff !important;
        animation: borderGlow 4s linear infinite;
        text-align: center;
    }

    @keyframes borderGlow {
        0% { box-shadow: 0 0 12px #ff0000; }
        50% { box-shadow: 0 0 25px #ff3333, 0 0 35px #ff1111; }
        100% { box-shadow: 0 0 12px #ff0000; }
    }

    input, select {
        background-color: rgba(31, 31, 31, 0.2) !important;
        color: #fff !important;
        border: 1px solid #ff3300 !important;
        border-radius: 10px;
        padding: 10px;
        text-align: center;
    }

    #gameUI .joinAlBtn, a {
        animation: 5s infinite linear both normal redRainbow;
    }

    @keyframes redRainbow {
        0% { filter: brightness(1) hue-rotate(0deg); }
        100% { filter: brightness(1.2) hue-rotate(360deg); }
    }

    .resourceDisplay, #killCounter {
        width: 120px;
        margin: 0 auto;
        border: 3px solid #ff2222;
        border-radius: 12px;
        background-color: rgba(50, 0, 0, 0.3);
        color: #fff;
        padding: 8px;
        text-align: center;
        font-size: 14px;
        box-shadow: 0 0 10px #ff0000;
    }

    .uiElement {
        border: 2px solid #ff4444;
        background-color: rgba(40, 0, 0, 0.25);
        padding: 10px;
        color: #fff;
        font-size: 14px;
        text-align: center;
        box-shadow: 0 0 10px #ff1111;
    }

    .actionBarItem {
        border: 3px solid #ff4444;
        border-radius: 50%;
        width: 65px;
        height: 65px;
        background-position: center;
        background-size: 55px 55px;
        background-color: rgba(255, 0, 0, 0.2);
        box-shadow: 0 0 15px #ff0000;
        transition: transform 0.2s ease-in-out;
    }

    .actionBarItem:hover {
        transform: scale(1.1);
        box-shadow: 0 0 18px #ff6666;
        background-color: rgba(255, 70, 70, 0.35);
    }

    #itemInfoHolder {
        position: absolute;
        top: 25px;
        left: 50%;
        transform: translateX(-50%);
        width: 350px;
        background-color: rgba(0, 0, 0, 0.3);
        border: 2px solid #ff3333;
        border-radius: 12px;
        color: white;
        padding: 10px;
        font-size: 14px;
        text-align: center;
        box-shadow: 0 0 15px #ff0000;
    }

    ::-webkit-scrollbar {
        width: 12px;
    }

    ::-webkit-scrollbar-track {
        background: rgba(0,0,0,0.2);
        margin-top: 10px;
        margin-bottom: 10px;
    }

    ::-webkit-scrollbar-thumb {
        background: #ff3333;
        border-radius: 0px;
        box-shadow: 0 0 12px #ff3333;
    }
    `;

    const style = document.createElement('style');
    style.innerText = css;
    document.head.appendChild(style);

    const observer = new MutationObserver(() => {
        const gameName = document.getElementById('gameName');
        if (gameName && gameName.innerText !== '𝕩-ℝ𝕖𝕕𝔻𝕣𝕒𝕘𝕠𝕟 ℂ𝕝𝕚𝕖𝕟𝕥') {
            gameName.innerText = '';
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });

    console.log("x-RedDragon Client Full Customization Applied.");

    const waitForMap = setInterval(() => {
        const map = document.getElementById("mapDisplay");
        if (map) {
            map.style.backgroundImage = "url('http://i.imgur.com/Qllo1mA.png')";
            map.style.backgroundSize = "cover";
            map.style.border = "3px solid #ff3333";
            map.style.boxShadow = "0 0 15px #ff0000";
            map.style.borderRadius = "8px";
            clearInterval(waitForMap);
        }
    }, 1);
})();

(function() {
    'use strict';

    // Cambiar título
    document.title = "𝕩-ℝ𝕖𝕕𝔻𝕣𝕒𝕘𝕠𝕟 ℂ𝕝𝕚𝕖𝕟𝕥";

    // Eliminar favicons antiguos
    const oldIcons = document.querySelectorAll("link[rel*='icon']");
    oldIcons.forEach(icon => icon.remove());

    // Crear y añadir nuevo favicon
    const newFavicon = document.createElement("link");
    newFavicon.rel = "icon";
    newFavicon.type = "image/png";
    newFavicon.href = "https://i.imgur.com/vXgDJSp.png";
    document.head.appendChild(newFavicon);
})();

(function () {
    'use strict';

    // Variables para FPS y Ping
    let frameCount = 0;
    let fps = 0;
    let ping = 0;
    let lastLoop = performance.now();
    let pingTimes = [];

    // Función para obtener el ping real con fetch y promediarlo
    function updatePing() {
        const startTime = performance.now();
        fetch(window.location.href, { method: 'HEAD', cache: "no-store" })
            .then(() => {
                const endTime = performance.now();
                const latency = Math.round(endTime - startTime);
                pingTimes.push(latency);
                if (pingTimes.length > 10) pingTimes.shift();
                ping = Math.round(pingTimes.reduce((a, b) => a + b, 0) / pingTimes.length);
            })
            .catch(() => {
                ping = 0;
            });

        setTimeout(updatePing, 1000); // Actualiza cada 1s
    }
    updatePing();

    // FPS con requestAnimationFrame
    function updateFPS() {
        const now = performance.now();
        frameCount++;
        if (now - lastLoop >= 1000) {
            fps = Math.round(frameCount / ((now - lastLoop) / 1000));
            frameCount = 0;
            lastLoop = now;
        }
        requestAnimationFrame(updateFPS);
    }
    updateFPS();

    // Crear el contador visual con el nuevo diseño Orbitron
    const statsContainer = document.createElement('div');
    statsContainer.id = 'fpsPingDisplay';
    Object.assign(statsContainer.style, {
        position: 'fixed',
        top: '12px',
        left: '50%',
        transform: 'translateX(-50%)',
        fontSize: '22px',
        fontFamily: "'Orbitron', sans-serif",
        color: '#FFFFFF',
        textShadow: '0 0 3px #000000, 1px 1px 0 #000, -1px -1px 0 #000',
        background: 'none',
        padding: '0',
        borderRadius: '0',
        border: 'none',
        boxShadow: 'none',
        zIndex: '0', // Debajo de los botones
        pointerEvents: 'none'
    });
    document.body.appendChild(statsContainer);

    // Actualiza cada 500ms el texto visible
    function updateStatsDisplay() {
        statsContainer.textContent = `FPS: ${fps} | Ping: ${ping}ms`;
        setTimeout(updateStatsDisplay, 500);
    }
    updateStatsDisplay();

    // Acceso desde consola opcional
    window.getStats = function () {
        return { fps, ping };
    };
})();

(function () {
    "use strict";

    let ws;
    const msgpack = window.msgpack;
    WebSocket.prototype._send = WebSocket.prototype.send;
    WebSocket.prototype.send = function (m) {
        if (!ws) {
            ws = this;
            window.ws = this;
        }
        this._send(m);
    };

    let hue = 0;

    function changeStyle() {
        const ageBarBody = document.getElementById("ageBarBody");
        if (ageBarBody) {
            hue = (hue + 2) % 360;
            const rgbColor = `hsl(${hue}, 100%, 50%)`;
            ageBarBody.style.backgroundColor = rgbColor;
            ageBarBody.style.border = `2px solid ${rgbColor}`;
            ageBarBody.style.transform = "translateY(-2px)";
        }
    }

    setInterval(changeStyle, 100);

    const previousReloadStates = new Map();
    const turretCooldown = 2500;
    let turretLastClickTime = null;
    let turretReady = true;

    window.Cow.setCodec(msgpack);
    CanvasRenderingContext2D.prototype._roundRect = CanvasRenderingContext2D.prototype.roundRect;

    window.addEventListener("mousedown", (e) => {
        if (e.button === 1 && turretReady) {
            turretLastClickTime = Date.now();
            turretReady = false;
        }
    });

    window.Cow.addRender("global", () => {
        const ctx = window.Cow.renderer.context;

        window.Cow.playersManager.eachVisible(player => {
            if (!player || !player.alive) return;

            const width = window.config.healthBarWidth / 2 - window.config.healthBarPad / 2;
            const yOffset = player.renderY + player.scale + window.config.nameY - 5;

            const primaryReload = Math.min(Math.max(player.reloads.primary.count / player.reloads.primary.max, 0), 1);
            const secondaryReload = Math.min(Math.max(player.reloads.secondary.count / player.reloads.secondary.max, 0), 1);

            const pad = window.config.healthBarPad;
            const height = 17;
            const radius = 8;

            // Barra primaria
            ctx.save();
            ctx.fillStyle = "#3d3f42";
            ctx.translate(player.renderX - width * 1.19, yOffset);
            ctx.beginPath();
            ctx._roundRect(-width - pad, -8.5, 2 * width + 2 * pad, height, radius);
            ctx.fill();
            ctx.restore();

            ctx.save();
            ctx.fillStyle = player.isAlly ? "#8ecc51" : "#cc5151";
            ctx.translate(player.renderX - width * 1.19, yOffset);
            ctx.beginPath();
            ctx._roundRect(-width, -8.5 + pad, 2 * width * primaryReload, height - 2 * pad, radius - 1);
            ctx.fill();
            ctx.restore();

            // Barra secundaria
            ctx.save();
            ctx.fillStyle = "#3d3f42";
            ctx.translate(player.renderX + width * 1.19, yOffset);
            ctx.beginPath();
            ctx._roundRect(-width - pad, -8.5, 2 * width + 2 * pad, height, radius);
            ctx.fill();
            ctx.restore();

            ctx.save();
            ctx.fillStyle = player.isAlly ? "#8ecc51" : "#cc5151";
            ctx.translate(player.renderX + width * 1.19, yOffset);
            ctx.beginPath();
            ctx._roundRect(-width, -8.5 + pad, 2 * width * secondaryReload, height - 2 * pad, radius - 1);
            ctx.fill();
            ctx.restore();

            // === Contador de HP ===
            const hpCurrent = Math.floor(player.health);
            const hpMax = Math.floor(player.maxHealth || 100);
            const hpText = `HP: ${hpCurrent}/${hpMax}`;

            ctx.save();
            ctx.font = "bold 20px GameFont"; // Usa GameFont, o Arial si no carga
            ctx.textAlign = "center";
            ctx.lineWidth = 3;
            ctx.strokeStyle = "black";
            ctx.fillStyle = "white";

            const textX = player.renderX;
            const textY = yOffset + 32; // 12 píxeles más abajo

            ctx.strokeText(hpText, textX, textY); // Borde negro
            ctx.fillText(hpText, textX, textY);   // Letra blanca
            ctx.restore();
        });
    });
})();

// @require      https://update.greasyfork.org/scripts/480301/1283571/CowJS.js
// @require      https://update.greasyfork.org/scripts/480303/1282926/MooUI.js

(function () {
    "use strict";

    // Asegurarse de que el script se inicie después de que el juego esté completamente cargado
    function init() {
        if (!window.Cow || !window.CowUtils) {
            setTimeout(init, 100);
            return;
        }

        const { Cow, CowUtils } = window;

        const settings = {
            "health-bars": true,
            "circle-bars": false,
            "in-look-dir": false,
            "friendly-color": "#56e319",
            "enemy-color": "#ff3333",
            "hit-counter": true
        };

        // Función para determinar si una construcción pertenece al jugador
        function isOwnBuilding(object) {
            // Verificar si tiene la propiedad de grupo (team)
            if (object && Cow.player) {
                // En MooMoo.io la propiedad group o team indica el equipo
                if (object.group !== undefined && Cow.player.group !== undefined) {
                    return object.group === Cow.player.group;
                }

                // Alternativa: verificar por ownerID o owner.sid
                if (object.owner && Cow.player.sid) {
                    return object.owner.sid === Cow.player.sid;
                }

                // Otra alternativa: comprobar teamID o team
                if (object.team !== undefined && Cow.player.team !== undefined) {
                    return object.team === Cow.player.team;
                }
            }

            // Por defecto si no podemos determinar, asumimos que es enemiga
            return false;
        }

        function drawHealthBar(context, object) {
            const healthBarWidth = 50; // Ancho predeterminado si window.config no está disponible
            const healthBarPad = 5;    // Padding predeterminado si window.config no está disponible

            // Usar configuración del juego si está disponible
            const width = (window.config && window.config.healthBarWidth ? window.config.healthBarWidth / 2 : healthBarWidth / 2) -
                         (window.config && window.config.healthBarPad ? window.config.healthBarPad / 2 : healthBarPad / 2);
            const height = 17;
            const radius = 8;

            // Determinar el color basado en si la construcción es del jugador o enemiga
            const barColor = isOwnBuilding(object) ? settings["friendly-color"] : settings["enemy-color"];

            context.save();
            context.translate(object.renderX || object.x, object.renderY || object.y);

            // Fondo de la barra de vida
            context.fillStyle = "#3d3f42";
            if (context.roundRect) {
                context.roundRect(-width - healthBarPad, -height / 2, 2 * width + 2 * healthBarPad, height, radius);
            } else {
                // Alternativa si roundRect no está disponible
                context.beginPath();
                context.moveTo(-width - healthBarPad + radius, -height / 2);
                context.arcTo(-width - healthBarPad + 2 * width + 2 * healthBarPad, -height / 2, -width - healthBarPad + 2 * width + 2 * healthBarPad, -height / 2 + height, radius);
                context.arcTo(-width - healthBarPad + 2 * width + 2 * healthBarPad, -height / 2 + height, -width - healthBarPad, -height / 2 + height, radius);
                context.arcTo(-width - healthBarPad, -height / 2 + height, -width - healthBarPad, -height / 2, radius);
                context.arcTo(-width - healthBarPad, -height / 2, -width - healthBarPad + radius, -height / 2, radius);
                context.closePath();
            }
            context.fill();

            // Barra de vida con el color según propiedad
            context.fillStyle = barColor;
            const healthRatio = (object.health || object.hp || 100) / (object.maxHealth || object.maxHP || 100);

            if (context.roundRect) {
                context.roundRect(-width, -height / 2 + healthBarPad, 2 * width * healthRatio, height - 2 * healthBarPad, radius - 1);
            } else {
                // Alternativa si roundRect no está disponible
                context.beginPath();
                context.moveTo(-width + (radius - 1), -height / 2 + healthBarPad);
                context.arcTo(-width + 2 * width * healthRatio, -height / 2 + healthBarPad, -width + 2 * width * healthRatio, -height / 2 + height - 2 * healthBarPad, radius - 1);
                context.arcTo(-width + 2 * width * healthRatio, -height / 2 + height - 2 * healthBarPad, -width, -height / 2 + height - 2 * healthBarPad, radius - 1);
                context.arcTo(-width, -height / 2 + height - 2 * healthBarPad, -width, -height / 2 + healthBarPad, radius - 1);
                context.arcTo(-width, -height / 2 + healthBarPad, -width + (radius - 1), -height / 2 + healthBarPad, radius - 1);
                context.closePath();
            }
            context.fill();

            // Mostrar texto de propietario para depuración (quitar en producción)
            /*
            context.fillStyle = "#fff";
            context.font = "10px Arial";
            context.fillText(isOwnBuilding(object) ? "Mío" : "Enemigo", 0, -30);
            if (object.group !== undefined) context.fillText("Group: " + object.group, 0, -42);
            if (object.team !== undefined) context.fillText("Team: " + object.team, 0, -54);
            if (object.owner) context.fillText("Owner: " + (object.owner.sid || "?"), 0, -66);
            */

            context.restore();
        }

        function drawHitCounter(context, object) {
            if (!Cow.player || !Cow.player.weapon) return;

            try {
                const damage = Cow.player.weapon.dmg * (Cow.items.variants[Cow.player.weaponVariant]?.val || 1);
                const damageAmount = damage * (Cow.player.weapon.sDmg || 1) * (Cow.player.skin?.id === 40 ? 3.3 : 1);
                const objectHealth = object.health || object.hp || 100;
                const hits = Math.ceil(objectHealth / damageAmount);
                const offsetY = 22;

                context.save();
                context.font = `18px Hammersmith One`;
                context.fillStyle = "#fff";
                context.textBaseline = "middle";
                context.textAlign = "center";
                context.lineWidth = 8;
                context.lineJoin = "round";
                context.translate(object.renderX || object.x, object.renderY || object.y);
                context.strokeText(hits, 0, offsetY);
                context.fillText(hits, 0, offsetY);
                context.restore();
            } catch (e) {
                console.error("Error en hit counter:", e);
            }
        }

        // Función principal para renderizar las barras de vida
        function renderHealthBars() {
            if (!Cow.player) return;
            const { context } = Cow.renderer;

            try {
                Cow.objectsManager.eachVisible((object) => {
                    // Solo procesar objetos que sean estructuras/items
                    if (!object.isItem && !object.isStructure && !object.buildingType && !object.type) return;

                    // Solo procesar objetos que tengan salud
                    if ((object.health === undefined && object.hp === undefined) ||
                        (object.maxHealth === undefined && object.maxHP === undefined)) return;

                    const angle = CowUtils.getDirection ?
                        CowUtils.getDirection(object, Cow.player) :
                        Math.atan2(object.y - Cow.player.y, object.x - Cow.player.x);

                    // Verificar si está en la dirección de mirada si esta opción está activada
                    if (settings["in-look-dir"] &&
                        CowUtils.getAngleDist &&
                        CowUtils.getAngleDist(angle, Cow.player.lookAngle) > (Cow.config?.gatherAngle || Math.PI/2)) {
                        return;
                    }

                    // Dibujar contador de golpes si está activado
                    if (settings["hit-counter"]) {
                        drawHitCounter(context, object);
                    }

                    // Dibujar barra de vida si está activado
                    if (settings["health-bars"] && !settings["circle-bars"]) {
                        drawHealthBar(context, object);
                    }
                });
            } catch (e) {
                console.error("Error en renderHealthBars:", e);
            }
        }

        // Agregar la función de renderizado al loop del juego
        if (Cow.addRender) {
            Cow.addRender("building-health-bars", renderHealthBars);
        } else {
            // Alternativa si addRender no está disponible: usar requestAnimationFrame
            function renderLoop() {
                try {
                    if (Cow && Cow.player && Cow.renderer && Cow.renderer.context) {
                        renderHealthBars();
                    }
                } catch (e) {
                    console.error("Error en renderLoop:", e);
                }
                requestAnimationFrame(renderLoop);
            }
            requestAnimationFrame(renderLoop);
        }

        console.log("[Building Ownership Health Bars] Script iniciado correctamente");
    }

    // Iniciar el script
    if (document.readyState === "complete") {
        init();
    } else {
        window.addEventListener("load", init);
    }
})();

(function () {
    'use strict';

    let ws = null;
    let { msgpack } = window;
    let playerID = null;
    let myPlayer = {
        id: null, x: null, y: null, dir: null, object: null, weapon: null,
        clan: null, isLeader: null, maxXP: 300, XP: 0, age: 1,
        hat: null, accessory: null, isSkull: null, maxHealth: 100,
        health: 100
    };
    let players = [], enemy = [], nearestEnemy = {};
    let gameCanvas = document.getElementById("gameCanvas");
    let width = window.innerWidth;
    let height = window.innerHeight;
    let mouseX, mouseY;

    const sendPacket = (packet, ...data) => {
        if (ws && ws.readyState === WebSocket.OPEN) {
            ws.send(new Uint8Array(msgpack.encode([packet, data])));
        }
    };

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

    const updateHealth = (health, playerIDCheck) => {
        if (myPlayer.id === playerIDCheck) {
            myPlayer.health = health;
            console.log("[🩸 Salud actualizada]", health);
        }
    };

    const handleMessage = (message) => {
        const decoded = msgpack.decode(new Uint8Array(message.data));
        const data = Array.isArray(decoded) && decoded.length > 1 ? [decoded[0], ...decoded[1]] : decoded;
        if (!data) return;

        const type = data[0];
        if (type === "C" && myPlayer.id == null) {
            myPlayer.id = data[1];
            console.log("[✔️ Verificación] ID del jugador:", myPlayer.id);
        }
        if (type === "a") updatePlayers(data);
        if (type === "O") updateHealth(data[2], data[1]);
    };

    const socketFound = (sock) => {
        sock.addEventListener("message", handleMessage);
        if (gameCanvas) {
            gameCanvas.addEventListener("mousemove", ({ x, y }) => {
                mouseX = x;
                mouseY = y;
            });
        }
        window.addEventListener("resize", () => {
            width = window.innerWidth;
            height = window.innerHeight;
        });
    };

    WebSocket.prototype.oldSend = WebSocket.prototype.send;
    WebSocket.prototype.send = function (m) {
        if (!ws) {
            ws = this;
            document.websocket = this;
            socketFound(this);
            console.log("[🔌 WebSocket] Interceptado correctamente.");
        }
        this.oldSend(m);
    };

    // Anti-Altcha (captcha visual)
    const altchaCheck = setInterval(() => {
        let altcha = document.getElementById('altcha');
        let altchaBox = document.getElementById('altcha_checkbox');
        if (altcha && altchaBox) {
            altcha.style.display = 'none';
            altchaBox.checked = true;
            altchaBox.click();
            clearInterval(altchaCheck);
            console.log("[🚫 Altcha] Eliminado.");
        }
    }, 500);
})();

// AutoReload
(function() {
    "use strict";

    // Función para retrasar la ejecución (sleep)
    function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    // Recarga la página después de 1.5 segundos
    async function refreshPage() {
        await delay(1500);
        window.onbeforeunload = null;
        location.reload();
    }

    // Crea un hook (interceptor) para la propiedad indicada en el objeto target
    function interceptProperty(target, propName, onSetCallback) {
        const hiddenKey = Symbol(propName);
        Object.defineProperty(target, propName, {
            get() {
                return this[hiddenKey];
            },
            set(value) {
                onSetCallback(this, hiddenKey, value);
            },
            configurable: true
        });
    }

    // Intercepta una función para envolverla y modificar su comportamiento
    function wrapFunction(originalFunc, wrapper) {
        return new Proxy(originalFunc, {
            apply(target, thisArg, args) {
                return wrapper.call(thisArg, target, args);
            }
        });
    }

    // Hook para capturar cualquier asignación a "errorCallback" en Object.prototype
    interceptProperty(Object.prototype, "errorCallback", (obj, key, val) => {
        obj[key] = val;

        if (typeof val !== "function") return;

        // Reemplaza la función original con un Proxy que recarga la página
        obj[key] = wrapFunction(val, (target, args) => {
            // Anula alert para evitar alertas molestas
            window.alert = () => {};
            // Recarga la página
            refreshPage();
            // Ejecuta la función original
            return target.apply(this, args);
        });
    });

    // Intercepta setters de eventos de WebSocket (onclose y onerror)
    ["onclose", "onerror"].forEach(eventName => {
        const descriptor = Object.getOwnPropertyDescriptor(WebSocket.prototype, eventName);
        if (!descriptor || !descriptor.set) return;

        Object.defineProperty(WebSocket.prototype, eventName, {
            set(handler) {
                // Envuelve el handler original para recargar la página al dispararse
                const wrappedHandler = wrapFunction(handler, (target, args) => {
                    refreshPage();
                    return target.apply(this, args);
                });
                descriptor.set.call(this, wrappedHandler);
            }
        });
    });

})();

//esp

localStorage.setItem("moofoll", true);

const players = [];
const property = "team";

Object.defineProperty(Object.prototype, property, {
    get: function() {
        if (this.isPlayer && !players.find(p => p.id === this.id)) {
            players.push(this);
        }
        return this[`_${property}`];
    },
    set: function(value) {
        this[`_${property}`] = value;
    },
    configurable: true
});

let ctx, owner, camX = 0, camY = 0;
const conf = {
    radar: {
        colorEnemy: "#ff0000",
        colorAlly: "#00ff00",
        w: 20,
        h: 20
    },
    maxScreenWidth: 1920,
    maxScreenHeight: 1080
};

// Crear flecha detrás de #mainMenu
const createArrow = (id) => {
    const el = document.createElement("div");
    el.id = `radarArrow-${id}`;
    el.style.position = "absolute";
    el.style.width = "0";
    el.style.height = "0";
    el.style.borderStyle = "solid";
    el.style.borderWidth = `10px 0 10px ${conf.radar.w}px`;
    el.style.borderColor = `transparent transparent transparent ${conf.radar.colorEnemy}`;
    el.style.display = "none";
    el.style.zIndex = 50;

    const menu = document.getElementById("mainMenu");
    if (menu && menu.parentNode) {
        menu.parentNode.insertBefore(el, menu);
    } else {
        document.body.appendChild(el);
    }
};

const updateArrow = (id, x, y, color = conf.radar.colorEnemy, opacity = 1, angle = 0) => {
    const arrow = document.getElementById(`radarArrow-${id}`);
    if (!arrow) return;
    arrow.style.left = `${x}px`;
    arrow.style.top = `${y}px`;
    arrow.style.display = "block";
    arrow.style.opacity = opacity;
    arrow.style.transform = `rotate(${angle}deg)`;
    arrow.style.borderColor = `transparent transparent transparent ${color}`;
};

const removeArrow = (id) => {
    const arrow = document.getElementById(`radarArrow-${id}`);
    if (arrow) arrow.remove();
};

const getDistance = (x1, y1, x2, y2) => Math.hypot(x2 - x1, y2 - y1);
const getDirection = (x1, y1, x2, y2) => Math.atan2(y2 - y1, x2 - x1);
const RtoD = (r) => r * 180 / Math.PI;

// Función principal del radar
const radar = () => {
    if (!ctx) {
        const gameCanvas = document.getElementById("gameCanvas");
        if (!gameCanvas) return;
        ctx = gameCanvas.getContext("2d");
    }

    owner = players.find(p => p.visible && p.wood);
    if (!owner) return;

    const centerX = window.innerWidth / 2;
    const centerY = window.innerHeight / 2;

    camX += ((owner.x - camX) * 0.1);
    camY += ((owner.y - camY) * 0.1);

    // Eliminación inmediata de jugadores inválidos
    for (let i = players.length - 1; i >= 0; i--) {
        const p = players[i];
        if (!p.visible || typeof p.x !== "number" || typeof p.y !== "number") {
            removeArrow(p.id);
            players.splice(i, 1);
        }
    }

    const menu = document.getElementById("mainMenu");
    const menuVisible = menu && getComputedStyle(menu).display !== "none";

    for (let player of players) {
        if (player.id === owner.id) continue;

        const isAlly = player.team && player.team === owner.team;
        const color = isAlly ? conf.radar.colorAlly : conf.radar.colorEnemy;
        const arrowId = player.id;

        let rad = getDirection(owner.x, owner.y, player.x, player.y);
        let per = getDistance(0, 0, (owner.x - player.x), (owner.y - player.y) * (16 / 9)) * 100 / (conf.maxScreenHeight / 2);
        let alpha = Math.min(per / centerY, 1);
        let dis = centerY * alpha;

        let tx = centerX + dis * Math.cos(rad) - conf.radar.w / 2;
        let ty = centerY + dis * Math.sin(rad) - conf.radar.h / 2;

        if (!document.getElementById(`radarArrow-${arrowId}`)) {
            createArrow(arrowId);
        }

        if (!menuVisible) {
            updateArrow(arrowId, tx, ty, color, alpha, RtoD(rad));
        } else {
            removeArrow(arrowId);
        }
    }
};

let lastTime = Date.now();
function gameLoop() {
    requestAnimationFrame(gameLoop);
    radar();
}
gameLoop();

// Intervalo corto para ocultar flechas inmediatamente cuando el menú se muestra
setInterval(() => {
    const menu = document.getElementById("mainMenu");
    if (!menu) return;
    if (getComputedStyle(menu).display !== "none") {
        document.querySelectorAll("[id^=radarArrow-]").forEach(a => a.style.display = "none");
    }
}, 50);

(function () {
    'use strict';

    // Crear e insertar estilos CSS exclusivos del menú
const style = document.createElement('style');
style.textContent = `
    #moddedMenu {
        position: fixed;
        top: 10px;
        left: 10px;
        width: 300px;
        background: rgba(0, 0, 0, 0.95);
        border: 4px solid #ff0000;
        border-radius: 15px;
        z-index: 9999;
        color: white;
        font-family: monospace;
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 20px;
        box-sizing: border-box;
        opacity: 0;
        transform: scale(0.5);
        transition: opacity 0.3s ease, transform 0.3s ease;
        pointer-events: none;
    }

    #moddedMenu.open {
        opacity: 1;
        transform: scale(1);
        pointer-events: auto;
    }

    #moddedMenuContent {
        width: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
    }

    #menuTitle {
        font-size: 1.6em;
        color: #ff0000;
        text-align: center;
        margin-bottom: 20px;
    }

    .rd-select, .rd-button {
        font-family: monospace;
        width: 100%;
        max-width: 90%;
        font-size: 1em;
        padding: 8px;
        margin-bottom: 10px;
        border: 1px solid #ff0000;
        border-radius: 5px;
        background: #111;
        color: #ff0000;
        outline: none;
    }

    .rd-button {
        cursor: pointer;
        font-weight: bold;
        transition: background-color 0.3s ease;
    }

    .rd-button:hover {
        background-color: #cc0000;
    }

    #buttons {
        display: flex;
        justify-content: space-between;
        width: 90%;
        gap: 10px;
    }
`;
document.head.appendChild(style);

// Crear el menú
    const musicTracks = [
        { name: "Alan Walker - Faded", url: "https://files.catbox.moe/p9om0y.mp3" },
        { name: "Alan Walker - Alone", url: "https://files.catbox.moe/xptxat.mp3" },
        { name: "TheFatRat - Unity", url: "https://files.catbox.moe/cx5uyo.mp3" },
        { name: "TheFatRat - Monody", url: "https://files.catbox.moe/7fwpbl.mp3" },
        { name: "NEFFEX - Fight Back", url: "https://files.catbox.moe/iif5rx.mp3" },
        { name: "NEFFEX - Never Give Up", url: "https://files.catbox.moe/2pamid.mp3" },
        { name: "Alan Walker - Dust", url: "https://files.catbox.moe/w1sb9x.mp3" },
        { name: "Alan Walker - Catch Me", url: "https://files.catbox.moe/74b06c.mp3" },
        { name: "Alan Walker - Last Song", url: "https://files.catbox.moe/nuta1v.mp3" },
        { name: "Egzod - Royalty ft. Neoni", url: "https://files.catbox.moe/zlfc04.mp3" }
    ];

const menu = document.createElement('div');
menu.id = 'moddedMenu';

const content = document.createElement('div');
content.id = 'moddedMenuContent';

const title = document.createElement('div');
title.id = 'menuTitle';
title.textContent = '𝕸𝖚𝖘𝖎𝖈';

const musicSelect = document.createElement('select');
musicSelect.className = 'rd-select';
musicTracks.forEach(track => {
    const option = document.createElement('option');
    option.value = track.url;
    option.textContent = track.name;
    musicSelect.appendChild(option);
});

const buttonsDiv = document.createElement('div');
buttonsDiv.id = 'buttons';

const playBtn = document.createElement('button');
playBtn.className = 'rd-button';
playBtn.textContent = '▶ Play';

const stopBtn = document.createElement('button');
stopBtn.className = 'rd-button';
stopBtn.textContent = '■ Stop';

buttonsDiv.appendChild(playBtn);
buttonsDiv.appendChild(stopBtn);

const modeSelect = document.createElement('select');
modeSelect.className = 'rd-select';
const repeatOption = document.createElement('option');
repeatOption.value = 'repeat';
repeatOption.textContent = '🔁 Repeat Current';

const nextOption = document.createElement('option');
nextOption.value = 'next';
nextOption.textContent = '⏭️ Play Next';

modeSelect.appendChild(repeatOption);
modeSelect.appendChild(nextOption);

const audio = document.createElement('audio');
audio.id = 'audioPlayer';

// Lógica de reproducción
function playMusic() {
    audio.src = musicSelect.value;
    audio.play();
}

playBtn.addEventListener('click', () => {
    if (audio.src !== musicSelect.value) {
        audio.src = musicSelect.value;
    }
    audio.play();
});

stopBtn.addEventListener('click', () => {
    audio.pause();
    audio.currentTime = 0;
});

musicSelect.addEventListener('change', () => {
    if (!audio.paused) playMusic();
});

audio.addEventListener('ended', () => {
    if (modeSelect.value === 'repeat') {
        audio.currentTime = 0;
        audio.play();
    } else if (modeSelect.value === 'next') {
        const currentIndex = musicSelect.selectedIndex;
        const nextIndex = (currentIndex + 1) % musicSelect.options.length;
        musicSelect.selectedIndex = nextIndex;
        playMusic();
    }
});

content.appendChild(title);
content.appendChild(musicSelect);
content.appendChild(buttonsDiv);
content.appendChild(modeSelect);
content.appendChild(audio);

menu.appendChild(content);
document.body.appendChild(menu);

// Tecla Escape para abrir/cerrar menú con animación
document.addEventListener('keydown', (e) => {
    if (e.key === 'Escape') {
        menu.classList.toggle('open');
    }
});

})();