betteraimbot

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

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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);

})();