Hypr Astra

MOD EVOWARS.IO VIP BY nthch

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

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.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         Hypr Astra
// @namespace    http://tampermonkey.net/
// @version      3.6.0
// @description  MOD EVOWARS.IO VIP BY nthch
// @author       nthch
// @match        https://evowars.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=evowars.io
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    let isAuthenticated = true;
    const WEAPON_STATS = {
            0: {
                distance: 200,
                degrees: 125
            },
            2: {
                distance: 235,
                degrees: 90
            },
            3: {
                distance: 245,
                degrees: 125
            },
            4: {
                distance: 260,
                degrees: 125
            },
            5: {
                distance: 300,
                degrees: 133
            },
            6: {
                distance: 340,
                degrees: 125
            },
            7: {
                distance: 380,
                degrees: 131
            },
            8: {
                distance: 343,
                degrees: 130
            },
            9: {
                distance: 350,
                degrees: 125
            },
            10: {
                distance: 470,
                degrees: 133
            },
            11: {
                distance: 510,
                degrees: 129
            },
            12: {
                distance: 520,
                degrees: 133
            },
            13: {
                distance: 555,
                degrees: 134
            },
            14: {
                distance: 595,
                degrees: 125
            },
            15: {
                distance: 650,
                degrees: 129
            },
            16: {
                distance: 655,
                degrees: 131
            },
            17: {
                distance: 660,
                degrees: 125
            },
            18: {
                distance: 695,
                degrees: 125
            },
            19: {
                distance: 690,
                degrees: 125
            },
            20: {
                distance: 710,
                degrees: 130
            },
            21: {
                distance: 775,
                degrees: 130
            },
            22: {
                distance: 805,
                degrees: 136
            },
            23: {
                distance: 680,
                degrees: 122
            },
            24: {
                distance: 870,
                degrees: 125
            },
            25: {
                distance: 940,
                degrees: 137
            },
            26: {
                distance: 975,
                degrees: 130
            },
            27: {
                distance: 1050,
                degrees: 125
            },
            28: {
                distance: 1095,
                degrees: 125
            },
            29: {
                distance: 1000,
                degrees: 135
            },
            30: {
                distance: 995,
                degrees: 125
            },
            31: {
                distance: 1050,
                degrees: 130
            },
            32: {
                distance: 1145,
                degrees: 134
            },
            33: {
                distance: 1120,
                degrees: 139
            },
            34: {
                distance: 1125,
                degrees: 124
            },
            35: {
                distance: 1145,
                degrees: 135
            },
            36: {
                distance: 1250,
                degrees: 122
            },
            37: {
                distance: 1300,
                degrees: 125
            },
            38: {
                distance: 1300,
                degrees: 125
            },
            39: {
                distance: 1300,
                degrees: 125
            }
        },
        getWeaponStats = level => WEAPON_STATS[level] || {
            distance: Math.max(200, level * 30),
            degrees: 125
        },
        ARC_MULTIPLIERS = {
            1: 0.6,
            2: 0.75,
            3: 0.7,
            4: 0.75,
            5: 0.8,
            6: 0.75,
            7: 0.76,
            8: 0.75,
            9: 0.9,
            10: 0.95,
            11: 0.8,
            12: 0.75,
            13: 0.75,
            14: 0.8,
            15: 0.8,
            16: 0.7,
            17: 0.75,
            18: 0.8,
            19: 0.8,
            20: 0.85,
            21: 0.85,
            22: 0.81,
            23: 0.82,
            24: 1.05,
            25: 0.85,
            26: 0.8,
            27: 0.85,
            28: 0.78,
            29: 0.7,
            30: 0.8,
            31: 0.85,
            32: 0.85,
            33: 0.8,
            34: 0.8,
            35: 0.83,
            36: 0.9,
            37: 0.85,
            38: 0.9,
            39: 0.95,
            40: 0.91,
            41: 1.06
        },
        getArcMultiplier = (level, defaultMul) => ARC_MULTIPLIERS[Math.floor(level) + 1] || defaultMul;
    window.ultimate_ghostAngle = null, window.ultimate_angleLockTime = 0;
    const styleElement = document.createElement("style");
    styleElement.innerHTML = "\n        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap');\n        #evo-lock-screen { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: linear-gradient(135deg, #0f0c20 0%, #15102a 50%, #060409 100%); z-index: 2147483646; display: flex; align-items: center; justify-content: center; pointer-events: all; }\n        #evo-key-panel { width: 420px; background: rgba(12, 10, 18, 0.96); border: 2px solid #3b82f6; border-radius: 20px; font-family: 'Inter', sans-serif; padding: 30px; box-shadow: 0 0 40px rgba(59, 130, 246, 0.6); color: #fff; text-align: center; z-index: 2147483647; }\n        #evo-key-input { width: 100%; padding: 14px; margin: 20px 0; background: #161421; border: 1px solid rgba(59, 130, 246, 0.4); border-radius: 10px; color: #fff; font-size: 15px; text-align: center; outline: none; box-sizing: border-box; font-weight: 600; letter-spacing: 0.5px; }\n        #evo-key-input:focus { border-color: #3b82f6; box-shadow: 0 0 10px rgba(59,130,246,0.5); }\n        .evo-key-btn { width: 48%; padding: 12px; border-radius: 10px; font-weight: bold; font-size: 13px; cursor: pointer; border: none; margin: 5px 1%; transition: 0.2s; }\n        #evo-btn-submit { background: #3b82f6; color: white; } #evo-btn-submit:hover { background: #2563eb; box-shadow: 0 0 12px rgba(59,130,246,0.6); }\n        #evo-btn-getkey { background: #221f36; color: #60a5fa; border: 1px solid rgba(59, 130, 246, 0.4); } #evo-btn-getkey:hover { background: #2d294a; }\n        #evo-btn-tele { background: #0088cc; color: white; box-shadow: 0 4px 15px rgba(0, 136, 204, 0.3); width: 100%; margin: 0; transition: 0.2s; }\n        #evo-btn-tele:hover { background: #007bb3; box-shadow: 0 0 15px rgba(0, 136, 204, 0.6); transform: scale(1.02); }\n        #evo-key-msg { font-size: 13px; margin-top: 15px; font-weight: 600; min-height: 20px; line-height: 1.4; }\n        #evo-toggle-btn { display: none !important; }\n        #evo-menu { position: fixed; bottom: 15px; left: 50%; transform: translateX(-50%); width: 700px; height: 460px; background: rgba(12, 18, 34, 0.65); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border-radius: 16px; font-family: 'Inter', sans-serif; color: #cbd5e1; z-index: 99998; box-shadow: 0 20px 50px rgba(0,0,0,0.6), inset 0 1px 1px rgba(255, 255, 255, 0.2); border: 1px solid rgba(255, 255, 255, 0.15); display: flex; flex-direction: column; overflow: hidden; min-width: 550px; display: none; user-select: none; }\n        #evo-menu::-webkit-scrollbar { width: 6px; height: 6px; } #evo-menu::-webkit-scrollbar-thumb { background: rgba(59, 130, 246, 0.6); border-radius: 4px; }\n        #evo-header { height: 48px; min-height: 48px; padding: 0 20px; font-size: 15px; font-weight: 700; color: #fff; border-bottom: 1px solid rgba(255, 255, 255, 0.12); cursor: default; display: flex; align-items: center; justify-content: space-between; background: linear-gradient(90deg, rgba(30, 58, 138, 0.6), rgba(15, 23, 42, 0.6)); position: sticky; top: 0; z-index: 2; user-select: none; border-radius: 16px 16px 0 0; }\n        .evo-body { display: flex; padding: 20px; gap: 20px; flex-wrap: wrap; overflow-y: auto; height: calc(100% - 48px); } .evo-col { flex: 1; min-width: 220px; display: flex; flex-direction: column; gap: 15px; }\n        .section-title { font-size: 11px; font-weight: 800; letter-spacing: 1.5px; color: #38bdf8; margin-bottom: 6px; text-transform: uppercase; }\n        .section { background: rgba(255, 255, 255, 0.04); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border-radius: 12px; padding: 15px; display: flex; flex-direction: column; gap: 12px; border: 1px solid rgba(255, 255, 255, 0.08); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); }\n        .row { display: flex; justify-content: space-between; align-items: center; font-size: 13px; color: #f1f5f9; font-weight: 500; }\n        .switch { position: relative; width: 36px; height: 20px; flex-shrink: 0;} .switch input { opacity: 0; width: 0; height: 0; }\n        .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background: rgba(255,255,255,0.15); transition: .2s; border-radius: 20px; border: 1px solid rgba(255,255,255,0.1); } .slider:before { position: absolute; content: \"\"; height: 14px; width: 14px; left: 2px; bottom: 2px; background: #fff; transition: .2s; border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,0.3); }\n        input:checked + .slider { background: #3b82f6; border-color: #60a5fa; box-shadow: 0 0 12px rgba(59, 130, 246, 0.6); } input:checked + .slider:before { transform: translateX(16px); }\n        .range-wrap { display: flex; align-items: center; gap: 10px; width: 60%; } input[type=range] { -webkit-appearance: none; width: 100%; height: 6px; background: rgba(255,255,255,0.15); border-radius: 3px; outline: none; } input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%; background: #3b82f6; cursor: pointer; box-shadow: 0 0 8px rgba(59,130,246,0.8); border: 1px solid #60a5fa; }\n        .val-disp { color: #38bdf8; font-size: 12px; font-weight: bold; min-width: 35px; text-align: right; }\n        #evo-expire-warning { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(225, 29, 72, 0.95); border: 2px solid #fda4af; color: #fff; padding: 12px 25px; border-radius: 10px; font-family: 'Inter', sans-serif; font-size: 14px; font-weight: 800; z-index: 2147483647; box-shadow: 0 0 30px rgba(225, 29, 72, 0.8); display: none; pointer-events: none; transition: opacity 0.5s; text-align: center; text-transform: uppercase; letter-spacing: 1px; }\n        #evo-kick-screen { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(5, 3, 10, 0.98); z-index: 2147483647; display: flex; flex-direction: column; align-items: center; justify-content: center; font-family: 'Inter', sans-serif; display: none; pointer-events: all; }\n        .kick-btn { margin-top: 25px; padding: 14px 30px; background: #3b82f6; color: #fff; border: none; border-radius: 10px; cursor: pointer; font-weight: 800; font-size: 15px; transition: 0.2s; text-transform: uppercase; }\n        .kick-btn:hover { background: #2563eb; transform: scale(1.05); box-shadow: 0 0 20px rgba(59,130,246,0.6); }\n        #evo-lang-btn { cursor: pointer; font-size: 11px; font-weight: 600; border: 1px solid rgba(59, 130, 246, 0.5); background: rgba(59, 130, 246, 0.2); padding: 3px 8px; border-radius: 6px; color: #fff; transition: 0.2s; }\n        #evo-lang-btn:hover { background: rgba(59, 130, 246, 0.4); box-shadow: 0 0 10px rgba(59,130,246,0.5); }\n    ";
    document.head.appendChild(styleElement);
    const MENU_HTML = "\n        <div id=\"evo-menu\">\n            <div id=\"evo-header\">\n                <span data-i18n=\"title\">Hypr Astra by nthch</span>\n                <span style=\"display:flex; gap:10px; align-items:center;\">\n                    <span id=\"evo-lang-btn\">VN</span>\n                </span>\n            </div>\n            <div class=\"evo-body\">\n                <div class=\"evo-col\">\n                    <div>\n                        <div class=\"section-title\" data-i18n=\"visuals\">VISUALS (ESP)</div>\n                        <div class=\"section\">\n                            <div class=\"row\"><span data-i18n=\"visual_cd\">Visual CD Timer</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-cooldown\" checked><span class=\"slider\"></span></label></div>\n                            <div class=\"row\"><span data-i18n=\"blaze\">Blaze Perfect Arc</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-player-arc\" checked><span class=\"slider\"></span></label></div>\n                            <div class=\"row\"><span data-i18n=\"lines\">Draw Target Lines</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-lines\" checked><span class=\"slider\"></span></label></div>\n                            <div class=\"row\"><span data-i18n=\"hitboxes\">Show Hitboxes & Level</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-hitboxes\" checked><span class=\"slider\"></span></label></div>\n                        </div>\n                    </div>\n                    <div>\n                        <div class=\"section-title\" data-i18n=\"utility\">MODIFIERS & UTILITY</div>\n                        <div class=\"section\">\n                            <div class=\"row\"><span data-i18n=\"zoom\">Camera Zoom</span><div class=\"range-wrap\"><input type=\"range\" id=\"c-zoom\" min=\"0.1\" max=\"4\" step=\"0.05\" value=\"1\"><div class=\"val-disp\" id=\"v-zoom\">1.00</div></div></div>\n                        </div>\n                    </div>\n                </div>\n                <div class=\"evo-col\">\n                    <div>\n                        <div class=\"section-title\" data-i18n=\"combat\">COMBAT AUTOMATION</div>\n                        <div class=\"section\">\n                            <div class=\"row\" style=\"color: #f43f5e;\"><span data-i18n=\"filter\">Filter Low Lv (E)</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-filter\" checked><span class=\"slider\"></span></label></div>\n                            <div class=\"row\" style=\"color: #fbbf24;\"><span data-i18n=\"punish\">True AI Punish</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-punish\"><span class=\"slider\"></span></label></div>\n                            <div class=\"row\" style=\"color: #34d399;\"><span data-i18n=\"predator\">Predator Aim</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-blaze-aim\" checked disabled><span class=\"slider\"></span></label></div>\n                            <div class=\"row\"><span data-i18n=\"dodge\">Auto-Dodge</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-dodge\"><span class=\"slider\"></span></label></div>\n                            <div class=\"row\"><span data-i18n=\"sprint\">Auto-Sprint</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-sprint\"><span class=\"slider\"></span></label></div>\n                            <div class=\"row\"><span data-i18n=\"hunt\">Auto-Hunt</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-hunt\"><span class=\"slider\"></span></label></div>\n                            <div class=\"row\"><span data-i18n=\"attack\">Auto-Attack</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-attack\"><span class=\"slider\"></span></label></div>\n                            <div class=\"row\" style=\"color: #a78bfa;\"><span data-i18n=\"teamMode\">Team Mode (FFA off)</span><label class=\"switch\"><input type=\"checkbox\" id=\"c-teamMode\"><span class=\"slider\"></span></label></div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    ";
    const menuContainer = document.createElement("div");
    menuContainer.innerHTML = MENU_HTML;
    document.body.appendChild(menuContainer);
    const menuElement = document.getElementById("evo-menu");
    let menuVisible = false;
    const toggleMenu = () => {
        if (!isAuthenticated) return;
        menuVisible = !menuVisible, menuElement.style.display = menuVisible ? "flex" : "none";
    };
    const config = {
            filter: true,
            lines: true,
            hitboxes: true,
            cooldown: true,
            playerArc: true,
            punish: false,
            dodge: false,
            sprint: false,
            hunt: false,
            attack: false,
            zoom: 1,
            lvlMin: 0,
            lvlMax: 200,
            reachMul: 0.85,
            teamMode: false   // Thêm tùy chọn team mode, mặc định FFA (false)
        },
        uiRefs = {
            menuCheckboxes: {},
            panelButtons: {}
        },
        syncUI = () => {
            for (let key in uiRefs.menuCheckboxes) {
                if (uiRefs.menuCheckboxes[key]) uiRefs.menuCheckboxes[key].checked = config[key];
            }
            for (let key2 in uiRefs.panelButtons) {
                if (uiRefs.panelButtons[key2]) uiRefs.panelButtons[key2]();
            }
        },
        TRANSLATIONS = {
            vi: {
                title: "Hypr Astra by nthch",
                visuals: "HIỂN THỊ (ESP)",
                visual_cd: "Đếm ngược CD",
                blaze: "Cung Blaze",
                lines: "Đường mục tiêu",
                hitboxes: "Khung hitbox",
                utility: "MODIFIER & TIỆN ÍCH",
                zoom: "Thu phóng Camera",
                combat: "TỰ ĐỘNG CHIẾN ĐẤU",
                filter: "Lọc cấp thấp",
                punish: "Tích hợp AI",
                predator: "Ngắm bắn Predator",
                dodge: "Tự né (Dodge)",
                sprint: "Tự tăng tốc (Sprint)",
                hunt: "Tự săn (Hunt)",
                attack: "Tự chém (Attack)",
                p_filter: "Lọc cấp thấp (E)",
                p_punish: "Tích hợp AI (P)",
                p_dodge: "Tự né (W)",
                p_attack: "Tự chém (C)",
                p_hunt: "Tự săn (V)",
                p_sprint: "Tự tăng tốc (B)",
                teamMode: "Chế độ đội (tắt FFA)"
            },
            en: {
                title: "Hypr Astra by nthch",
                visuals: "VISUALS (ESP)",
                visual_cd: "Visual CD Timer",
                blaze: "Blaze Perfect Arc",
                lines: "Draw Target Lines",
                hitboxes: "Show Hitboxes",
                utility: "MODIFIERS & UTILITY",
                zoom: "Camera Zoom",
                combat: "COMBAT AUTOMATION",
                filter: "Filter Low Lv (E)",
                punish: "True AI Punish",
                predator: "Predator Aim",
                dodge: "Auto-Dodge",
                sprint: "Auto-Sprint",
                hunt: "Auto-Hunt",
                attack: "Auto-Attack",
                p_filter: "Filter Low Lv (E)",
                p_punish: "True AI Punish (P)",
                p_dodge: "Auto-Dodge (W)",
                p_attack: "Ghost Hit (C)",
                p_hunt: "Auto-Hunt (V)",
                p_sprint: "Auto-Sprint (B)",
                teamMode: "Team Mode (FFA off)"
            }
        };
    config.lang = config.lang || "vi";
    const saveConfig = () => localStorage.setItem("evo_ult_cfg_v7", JSON.stringify(config)),
        loadConfig = () => {
            const saved = localStorage.getItem("evo_ult_cfg_v7");
            if (saved) try {
                Object.assign(config, JSON.parse(saved));
            } catch (err) {}
        };
    loadConfig();
    const bindCheckbox = (elId, configKey) => {
        const el = document.getElementById(elId);
        el && (el.checked = config[configKey], uiRefs.menuCheckboxes[configKey] = el, el.addEventListener("change", ev => {
            config[configKey] = ev.target.checked, saveConfig(), syncUI();
        }));
    };
    bindCheckbox("c-filter", "filter");
    bindCheckbox("c-lines", "lines");
    bindCheckbox("c-hitboxes", "hitboxes");
    bindCheckbox("c-cooldown", "cooldown");
    bindCheckbox("c-player-arc", "playerArc");
    bindCheckbox("c-punish", "punish");
    bindCheckbox("c-dodge", "dodge");
    bindCheckbox("c-sprint", "sprint");
    bindCheckbox("c-hunt", "hunt");
    bindCheckbox("c-attack", "attack");
    bindCheckbox("c-teamMode", "teamMode");  // Gán checkbox mới

    const bindSlider = (sliderId, dispId, configKey, showVal = true, suffix = "") => {
        const slider = document.getElementById(sliderId),
            disp = document.getElementById(dispId);
        slider.value = config[configKey], disp.textContent = (showVal ? config[configKey].toFixed(2) : config[configKey]) + suffix, slider.addEventListener("input", ev => {
            config[configKey] = showVal ? parseFloat(ev.target.value) : parseInt(ev.target.value), disp.textContent = (showVal ? config[configKey].toFixed(2) : config[configKey]) + suffix, saveConfig();
        });
    };
    bindSlider("c-zoom", "v-zoom", "zoom");
    let runtime = null,
        playerType = null,
        gameCanvas = null,
        mouseInstance = null,
        isAimOverriding = false,
        aimX = 0,
        aimY = 0,
        lastAttackTime = 0,
        isRightClickHeld = false,
        lastRightClickTime = 0,
        centerX = window.innerWidth / 2,
        centerY = window.innerHeight / 2;
    const enemyTracker = new Map();
    document.addEventListener("mousemove", e => {
        e.isTrusted && (centerX = e.clientX, centerY = e.clientY);
    }), document.addEventListener("contextmenu", e => {
        if (e.target === gameCanvas) e.preventDefault();
    });
    const overlayCanvas = document.createElement("canvas"),
        ctx = overlayCanvas.getContext("2d");
    document.body.appendChild(overlayCanvas), overlayCanvas.style.cssText = "position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9997;", window.addEventListener("resize", () => {
        overlayCanvas.width = window.innerWidth, overlayCanvas.height = window.innerHeight;
    }), window.dispatchEvent(new Event("resize"));
    const getLevel = inst => inst.instance_vars && inst.instance_vars.length > 10 ? inst.instance_vars[10] : 0,
        getHitboxPolygon = obj => {
            if (obj.bbox_changed && obj.update_bbox) obj.update_bbox();
            if (obj.collision_poly && typeof obj.collision_poly.is_empty === "function" && !obj.collision_poly.is_empty()) {
                obj.collision_poly.cache_poly(obj.width, obj.height, obj.angle);
                const points = [],
                    ptsCache = obj.collision_poly.pts_cache;
                for (let i = 0; i < obj.collision_poly.pts_count; i++) points.push({
                    x: ptsCache[i * 2] + obj.x,
                    y: ptsCache[i * 2 + 1] + obj.y
                });
                return points;
            }
            if (obj.bquad) {
                const bquad = obj.bquad;
                return [{
                    x: bquad.tlx,
                    y: bquad.tly
                }, {
                    x: bquad.trx,
                    y: bquad.try_
                }, {
                    x: bquad.brx,
                    y: bquad.bry
                }, {
                    x: bquad.blx,
                    y: bquad.bly
                }];
            }
            return [];
        },
        simulateLeftClick = (x, y) => {
            if (!gameCanvas) return;
            gameCanvas.dispatchEvent(new MouseEvent("mousedown", {
                clientX: x,
                clientY: y,
                button: 0,
                bubbles: true
            })), setTimeout(() => gameCanvas.dispatchEvent(new MouseEvent("mouseup", {
                clientX: x,
                clientY: y,
                button: 0,
                bubbles: true
            })), 15);
        },
        simulateRightClick = (isDown, x, y) => {
            if (!gameCanvas) return;
            const eventType = isDown ? "mousedown" : "mouseup",
                opts = {
                    clientX: x,
                    clientY: y,
                    button: 2,
                    buttons: isDown ? 2 : 0,
                    bubbles: true,
                    cancelable: true
                };
            gameCanvas.dispatchEvent(new MouseEvent(eventType, opts)), document.dispatchEvent(new MouseEvent(eventType, opts));
        },
        mainTick = () => {
            if (!isAuthenticated || !runtime || !runtime.running_layout || !playerType) return;
            ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
            const now = performance.now();
            now > window.ultimate_angleLockTime && (window.ultimate_ghostAngle = null);
            const scrollX = runtime.running_layout.scrollX,
                scrollY = runtime.running_layout.scrollY;
            let localPlayer = null,
                closestDist = Infinity;
            for (const player of playerType.instances) {
                const dist = Math.hypot(player.x - scrollX, player.y - scrollY);
                dist < closestDist && (closestDist = dist, localPlayer = player);
            }
            if (!localPlayer) {
                isAimOverriding = false, window.ultimate_ghostAngle = null;
                isRightClickHeld && (simulateRightClick(false, centerX, centerY), isRightClickHeld = false);
                return;
            }
            const canvasRect = gameCanvas.getBoundingClientRect(),
                canvasCenterX = canvasRect.left + canvasRect.width / 2,
                canvasCenterY = canvasRect.top + canvasRect.height / 2,
                scaleX = canvasRect.width / gameCanvas.width,
                scaleY = canvasRect.height / gameCanvas.height,
                layerScale = localPlayer.layer.getScale(),
                localScreenX = canvasCenterX + (localPlayer.x - scrollX) * layerScale * scaleX,
                localScreenY = canvasCenterY + (localPlayer.y - scrollY) * layerScale * scaleY,
                localLevel = getLevel(localPlayer),
                localRadius = localPlayer.width * 0.35,
                localWeaponData = getWeaponStats(localLevel),
                weaponDistance = localWeaponData.distance,
                weaponDegrees = localWeaponData.degrees,
                reachMultiplier = getArcMultiplier(localLevel, config.reachMul),
                maxReach = weaponDistance * reachMultiplier,
                canAttack = now - lastAttackTime > Math.max(150, localLevel * 20);
            let unused1 = false,
                shouldAttack = false,
                shouldSprint = false,
                attackTarget = null,
                attackTargetDist = Infinity,
                dodgeTarget = null,
                dodgeTargetDist = Infinity,
                huntTarget = null,
                huntTargetDist = Infinity,
                punishTarget = null,
                punishTargetDist = Infinity;
            const activeUids = new Set();

            // Lấy team ID của local nếu bật teamMode
            let localTeamId = null;
            if (config.teamMode && localPlayer.instance_vars && localPlayer.instance_vars.length > 36) {
                localTeamId = localPlayer.instance_vars[36];
            }

            if (config.hitboxes) {
                const selfHitbox = getHitboxPolygon(localPlayer);
                if (selfHitbox.length > 1) {
                    ctx.beginPath(), ctx.moveTo(canvasCenterX + (selfHitbox[0].x - scrollX) * layerScale * scaleX, canvasCenterY + (selfHitbox[0].y - scrollY) * layerScale * scaleY);
                    for (let polyIdx = 1; polyIdx < selfHitbox.length; polyIdx++) ctx.lineTo(canvasCenterX + (selfHitbox[polyIdx].x - scrollX) * layerScale * scaleX, canvasCenterY + (selfHitbox[polyIdx].y - scrollY) * layerScale * scaleY);
                    ctx.closePath(), ctx.fillStyle = "rgba(34, 197, 94, 0.15)", ctx.fill(), ctx.strokeStyle = "#22c55e", ctx.lineWidth = 1.5, ctx.stroke();
                }
            }
            for (const enemy of playerType.instances) {
                if (enemy.uid === localPlayer.uid || enemy.width <= 0) continue;

                // Nếu bật teamMode, bỏ qua đồng đội
                if (config.teamMode && localTeamId !== null && enemy.instance_vars && enemy.instance_vars.length > 36) {
                    if (enemy.instance_vars[36] === localTeamId) {
                        continue;
                    }
                }

                activeUids.add(enemy.uid);
                !enemyTracker.has(enemy.uid) && enemyTracker.set(enemy.uid, {
                    wasInThreat: false,
                    attackStart: 0,
                    lastX: enemy.x,
                    lastY: enemy.y,
                    vx: 0,
                    vy: 0
                });
                const enemyData = enemyTracker.get(enemy.uid);
                enemyData.vx = enemy.x - enemyData.lastX, enemyData.vy = enemy.y - enemyData.lastY, enemyData.lastX = enemy.x, enemyData.lastY = enemy.y;
                const dx = localPlayer.x - enemy.x,
                    dy = localPlayer.y - enemy.y,
                    distToEnemy = Math.hypot(dx, dy),
                    enemyLevel = getLevel(enemy),
                    enemyLevelFloored = Math.floor(enemyLevel) + 1,
                    enemyWeaponData = getWeaponStats(enemyLevel),
                    enemyReachMul = getArcMultiplier(enemyLevel, config.reachMul),
                    enemyMaxReach = enemyWeaponData.distance * enemyReachMul,
                    enemyRadius = enemy.width * 0.35,
                    enemyScreenX = canvasCenterX + (enemy.x - scrollX) * layerScale * scaleX,
                    enemyScreenY = canvasCenterY + (enemy.y - scrollY) * layerScale * scaleY;
                let isValidTarget = true;
                config.filter && localLevel > enemyLevel && localLevel - enemyLevel >= 15 && (isValidTarget = false);
                const isInLevelRange = enemyLevel >= config.lvlMin && enemyLevel <= config.lvlMax && isValidTarget;
                let threatBuffer = enemyLevel >= localLevel ? 80 : 40;
                (enemyLevelFloored === 27 || enemyLevelFloored === 28) && (threatBuffer += 120);
                const threatRange = enemyMaxReach + localRadius + threatBuffer,
                    isInThreat = distToEnemy < threatRange,
                    isApproaching = enemyData.vx * dx + enemyData.vy * dy < 0;
                isInThreat && !enemyData.wasInThreat && (isApproaching || distToEnemy < enemyMaxReach) && (enemyData.attackStart = now);
                enemyData.wasInThreat = isInThreat;
                if (config.playerArc && isInLevelRange) {
                    ctx.beginPath(), ctx.arc(enemyScreenX, enemyScreenY, enemyMaxReach * layerScale * scaleX, 0, Math.PI * 2), ctx.strokeStyle = isInThreat ? "rgba(239, 68, 68, 0.8)" : "rgba(251, 191, 36, 0.4)", ctx.lineWidth = 1.5;
                    if (ctx.setLineDash) ctx.setLineDash([4, 4]);
                    ctx.stroke();
                    if (ctx.setLineDash) ctx.setLineDash([]);
                }
                const maxCooldown = Math.max(800, enemyLevel * 60);
                let isSwinging = false,
                    isOnCooldown = false,
                    timeSinceAttack = 0;
                if (enemyData.attackStart) {
                    timeSinceAttack = now - enemyData.attackStart;
                    if (timeSinceAttack < 300) isSwinging = true;
                    else timeSinceAttack < maxCooldown && (isOnCooldown = true);
                }
                if (isSwinging && isInThreat) distToEnemy < dodgeTargetDist && (dodgeTargetDist = distToEnemy, dodgeTarget = enemy);
                else {
                    if (!canAttack && !isOnCooldown && distToEnemy < threatRange + 60) distToEnemy < dodgeTargetDist && (dodgeTargetDist = distToEnemy, dodgeTarget = enemy);
                    else !isOnCooldown && enemyLevel >= localLevel && isInThreat && distToEnemy < dodgeTargetDist && (dodgeTargetDist = distToEnemy, dodgeTarget = enemy);
                }
                if (config.punish && isInLevelRange && (isOnCooldown || isSwinging && !isInThreat)) distToEnemy < dodgeTargetDist && (punishTargetDist = distToEnemy, punishTarget = enemy);
                else isInLevelRange && enemyLevel < localLevel && distToEnemy < huntTargetDist && (huntTargetDist = distToEnemy, huntTarget = enemy);
                isInLevelRange && distToEnemy < maxReach + enemyRadius && distToEnemy < attackTargetDist && (attackTargetDist = distToEnemy, attackTarget = enemy);
                const enemyRadiusScaled = enemyRadius * layerScale * scaleX;
                if (config.hitboxes) {
                    const enemyHitbox = getHitboxPolygon(enemy);
                    if (enemyHitbox.length > 1) {
                        ctx.beginPath(), ctx.moveTo(canvasCenterX + (enemyHitbox[0].x - scrollX) * layerScale * scaleX, canvasCenterY + (enemyHitbox[0].y - scrollY) * layerScale * scaleY);
                        for (let idx = 1; idx < enemyHitbox.length; idx++) ctx.lineTo(canvasCenterX + (enemyHitbox[idx].x - scrollX) * layerScale * scaleX, canvasCenterY + (enemyHitbox[idx].y - scrollY) * layerScale * scaleY);
                        ctx.closePath();
                        if (isSwinging) ctx.fillStyle = "rgba(239, 68, 68, 0.3)", ctx.fill(), ctx.strokeStyle = "#ef4444", ctx.lineWidth = 2.5, ctx.stroke();
                        else isOnCooldown ? (ctx.fillStyle = "rgba(251, 191, 36, 0.2)", ctx.fill(), ctx.strokeStyle = "#fbbf24", ctx.lineWidth = 2.5, ctx.stroke()) : (ctx.fillStyle = isInLevelRange && enemyLevel <= localLevel ? "rgba(34, 197, 94, 0.1)" : "rgba(239, 68, 68, 0.1)", ctx.fill(), ctx.strokeStyle = isInLevelRange && enemyLevel <= localLevel ? "#22c55e" : isInLevelRange ? "#ef4444" : "#9ca3af", ctx.lineWidth = 1.5, ctx.stroke());
                    }
                    const labelY = enemyScreenY - enemyRadiusScaled - 12;
                    ctx.font = "900 12px Inter, sans-serif", ctx.textAlign = "center", ctx.lineWidth = 3, ctx.strokeStyle = "#000000", ctx.strokeText("Lv." + enemyLevelFloored, enemyScreenX, labelY), ctx.fillStyle = isInLevelRange ? "#ffffff" : "#9ca3af", ctx.fillText("Lv." + enemyLevelFloored, enemyScreenX, labelY);
                }
                if (config.cooldown) {
                    if (isSwinging) ctx.fillStyle = "#ef4444", ctx.strokeStyle = "#000", ctx.lineWidth = 3, ctx.font = "900 13px Inter", ctx.strokeText("⚔️ SWINGING!", enemyScreenX, enemyScreenY - enemyRadiusScaled - 30), ctx.fillText("⚔️ SWINGING!", enemyScreenX, enemyScreenY - enemyRadiusScaled - 30);
                    else {
                        if (isOnCooldown) {
                            const cdText = Math.max(0, (maxCooldown - timeSinceAttack) / 1000).toFixed(1),
                                cdPct = timeSinceAttack / maxCooldown;
                            ctx.fillStyle = "#fbbf24", ctx.strokeStyle = "#000", ctx.lineWidth = 3, ctx.font = "900 12px Inter", ctx.strokeText("⏳ CD: " + cdText + "s", enemyScreenX, enemyScreenY - enemyRadiusScaled - 30), ctx.fillText("⏳ CD: " + cdText + "s", enemyScreenX, enemyScreenY - enemyRadiusScaled - 30);
                            const barW = 50,
                                barH = 6,
                                barX = enemyScreenX - barW / 2,
                                barY = enemyScreenY - enemyRadiusScaled - 45;
                            ctx.fillStyle = "rgba(0,0,0,0.8)", ctx.fillRect(barX, barY, barW, barH), ctx.fillStyle = "#fbbf24", ctx.fillRect(barX, barY, barW * (1 - cdPct), barH), ctx.strokeStyle = "#000", ctx.lineWidth = 1, ctx.strokeRect(barX, barY, barW, barH);
                        }
                    }
                }
                config.lines && isInLevelRange && (ctx.beginPath(), ctx.moveTo(localScreenX, localScreenY), ctx.lineTo(enemyScreenX, enemyScreenY), ctx.strokeStyle = isSwinging ? "#ef4444" : isOnCooldown ? "#fbbf24" : distToEnemy < threatRange + 50 ? "rgba(239, 68, 68, 0.6)" : "rgba(168, 85, 247, 0.3)", ctx.lineWidth = isOnCooldown || isSwinging ? 2 : 1, ctx.stroke());
            }
            for (const uid of enemyTracker.keys()) {
                if (!activeUids.has(uid)) enemyTracker.delete(uid);
            }
            if (config.dodge && dodgeTarget) {
                unused1 = true, isAimOverriding = true;
                const dodgeDx = localPlayer.x - dodgeTarget.x,
                    dodgeDy = localPlayer.y - dodgeTarget.y,
                    dodgeDist = Math.hypot(dodgeDx, dodgeDy);
                aimX = canvasCenterX + dodgeDx / dodgeDist * 800, aimY = canvasCenterY + dodgeDy / dodgeDist * 800;
                const dodgeData = enemyTracker.get(dodgeTarget.uid),
                    isEarlyAttack = dodgeData && dodgeData.attackStart && now - dodgeData.attackStart < 300,
                    dodgeDistRaw = Math.hypot(localPlayer.x - dodgeTarget.x, localPlayer.y - dodgeTarget.y),
                    dodgeLvl = Math.floor(getLevel(dodgeTarget)) + 1;
                let extraBuf = 30;
                (dodgeLvl === 27 || dodgeLvl === 28) && (extraBuf += 100);
                const inRange = dodgeDistRaw < getWeaponStats(getLevel(dodgeTarget)).distance + localRadius + extraBuf;
                (config.sprint || isEarlyAttack || inRange || !canAttack) && (shouldSprint = true);
            } else {
                if (config.punish && punishTarget) {
                    isAimOverriding = true;
                    const punishDx = punishTarget.x - localPlayer.x,
                        punishDy = punishTarget.y - localPlayer.y,
                        punishDist = Math.hypot(punishDx, punishDy);
                    aimX = canvasCenterX + punishDx / punishDist * 800, aimY = canvasCenterY + punishDy / punishDist * 800, shouldSprint = true;
                } else {
                    if (config.hunt && huntTarget) {
                        isAimOverriding = true;
                        const huntDx = huntTarget.x - localPlayer.x,
                            huntDy = huntTarget.y - localPlayer.y,
                            huntDist = Math.hypot(huntDx, huntDy);
                        aimX = canvasCenterX + huntDx / huntDist * 800, aimY = canvasCenterY + huntDy / huntDist * 800;
                        if (config.sprint) shouldSprint = true;
                    } else isAimOverriding = false, aimX = centerX, aimY = centerY;
                }
            }
            if (config.attack && canAttack && attackTarget) {
                shouldAttack = true;
                const atkData = enemyTracker.get(attackTarget.uid);
                let predictX = attackTarget.x,
                    predictY = attackTarget.y;
                atkData && (predictX += atkData.vx * 10, predictY += atkData.vy * 10);
                let arcDeg = weaponDegrees;
                (attackTarget.width < 150 || getLevel(attackTarget) < 10) && (arcDeg = weaponDegrees * 0.4);
                const attackAngle = Math.atan2(predictY - localPlayer.y, predictX - localPlayer.x) + arcDeg * Math.PI / 180;
                let angleDeg = attackAngle * 180 / Math.PI % 360;
                if (angleDeg < 0) angleDeg += 360;
                window.ultimate_ghostAngle = angleDeg, window.ultimate_angleLockTime = now + 150;
            }
            if (config.playerArc) {
                let cursorX = isAimOverriding ? aimX : centerX,
                    cursorY = isAimOverriding ? aimY : centerY,
                    worldCursorX = (cursorX - canvasCenterX) / (layerScale * scaleX) + scrollX,
                    worldCursorY = (cursorY - canvasCenterY) / (layerScale * scaleY) + scrollY,
                    aimAngle = window.ultimate_ghostAngle !== null ? window.ultimate_ghostAngle : Math.atan2(worldCursorY - localPlayer.y, worldCursorX - localPlayer.x) * 180 / Math.PI,
                    arcStart = aimAngle * Math.PI / 180 - weaponDegrees * Math.PI / 180;
                ctx.beginPath(), ctx.arc(localScreenX, localScreenY, weaponDistance * layerScale * scaleX, arcStart - 1.09, arcStart + 1.09, false), ctx.strokeStyle = "rgba(0, 255, 0, 0.2)", ctx.lineWidth = 1, ctx.stroke(), ctx.beginPath(), ctx.moveTo(localScreenX, localScreenY), ctx.arc(localScreenX, localScreenY, maxReach * layerScale * scaleX, arcStart - 1.09, arcStart + 1.09, false), ctx.lineTo(localScreenX, localScreenY), ctx.fillStyle = "rgba(0, 255, 0, 0.1)", ctx.fill(), ctx.strokeStyle = "rgba(0, 255, 0, 0.8)", ctx.lineWidth = 2, ctx.stroke();
            }
            isAimOverriding && mouseInstance && (mouseInstance.mouseXcanvas = aimX, mouseInstance.mouseYcanvas = aimY);
            if (shouldSprint)(!isRightClickHeld || now - lastRightClickTime > 50) && (simulateRightClick(true, aimX || centerX, aimY || centerY), isRightClickHeld = true, lastRightClickTime = now);
            else isRightClickHeld && (simulateRightClick(false, aimX || centerX, aimY || centerY), isRightClickHeld = false);
            shouldAttack && now - lastAttackTime > 60 && (simulateLeftClick(aimX, aimY), lastAttackTime = now);
        },
        animationLoop = () => {
            try {
                if (isAuthenticated && runtime && runtime.running_layout) {
                    for (let layer of runtime.running_layout.layers) {
                        if (layer && layer.scale !== config.zoom) {
                            layer.scale = config.zoom;
                            if (layer.setZIndicesStaleFrom) layer.setZIndicesStaleFrom(0);
                        }
                    }
                    mainTick();
                } else ctx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height), isAimOverriding = false, window.ultimate_ghostAngle = null, isRightClickHeld && (simulateRightClick(false, centerX, centerY), isRightClickHeld = false);
            } catch {}
            requestAnimationFrame(animationLoop);
        },
        hookGame = () => {
            gameCanvas = runtime.canvas;
            gameCanvas && gameCanvas.addEventListener("mousemove", mouseProto => {
                isAimOverriding && (mouseProto.stopImmediatePropagation(), mouseProto.stopPropagation());
            }, true);
            if (window.cr.plugins_.Mouse) {
                const mouseProto = window.cr.plugins_.Mouse.prototype.Instance.prototype,
                    origMouseMove = mouseProto.onMouseMove;
                mouseProto.onMouseMove = function() {
                    mouseInstance = this, origMouseMove.apply(this, arguments), isAimOverriding && (this.mouseXcanvas = aimX, this.mouseYcanvas = aimY);
                };
            }
            if (window.cr.plugins_.NSG_PowerWS) {
                const wsProto = window.cr.plugins_.NSG_PowerWS.prototype.Instance.prototype,
                    origTick = wsProto.tick;
                wsProto.tick = function() {
                    if (this.wsWorker && !this.wsWorker._ultimateHooked) {
                        this.wsWorker._ultimateHooked = true;
                        const type_ = this.wsWorker.postMessage;
                        this.wsWorker.postMessage = function(msg) {
                            return msg && msg.action === "send" && msg.data && msg.data.a === "ps" && window.ultimate_ghostAngle !== null && (msg.data.d.a = Math.round(window.ultimate_ghostAngle)), type_.apply(this, arguments);
                        };
                    }
                    origTick.apply(this, arguments);
                };
            }
            for (const type_ of runtime.types_by_index) {
                if (type_ && type_.instvar_sids && type_.instvar_sids.length === 72) {
                    playerType = type_;
                    break;
                }
            }
            animationLoop();
        };
    const createShortcutPanel = () => {
            syncUI();
            document.addEventListener("keydown", event => {
                if (document.activeElement.tagName === "INPUT") return;
                if (event.altKey && (event.key.toLowerCase() === "q" || event.code === "KeyQ")) {
                    event.preventDefault();
                    toggleMenu();
                    return;
                }
                let changed = false;
                switch (event.key.toLowerCase()) {
                    case "e":
                        config.filter = !config.filter, changed = true;
                        break;
                    case "p":
                        config.punish = !config.punish, changed = true;
                        break;
                    case "w":
                        config.dodge = !config.dodge, changed = true;
                        break;
                    case "c":
                        config.attack = !config.attack, changed = true;
                        break;
                    case "v":
                        config.hunt = !config.hunt, changed = true;
                        break;
                    case "b":
                        config.sprint = !config.sprint, changed = true;
                        break;
                    case "insert":
                        toggleMenu();
                        break;
                }
                changed && (saveConfig(), syncUI());
            });
        },
        applyTranslation = () => {
            const langStrings = TRANSLATIONS[config.lang];
            document.querySelectorAll("[data-i18n]").forEach(el => {
                const key = el.getAttribute("data-i18n");
                if (langStrings[key]) el.textContent = langStrings[key];
            }), document.getElementById("evo-lang-btn").textContent = config.lang === "vi" ? "🇻🇳 VN" : "🇺🇸 EN", syncUI();
        },
        activateMod = () => {
            isAuthenticated = true;
            const lockScreen = document.getElementById("evo-lock-screen");
            if (lockScreen) lockScreen.remove();
            menuVisible = true, menuElement.style.display = "flex";
            const pollInterval = setInterval(() => {
                window.cr_getC2Runtime && (runtime = window.cr_getC2Runtime()) && (clearInterval(pollInterval), hookGame(), createShortcutPanel());
            }, 500);
        };
    // Key system removed - activate the mod immediately and open the menu.
    setTimeout(() => {
        activateMod();
        const langBtn = document.getElementById("evo-lang-btn");
        langBtn && langBtn.addEventListener("click", () => {
            config.lang = config.lang === "vi" ? "en" : "vi", saveConfig(), applyTranslation();
        });
        applyTranslation();
    }, 300);
})();