EvoWare

Killaura, dodging, ESP, zoom, auto‑respawn and much more.| NEW: predictive dodge, custom hotkey (for opening and closing the menu), faster/lighter ESP, fixed dodge toggles and improved killaura. Buy me a coffee :) paypal.me/Izkuh

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         EvoWare
// @namespace    http://tampermonkey.net/
// @version      2026-05-25
// @description  Killaura, dodging, ESP, zoom, auto‑respawn and much more.| NEW: predictive dodge, custom hotkey (for opening and closing the menu), faster/lighter ESP, fixed dodge toggles and improved killaura. Buy me a coffee :) paypal.me/Izkuh
// @author       #Ex
// @match        https://evowars.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=evowars.io
// @grant        none
// @license      GPL-3.0
// ==/UserScript==

(function() {
    'use strict';

    const iframe = document.createElement('iframe');
    iframe.id = 'evoware-frame';
    iframe.style.cssText = `position:fixed!important;top:50px!important;left:50px!important;width:560px!important;height:600px!important;border:none!important;z-index:2147483647!important;background:transparent!important;pointer-events:auto!important;transition:opacity 0.15s!important;`;
    document.body.appendChild(iframe);
    const doc = iframe.contentDocument || iframe.contentWindow.document;
    doc.open();
    doc.write(`<!DOCTYPE html><html><head><style>
        *{margin:0;padding:0;box-sizing:border-box}
        html,body{height:100%;width:100%}
        body{background:rgba(8,4,16,0.96);font-family:'Segoe UI',Arial,sans-serif;color:#b8a0d0;border-radius:12px;border:1px solid #4a2080;box-shadow:0 0 40px rgba(100,20,200,0.4);display:flex;flex-direction:column}
        #drag-bar{height:42px;padding:0 16px;font-size:14px;font-weight:600;color:#c8b0f0;border-bottom:1px solid #4a2080;cursor:grab;display:flex;align-items:center;justify-content:space-between;user-select:none;background:linear-gradient(180deg,rgba(30,10,50,0.98),rgba(20,5,35,0.98));flex-shrink:0;border-radius:12px 12px 0 0}
        #drag-bar:active{cursor:grabbing}
        .body{display:flex;flex:1;background:rgba(12,6,22,0.96);overflow:hidden;border-radius:0 0 12px 12px}
        .content{display:flex;padding:14px;gap:14px;overflow-y:auto;flex:1}
        .col{flex:1;display:flex;flex-direction:column;gap:10px;min-width:0}
        .st{font-size:11px;font-weight:700;letter-spacing:1.5px;margin-bottom:4px;text-transform:uppercase;color:#7050b0}
        .sec{background:rgba(18,10,30,0.9);border-radius:8px;padding:12px 14px;display:flex;flex-direction:column;gap:10px;border:1px solid #302050}
        .row{display:flex;justify-content:space-between;align-items:center;font-size:13px;color:#c8b8e0;user-select:none;gap:12px}
        .tgl{position:relative;width:56px;height:28px;display:inline-block;cursor:pointer;flex-shrink:0}
        .tgl input{opacity:0;width:0;height:0;position:absolute}
        .sld{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:#2a1840;transition:0.2s;border-radius:28px}
        .sld:before{position:absolute;content:"";height:22px;width:22px;left:3px;bottom:3px;background:#6040a0;transition:0.2s;border-radius:50%}
        input:checked+.sld{background:#7c3aed;box-shadow:0 0 8px rgba(124,58,237,0.5)}
        input:checked+.sld:before{transform:translateX(28px);background:#fff}
        .rw{display:flex;align-items:center;gap:8px;flex:1;position:relative}
        .slider-wrap{position:relative;width:100%;height:22px}
        .slider-wrap input[type=range]{position:absolute;width:100%;top:50%;transform:translateY(-50%);-webkit-appearance:none;appearance:none;background:transparent;pointer-events:none;z-index:2;height:6px}
        .slider-wrap input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border-radius:50%;background:#7c3aed;cursor:pointer;pointer-events:auto;border:1px solid #a78bfa;box-shadow:0 0 6px rgba(124,58,237,0.4)}
        .slider-track-bg{position:absolute;top:50%;transform:translateY(-50%);width:100%;height:4px;background:#2a1840;border-radius:3px;z-index:1}
        .slider-track-fill{position:absolute;top:50%;transform:translateY(-50%);height:4px;background:#7c3aed;border-radius:3px;z-index:1;box-shadow:0 0 6px rgba(124,58,237,0.3)}
        .val{color:#8060c0;font-size:11px;min-width:32px;text-align:right;flex-shrink:0}
        .stat-val{font-weight:600;font-size:12px}
        .help{display:inline-block;width:16px;height:16px;border-radius:50%;background:#4a2080;color:#c8b0f0;text-align:center;line-height:16px;font-size:11px;font-weight:700;cursor:help;margin-left:4px;position:relative}
        .help:hover::after{content:attr(data-tip);position:absolute;left:22px;top:-6px;background:#1a0a2e;color:#fff;padding:6px 10px;border-radius:6px;font-size:11px;white-space:nowrap;z-index:10;border:1px solid #7c3aed}
        ::-webkit-scrollbar{width:4px}
        ::-webkit-scrollbar-track{background:#1a0a2e}
        ::-webkit-scrollbar-thumb{background:#4a2080;border-radius:4px}
        .hotkey-row{background:rgba(124,58,237,0.15);border:1px solid #7c3aed;border-radius:6px;padding:8px 12px;margin-top:4px}
        .hotkey-btn{background:#2a1840;border:1px solid #7c3aed;color:#c8b8e0;padding:4px 12px;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600}
        .hotkey-btn:hover{background:#7c3aed;color:#fff}
        .hotkey-display{background:#1a0a2e;border:1px solid #4a2080;border-radius:4px;padding:4px 12px;font-family:monospace;font-size:12px;min-width:80px;text-align:center}
        .waiting-txt{color:#22c55e;font-size:11px}
    </style></head><body>
        <div id="drag-bar"><span>⚔ EvoWare</span><span>Drag</span></div>
        <div class="body"><div class="content">
            <div class="col">
                <div><div class="st">Combat</div><div class="sec">
                    <div class="row"><span>Killaura<span class="help" data-tip="Auto-aim & attack">?</span></span><label class="tgl"><input type="checkbox" id="tgl-killaura"><span class="sld"></span></label></div>
                    <div class="row"><span>Target Level<span class="help" data-tip="Min/Max enemy level to attack">?</span></span><span class="val" id="val-lvlrange">0-100</span></div>
                    <div class="slider-wrap"><div class="slider-track-bg"></div><div class="slider-track-fill" id="track-lvl"></div><input type="range" id="rng-lvlmin" min="0" max="100" value="0" step="1"><input type="range" id="rng-lvlmax" min="0" max="100" value="100" step="1"></div>
                    <div class="row"><span>Dodge<span class="help" data-tip="Turn & boost away from threats">?</span></span><label class="tgl"><input type="checkbox" id="tgl-dodgeatk"><span class="sld"></span></label></div>
                    <div class="row"><span>Dodge on CD<span class="help" data-tip="Dodge only while attack recharging">?</span></span><label class="tgl"><input type="checkbox" id="tgl-dodgecd"><span class="sld"></span></label></div>
                    <div class="row"><span>Auto Respawn<span class="help" data-tip="Auto-click Play after death">?</span></span><label class="tgl"><input type="checkbox" id="tgl-autorespawn"><span class="sld"></span></label></div>
                    <div class="row"><span>Swing Angle<span class="help" data-tip="Aim offset from target direction">?</span></span><div class="rw"><div class="slider-wrap"><div class="slider-track-bg"></div><div class="slider-track-fill" id="track-offset"></div><input type="range" id="rng-offset" min="0" max="360" value="135"></div><span class="val" id="val-offset">135°</span></div></div>
                </div></div>
                <div><div class="st">Visuals</div><div class="sec">
                    <div class="row"><span>Enemy Range<span class="help" data-tip="Show enemy attack radius">?</span></span><label class="tgl"><input type="checkbox" id="tgl-enemyrange"><span class="sld"></span></label></div>
                    <div class="row"><span>Attack Timer<span class="help" data-tip="Show enemy attack cooldown">?</span></span><label class="tgl"><input type="checkbox" id="tgl-cooldown"><span class="sld"></span></label></div>
                    <div class="row"><span>Tracers<span class="help" data-tip="Line to each enemy">?</span></span><label class="tgl"><input type="checkbox" id="tgl-tracers"><span class="sld"></span></label></div>
                    <div class="row"><span>Hitboxes<span class="help" data-tip="Show enemy collision size">?</span></span><label class="tgl"><input type="checkbox" id="tgl-hitboxes"><span class="sld"></span></label></div>
                    <div class="row"><span>Target Glow<span class="help" data-tip="Highlight current target">?</span></span><label class="tgl"><input type="checkbox" id="tgl-killable"><span class="sld"></span></label></div>
                </div></div>
            </div>
            <div class="col">
                <div><div class="st">Settings</div><div class="sec">
                    <div class="row"><span>Zoom<span class="help" data-tip="Adjust camera zoom">?</span></span><div class="rw"><div class="slider-wrap"><div class="slider-track-bg"></div><div class="slider-track-fill" id="track-zoom"></div><input type="range" id="rng-zoom" min="0.2" max="3" step="0.05" value="0.5"></div><span class="val" id="val-zoom">0.50x</span></div></div>
                </div></div>
                <div><div class="st">Hotkey</div><div class="sec">
                    <div class="hotkey-row">
                        <div class="row"><span>Toggle Key</span><button class="hotkey-btn" id="change-hotkey">Set</button></div>
                        <div class="row" style="margin-top:8px"><span>Current:</span><span class="hotkey-display" id="current-hotkey">Insert</span></div>
                        <div class="row" style="margin-top:4px"><span class="waiting-txt" id="waiting-status"></span></div>
                    </div>
                </div></div>
                <div><div class="st">Info</div><div class="sec">
                    <div class="row"><span>Level</span><span class="stat-val" id="txt-level">--</span></div>
                    <div class="row"><span>Range</span><span class="stat-val" id="txt-range">--</span></div>
                    <div class="row"><span>Nearby</span><span class="stat-val" id="txt-nearby">0</span></div>
                </div></div>
            </div>
        </div></div>
        <script>
            function S(t,d){window.parent.postMessage({evoware:1,type:t,data:d},'*')}
            const D=document.getElementById('drag-bar');let dr=false,sx,sy,sl,st;
            D.addEventListener('mousedown',function(e){if(e.target.tagName==='INPUT'||e.target.tagName==='LABEL')return;dr=true;sx=e.clientX;sy=e.clientY;const f=window.parent.document.getElementById('evoware-frame');sl=f.offsetLeft;st=f.offsetTop;e.preventDefault()});
            window.addEventListener('mousemove',function(e){if(!dr)return;const f=window.parent.document.getElementById('evoware-frame');let nx=sl+(e.clientX-sx),ny=st+(e.clientY-sy);nx=Math.max(0,Math.min(nx,window.parent.innerWidth-f.offsetWidth));ny=Math.max(0,Math.min(ny,window.parent.innerHeight-f.offsetHeight));f.style.left=nx+'px';f.style.top=ny+'px'});
            window.addEventListener('mouseup',function(){dr=false});
            function UR(r,t,min,max,vel,suf,cb){const v=parseFloat(r.value);t.style.left='0%';t.style.width=((v-min)/(max-min)*100)+'%';vel.textContent=(suf==='°'?parseInt(v):v.toFixed(2))+suf;if(cb)cb(v);}
            const rM=document.getElementById('rng-lvlmin'),rX=document.getElementById('rng-lvlmax'),tL=document.getElementById('track-lvl'),vL=document.getElementById('val-lvlrange');
            function uL(){let mn=parseInt(rM.value),mx=parseInt(rX.value);if(mn>mx){[mn,mx]=[mx,mn];rM.value=mn;rX.value=mx;}tL.style.left=(mn/100*100)+'%';tL.style.width=((mx-mn)/100*100)+'%';vL.textContent=mn+'-'+mx;S('lvlrange',{min:mn,max:mx});}
            rM.addEventListener('input',uL);rX.addEventListener('input',uL);uL();
            const rO=document.getElementById('rng-offset'),tO=document.getElementById('track-offset'),vO=document.getElementById('val-offset');UR(rO,tO,0,360,vO,'°',function(v){S('offset',v)});rO.addEventListener('input',function(){UR(rO,tO,0,360,vO,'°',function(v){S('offset',v)})});
            const rZ=document.getElementById('rng-zoom'),tZ=document.getElementById('track-zoom'),vZ=document.getElementById('val-zoom');UR(rZ,tZ,0.2,3,vZ,'x',function(v){S('zoom',v)});rZ.addEventListener('input',function(){UR(rZ,tZ,0.2,3,vZ,'x',function(v){S('zoom',v)})});
            document.getElementById('tgl-killaura').addEventListener('change',function(){S('killaura',this.checked)});
            document.getElementById('tgl-dodgeatk').addEventListener('change',function(){S('dodgeatk',this.checked)});
            document.getElementById('tgl-dodgecd').addEventListener('change',function(){S('dodgecd',this.checked)});
            document.getElementById('tgl-autorespawn').addEventListener('change',function(){S('autorespawn',this.checked)});
            document.getElementById('tgl-enemyrange').addEventListener('change',function(){S('enemyrange',this.checked)});
            document.getElementById('tgl-cooldown').addEventListener('change',function(){S('cooldown',this.checked)});
            document.getElementById('tgl-tracers').addEventListener('change',function(){S('tracers',this.checked)});
            document.getElementById('tgl-hitboxes').addEventListener('change',function(){S('hitboxes',this.checked)});
            document.getElementById('tgl-killable').addEventListener('change',function(){S('killable',this.checked)});

            let waiting=false;
            const curDisp=document.getElementById('current-hotkey');
            const waitStatus=document.getElementById('waiting-status');
            const changeBtn=document.getElementById('change-hotkey');

            function getKeyDisplay(k){const m={'Insert':'Insert','Delete':'Del','Home':'Home','End':'End','PageUp':'PgUp','PageDown':'PgDn','ArrowUp':'↑','ArrowDown':'↓','ArrowLeft':'←','ArrowRight':'→',' ':'Space','Escape':'Esc'};return m[k]||(k.length===1?k.toUpperCase():k);}
            function saveHotkey(k){localStorage.setItem('evoware_toggle_key',k);curDisp.textContent=getKeyDisplay(k);window.parent.postMessage({type:'hotkeyChanged',key:k},'*');}
            function loadHotkey(){const s=localStorage.getItem('evoware_toggle_key');curDisp.textContent=getKeyDisplay(s||'Insert');return s||'Insert';}
            changeBtn.addEventListener('click',function(){waiting=true;waitStatus.textContent='Press any key...';changeBtn.textContent='Waiting...';});
            function captureKey(e){if(!waiting)return;e.preventDefault();e.stopPropagation();let k=e.key;if(k===' ')k=' ';if(k&&k!=='Insert')saveHotkey(k);waiting=false;waitStatus.textContent='';changeBtn.textContent='Set';}
            document.addEventListener('keydown',captureKey);
            loadHotkey();

            window.addEventListener('message',function(e){if(!e.data||!e.data.evoware)return;if(e.data.type==='updateStats'){document.getElementById('txt-level').textContent=e.data.level;document.getElementById('txt-range').textContent=e.data.range;document.getElementById('txt-nearby').textContent=e.data.nearby}});
        <\/script></body></html>`);
    doc.close();

    let rt, pType, gameCanvas;
    let showTracers = false, showHitboxes = false, showKillable = false, showEnemyRange = false, showCooldown = false;
    let killaura = false, dodgeAttacks = false, dodgeOnCD = false;
    let autoRespawn = false;
    let offset = 135, zoomLevel = 0.5, lvlMin = 0, lvlMax = 100;
    let target = null, targetLevel = 0, menuVisible = true;
    let lastDodge = 0, dodgeCD = 1200;
    let lastAttackTime = 0;
    let enemyTimers = new Map(), gameStartTime = 0;
    let enemyPrevAngle = new Map();
    let enemyPrevDist = new Map();
    let lastRespawnTry = 0;
    let boostTimeout = null;
    let toggleKey = 'Insert';

    let lastDrawTime = 0;
    let drawThrottle = 16;
    let lastDodgeCheck = 0;
    let dodgeCheckThrottle = 30;
    let lastStatsUpdate = 0;
    let statsThrottle = 200;

    function loadToggleKey(){const s=localStorage.getItem('evoware_toggle_key');if(s)toggleKey=s;}
    loadToggleKey();

    const getAttackCooldown = (level) => Math.max(100, level * 100);

    function applyZoom() {
        if (rt?.running_layout?.layers) {
            for (const l of rt.running_layout.layers) {
                if (l?.scale !== undefined) { l.scale = zoomLevel; if (l.setZIndicesStaleFrom) l.setZIndicesStaleFrom(0); }
            }
            rt.redraw = true;
        }
    }

    function gs(obj) {
        if (!obj) return { lv: 0, sz: 0, rng: 0 };
        let lv = 0, sz = obj.width || 0;
        try {
            if (obj.instance_vars) {
                let lvs = [];
                for (let i = 0; i < obj.instance_vars.length; i++) {
                    const v = obj.instance_vars[i];
                    if (typeof v === 'number' && v >= 1 && v <= 200 && Number.isInteger(v)) lvs.push({ i, v });
                }
                if (lvs.length) {
                    lvs.sort((a, b) => b.v - a.v);
                    const early = lvs.filter(l => l.i < 15);
                    lv = early.length >= 2 ? early[1].v : (early.length === 1 ? early[0].v + 1 : lvs[0].v + 1);
                }
            }
        } catch (e) {}
        if (!lv && sz > 0) lv = Math.max(1, Math.floor(sz / 3.5));
        return { lv, sz, rng: sz * 2.2 };
    }

    function getPlayer() {
        if (!rt?.running_layout || !pType) return null;
        const instances = pType.instances;
        let self = null, md = Infinity;
        for (let i = 0; i < instances.length; i++) {
            const p = instances[i]; if (!p) continue;
            const d = Math.hypot(p.x - rt.running_layout.scrollX, p.y - rt.running_layout.scrollY);
            if (d < md && d < 500) { md = d; self = p; }
        }
        return self;
    }

    function updateStats() {
        const self = getPlayer();
        if (!self) {
            iframe.contentWindow.postMessage({ evoware: 1, type: 'updateStats', level: '--', range: '--', nearby: 0 }, '*');
            return;
        }
        const ps = gs(self);
        let nearby = 0;
        const instances = pType.instances;
        for (let i = 0; i < instances.length; i++) {
            const p = instances[i];
            if (!p || p.uid === self.uid) continue;
            if (p.width <= 0) continue;
            const dist = Math.hypot(p.x - self.x, p.y - self.y);
            if (dist <= ps.rng) nearby++;
        }
        iframe.contentWindow.postMessage({
            evoware: 1, type: 'updateStats',
            level: 'Lv.' + ps.lv,
            range: Math.floor(ps.rng) + 'u',
            nearby: nearby
        }, '*');
    }

    function simMouse(type, clientX, clientY, button = 0, buttons = 0) {
        if (!gameCanvas) return;
        const opts = { bubbles: true, cancelable: true, view: window, clientX, clientY, button, buttons };
        gameCanvas.dispatchEvent(new MouseEvent(type, opts));
        document.dispatchEvent(new MouseEvent(type, opts));
    }

    function killAll() {
        const self = getPlayer(); if (!self) return;
        const ps = gs(self); if (!ps) return;
        const rect = gameCanvas.getBoundingClientRect();
        const sc = self.layer.getScale();
        const vx = rect.left + rect.width/2, vy = rect.top + rect.height/2;
        const instances = pType.instances;

        let bestTarget = null;
        let bestDist = Infinity;
        for (let i = 0; i < instances.length; i++) {
            const p = instances[i]; if (!p || p.uid === self.uid) continue;
            if (p.width <= 0) continue;
            const dist = Math.hypot(p.x - self.x, p.y - self.y);
            if (dist > ps.rng) continue;
            const es = gs(p);
            if (es.lv < lvlMin || es.lv > lvlMax) continue;
            if (dist < bestDist) {
                bestDist = dist;
                bestTarget = p;
            }
        }
        if (!bestTarget) { target = null; targetLevel = 0; return; }
        target = bestTarget;
        targetLevel = gs(target).lv;

        const dx = target.x - self.x, dy = target.y - self.y;
        const angleToTarget = Math.atan2(dy, dx);
        const offsetRad = (offset * Math.PI) / 180;
        self.angle = ((angleToTarget + offsetRad) % (Math.PI * 2) + (Math.PI * 2)) % (Math.PI * 2);

        const sx = vx + (target.x - self.x) * sc;
        const sy = vy + (target.y - self.y) * sc;
        simMouse('mousedown', sx, sy, 0, 1);
        simMouse('mouseup', sx, sy, 0, 0);

        lastAttackTime = performance.now();
    }

    function getThreats(self, ps) {
        if (!self || !pType) return [];
        const threats = [];
        const instances = pType.instances;

        for (let i = 0; i < instances.length; i++) {
            const p = instances[i];
            if (!p || p.uid === self.uid) continue;
            if (p.width <= 0) continue;

            const dx = p.x - self.x;
            const dy = p.y - self.y;
            const dist = Math.hypot(dx, dy);
            const es = gs(p);

            const threatRange = es.rng * 1.5;
            if (dist > threatRange) continue;
            if (es.lv < lvlMin || es.lv > lvlMax) continue;

            let isAttacking = false;
            let isBoosting = false;

            const angle = p.angle || 0;
            const prevAngle = enemyPrevAngle.get(p.uid);
            if (prevAngle !== undefined) {
                const angleDiff = Math.abs(angle - prevAngle);
                const ndiff = Math.min(angleDiff, Math.PI * 2 - angleDiff);
                if (ndiff > 0.15) {
                    const angleToUs = Math.atan2(dy, dx);
                    const facingUs = Math.abs(angle - angleToUs) < 0.8;
                    if (facingUs || dist < es.rng) {
                        isAttacking = true;
                    }
                }
            }
            enemyPrevAngle.set(p.uid, angle);

            const prevDist = enemyPrevDist.get(p.uid);
            if (prevDist !== undefined) {
                const distChange = prevDist - dist;
                if (distChange > 20 && dist < es.rng * 1.2) {
                    isBoosting = true;
                }
            }
            enemyPrevDist.set(p.uid, dist);

            threats.push({ dx, dy, dist, es, isAttacking, isBoosting });
        }

        const alive = new Set();
        for (let i = 0; i < instances.length; i++) { const p = instances[i]; if (p && p.uid && p.width > 0) alive.add(p.uid); }
        for (const uid of enemyPrevAngle.keys()) { if (!alive.has(uid)) enemyPrevAngle.delete(uid); }
        for (const uid of enemyPrevDist.keys()) { if (!alive.has(uid)) enemyPrevDist.delete(uid); }

        return threats;
    }

    function tryDodge(self) {
        const now = performance.now();

        if (!dodgeAttacks) return false;
        if (now - lastDodge < dodgeCD) return false;

        if (dodgeOnCD) {
            const timeSinceAttack = now - lastAttackTime;
            if (timeSinceAttack < 300) return false;
        }

        const ps = gs(self);
        const threats = getThreats(self, ps);
        if (threats.length === 0) return false;

        let urgent = false;
        let closestThreat = null;

        for (const t of threats) {
            if (t.isAttacking && t.dist < t.es.rng) urgent = true;
            if (t.isBoosting && t.dist < t.es.rng * 1.2) urgent = true;
            if (closestThreat === null || t.dist < closestThreat.dist) {
                closestThreat = t;
            }
        }

        if (urgent || (closestThreat && closestThreat.dist < ps.rng)) {
            const escapeAngle = Math.atan2(-closestThreat.dy, -closestThreat.dx);
            self.angle = escapeAngle;

            const pushStrength = Math.min(12, 8 + (ps.rng - closestThreat.dist) / 20);
            const pushX = -closestThreat.dx / closestThreat.dist * pushStrength;
            const pushY = -closestThreat.dy / closestThreat.dist * pushStrength;
            self.x += pushX;
            self.y += pushY;
            if (rt?.running_layout) {
                rt.running_layout.scrollX += pushX;
                rt.running_layout.scrollY += pushY;
            }

            if (boostTimeout) clearTimeout(boostTimeout);
            const rect = gameCanvas.getBoundingClientRect();
            const cx = rect.left + rect.width/2;
            const cy = rect.top + rect.height/2;
            simMouse('mousedown', cx, cy, 2, 2);
            boostTimeout = setTimeout(() => {
                simMouse('mouseup', cx, cy, 2, 0);
                boostTimeout = null;
            }, 150);

            lastDodge = now;
            return true;
        }
        return false;
    }

    function tryRespawn() {
        const now = performance.now();
        if (now - lastRespawnTry < 2500) return;
        lastRespawnTry = now;
        const rect = gameCanvas.getBoundingClientRect();
        const cx = rect.left + rect.width/2;
        const cy = rect.top + rect.height/2;
        document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', keyCode: 13, bubbles: true }));
        document.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter', keyCode: 13, bubbles: true }));
        const offsets = [0, -30, 30, -60, 60];
        offsets.forEach((offY, idx) => {
            setTimeout(() => {
                simMouse('mousedown', cx, cy + offY, 0, 1);
                simMouse('mouseup', cx, cy + offY, 0, 0);
            }, idx * 80);
        });
    }

    function updateTimers() {
        if (!pType || !gameStartTime) return;
        const instances = pType.instances;
        for (let i = 0; i < instances.length; i++) {
            const p = instances[i]; if (!p || !p.uid || p.width <= 0) continue;
            if (!enemyTimers.has(p.uid)) {
                const es = gs(p);
                enemyTimers.set(p.uid, { cd: getAttackCooldown(es.lv) });
            }
        }
        const alive = new Set(); for (let i = 0; i < instances.length; i++) { const p = instances[i]; if (p && p.uid && p.width > 0) alive.add(p.uid); }
        for (const uid of enemyTimers.keys()) { if (!alive.has(uid)) enemyTimers.delete(uid); }
    }

    const esp = document.createElement('canvas');
    const ctx = esp.getContext('2d');
    esp.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:999998;';
    esp.width = innerWidth; esp.height = innerHeight;
    document.body.appendChild(esp);
    window.addEventListener('resize', () => { esp.width = innerWidth; esp.height = innerHeight; });

    function draw() {
        if (!rt?.running_layout || !pType) return;

        const now = performance.now();
        if (now - lastDrawTime < drawThrottle) return;
        lastDrawTime = now;

        const instances = pType.instances;
        const self = getPlayer();
        if (!self) return;

        const ps = gs(self);
        const rect = gameCanvas.getBoundingClientRect();
        const sc = self.layer.getScale();
        const vx = rect.left + rect.width / 2;
        const vy = rect.top + rect.height / 2;

        ctx.clearRect(0, 0, esp.width, esp.height);

        if (dodgeAttacks) {
            const sr = Math.max(ps.rng, 80) * 1.5 * sc;
            ctx.beginPath();
            ctx.arc(vx, vy, sr, 0, Math.PI * 2);
            ctx.strokeStyle = dodgeOnCD ? "rgba(251,191,36,0.4)" : "rgba(74,222,128,0.4)";
            ctx.lineWidth = 2;
            ctx.setLineDash([5, 5]);
            ctx.stroke();
            ctx.setLineDash([]);
        }

        if (killaura) {
            ctx.beginPath();
            ctx.arc(vx, vy, ps.rng * sc, 0, Math.PI * 2);
            ctx.strokeStyle = "rgba(124,58,237,0.3)";
            ctx.lineWidth = 1.5;
            ctx.setLineDash([4, 4]);
            ctx.stroke();
            ctx.setLineDash([]);
        }

        if (killaura && target) {
            const angle = self.angle || 0;
            const bl2 = (ps.sz * 0.9) * sc;
            const dx = target.x - self.x;
            const dy = target.y - self.y;
            const da = Math.atan2(dy, dx);

            ctx.beginPath();
            ctx.moveTo(vx, vy);
            ctx.lineTo(vx + Math.cos(da) * bl2, vy + Math.sin(da) * bl2);
            ctx.strokeStyle = "rgba(255,255,255,0.4)";
            ctx.lineWidth = 1.5;
            ctx.setLineDash([2, 2]);
            ctx.stroke();

            ctx.beginPath();
            ctx.moveTo(vx, vy);
            ctx.lineTo(vx + Math.cos(angle) * bl2, vy + Math.sin(angle) * bl2);
            ctx.strokeStyle = "#a855f7";
            ctx.lineWidth = 3;
            ctx.setLineDash([]);
            ctx.stroke();

            const or2 = (offset * Math.PI) / 180;
            ctx.beginPath();
            ctx.moveTo(vx, vy);
            ctx.lineTo(vx + Math.cos(da + or2) * bl2, vy + Math.sin(da + or2) * bl2);
            ctx.strokeStyle = "#22c55e";
            ctx.lineWidth = 2;
            ctx.setLineDash([3, 3]);
            ctx.stroke();
            ctx.setLineDash([]);
        }

        for (let i = 0; i < instances.length; i++) {
            const p = instances[i];
            if (!p || p.uid === self.uid) continue;
            if (p.width <= 0) continue;

            const es = gs(p);
            if (es.lv <= 0) continue;

            const px = vx + (p.x - self.x) * sc;
            const py = vy + (p.y - self.y) * sc;
            const dist = Math.hypot(p.x - self.x, p.y - self.y);
            const inRng = dist <= ps.rng;
            const killable = es.lv <= ps.lv;
            const danger = es.lv > ps.lv * 1.3;

            if (showCooldown) {
                const t = enemyTimers.get(p.uid);
                if (t) {
                    const elapsed = (performance.now() - gameStartTime) % t.cd;
                    const remaining = t.cd - elapsed;
                    const pct = elapsed / t.cd;

                    ctx.beginPath();
                    ctx.arc(px, py, 28, 0, Math.PI * 2);
                    ctx.strokeStyle = "rgba(255,255,255,0.08)";
                    ctx.lineWidth = 2.5;
                    ctx.stroke();

                    ctx.beginPath();
                    ctx.arc(px, py, 28, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * pct);
                    ctx.strokeStyle = pct < 0.15 ? "#22c55e" : "#f59e0b";
                    ctx.lineWidth = 2.5;
                    ctx.stroke();

                    ctx.fillStyle = "#fff";
                    ctx.font = "bold 9px Arial";
                    ctx.textAlign = "center";
                    ctx.fillText((remaining / 1000).toFixed(1) + "s", px, py - 34);
                }
            }

            if (showKillable && inRng) {
                ctx.beginPath();
                ctx.arc(px, py, 22, 0, Math.PI * 2);
                if (killable) {
                    ctx.shadowColor = "#22c55e";
                    ctx.shadowBlur = 15;
                    ctx.strokeStyle = "#22c55e";
                    ctx.lineWidth = 3;
                    ctx.stroke();
                    ctx.shadowBlur = 0;
                } else if (danger) {
                    ctx.shadowColor = "#ef4444";
                    ctx.shadowBlur = 15;
                    ctx.strokeStyle = "#ef4444";
                    ctx.lineWidth = 3;
                    ctx.setLineDash([4, 4]);
                    ctx.stroke();
                    ctx.setLineDash([]);
                    ctx.shadowBlur = 0;
                }
            }

            if (showEnemyRange && es.rng > 0) {
                ctx.beginPath();
                ctx.arc(px, py, es.rng * sc, 0, Math.PI * 2);
                ctx.strokeStyle = dist < es.rng ? "rgba(255,60,60,0.9)" : "rgba(255,60,60,0.5)";
                ctx.lineWidth = 2;
                ctx.setLineDash([2, 2]);
                ctx.stroke();
                ctx.setLineDash([]);
            }

            if (showHitboxes) {
                const r = (p.width / 2) * sc * 1.5;
                ctx.beginPath();
                ctx.arc(px, py, r, 0, Math.PI * 2);
                ctx.fillStyle = killable ? "rgba(34,197,94,0.08)" : "rgba(124,58,237,0.08)";
                ctx.fill();
                ctx.strokeStyle = killable ? "#22c55e" : "#a855f7";
                ctx.lineWidth = 1;
                ctx.stroke();
            }

            if (showTracers) {
                ctx.beginPath();
                ctx.moveTo(vx, vy);
                ctx.lineTo(px, py);
                ctx.strokeStyle = inRng ? (killable ? "rgba(34,197,94,0.6)" : "rgba(168,85,247,0.5)") : "rgba(168,85,247,0.12)";
                ctx.lineWidth = inRng ? 1.5 : 0.5;
                ctx.stroke();
            }

            if (killaura && target && p.uid === target.uid) {
                ctx.beginPath();
                ctx.arc(px, py, 26, 0, Math.PI * 2);
                ctx.strokeStyle = "#a855f7";
                ctx.lineWidth = 2.5;
                ctx.setLineDash([3, 3]);
                ctx.stroke();
                ctx.setLineDash([]);
                ctx.fillStyle = "#fff";
                ctx.font = "bold 10px Arial";
                ctx.textAlign = "center";
                ctx.fillText("Lv." + es.lv, px, py - 30);
            }
        }
    }

    function loop() {
        try {
            if (rt?.running_layout) {
                if (!gameStartTime) gameStartTime = performance.now();
                applyZoom();
                updateTimers();

                const now = performance.now();
                if (now - lastStatsUpdate >= statsThrottle) {
                    lastStatsUpdate = now;
                    updateStats();
                }

                const needDraw = showTracers || showHitboxes || showKillable || showEnemyRange || showCooldown || killaura || dodgeAttacks;
                if (needDraw) draw(); else ctx.clearRect(0, 0, esp.width, esp.height);

                if (pType) {
                    const self = getPlayer();
                    if (self) {
                        if (killaura) killAll();
                        if (now - lastDodgeCheck >= dodgeCheckThrottle) {
                            lastDodgeCheck = now;
                            if (dodgeAttacks) {
                                tryDodge(self);
                            }
                        }
                    } else {
                        if (autoRespawn) tryRespawn();
                    }
                }
            }
        } catch (e) {}
        requestAnimationFrame(loop);
    }

    const init = setInterval(() => {
        if (window.cr_getC2Runtime && (rt = window.cr_getC2Runtime())) {
            clearInterval(init);
            gameCanvas = rt.canvas;
            for (const t of rt.types_by_index) {
                if (t?.instvar_sids?.length === 72) { pType = t; break; }
            }
            if (!pType) {
                for (const t of rt.types_by_index) {
                    if (t?.instances?.length > 0) {
                        const i = t.instances[0];
                        if (i.x !== undefined && i.y !== undefined && i.width !== undefined && i.angle !== undefined) {
                            pType = t; break;
                        }
                    }
                }
            }
            loop();
        }
    }, 500);

    window.addEventListener('message', function(e) {
        if (!e.data || !e.data.evoware) return;
        switch (e.data.type) {
            case 'killaura': killaura = e.data.data; if (!killaura) { target = null; targetLevel = 0; } break;
            case 'dodgeatk': dodgeAttacks = e.data.data; if (!dodgeAttacks && boostTimeout) clearTimeout(boostTimeout); break;
            case 'dodgecd': dodgeOnCD = e.data.data; break;
            case 'autorespawn': autoRespawn = e.data.data; break;
            case 'lvlrange': lvlMin = e.data.data.min; lvlMax = e.data.data.max; break;
            case 'enemyrange': showEnemyRange = e.data.data; break;
            case 'cooldown': showCooldown = e.data.data; break;
            case 'tracers': showTracers = e.data.data; break;
            case 'hitboxes': showHitboxes = e.data.data; break;
            case 'killable': showKillable = e.data.data; break;
            case 'offset': offset = e.data.data; break;
            case 'zoom': zoomLevel = e.data.data; break;
        }
    });

    window.addEventListener('message', function(e) {
        if (e.data && e.data.type === 'hotkeyChanged') {
            toggleKey = e.data.key;
            localStorage.setItem('evoware_toggle_key', toggleKey);
        }
    });

    document.addEventListener('keydown', function(e) {
        if (e.key === toggleKey) {
            e.preventDefault();
            e.stopPropagation();
            menuVisible = !menuVisible;
            iframe.style.opacity = menuVisible ? '1' : '0';
            iframe.style.pointerEvents = menuVisible ? 'auto' : 'none';
        }
    }, true);
})();