Greasy Fork is available in English.

betteraimbot

Modified to allow toggling Chams with [K] for environment maintenance.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         betteraimbot
// @namespace    http://tampermonkey.net/
// @version      12
// @description  Modified to allow toggling Chams with [K] for environment maintenance.
// @author       furtgay & AI
// @match        *://*deadshot.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=deadshot.io
// @grant        unsafeWindow
// @run-at       document-start
// @license      MIT
// ==/UserScript==
(() => {
    'use strict';

    const config = {
        fov: 100,
        sensitivity: 0.1,
        headOffset: 0.6,
        prediction: 2,
        enemyColor: '#ff0000',
        boxEnabled: false,
    };

    // 環境維護開關:控制透視功能的執行
    let chamsEnabled = true;

    const MENU_STYLE = `
        #cheat-menu {
            position: fixed; top: 20px; right: 20px; width: 220px;
            background: rgba(10, 10, 10, 0.9); backdrop-filter: blur(10px);
            border: 1px solid rgba(0, 255, 210, 0.5); border-radius: 8px;
            color: #fff; font-family: sans-serif; padding: 15px; z-index: 2147483647;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); user-select: none; display: block;
            transition: opacity 0.2s ease;
        }
        .a-row { margin-bottom: 12px; }
        .a-label { display: block; font-size: 10px; color: #888; margin-bottom: 5px; text-transform: uppercase; }
        .a-val { float: right; color: #00ffd2; }
        .a-slider { width: 100%; height: 4px; background: #333; border-radius: 2px; appearance: none; outline: none; }
        .a-slider::-webkit-slider-thumb { appearance: none; width: 12px; height: 12px; background: #00ffd2; border-radius: 50%; cursor: pointer; box-shadow: 0 0 8px #00ffd2; }
        .a-btn {
            width: 100%; padding: 6px; background: rgba(0, 255, 210, 0.1);
            border: 1px solid rgba(0, 255, 210, 0.3); color: #00ffd2;
            font-size: 11px; cursor: pointer; margin-bottom: 5px; border-radius: 4px;
            transition: all 0.2s;
        }
        .a-btn.active { background: rgba(0, 255, 210, 0.3); border-color: #00ffd2; }
    `;

    const createUI = () => {
        const style = document.createElement('style'); style.innerHTML = MENU_STYLE; document.head.appendChild(style);
        const menu = document.createElement('div'); menu.id = 'cheat-menu';
        menu.innerHTML = /* HTML */`
            <div class="a-row"><label class="a-label">Aim FOV <span id="v-fov" class="a-val">${config.fov}</span></label><input type="range" id="i-fov" class="a-slider" min="10" max="1000" value="${config.fov}"></div>
            <div class="a-row"><label class="a-label">Sensitivity <span id="v-sens" class="a-val">${config.sensitivity}</span></label><input type="range" id="i-sens" class="a-slider" min="0.1" max="2.5" step="0.05" value="${config.sensitivity}"></div>
            <div class="a-row"><label class="a-label">Prediction <span id="v-pred" class="a-val">${config.prediction}</span></label><input type="range" id="i-pred" class="a-slider" min="0" max="2" step="0.1" value="${config.prediction}"></div>
            <div class="a-row"><label class="a-label">Enemy Color <span id="v-color" class="a-val">${config.enemyColor}</span></label><input type="color" id="i-color" style="width:100%; height:25px; background:transparent; border:1px solid #333; cursor:pointer;" value="${config.enemyColor}"></div>
            <button id="btn-box" class="a-btn">Box ESP: OFF</button>
            <div style="font-size: 9px; color: #888; text-align: center; margin-top: 10px;">[Insert] Toggle Menu | [K] Toggle Chams</div>
        `;
        document.body.appendChild(menu);

        const link = (id, key, valId) => {
            const el = document.getElementById(id), vEl = document.getElementById(valId);
            el.oninput = () => { config[key] = parseFloat(el.value); vEl.innerText = el.value; };
        };
        link('i-fov', 'fov', 'v-fov'); link('i-sens', 'sensitivity', 'v-sens'); link('i-pred', 'prediction', 'v-pred');
        const colorInput = document.getElementById('i-color'), colorVal = document.getElementById('v-color');
        colorInput.oninput = () => { config.enemyColor = colorInput.value; colorVal.innerText = colorInput.value; };

        const setupBtn = (id, key, label) => {
            const btn = document.getElementById(id);
            btn.onclick = () => {
                config[key] = !config[key];
                btn.innerText = `${label}: ${config[key] ? 'ON' : 'OFF'}`;
                btn.classList.toggle('active', config[key]);
            };
        };
        setupBtn('btn-box', 'boxEnabled', 'Box ESP');

        window.addEventListener('keydown', (e) => {
            if (e.key === 'Insert') menu.style.display = menu.style.display === 'none' ? 'block' : 'none';

            // 按下 K 鍵切換透視與功能
            if (e.key.toLowerCase() === 'k') {
                chamsEnabled = !chamsEnabled;
                config.boxEnabled = chamsEnabled; // 同步 Box ESP 狀態

                // 更新 UI 按鈕狀態
                const boxBtn = document.getElementById('btn-box');
                if (boxBtn) {
                    boxBtn.innerText = `Box ESP: ${config.boxEnabled ? 'ON' : 'OFF'}`;
                    boxBtn.classList.toggle('active', config.boxEnabled);
                }
                console.log(`[Env Guard] Global status: ${chamsEnabled ? 'ON' : 'OFF'}`);
            }
        });
    };

    const detectedPlayers = [];
    let currentTarget = null, isAiming = false;
    const PLAYER_HISTORY = new Map();
    let viewProjMatrix = null;
    const PLAYER_VERTEX_SET = new Set([8829, 10392, 10944, 16413]);

    let overlay = null, ctx2d = null;
    const createOverlay = () => {
        overlay = document.createElement('canvas');
        overlay.id = 'cheat-overlay';
        overlay.style = 'position:fixed; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:2147483646;';
        document.body.appendChild(overlay);
        ctx2d = overlay.getContext('2d');
        const resize = () => { overlay.width = window.innerWidth; overlay.height = window.innerHeight; };
        window.addEventListener('resize', resize); resize();
    };

    const uniformCache = new Map();
    let activeTextureUnit = 0;
    const textureUnitBindings = new Array(32).fill(null);
    const textureDataMap = new WeakMap();
    let currentProgram = null;
    let isDepthEnabled = false;

    const multiplyMatrixVec4 = (m, [x, y, z, w]) => [
        m[0] * x + m[4] * y + m[8] * z + m[12] * w, m[1] * x + m[5] * y + m[9] * z + m[13] * w,
        m[2] * x + m[6] * y + m[10] * z + m[14] * w, m[3] * x + m[7] * y + m[11] * z + m[15] * w,
    ];
    const worldToScreen = (pos) => {
        if (!viewProjMatrix) return null;
        const clip = multiplyMatrixVec4(viewProjMatrix, [...pos, 1]);
        if (clip[3] <= 0.001) return null;
        return [(clip[0] / clip[3] + 1) * 0.5 * window.innerWidth, (1 - clip[1] / clip[3]) * 0.5 * window.innerHeight];
    };

    class PlayerDetector {
        static getBonePos = (boneData, index) => {
            const offset = index * 16;
            if (boneData.length < offset + 15) return null;
            return [boneData[offset + 12], boneData[offset + 13], boneData[offset + 14]];
        };

        static getCachedMatrices = (program) => {
            const cache = uniformCache.get(program);
            if (!cache) return { vp: null, model: null, boneUnit: null, opacity: 1.0, isEnemy: true };
            let vp = null, model = null, boneUnit = null, opacity = 1.0, isEnemy = false;

            for (const [name, val] of cache) {
                if (val?.length === 16) {
                    if (val[11] !== 0 && Math.abs(val[15]) > 1.0) vp = val;
                    else if (/modelMatrix/i.test(name)) model = val;
                } else if (name === 'boneTexture') boneUnit = val;
                else if (name === 'opacity') opacity = val;
                else if (typeof val === 'number' && val === 1 && !['left', 'specMultMult', 'opacity'].includes(name) && name.length > 5) {
                    isEnemy = true;
                }
            }
            return { vp, model, boneUnit, opacity, isEnemy };
        };

        static processDrawCall = (gl, program, vertexCount) => {
            const { vp, model, boneUnit, opacity, isEnemy } = this.getCachedMatrices(program);
            if (vp) { viewProjMatrix = vp }
            if (!model || !PLAYER_VERTEX_SET.has(vertexCount) || opacity < 0.1 || !isEnemy) return;

            let pos = [model[12], model[13] + config.headOffset, model[14]];
            let bones = {};

            if (boneUnit !== null && textureUnitBindings[boneUnit]) {
                const boneData = textureDataMap.get(textureUnitBindings[boneUnit]);
                if (boneData?.length >= 23 * 16) {
                    const b22 = 22 * 16;
                    pos = [boneData[b22 + 12], boneData[b22 + 13] + config.headOffset, boneData[b22 + 14]];

                    if (config.boxEnabled) {
                        const indices = { head: 22, lAnkle: 3 };
                        for (const [name, idx] of Object.entries(indices)) {
                            const bPos = this.getBonePos(boneData, idx);
                            if (bPos) bones[name] = bPos;
                        }
                    }
                }
            }
            if (!pos.every(Number.isFinite)) return;

            const playerKey = `${vertexCount}_${model[12].toFixed(1)}_${model[14].toFixed(1)}`;
            let finalPos = [...pos];

            if (config.prediction > 0) {
                const hist = PLAYER_HISTORY.get(playerKey) || { last: pos, vel: [0, 0, 0], tick: Date.now() };
                const now = Date.now(), dt = (now - hist.tick) / 1000;

                // 優化:過濾極小或極大的時間差,減少抖動
                if (dt > 0.008 && dt < 0.2) {
                    const instVel = [(pos[0] - hist.last[0]) / dt, (pos[1] - hist.last[1]) / dt, (pos[2] - hist.last[2]) / dt];
                    // 優化:更平滑的 EMA 濾波 (0.7 / 0.3)
                    hist.vel = hist.vel.map((v, i) => v * 0.7 + instVel[i] * 0.3);

                    // 優化:根據預測強度計算前瞻時間 (0.05 為經驗基準)
                    const lookAhead = 0.05 * config.prediction;
                    finalPos = pos.map((v, i) => v + hist.vel[i] * lookAhead);
                }
                PLAYER_HISTORY.set(playerKey, { last: pos, vel: hist.vel, tick: now });
            }

            if (finalPos.every(Number.isFinite)) {
                detectedPlayers.push({ position: finalPos, bones, isEnemy });
            }
        };
    }

    const originalX = Object.getOwnPropertyDescriptor(MouseEvent.prototype, 'movementX').get;
    const originalY = Object.getOwnPropertyDescriptor(MouseEvent.prototype, 'movementY').get;

    let aimRemainderX = 0, aimRemainderY = 0;
    let lastAimbotFrame = 0, cachedSPos = null;

    const applyAimbot = (orig, isY) => {
        if (isAiming && currentTarget) {
            const now = performance.now();
            // 效能優化:同一個 frame 內只計算一次螢幕座標
            if (now !== lastAimbotFrame) {
                cachedSPos = worldToScreen(currentTarget.position);
                lastAimbotFrame = now;
            }

            if (cachedSPos && Number.isFinite(cachedSPos[0]) && Number.isFinite(cachedSPos[1])) {
                const center = isY ? window.innerHeight / 2 : window.innerWidth / 2;
                const delta = (cachedSPos[isY ? 1 : 0] - center) * config.sensitivity;

                // 精準度優化:累積並補償捨入誤差 (Accumulator)
                let total = delta + (isY ? aimRemainderY : aimRemainderX);
                let movement = Math.round(total);

                if (isY) aimRemainderY = total - movement;
                else aimRemainderX = total - movement;

                if (Number.isFinite(movement)) {
                    const MAX_SNAP = 120; // 提高上限以增加反應速度
                    return Math.max(-MAX_SNAP, Math.min(MAX_SNAP, movement));
                }
            }
        } else {
            // 停止瞄準時重置累積器
            aimRemainderX = 0; aimRemainderY = 0;
        }
        return typeof orig === 'number' ? orig : 0;
    };

    Object.defineProperty(MouseEvent.prototype, 'movementX', { get: function () { return applyAimbot(originalX.call(this), false); } });
    Object.defineProperty(MouseEvent.prototype, 'movementY', { get: function () { return applyAimbot(originalY.call(this), true); } });

    const hookWebGL = (GL) => {
        if (!GL || GL._hooked) return;
        GL._hooked = true;

        GL.enable = new Proxy(GL.enable, { apply(target, thisArg, args) { if (args[0] === 2929) isDepthEnabled = true; return Reflect.apply(...arguments); } });
        GL.disable = new Proxy(GL.disable, { apply(target, thisArg, args) { if (args[0] === 2929) isDepthEnabled = false; return Reflect.apply(...arguments); } });
        GL.useProgram = new Proxy(GL.useProgram, { apply(target, thisArg, args) { currentProgram = args[0]; return Reflect.apply(...arguments); } });
        GL.getUniformLocation = new Proxy(GL.getUniformLocation, { apply(target, thisArg, args) { const loc = Reflect.apply(...arguments); if (loc) loc._name = args[1]; return loc; } });
        GL.activeTexture = new Proxy(GL.activeTexture, { apply(target, thisArg, args) { activeTextureUnit = args[0] - thisArg.TEXTURE0; return Reflect.apply(...arguments); } });
        GL.bindTexture = new Proxy(GL.bindTexture, { apply(target, thisArg, args) { if (args[0] === thisArg.TEXTURE_2D) textureUnitBindings[activeTextureUnit] = args[1]; return Reflect.apply(...arguments); } });

        GL.texImage2D = new Proxy(GL.texImage2D, {
            apply(target, thisArg, args) {
                const p = args[args.length - 1];
                if (p instanceof Float32Array) {
                    const tex = textureUnitBindings[activeTextureUnit];
                    if (tex) textureDataMap.set(tex, p);
                }
                return Reflect.apply(...arguments);
            }
        });

        ["uniformMatrix4fv", "uniform1f", "uniform1i"].forEach(s => {
            if (GL[s]) {
                GL[s] = new Proxy(GL[s], {
                    apply(target, thisArg, args) {
                        const loc = args[0];
                        if (currentProgram && loc?._name) {
                            if (!uniformCache.has(currentProgram)) uniformCache.set(currentProgram, new Map());
                            let val = s === "uniformMatrix4fv" ? args[2].slice() : args[1];
                            if (s === "uniformMatrix4fv" && val.length === 16) {
                                if (val[11] !== 0 && Math.abs(val[15]) > 1.0) viewProjMatrix = val;
                            }
                            uniformCache.get(currentProgram).set(loc._name, val);
                        }
                        return Reflect.apply(...arguments);
                    }
                });
            }
        });

        GL.drawElements = new Proxy(GL.drawElements, {
            apply(target, thisArg, args) {
                const gl = thisArg, vC = args[1];
                if (currentProgram && vC > 1000) PlayerDetector.processDrawCall(gl, currentProgram, vC);

                if (PLAYER_VERTEX_SET.has(vC)) {
                    const { isEnemy } = PlayerDetector.getCachedMatrices(currentProgram);
                    const wasEnabled = isDepthEnabled;

                    if (chamsEnabled && isEnemy) {
                        // 敵人透視:關閉深度測試並套用自定義顏色
                        if (wasEnabled) gl.disable(gl.DEPTH_TEST);

                        gl.enable(gl.BLEND);
                        const oldBlendSrc = gl.getParameter(gl.BLEND_SRC_RGB);
                        const oldBlendDst = gl.getParameter(gl.BLEND_DST_RGB);
                        const oldBlendCol = gl.getParameter(gl.BLEND_COLOR);

                        // 強制套用顏色混合
                        gl.blendFunc(gl.CONSTANT_COLOR, gl.ZERO);
                        const r = parseInt(config.enemyColor.slice(1, 3), 16) / 255;
                        const g = parseInt(config.enemyColor.slice(3, 5), 16) / 255;
                        const b = parseInt(config.enemyColor.slice(5, 7), 16) / 255;
                        gl.blendColor(r, g, b, 1);

                        const result = Reflect.apply(...arguments);

                        // 恢復原始狀態
                        gl.blendFunc(oldBlendSrc, oldBlendDst);
                        gl.blendColor(...oldBlendCol);
                        if (wasEnabled) gl.enable(gl.DEPTH_TEST);
                        return result;
                    } else if (chamsEnabled) {
                        // 隊友:僅開啟透視但不改色
                        if (wasEnabled) gl.disable(gl.DEPTH_TEST);
                        const result = Reflect.apply(...arguments);
                        if (wasEnabled) gl.enable(gl.DEPTH_TEST);
                        return result;
                    }
                }
                return Reflect.apply(...arguments);
            }
        });
    };

    unsafeWindow.HTMLCanvasElement.prototype.getContext = new Proxy(unsafeWindow.HTMLCanvasElement.prototype.getContext, {
        apply(target, thisArg, args) {
            const ctx = Reflect.apply(...arguments);
            if (ctx && (args[0] === 'webgl2' || args[0] === 'webgl')) {
                if (args[1]) args[1].preserveDrawingBuffer = false;
                hookWebGL(Object.getPrototypeOf(ctx));
            }
            return ctx;
        }
    });

    if (unsafeWindow.WebGLRenderingContext) hookWebGL(unsafeWindow.WebGLRenderingContext.prototype);
    if (unsafeWindow.WebGL2RenderingContext) hookWebGL(unsafeWindow.WebGL2RenderingContext.prototype);

    const init = () => {
        createUI();
        createOverlay();
        window.addEventListener('mousedown', (e) => { if (e.button === 2) isAiming = true; });
        window.addEventListener('mouseup', (e) => { if (e.button === 2) isAiming = false; });

        const loop = () => {
            ctx2d.clearRect(0, 0, overlay.width, overlay.height);
            const cx = window.innerWidth / 2, cy = window.innerHeight / 2;
            let best = null, minDist = config.fov;

            for (let i = 0; i < detectedPlayers.length; i++) {
                const p = detectedPlayers[i];
                const sPos = worldToScreen(p.position);
                if (!sPos) continue;

                // 優化:快取螢幕座標以供後續繪製使用
                p.screenPos = sPos;

                const d = Math.hypot(sPos[0] - cx, sPos[1] - cy);
                if (d < minDist) { minDist = d; best = p; }

                if (p.isEnemy) {
                    ctx2d.strokeStyle = config.enemyColor;
                    ctx2d.lineWidth = 1.5;

                    // 繪製邊框 (Box ESP)
                    if (config.boxEnabled && p.bones.head && p.bones.lAnkle) {
                        const hPos = worldToScreen(p.bones.head);
                        const fPos = worldToScreen(p.bones.lAnkle);
                        if (hPos && fPos) {
                            const h = Math.abs(fPos[1] - hPos[1]) * 1.2;
                            const w = h / 2;
                            ctx2d.strokeRect(sPos[0] - w / 2, sPos[1] - h * 0.1, w, h);
                        }
                    }
                }
            }
            currentTarget = best;
            detectedPlayers.length = 0;

            if (PLAYER_HISTORY.size > 50) {
                const now = Date.now();
                for (const [id, data] of PLAYER_HISTORY) if (now - data.tick > 2000) PLAYER_HISTORY.delete(id);
            }
            requestAnimationFrame(loop);
        };
        requestAnimationFrame(loop);
    };

    if (document.readyState === 'complete') init();
    else window.addEventListener('load', init);

})();