EvoWare

Killaura, Dodge, ESP, Zoom, Auto-Respawn and more. Buy me a coffee:) Paypal.me/Izkuh For early access features check out Patreon.com/EvoWareV1

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         EvoWare
// @namespace    http://tampermonkey.net/
// @version      1.5.5
// @description  Killaura, Dodge, ESP, Zoom, Auto-Respawn and more. Buy me a coffee:) Paypal.me/Izkuh For early access features check out Patreon.com/EvoWareV1
// @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);
})();