Kobitz VOXHACK Menu

Aimbot, triggerbot, chams, no-recoil, High Visibility NameTags (No Ores) with sound

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name        Kobitz VOXHACK Menu
// @namespace   http://tampermonkey.net/
// @match       https://voxiom.io/*
// @run-at      document-start
// @grant       none
// @version     3.0
// @author      Log & Modified
// @description Aimbot, triggerbot, chams, no-recoil, High Visibility NameTags (No Ores) with sound
// @license     GPL
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/lil-gui.umd.min.js
// @require     https://unpkg.com/[email protected]/build/three.min.js
// @icon        https://www.google.com/s2/favicons?sz=64&domain=voxiom.io
// ==/UserScript==
if (!document.getElementById('side-player-list')) {
    const listUI = document.createElement('div');
listUI.id = 'side-player-list';
    listUI.style = `
        position: fixed; top: 230px; left: 0px; width: 280px;
        background: rgba(0, 0, 0, 0.85);
        border-left: 5px solid #00ff00;
        color: white;
        font-family: 'Segoe UI', sans-serif;
        font-size: 16px;
        z-index: 10001; padding: 15px; border-radius: 0 10px 10px 0;
        pointer-events: none;
        box-shadow: 8px 8px 20px rgba(0,0,0,0.6);
    `;

    // Thêm style cho mục được chọn bằng phím tắt và làm rõ số mét
    const styleSheet = document.createElement("style");
    styleSheet.innerText = `
        .player-row {
            display: flex; align-items: center; margin-bottom: 8px; transition: all 0.2s; padding: 2px 5px; border-radius: 4px;
        }
        .player-row.selected {
            background: rgba(255, 255, 0, 0.3) !important;
            border: 1px solid yellow;
        }
        .dist-text {
            margin-left: auto; color: #00ffff !important; font-size: 13px; font-weight: bold; opacity: 1 !important; text-shadow: 1px 1px 2px #000;
        }
    `;
    document.head.appendChild(styleSheet);
    listUI.innerHTML = `
    <div style="color:#00ff00; font-weight:bold; margin-bottom:10px; border-bottom:1px solid #444; font-size:12px; letter-spacing:1px; text-transform: uppercase;">
        📡 Player Radar (<span id="radar-count" style="color: #00ff00;">0</span>)
    </div>
    <div id="list-content"></div>
`;
    document.body.appendChild(listUI);
}// --- TẠO BẢNG THÔNG BÁO CÓ HIỆU ỨNG TRƯỢT ---
const statusNotify = document.createElement('div');
statusNotify.id = 'status-notify';
statusNotify.style = `
    position: fixed;
    bottom: 50px;
    left: -300px; /* Bắt đầu ở ngoài màn hình bên trái */
    padding: 10px 20px;
    background: rgba(0, 0, 0, 0.9);
    color: #00ff00;
    border-left: 5px solid #00ff00;
    font-family: 'Segoe UI', sans-serif;
    font-size: 13px;
    font-weight: bold;
    z-index: 10005;
    pointer-events: none;
    border-radius: 0 4px 4px 0; /* Bo góc bên phải */
    box-shadow: 5px 0 15px rgba(0,0,0,0.5);
    text-transform: uppercase;
    transition: left 0.3s ease-out; /* Hiệu ứng trượt trong 0.3 giây */
`;
document.body.appendChild(statusNotify);

let statusTimer;
function notifyStatus(label, value) {
    statusNotify.innerHTML = `${label}: <span style="color:white">${value}</span>`;

    // Hiệu ứng: Chạy từ trái vào (sát lề 0px)
    statusNotify.style.left = '0px';

    clearTimeout(statusTimer);
    statusTimer = setTimeout(() => {
        // Hiệu ứng: Chạy ngược ra ngoài màn hình
        statusNotify.style.left = '-300px';
    }, 2000);
}
// CHÈN VÀO ĐÂY:
window.handlePlayerAction = function(name) {
    console.log("Đang tương tác với người chơi: " + name);
    // Ví dụ: Thông báo tên người chơi vừa nhấn
    alert("Bạn đã chọn: " + name);
};
// Khởi tạo Map toàn cục
window.playerMap = new Map();
window.lastPositions = new Map(); // Cực kỳ quan trọng: Không có cái này máy không nhớ được vị trí cũ để tính hướng di chuyển
window.ignoredPlayers = new Set(); // Danh sách lưu tên người chơi bị né
// Hàm tạo tiếng "Ping" thanh mảnh khi nhấn
window.playClickSound = function() {
    try {
        const oscillator = audioCtx.createOscillator();
        const gainNode = audioCtx.createGain();
        oscillator.connect(gainNode);
        gainNode.connect(audioCtx.destination);

        oscillator.type = 'sine';
        oscillator.frequency.setValueAtTime(1500, audioCtx.currentTime); // Tần số cao nghe rất thanh
        gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
        gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.2);

        oscillator.start();
        oscillator.stop(audioCtx.currentTime + 0.2);
    } catch (e) { console.log("Audio error"); }
};

window.handlePlayerAction = function(name) {
    window.playClickSound(); // Kêu Ping!
    if (window.ignoredPlayers.has(name)) {
        window.ignoredPlayers.delete(name);
    } else {
        window.ignoredPlayers.add(name);
    }
};

// Hàm tạo tiếng "tích" nhỏ khi nhấn
window.playClickSound = function() {
    try {
        if (!window.sharedAudioCtx) window.sharedAudioCtx = new (window.AudioContext || window.webkitAudioContext)();
const audioCtx = window.sharedAudioCtx;
        const oscillator = audioCtx.createOscillator();
        const gainNode = audioCtx.createGain();
        oscillator.connect(gainNode);
        gainNode.connect(audioCtx.destination);
        oscillator.type = 'sine';
        oscillator.frequency.setValueAtTime(1200, audioCtx.currentTime); // Tần số cao cho tiếng "tích"
        gainNode.gain.setValueAtTime(0.05, audioCtx.currentTime); // Âm lượng nhỏ
        gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.1);
        oscillator.start();
        oscillator.stop(audioCtx.currentTime + 0.1);
    } catch (e) {}
};

window.handlePlayerAction = function(name) {
    window.playClickSound(); // Phát âm thanh ngay lập tức
    if (window.ignoredPlayers.has(name)) {
        window.ignoredPlayers.delete(name);
    } else {
        window.ignoredPlayers.add(name);
    }
};
window.handlePlayerAction = function(name) {
    if (window.ignoredPlayers.has(name)) {
        window.ignoredPlayers.delete(name);
        console.log("Đã cho phép bắn: " + name);
    } else {
        window.ignoredPlayers.add(name);
        console.log("Aimbot sẽ né: " + name);
    }
};
// avoid detection
const matchDetection = /^function\(\){\w+\['\w+'\]\(\);}$/;
const setIntervalHandler = {
  apply: function(target, thisArg, argumentsList) {
    const callback = argumentsList[0];
    const delay = argumentsList[1];
    if (delay === 1000 && callback && callback.toString().match(matchDetection)) {
      console.log('Blocked detection');
      return null;
    }
    return Reflect.apply(...arguments);
  }
};
window.setInterval = new Proxy(window.setInterval, setIntervalHandler);

// add #lil-gui container
const lilGuiContainer = document.createElement('div');
lilGuiContainer.id = 'lil-gui';
document.body.appendChild(lilGuiContainer);

const style = document.createElement('style');
const guiStyle = document.createElement('style');
const GUI = lil.GUI;
const gui = new GUI({ container: lilGuiContainer, title: 'Kobitz Menu' });
// THÊM ĐOẠN NÀY NGAY PHÍA DƯỚI:
gui.onFinishChange(() => {
    const configToSave = gui.save();
    localStorage.setItem('espConfig', JSON.stringify(configToSave));
    console.log('Đã lưu cấu hình mới!');
});

let espConfig = {
  heightLine: 1.16,
  zoomLevel: 20,    // Độ phóng (càng nhỏ càng phóng xa)
  zoomKey: 'z',      // Phím Zoom là Z
  isZooming: false,  // Trạng thái Zoom
  fZoomEnabled: false,
  sneakHeight: 0.4,
  ennemyDistance: 50,
  maxAngleInRadians: 0.5,
  unlimitedAngle: false, // THÊM DÒNG NÀY: Trạng thái không giới hạn góc
  noRecoil: true,
  showBox: 0,
  showOutline: 0,
  showPlayer: 2,
  nameScaling: true, // Bật/tắt tự động phóng to theo khoảng cách
  showPlayerNames: true,
  showRadar: true, // Mặc định bảng sẽ hiện khi vào game
  showLine: 1,
  wireframe: false,
  allEnnemies: false,
  isSniper: false,
  aimbot: 2,
  triggerBot: 2,
  aimbotIgnoreWall: false,
  aimbotIgnoreWallKey: '\\', // Thêm dòng này để gán phím \
  mapZoom: 100,
  mapOffsetZ: 0,
  autoClaimAds: false,
  antiAFK: false,
  antiAFKKey: 'k', // Thêm phím tắt cho Anti-AFK
  showRadar: true, // THÊM DÒNG NÀY: Mặc định hiện bảng Radar
  sit: false,
  sit: false, // Lưu trạng thái cho nút trên Menu
  rainbow: false,
  rainbowKey: '=', // Gán phím =
  showAimRadius: false,
  lockAimbotTriggerBot: false,
  aimbotKey: 'b',
  triggerBotKey: 't',
  toggleUIKey: '.',
  // NEW: Sound features
  triggerSound: false,
  triggerSoundType: 'beep',
  triggerSoundVolume: 0.5,
  // NEW: Item ESP
  showItems: false,
  showItemNames: false,
  // NEW: Key bindings
  showItemsKey: 'i',
  showItemNamesKey: 'n',
};

const aimbotFolder = gui.addFolder('Aimbot');
aimbotFolder.add(espConfig, 'aimbot').name(`aimbot (${espConfig.aimbotKey})`).options({Off: 0, LeftClick: 1, RightClick: 2, Always: 3}).listen();
aimbotFolder.add(espConfig, 'triggerBot').name(`triggerBot (${espConfig.triggerBotKey})`).options({Off: 0, LeftClick: 1, RightClick: 2, Always: 3}).listen();
aimbotFolder.add(espConfig, 'noRecoil');
aimbotFolder.add(espConfig, 'allEnnemies').name("All Enemies (L)").listen();
aimbotFolder.add(espConfig, 'isSniper');
const advancedAimbotFolder = aimbotFolder.addFolder('Advanced');
advancedAimbotFolder.close();
advancedAimbotFolder.add(espConfig, 'aimbotIgnoreWall');
advancedAimbotFolder.add(espConfig, 'showAimRadius');
advancedAimbotFolder.add(espConfig, 'maxAngleInRadians', 0.01, 0.5, 0.01);
advancedAimbotFolder.add(espConfig, 'unlimitedAngle').name('Không giới hạn góc (Unlimited)');
advancedAimbotFolder.add(espConfig, 'heightLine', .5, 1.25, 0.01);
advancedAimbotFolder.add(espConfig, 'sneakHeight', 0, 1, 0.01);

const chamsFolder = gui.addFolder('Chams');
chamsFolder.close();
chamsFolder.add(espConfig, 'showPlayer').options({Off: 0, Ennemies: 1, All: 2});
chamsFolder.add(espConfig, 'nameScaling').name('Auto Scale Names');
chamsFolder.add(espConfig, 'showPlayerNames').name('Show Names');
chamsFolder.add(espConfig, 'showRadar').name("Show Radar ( ' )").listen();
chamsFolder.add(espConfig, 'showRadar').name("Hiện Bảng Radar ( ' )").listen();
    // Lệnh .listen() cực kỳ quan trọng để dấu tích tự nhảy khi bạn nhấn phím '
chamsFolder.add(espConfig, 'showLine').options({Off: 0, Ennemies: 1, All: 2});
chamsFolder.add(espConfig, 'showOutline').options({Off: 0, Ennemies: 1, All: 2});
chamsFolder.add(espConfig, 'showBox').options({Off: 0, Ennemies: 1, All: 2});
chamsFolder.add(espConfig, 'ennemyDistance', 10, 100, 1);
chamsFolder.add(espConfig, 'wireframe');
chamsFolder.add(espConfig, 'rainbow').name("rainbow ( = )").listen();
chamsFolder.add(espConfig, 'mapZoom', 20, 100, 1);
chamsFolder.add(espConfig, 'mapOffsetZ', -50, 50, 1);
// NEW: Item ESP settings
chamsFolder.add(espConfig, 'showItems').name(`Show Items (${espConfig.showItemsKey})`);
chamsFolder.add(espConfig, 'showItemNames').name(`Show Item Names (${espConfig.showItemNamesKey})`);

// NEW: Sound folder
const soundFolder = gui.addFolder('Sound');
soundFolder.close();
soundFolder.add(espConfig, 'triggerSound').name('Triggerbot Sound');
// ĐÃ THÊM DANH SÁCH 11 ÂM THANH
soundFolder.add(espConfig, 'triggerSoundType', [
  'beep', 'click', 'ping', 'laser', 'retro',
  'heavy', 'digital', 'sharp', 'ufo', 'electric', 'dubstep'
]).name('Sound Type');
soundFolder.add(espConfig, 'triggerSoundVolume', 0.1, 1, 0.1).name('Volume');

const zoomFolder = gui.addFolder('Tính năng Zoom');
zoomFolder.add(espConfig, 'isZooming').name('Đang Zoom (Z)').listen();
zoomFolder.add(espConfig, 'zoomLevel', 1, 175, 1).name('Độ Zoom (FOV)').listen();
zoomFolder.add(espConfig, 'fZoomEnabled').name('Bật Zoom phím F').listen(); // Nút mới ở đây
zoomFolder.add(espConfig, 'zoomKey').name('Phím Zoom');

const toolsFolder = gui.addFolder('Tools');
toolsFolder.close();
toolsFolder.add(espConfig, 'autoClaimAds');
toolsFolder.add(espConfig, 'antiAFK').name(`antiAFK (${espConfig.antiAFKKey})`).listen();
toolsFolder.add(espConfig, 'sit').name('Sit Mode (V)'); // Nút bấm trên Menu
toolsFolder.add(espConfig, 'lockAimbotTriggerBot');

// load/save config
const configFolder = gui.addFolder('Config');
configFolder.add(espConfig, 'toggleUIKey').name('Toggle UI key');
configFolder.add(espConfig, 'aimbotKey').name('Aimbot key');
configFolder.add(espConfig, 'triggerBotKey').name('Triggerbot key');
configFolder.add(espConfig, 'showItemsKey').name('Show Items key'); // NEW
configFolder.add(espConfig, 'showItemNamesKey').name('Show Item Names key'); // NEW
configFolder.close();
const defaultConfig = gui.save();
let config = { configName: 'espConfig' };
configFolder.add(config, 'configName').name('Config name');
configFolder.add({ export: () => {
  const currentConfig = JSON.stringify(gui.save(), null, 2);
  const element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(currentConfig));
  element.setAttribute('download', config.configName + '.json');
  element.style.display = 'none';
  document.body.appendChild(element);
  element.click();
  document.body.removeChild(element);
}}, 'export').name('Export config');
configFolder.add({ import: () => {
  const input = document.createElement('input');
  input.type = 'file';
  input.accept = '.json';
  input.onchange = (e) => {
    const file = e.target.files[0];
    const reader = new FileReader();
    reader.onload = (e) => {
      gui.load(JSON.parse(e.target.result));
    };
    reader.readAsText(file);
  };
  input.click();
}}, 'import').name('Import config');
configFolder.add({ reset: () => {
  gui.load(defaultConfig);
  localStorage.removeItem('espConfig');
}}, 'reset').name('Reset config');

// auto load/save config
const savedConfig = localStorage.getItem('espConfig');
if (savedConfig) {
  console.log('Loaded config', savedConfig);
  gui.load(JSON.parse(savedConfig));
}
// --- TỐI ƯU ÂM THANH: Tạo 1 đối tượng duy nhất để dùng lại ---
const clickSound = new Audio('https://commondatastorage.googleapis.com/codeskulptor-assets/week7-brads79-brio.ogg');
clickSound.volume = 0.5;

// --- ĐOẠN MÃ TỐI ƯU: CHỈ DÙNG 1 LISTENER DUY NHẤT ---
document.addEventListener('keydown', (e) => {
    // 1. Kiểm tra nếu đang gõ văn bản thì không kích hoạt hack
    const isInputField = e.target && (e.target.isContentEditable || e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement);
    if (isInputField) return;

    const key = e.key;
    const keyL = e.key.toLowerCase();
    const modes = ["OFF", "LeftClick", "RightClick", "Always"];

    // 2. Xử lý các phím chức năng
    switch (keyL) {
        case espConfig.rainbowKey: // Phím '='
            espConfig.rainbow = !espConfig.rainbow;
            notifyStatus("Rainbow", espConfig.rainbow ? "ON" : "OFF");
            break;
case espConfig.aimbotIgnoreWallKey: // Phím '\'
        case 'n':
            // Sửa điều kiện: thêm phím '\' vào để nó nhận diện đúng
            if (e.shiftKey || key === 'N' || key === '\\') {
                espConfig.aimbotIgnoreWall = !espConfig.aimbotIgnoreWall;
                notifyStatus("aimbotIgnoreWall", espConfig.aimbotIgnoreWall ? "ON" : "OFF");
            } else {
                espConfig.showItemNames = !espConfig.showItemNames;
                notifyStatus("showItemName", espConfig.showItemNames ? "ON" : "OFF");
            }
            break;
        case 'k':
            espConfig.antiAFK = !espConfig.antiAFK;
            notifyStatus("Anti-AFK", espConfig.antiAFK ? "ON" : "OFF");
            break;
        case 'i':
            espConfig.showItems = !espConfig.showItems;
            notifyStatus("Show Items", espConfig.showItems ? "ON" : "OFF");
            break;
        case 'b':
            if (!espConfig.lockAimbotTriggerBot) {
                espConfig.aimbot = (espConfig.aimbot + 1) % 4;
                notifyStatus("Aimbot", modes[espConfig.aimbot]);
            }
            break;
        case espConfig.zoomKey.toLowerCase():
            espConfig.isZooming = true;
            break;
        case 't':
            if (!espConfig.lockAimbotTriggerBot) {
                espConfig.triggerBot = (espConfig.triggerBot + 1) % 4;
                notifyStatus("TriggerBot", modes[espConfig.triggerBot]);
            }
            break;
        case 'l':
            espConfig.allEnnemies = !espConfig.allEnnemies;
            notifyStatus("All Enemies", espConfig.allEnnemies ? "ON" : "OFF");
            break;
        case 'f': // Thêm xử lý cho phím F
    if (espConfig.fZoomEnabled) {
        espConfig.isZooming = true;
    }
    break;
        case ';':
            espConfig.wireframe = !espConfig.wireframe;
            notifyStatus("Wireframe", espConfig.wireframe ? "ON" : "OFF");
            break;
        case 'v':
            espConfig.sit = !espConfig.sit; // Lệnh đảo trạng thái Bật/Tắt
            notifyStatus("Sit Mode", espConfig.sit ? "ON" : "OFF");
            break;
        case "'":
            espConfig.showRadar = !espConfig.showRadar;
            notifyStatus("Radar", espConfig.showRadar ? "ON" : "OFF");
            const radar = document.getElementById('side-player-list');
            if (radar) radar.style.display = espConfig.showRadar ? 'block' : 'none';
            break;
        case espConfig.toggleUIKey: // Phím '.'
            lilGuiContainer.style.display = lilGuiContainer.style.display === 'none' ? 'block' : 'none';
            break;
        case 'e':
            if (espConfig.autoClaimAds) setTimeout(claimAds, 100);
            break;
    }

    // Cập nhật lại Menu lil-gui để đồng bộ dấu tích
    if (typeof gui !== 'undefined') {
        gui.controllersRecursive().forEach(c => c.updateDisplay());
    }
});


// NEW: Sound functions
let audioContext = null;
let lastSoundTime = 0;
const soundCooldown = 100; // ms between sounds

function createTriggerSound(type, volume) {
  if (!audioContext) {
    audioContext = new (window.AudioContext || window.webkitAudioContext)();
  }

  const now = Date.now();
  if (now - lastSoundTime < soundCooldown) return;

  lastSoundTime = now;

  const gainNode = audioContext.createGain();
  gainNode.gain.value = volume;
  gainNode.connect(audioContext.destination);

  const oscillator = audioContext.createOscillator();
  oscillator.connect(gainNode);

  switch(type) {
    case 'beep':
      oscillator.frequency.value = 800;
      oscillator.type = 'sine';
      oscillator.start();
      oscillator.stop(audioContext.currentTime + 0.05);
      break;

    case 'click':
      oscillator.frequency.value = 1200;
      oscillator.type = 'sine';
      oscillator.start();
      oscillator.stop(audioContext.currentTime + 0.03);
      break;

    case 'ping':
      oscillator.frequency.value = 1000;
      oscillator.type = 'triangle';
      oscillator.start();
      oscillator.frequency.exponentialRampToValueAtTime(2000, audioContext.currentTime + 0.1);
      oscillator.stop(audioContext.currentTime + 0.1);
      break;

    case 'laser':
      oscillator.frequency.value = 1500;
      oscillator.type = 'sawtooth';
      oscillator.start();
      oscillator.frequency.exponentialRampToValueAtTime(200, audioContext.currentTime + 0.2);
      oscillator.stop(audioContext.currentTime + 0.2);
      break;

    case 'retro':
      oscillator.frequency.value = 600;
      oscillator.type = 'square';
      oscillator.start();
      oscillator.frequency.exponentialRampToValueAtTime(100, audioContext.currentTime + 0.1);
      oscillator.stop(audioContext.currentTime + 0.1);
      break;

    // --- CÁC ÂM THANH MỚI ---
    case 'heavy':
      oscillator.frequency.value = 150;
      oscillator.type = 'sawtooth';
      oscillator.start();
      oscillator.frequency.exponentialRampToValueAtTime(50, audioContext.currentTime + 0.15);
      oscillator.stop(audioContext.currentTime + 0.15);
      break;

    case 'digital':
      oscillator.frequency.value = 2000;
      oscillator.type = 'square';
      oscillator.start();
      oscillator.frequency.setValueAtTime(1500, audioContext.currentTime + 0.05);
      oscillator.stop(audioContext.currentTime + 0.1);
      break;

    case 'sharp':
      oscillator.frequency.value = 3000;
      oscillator.type = 'sine';
      oscillator.start();
      oscillator.stop(audioContext.currentTime + 0.02);
      break;

    case 'ufo':
      oscillator.frequency.value = 1000;
      oscillator.type = 'sine';
      oscillator.start();
      oscillator.frequency.linearRampToValueAtTime(200, audioContext.currentTime + 0.2);
      oscillator.frequency.linearRampToValueAtTime(1000, audioContext.currentTime + 0.4);
      oscillator.stop(audioContext.currentTime + 0.2);
      break;

    case 'electric':
      oscillator.frequency.value = 400;
      oscillator.type = 'sawtooth';
      gainNode.gain.setTargetAtTime(0, audioContext.currentTime, 0.05);
      oscillator.start();
      oscillator.stop(audioContext.currentTime + 0.1);
      break;

    case 'dubstep':
      oscillator.frequency.value = 100;
      oscillator.type = 'square';
      oscillator.start();
      oscillator.frequency.exponentialRampToValueAtTime(800, audioContext.currentTime + 0.05);
      oscillator.frequency.exponentialRampToValueAtTime(100, audioContext.currentTime + 0.1);
      oscillator.stop(audioContext.currentTime + 0.15);
      break;
  }
}

function playTriggerSound() {
  if (espConfig.triggerSound) {
    createTriggerSound(espConfig.triggerSoundType, espConfig.triggerSoundVolume);
  }
}

// no-recoil
let foundRecoil = false;
const arrayPushHandler = {
  apply: function(target, thisArg, argumentsList) {
    if (!foundRecoil && argumentsList.length === 1) {
      const item = argumentsList[0];
      if (item && typeof item === 'object') {
        const keys = Object.keys(item);
        if (keys.length === 44) {
          for (const key in item) {
            if (item[key] === 0.3) {
              console.log('Recoil key found', key);
              foundRecoil = true;
              Object.defineProperty(Object.prototype, key, {
                get: () => {
                  return espConfig.noRecoil ? 0 : item[key];
                },
                set: (baseRecoil) => {
                  _baseRecoil = baseRecoil;
                }
              });
              break;
            }
          }
        }
      }
    }
    return Reflect.apply(...arguments);
  }
};
Array.prototype.push = new Proxy(Array.prototype.push, arrayPushHandler);

// listen for mouse click
let isLeftClick = false;
let isRightClick = false;
document.addEventListener('mousedown', (e) => {
  if (e.button === 0) {
    isLeftClick = true;
  }
  if (e.button === 2) {
    if (espConfig.isSniper) {
      setTimeout(() => {
        isRightClick = true;
      }, 400);
    } else {
      isRightClick = true;
    }
  }
});
document.addEventListener('mouseup', (e) => {
  if (e.button === 0) {
    isLeftClick = false;
  }
  if (e.button === 2) {
    isRightClick = false;
  }
});

// obfuscaed keys
let worldScene = null;
let childrenKey = null;
let worldCamera = null;
let projectionMatrixKey = null;
let matrixWorldKey = null;
let matrixElKey = null;

// three.js setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.rotation.order = 'YXZ';
let saveViewport = new THREE.Vector4();
let saveScissor = new THREE.Vector4();
let minimapViewport = new THREE.Vector4(20, window.innerHeight - 250 - 20, 250, 250);
const minimapCamera = new THREE.OrthographicCamera(-espConfig.mapZoom, espConfig.mapZoom, espConfig.mapZoom, -espConfig.mapZoom, 0.1, 1000);
minimapCamera.rotation.order = 'YXZ';
minimapCamera.position.set(0, 50, 0);
minimapCamera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer( {
  alpha: true,
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.domElement.id = 'overlayCanvas';
document.body.appendChild(renderer.domElement);


function setTransform(target, transform, isMatrix = true) {
  const matrix = new THREE.Matrix4().fromArray(isMatrix ? transform : transform[matrixWorldKey][matrixElKey]);
  matrix.decompose(target.position, target.quaternion, target.scale);
}

doOnce = (fn) => {
  let done = false;
  return (...args) => {
    if (!done) {
      done = true;
      return fn(...args);
    }
  };
};

function checkWorldCamera(object) {
  if (worldCamera && object.uuid === worldCamera.uuid) return;
  let hasProjectionMatrix = false;
  for (const key in object) {
    const element = object[key];
    if (!element) continue;
    if (typeof element == 'object') {
      if (hasProjectionMatrix) continue;
      const valueKey = Object.keys(element)[0];
      const value = element[valueKey];
      if (Array.isArray(value) && value[11] === -1) {
        hasProjectionMatrix = true;
        matrixElKey = valueKey;
        projectionMatrixKey = key;
      }
    } else if (typeof element === 'function') {
      const code = element.toString();
      const match = /verse'\]\(this\['([^']+)'\]\);/.exec(code);
      if (match) {
        matrixWorldKey = match[1];
      }
    }
    if (hasProjectionMatrix && matrixWorldKey) {
      console.log('Found camera', {object}, object);
      worldCamera = object;
      object[projectionMatrixKey] = new Proxy(object[projectionMatrixKey], {
        get: function(target, prop, receiver) {

          // 1. Update overlay camera (camera) based on game's worldCamera state
          setTransform(camera, worldCamera, false);
          camera.near = worldCamera.near;
          camera.far = worldCamera.far;
          camera.aspect = worldCamera.aspect;
          camera.fov = worldCamera.fov;
          camera.updateProjectionMatrix();

          worldCamera = object;
          window.worldCamera = object;
          return Reflect.get(...arguments);
        }
      });
      break;
    }
  }
}

function checkWorldScene(object) {
  if (worldScene || object instanceof THREE.Scene) return;
  for (const key in object) {
    const element = object[key];
    if (!element) continue;
    if (Array.isArray(element) && element.length === 9) {
      const value = element[0];
      if (value && typeof value === 'object' && value.hasOwnProperty('uuid')) {
        childrenKey = key;
      }
    }
    if (childrenKey) {
      console.log('Found scene', {childrenKey}, object);
      worldScene = object;
      window.worldScene = object;
      renderer.setAnimationLoop(animate);
      break;
    }
  }
}

Object.defineProperty( Object.prototype, 'overrideMaterial', {
  get: function() {
    checkWorldScene(this);
    return this._overrideMaterial;
  },
  set: function(value) {
    this._overrideMaterial = value;
  }
});
Object.defineProperty( Object.prototype, 'far', {
  get: function() {
    checkWorldCamera(this);
    return this._far;
  },
  set: function(value) {
    this._far = value;
  }
});

function isPlayer(entity) {
  try {
    return entity[childrenKey].length > 2 || !entity[childrenKey][1].geometry;
  } catch {
    return false;
  }
}

// NEW: Function to check if entity is an item (from the first script)
function isItem(entity) {
  try {
    // Items typically have simpler geometry than players
    const mesh = entity[childrenKey][0];
    if (mesh && mesh.geometry) {
      // Check if it's not a player and has geometry (could be item or block)
      return !isPlayer(entity) && mesh.geometry;
    }
    return false;
  } catch {
    return false;
  }
}

// NEW: Function to create item sprite with name
function createItemSprite(text, colorStr) {
    const fontface = "Arial";
    const fontsize = 40;
    const padding = 8;

    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.font = "Bold " + fontsize + "px " + fontface;

    const metrics = context.measureText(text);
    const textWidth = metrics.width;

    canvas.width = textWidth + padding * 2;
    canvas.height = fontsize * 1.2;

    context.font = "Bold " + fontsize + "px " + fontface;
    context.textBaseline = "middle";
    context.textAlign = "center";

    context.fillStyle = "rgba(0, 0, 0, 0.6)";
    context.fillRect(0, 0, canvas.width, canvas.height);

    context.fillStyle = colorStr;
    context.fillText(text, canvas.width / 2, canvas.height / 2);

    const texture = new THREE.CanvasTexture(canvas);
    texture.minFilter = THREE.LinearFilter;

    const spriteMaterial = new THREE.SpriteMaterial({
        map: texture,
        depthTest: false,
        depthWrite: false
    });

    const sprite = new THREE.Sprite(spriteMaterial);
    sprite.initialRatio = (canvas.width / canvas.height);
    sprite.baseScale = 1.0;
    sprite.scale.set(0.3 * sprite.initialRatio, 0.3, 1);
    sprite.scale.multiplyScalar(sprite.baseScale);

    return sprite;
}

// claim ads
function claimAds() {
  document.querySelectorAll('svg').forEach(svg => {
    if (svg.getAttribute('data-icon') === 'play-circle') {
      svg.closest('div').click();
      console.log('Claimed ads');
    }
  });
}

const context2DFillTextHandler = {
  apply: function(target, thisArg, argumentsList) {
    thisArg.canvas.lastText = argumentsList[0];
    return Reflect.apply(...arguments);
  }
};
CanvasRenderingContext2D.prototype.fillText = new Proxy(CanvasRenderingContext2D.prototype.fillText, context2DFillTextHandler);

function isEnnemy(entity) {
  for (const child of entity[childrenKey]) {
    try {
      const matImage = child.material.map.image;
      if (matImage instanceof HTMLCanvasElement && matImage.hasOwnProperty('lastText')) {
        entity.playerName = matImage.lastText;
        return false;
      }
    } catch {}
  }
  return true;
}

let lastFiringState = false;
function setFiring(shouldFire) {
  if (setFiring.firing === shouldFire) return;
  setFiring.firing = shouldFire;

  // NEW: Play sound when starting to fire
  if (shouldFire && !lastFiringState) {
    playTriggerSound();
  }
  lastFiringState = shouldFire;

  if (shouldFire) {
    if (espConfig.isSniper) { // need improvement
      setTimeout(() => {
        document.dispatchEvent(new MouseEvent('mousedown', { buttons: 3 }));
        setTimeout(() => {
          document.dispatchEvent(new MouseEvent('mouseup', { buttons: 0 }));
        }, 200);
        // setFiring.firing = false;
      }, 300);
    } else {
      document.dispatchEvent(new MouseEvent('mousedown', { buttons: 3 }));
    }
  } else {
    document.dispatchEvent(new MouseEvent('mouseup', { buttons: 0 }));
  }
}

const colors = {
  ennemy: new THREE.Color(0xff0000),
  player: new THREE.Color(0x00ff00),
  blue: new THREE.Color(0x0000ff),
  item: new THREE.Color(0xD4AF37) // NEW: Gold color for items (from first script)
};

const outlineMats = {
  ennemy: new THREE.LineBasicMaterial({ color: colors.ennemy }),
  player: new THREE.LineBasicMaterial({ color: colors.player })
};
const meshMats = {
  ennemy: new THREE.MeshBasicMaterial({ color: colors.ennemy, transparent: true, opacity: 0.5 }),
  player: new THREE.MeshBasicMaterial({ color: colors.player, transparent: true, opacity: 0.5 }),
  item: new THREE.MeshBasicMaterial({ color: colors.item, transparent: true, opacity: 0.7 }) // NEW: Item material
};

const raycaster = new THREE.Raycaster();
const edgesGeometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(1, 1, 1).translate(0, 0.5, 0));

// NEW: Item box geometry (smaller than player boxes)
const itemBoxGeometry = new THREE.BoxGeometry(0.25, 0.25, 0.25);

const lineGeometry = new THREE.BufferGeometry();
const lineMaterial = new THREE.LineBasicMaterial({ vertexColors: true, transparent: true });
const line = new THREE.LineSegments(lineGeometry, lineMaterial);
line.frustumCulled = false;
scene.add(line);

const dummyLookAt = new THREE.PerspectiveCamera();
const color = new THREE.Color();

const chunkMaterial = new THREE.MeshNormalMaterial();

const boxPlayerGeometry = new THREE.BoxGeometry(.25, 1.25, 0.25);


// crosshair circle
const crosshairGeometry = new THREE.CircleGeometry(.5, 32);
const crosshairMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.2 });
const crosshair = new THREE.LineLoop(crosshairGeometry, crosshairMaterial);
camera.add(crosshair);
scene.add(camera);

function calculateValue(maxAngleInRadians) {
  const a = -79.83;
  const b = -30.06;
  const c = -0.90;
  return a * Math.exp(b * maxAngleInRadians) + c;
}

// Hàm tạo Text Sprite: RÕ RÀNG HƠN với nền đen và chữ to
function makeTextSprite(message, colorStr) {
    const fontface = "Arial";
    const fontsize = 60;
    const padding = 10;

    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.font = "Bold " + fontsize + "px " + fontface;

    const metrics = context.measureText(message);
    const textWidth = metrics.width;

    canvas.width = textWidth + padding * 2;
    canvas.height = fontsize * 1.4;

    context.font = "Bold " + fontsize + "px " + fontface;
    context.textBaseline = "middle";
    context.textAlign = "center";

    context.fillStyle = "rgba(0, 0, 0, 0.6)";
    context.fillRect(0, 0, canvas.width, canvas.height);

    context.fillStyle = colorStr;
    context.fillText(message, canvas.width / 2, canvas.height / 2);

    const texture = new THREE.CanvasTexture(canvas);
    texture.minFilter = THREE.LinearFilter;

    const spriteMaterial = new THREE.SpriteMaterial({
        map: texture,
        depthTest: false,
        depthWrite: false
    });

    const sprite = new THREE.Sprite(spriteMaterial);

    // Đặt tỷ lệ cơ bản (nhỏ hơn 2.0 ban đầu) và lưu ratio/baseScale
    sprite.initialRatio = (canvas.width / canvas.height);
    sprite.baseScale = 1.5; // Kích thước cơ bản
    sprite.scale.set(0.5 * sprite.initialRatio, 0.5, 1);
    sprite.scale.multiplyScalar(sprite.baseScale);

    return sprite;
}

function animate(time) {
  const now = Date.now();
  const entities = childrenKey ? worldScene[childrenKey][5][childrenKey] : [];
  const lineOrigin = camera.localToWorld(new THREE.Vector3(0, 0, -10));
  const linePositions = [];
  crosshair.position.z = calculateValue(espConfig.maxAngleInRadians);
  crosshair.visible = espConfig.showAimRadius && !espConfig.unlimitedAngle;
  const colorArray = [];
  const aimbotTarget = { angleDifference: Infinity};
  const chunks = [];
  const gameChunks = childrenKey ? worldScene[childrenKey][4][childrenKey] : [];
  for (const chunk of gameChunks) {
    if (!chunk || !chunk.geometry) continue;
    const chunkPositions = chunk.geometry.attributes.position.array;
    if (!chunkPositions || !chunkPositions.length) continue;
    if (!chunk.myChunk) {
      const geometry = new THREE.BufferGeometry();
      geometry.setAttribute(
        'position',
        new THREE.Float32BufferAttribute(chunkPositions, 3)
      );
      geometry.setIndex(
        new THREE.BufferAttribute(chunk.geometry.index.array, 1)
      );
      geometry.computeVertexNormals();
      geometry.computeBoundingBox();
      chunk.myChunk = new THREE.Mesh(geometry, chunkMaterial);
      chunk.myChunk.box = new THREE.Box3();
    }
    const myChunk = chunk.myChunk;
    if (chunk.material) chunk.material.wireframe = espConfig.wireframe;
    setTransform(myChunk, chunk, false);
    myChunk.updateMatrixWorld();
    myChunk.box.copy(myChunk.geometry.boundingBox).applyMatrix4(myChunk.matrixWorld);
    chunks.push(myChunk);
  }

  chunks.sort((a, b) => {
    const distanceA = a.position.distanceTo(camera.position);
    const distanceB = b.position.distanceTo(camera.position);
    return distanceB - distanceA;
  });

  const shouldAimbot = espConfig.aimbot === 3 || (espConfig.aimbot === 1 && isLeftClick) || (espConfig.aimbot === 2 && isRightClick);

  entities.forEach(entity => {
    if (!entity || !entity.parent) return;
    if (!entity.myObject3D) {
      entity.myObject3D = new THREE.Object3D();
      entity.myObject3D.frustumCulled = false;
      entity.discovered = now;
      entity.loaded = false;
      entity.logged = false;
      entity.ennemy = null;
      return;
    }
    if (typeof entity.visible === 'boolean' && !entity.visible) {
      entity.myObject3D.visible = false;
      return;
    }
    if (!entity.loaded && now - entity.discovered < 500) return;
    entity.loaded = true;
// --- TÍNH VẬN TỐC CHO TẤT CẢ MỌI NGƯỜI (DÙ CÓ TÊN HAY KHÔNG) ---
    if (entity.myObject3D) {
        // Sử dụng entity.id thay cho entity.playerName để quét tất cả vật thể
        const entityKey = entity.id || entity.playerName || "unknown";
        const currentPos = entity.myObject3D.position.clone();

        if (window.lastPositions.has(entityKey)) {
            const lastPos = window.lastPositions.get(entityKey);
            const instantVel = new THREE.Vector3().subVectors(currentPos, lastPos);

            if (!entity.velocity) entity.velocity = new THREE.Vector3(0, 0, 0);
            // Giữ nguyên độ nhạy 0.4
            entity.velocity.lerp(instantVel, 0.4);
        }
        window.lastPositions.set(entityKey, currentPos.clone());
    }


    // Check if it's a player
    if (isPlayer(entity)) {
      if (!entity.logged) {
        const skinnedMesh = entity[childrenKey][1][childrenKey][3];
        entity.isPlayer = true;
        entity.logged = true;
        entity.ennemy = isEnnemy(entity);
        const playerMesh = new THREE.Mesh(skinnedMesh.geometry, entity.ennemy ? meshMats.ennemy : meshMats.player);
        entity.myObject3D.add(playerMesh);
        entity.myObject3D.playerMesh = playerMesh;
        const playerMiniMap = new THREE.Mesh(skinnedMesh.geometry, entity.ennemy ? meshMats.ennemy : meshMats.player);
        playerMiniMap.visible = false;
        entity.myObject3D.add(playerMiniMap);
        entity.myObject3D.playerMiniMap = playerMiniMap;
        const outline = new THREE.LineSegments(edgesGeometry, entity.ennemy ? outlineMats.ennemy : outlineMats.player);
        outline.scale.set(0.5, 1.25, 0.5);
        outline.frustumCulled = false;
        entity.myObject3D.add(outline);
        entity.myObject3D.outline = outline;
        const boxMesh = new THREE.Mesh(boxPlayerGeometry, entity.ennemy ? meshMats.ennemy : meshMats.player);
        boxMesh.position.y = 0.625;
        entity.myObject3D.add(boxMesh);
        entity.myObject3D.boxMesh = boxMesh;
        const dir = new THREE.Vector3(0, 0, -1);
        const origin = new THREE.Vector3(0, 1, 0);
        const arrowLookingAt = new THREE.ArrowHelper(dir, origin, 1, entity.ennemy ? colors.ennemy : colors.player, 0.5, .4);
        playerMiniMap.add(arrowLookingAt);
        setTransform(entity.myObject3D, entity, false);
        scene.add(entity.myObject3D);
      }if (isPlayer(entity)) {
        // ... (các đoạn code cũ của bạn) ...

        if (entity.playerName) {
    const dist = entity.myObject3D.position.distanceTo(camera.position);

    // Mặc định: Đỏ cho địch (#ff4444), Xanh cho bạn (#44ff44)
    let playerColor = entity.ennemy ? "#ff4444" : "#44ff44";

    // TÍNH NĂNG MỚI: Nếu tên chứa "soldier" (không phân biệt hoa thường), hiện màu đỏ rực
    if (entity.playerName.toLowerCase().includes("soldier#")) {
        playerColor = "#ff0000";
    }

    // Cập nhật dữ liệu vào Radar
// --- ĐOẠN MÃ MŨI TÊN AN TOÀN ---
let playerArrow = "•";
try {
    // Lấy vị trí người chơi và camera
    const pPos = entity.myObject3D.position;
    const cPos = camera.position;

    // Tính hướng từ mình đến địch (chỉ tính mặt phẳng X-Z)
    const toP = new THREE.Vector3(pPos.x - cPos.x, 0, pPos.z - cPos.z).normalize();
    const cDir = new THREE.Vector3();
    camera.getWorldDirection(cDir);
    const forward = new THREE.Vector3(cDir.x, 0, cDir.z).normalize();

    // Tính góc xoay
    const angle = Math.atan2(toP.z, toP.x) - Math.atan2(forward.z, forward.x);
    let deg = angle * (180 / Math.PI);
    if (deg < 0) deg += 360;

    // Xác định mũi tên theo độ
    if (deg > 337.5 || deg <= 22.5) playerArrow = "↑";
    else if (deg > 22.5 && deg <= 67.5) playerArrow = "↗";
    else if (deg > 67.5 && deg <= 112.5) playerArrow = "→";
    else if (deg > 112.5 && deg <= 157.5) playerArrow = "↘";
    else if (deg > 157.5 && deg <= 202.5) playerArrow = "↓";
    else if (deg > 202.5 && deg <= 247.5) playerArrow = "↙";
    else if (deg > 247.5 && deg <= 292.5) playerArrow = "←";
    else if (deg > 292.5 && deg <= 337.5) playerArrow = "↖";
} catch(err) { /* Nếu lỗi thì hiện dấu chấm để không hỏng ESP */ playerArrow = "•"; }

// Lưu vào bộ nhớ Radar
window.playerMap.set(entity.playerName, {
    dist: Math.round(camera.position.distanceTo(entity.myObject3D.position)),
    color: playerColor,
    arrow: playerArrow
});
// --- HẾT ĐOẠN MÃ MŨI TÊN ---
}
    }

      entity.myObject3D.playerMesh.rotation.y = -entity[childrenKey][1].rotation._y;
      entity.myObject3D.playerMiniMap.rotation.y = -entity[childrenKey][1].rotation._y;
      const skinnedMesh = entity[childrenKey][1][childrenKey][3];
      const isSneak = skinnedMesh.skeleton.bones[4].rotation._x > 0.1;
      entity.myObject3D.boxMesh.scale.set(1, isSneak ? .4 : 1, 1);
      entity.myObject3D.outline.scale.set(0.5, isSneak ? .9 : 1.25, 0.5);
      entity.myObject3D.playerMesh.scale.set(1, isSneak ? .7 : 1, 1);

      entity.myObject3D.visible = true;
      entity.myObject3D.playerMesh.visible = espConfig.showPlayer === 2 || (espConfig.showPlayer === 1 && !entity.ennemy);
      entity.myObject3D.boxMesh.visible = espConfig.showBox === 2 || (espConfig.showBox === 1 && entity.ennemy);
      entity.myObject3D.outline.visible = espConfig.showOutline === 2 || (espConfig.showOutline === 1 && entity.ennemy);
      setTransform(entity.myObject3D, entity, false);

if (espConfig.showPlayerNames && entity.playerName) {
        // 1. Khởi tạo Sprite nếu chưa có (Phần này giữ nguyên như bạn nói)
        if (!entity.myObject3D.nameSprite) {
            const spriteColor = entity.ennemy ? "#ff0000" : "#00ff00";
            const sprite = makeTextSprite(entity.playerName, spriteColor);
            entity.myObject3D.add(sprite);
            entity.myObject3D.nameSprite = sprite;
        }

        const nameSprite = entity.myObject3D.nameSprite;
        nameSprite.visible = true;
        nameSprite.position.y = isSneak ? 1.8 : 2.3;

        // --- BẮT ĐẦU PHẦN THÊM MỚI: LOGIC PHÓNG TO/THU NHỎ ---

        // Bước 1: Tính khoảng cách từ bạn đến đối thủ
        const distance = entity.myObject3D.position.distanceTo(camera.position);

      // Bước 2: Tính toán tỷ lệ (Multiplier) - ĐÃ ĐIỀU CHỈNH ĐỂ TO HƠN
        let scaleMultiplier = 1.0;
        if (espConfig.nameScaling) {
            // Giải thích các con số:
            // 10: Khoảng cách bắt đầu tính (mét)
            // 150: Khoảng cách tối đa để đạt độ phóng to lớn nhất (tăng từ 80 lên 150)
            // 0.5: Độ nhỏ khi ở gần
            // 6.0: Độ to cực đại khi ở xa (tăng từ 2.5 lên 6.0 - bạn có thể chỉnh số này to hơn nữa)

            const t = Math.max(0, Math.min(1, (distance - 10) / (150 - 10)));
            scaleMultiplier = 0.5 + (8.0 - 0.5) * t; // Thay 6.0 bằng 12.0 để to gấp đôi khi ở xa
        }

        // Bước 3: Áp dụng kích thước mới vào Sprite
        // Lưu ý: baseScale thường là 1.5 trong hàm makeTextSprite của bạn
        const finalScale = (nameSprite.baseScale || 1.5) * scaleMultiplier;
        nameSprite.scale.set(0.5 * nameSprite.initialRatio * finalScale, 0.5 * finalScale, 1);

        // --- KẾT THÚC PHẦN THÊM MỚI ---
    }
      // aimbot and line
      const pos = entity.myObject3D.position.clone();
      pos.y -= isSneak ? espConfig.sneakHeight : 0;
      // line
      if (espConfig.showLine === 2 || (espConfig.showLine === 1 && entity.ennemy)) {
        if (espConfig.rainbow) {
          color.setHSL(time % 2000 / 2000, 1, 0.5);
        } else if (entity.ennemy) {
          color.lerpColors(colors.ennemy, colors.player, pos.distanceTo(camera.position) / espConfig.ennemyDistance);
          color.a = .8;
        } else {
          color.set(colors.blue);
          color.a = .3;
        }
        linePositions.push(lineOrigin.x, lineOrigin.y, lineOrigin.z);
        pos.y += 1.25;
        linePositions.push(pos.x, pos.y, pos.z);
        pos.y -= 1.25;
        colorArray.push(color.r, color.g, color.b, color.a);
        colorArray.push(color.r, color.g, color.b, color.a);
      }
      pos.y += espConfig.heightLine;
      // aimbot
      if (shouldAimbot && (entity.ennemy || espConfig.allEnnemies)) {
        if (entity.playerName && window.ignoredPlayers.has(entity.playerName)) return;
        const distance = pos.distanceTo(camera.position);
        // Nếu tên người này nằm trong danh sách né, thì bỏ qua không nhắm
    if (entity.playerName && window.ignoredPlayers.has(entity.playerName)) return;
  // Lấy vị trí hiện tại
        let target = pos.clone();

      if (entity.velocity) {
            // Tăng số này nếu muốn đón đầu xa hơn (Trái/Phải)
            const leadFactor = 1.5;

            // 1. Lệch ngang (Trái/Phải/Tới/Lui)
            const horizontalLead = new THREE.Vector3(entity.velocity.x, 0, entity.velocity.z).multiplyScalar(leadFactor);
            target.add(horizontalLead);

            // 2. Ghì tâm khi nhảy (Lên/Xuống)
            if (entity.velocity.y > 0.01) {
              const pullDownStrength = 0.02; // THAY ĐỔI SỐ NÀY
              target.y -= pullDownStrength;
            }
        }
        const dummy = new THREE.PerspectiveCamera();
        setTransform(dummy, worldCamera, false);
        dummy.lookAt(target);
        const cameraVector = new THREE.Vector3(0, 0, -1).applyQuaternion(camera.quaternion);
        const targetVector = new THREE.Vector3(0, 0, -1).applyQuaternion(dummy.quaternion);
        const angleDifference = cameraVector.angleTo(targetVector);
        const currentMaxAngle = espConfig.unlimitedAngle ? Math.PI : espConfig.maxAngleInRadians;
        if (angleDifference < currentMaxAngle && angleDifference < aimbotTarget.angleDifference) {
          const directionV3 = new THREE.Vector3();
          directionV3.subVectors(target, camera.position).normalize();
          raycaster.set(camera.position, directionV3);
          let behindBlock = false;
          if (espConfig.aimbotIgnoreWall) {
            aimbotTarget.angleDifference = angleDifference;
            aimbotTarget.target = target;
          } else {
            for (const chunk of chunks) {
              if (raycaster.ray.intersectsBox(chunk.box)) {
                const hit = raycaster.intersectObject(chunk)[0];
                if (hit && hit.distance < distance) {
                  behindBlock = true;
                  break;
                }
              }
            }
            if (!behindBlock) {
              aimbotTarget.angleDifference = angleDifference;
              aimbotTarget.target = target;
              color.setHSL(time % 2000 / 2000, 1, 0.5);
            }
          }
        }
      }
    }
    // NEW: Handle items (non-player entities)
    else if (isItem(entity) && espConfig.showItems) {
      if (!entity.itemBox) {
        entity.itemBox = new THREE.Mesh(itemBoxGeometry, meshMats.item);
        entity.itemBox.frustumCulled = false;
        scene.add(entity.itemBox);

        // Create item name sprite if showItemNames is enabled
        if (espConfig.showItemNames) {
          // Try to get item name from canvas text
          let itemName = "Item";
          for (const child of entity[childrenKey]) {
            try {
              const matImage = child.material?.map?.image;
              if (matImage instanceof HTMLCanvasElement && matImage.hasOwnProperty('lastText')) {
                itemName = matImage.lastText || "Item";
                break;
              }
            } catch {}
          }
          entity.itemName = itemName;
          const itemSprite = createItemSprite(itemName, "#D4AF37");
          itemSprite.position.y = 0.5;
          entity.itemBox.add(itemSprite);
          entity.itemNameSprite = itemSprite;
        }
      }

      setTransform(entity.itemBox, entity, false);
      entity.itemBox.visible = true;

      // Update item name sprite visibility
      if (entity.itemNameSprite) {
        entity.itemNameSprite.visible = espConfig.showItemNames;
      }
    }
    // Hide item if showItems is false
    else if (entity.itemBox) {
      entity.itemBox.visible = false;
      if (entity.itemNameSprite) {
        entity.itemNameSprite.visible = false;
      }
    }
  });

  // aim at target
  if (espConfig.aimbot && shouldAimbot && aimbotTarget.target) {
    setTransform(dummyLookAt, worldCamera, false);
    dummyLookAt.lookAt(aimbotTarget.target);
    worldCamera.rotation.set(
      dummyLookAt.rotation.x,
      dummyLookAt.rotation.y,
      dummyLookAt.rotation.z
    );
  }
  // triggerbot
  const shouldTrigger = espConfig.triggerBot === 3 || (espConfig.triggerBot === 1 && isLeftClick) || (espConfig.triggerBot === 2 && isRightClick);
  if (shouldTrigger) {
    raycaster.set(camera.position, camera.getWorldDirection(new THREE.Vector3()));
    let hasHit = false;
    for (const entity of entities) {
      if (!entity.myObject3D?.visible) continue;
      if (entity.isPlayer && (entity.ennemy || espConfig.allEnnemies)) {
        const hit = raycaster.intersectObject(entity.myObject3D.playerMesh);
        if (hit.length) {
          hasHit = true;
          const distance = hit[0].distance;
          for (const chunk of chunks) {
            if (raycaster.ray.intersectsBox(chunk.box)) {
              const hitBlock = raycaster.intersectObject(chunk)[0];
              if (hitBlock && hitBlock.distance < distance) {
                hasHit = false;
                break;
              }
            }
          }
          if (hasHit) {
            break;
          }
        }
      }
    }
    setFiring(hasHit);
  } else {
    setFiring(false);
  }

  line.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colorArray, 4));
  line.geometry.setAttribute('position', new THREE.Float32BufferAttribute(linePositions, 3));
  line.visible = espConfig.showLine;

  renderer.render(scene, camera);

  // minimap
  // make entities larger for minimap
  const scale = espConfig.mapZoom / 3;
  entities.forEach(entity => {
    if (entity.isPlayer) {
      entity.myObject3D.playerMesh.visible = false;
      entity.myObject3D.boxMesh.visible = false;
      entity.myObject3D.outline.visible = false;
      if (entity.myObject3D.nameSprite) entity.myObject3D.nameSprite.visible = false; // Ẩn tên trên minimap
      entity.myObject3D.playerMiniMap.visible = true;
      entity.myObject3D.playerMiniMap.scale.set(scale, 1, scale);
    }
    // Hide items on minimap
    if (entity.itemBox) {
      entity.itemBox.visible = false;
    }
  });
  if (worldCamera) {
    line.visible = false;
    crosshair.visible = false;
    // update orthographic camera based on espConfig.mapZoom
    minimapCamera.left = -espConfig.mapZoom;
    minimapCamera.right = espConfig.mapZoom;
    minimapCamera.top = espConfig.mapZoom;
    minimapCamera.bottom = -espConfig.mapZoom;

    // update position with camera position
    minimapCamera.position.copy(camera.position);
    minimapCamera.position.y += 50;
    minimapCamera.position.z += espConfig.mapOffsetZ;
    minimapCamera.rotation.y = camera.rotation.y;
    minimapCamera.updateProjectionMatrix();

    renderer.getViewport(saveViewport);
    renderer.getScissor(saveScissor);
    let saveScissorTest = renderer.getScissorTest();
    renderer.setViewport(minimapViewport);
    renderer.setScissor(minimapViewport);
    renderer.setScissorTest(true);

    renderer.render(scene, minimapCamera);

    renderer.setViewport(saveViewport);
    renderer.setScissor(saveScissor);
    renderer.setScissorTest(saveScissorTest);
  }
  entities.forEach(entity => {
    if (entity.isPlayer) {
      entity.myObject3D.playerMiniMap.visible = false;
    }
  });

  scene.children.forEach(child => {
    if (child.type === 'Object3D') {
      child.visible = false;
    }
  });
}

window.addEventListener('resize', () => {
  renderer.setSize( window.innerWidth, window.innerHeight );
});

// add style to header
style.innerHTML = `
#overlayCanvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1000;
}
#lil-gui {
  position: absolute;
  top: 50%;
  right: 0;
  z-index: 1001;
  transform: translateY(-50%);
}
.lil-gui {
  --background-color: rgba(0, 0, 0, 0.5);
}
`;
guiStyle.innerHTML = `
.lil-gui {
	--title-background-color: #ff0019;
	--number-color: #00ff33;
}
`;
document.head.appendChild(style);
document.head.appendChild(guiStyle);

setInterval(() => {
  if (espConfig.antiAFK) {
    // Gửi sự kiện đè phím (keydown) cho cả 4 hướng cùng lúc
    // Không gửi sự kiện nhả phím (keyup) để mô phỏng việc giữ chặt
    document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 87 })); // W
    document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 67 })); // A
    document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 70 })); // S
  }
}, 20); // Lặp lại mỗi 20ms (cực nhanh, không có độ trễ)


// wait for load
window.addEventListener('load', () => {
  console.log('Loaded');
  if (espConfig.autoClaimAds) {
    setTimeout(() => {
      claimAds();
    }, 500);
  }
});
// Logic điều khiển Sit Mode
setInterval(() => {
  if (espConfig.sit) {
    // Nhấn phím C (keyCode 67)
    document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 67 }));

    // Nhả phím C sau 1ms để tạo vòng lặp nhấp nhả cực nhanh
    setTimeout(() => {
      document.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 67 }));
    }, 5);
  }
}, 0); // Lặp lại toàn bộ quá trình sau 2ms



setInterval(() => {
    const radarContainer = document.getElementById('side-player-list');
    if (!radarContainer) return;
  // Cập nhật số lượng người chơi vào tiêu đề
const countElement = document.getElementById('radar-count');
if (countElement && window.playerMap) {
    countElement.innerText = window.playerMap.size;
}

    // KIỂM TRA LỆNH TỪ MENU/PHÍM TẮT
    if (!espConfig.showRadar) {
        radarContainer.style.display = 'none'; // Tắt bảng
        return;
    } else {
        radarContainer.style.display = 'block'; // Mở bảng
    }

    const content = document.getElementById('list-content');
    if (!content) return;

    if (!window.playerMap || window.playerMap.size === 0) {
        content.innerHTML = '<div style="color:#ffcc00; font-size:12px; font-weight:bold; text-align:center;">🔍 ĐANG QUÉT...</div>';
    } else {
        const sortedPlayers = Array.from(window.playerMap.entries())
            .map(([name, data]) => ({ name, ...data }))
            .sort((a, b) => a.dist - b.dist);

    let html = '';
sortedPlayers.forEach((p, index) => {
    const isSoldier = p.color === "#ff0000";
    const shadow = isSoldier
        ? "1px 1px 0px #000, -1px -1px 0px #000, 1px -1px 0px #000, -1px 1px 0px #000, 0px 0px 3px #ffffff"
        : "1px 1px 1px #000";

    const isIgnored = window.ignoredPlayers && window.ignoredPlayers.has(p.name);
    const btnColor = isIgnored ? "#00ff00" : "#555";
    const btnText = isIgnored ? "✔" : "+";

    // Đổi thành window.selectedPlayerIndex để phím tắt điều khiển được
    const isSelected = (index === window.selectedPlayerIndex);

    html += `
        <div class="player-row ${isSelected ? 'selected' : ''}" style="color:${p.color}; text-shadow: ${shadow};">
            <span style="width:25px; text-align:center; font-size:18px; margin-right:5px; flex-shrink:0;">${p.arrow || "•"}</span>
            <button onmousedown="window.handlePlayerAction('${p.name}')" style="pointer-events: auto; margin-right: 10px; background: ${btnColor}; color: white; border: 1px solid rgba(255,255,255,0.3); border-radius: 4px; width: 26px; height: 26px; cursor: pointer; font-size: 16px; font-weight: bold; flex-shrink: 0; display: flex; align-items: center; justify-content: center;">${btnText}</button>
            <span style="flex-grow:1; overflow:hidden; text-overflow:ellipsis; font-weight:900;">${p.name}</span>
            <span class="dist-text" style="margin-left:auto; color:#00ffff; font-weight:bold; font-size:12px;">${p.dist}m</span>
        </div>
    `;
});

        content.innerHTML = html;
    }
    if (window.playerMap) window.playerMap.clear();
}, 250);
// --- Tính năng Phím tắt Điều khiển ---
let selectedPlayerIndex = 0;

window.addEventListener('keydown', (e) => {
    // Lấy danh sách người chơi hiện tại từ giao diện (các button handlePlayerAction)
    const playerRows = document.querySelectorAll('#side-player-list div[style*="margin-bottom:6px"]');
    if (playerRows.length === 0) return;

    // Phím '[' : Di chuyển lên
    if (e.key === '[') {
        selectedPlayerIndex = (selectedPlayerIndex <= 0) ? playerRows.length - 1 : selectedPlayerIndex - 1;
        updateSelectionHighlight(playerRows);
    }

    // Phím ']' : Di chuyển xuống
    if (e.key === ']') {
        selectedPlayerIndex = (selectedPlayerIndex >= playerRows.length - 1) ? 0 : selectedPlayerIndex + 1;
        updateSelectionHighlight(playerRows);
    }

    // Phím '/' : Kích hoạt né/hủy né cho người chơi đang chọn
    if (e.key === '/') {
        const selectedRow = playerRows[selectedPlayerIndex];
        if (selectedRow) {
            const btn = selectedRow.querySelector('button');
            if (btn) btn.onmousedown(); // Kích hoạt sự kiện nhấn nút
        }
    }
});

// Khởi tạo biến toàn cục
window.selectedPlayerIndex = -1;
let hideTimer = null;

// Thêm CSS cho class "selected" và hiệu ứng nháy khi nhấn "/"
if (!document.getElementById('selection-style')) {
    const s = document.createElement('style');
    s.id = 'selection-style';
    s.innerHTML = `
        .player-row { border: 1.5px solid transparent; transition: all 0.1s; padding: 2px 5px; border-radius: 5px; }
        .player-row.selected { background: rgba(0, 255, 0, 0.2) !important; border-color: #00ff00 !important; box-shadow: 0 0 10px #00ff00; }
        .player-row.clicked { background: rgba(255, 255, 0, 0.5) !important; border-color: yellow !important; }
    `;
    document.head.appendChild(s);
}

window.onkeydown = function(e) {
    if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') return;

    const rows = document.querySelectorAll('.player-row');
    if (rows.length === 0) return;

    if (e.key === '[') { // Lên
        window.selectedPlayerIndex = (window.selectedPlayerIndex <= 0) ? rows.length - 1 : window.selectedPlayerIndex - 1;
        e.preventDefault();
    }
    else if (e.key === ']') { // Xuống
        window.selectedPlayerIndex = (window.selectedPlayerIndex >= rows.length - 1) ? 0 : window.selectedPlayerIndex + 1;
        e.preventDefault();
    }
    else if (e.key === '/') { // Chọn
        if (window.selectedPlayerIndex >= 0 && rows[window.selectedPlayerIndex]) {
            const row = rows[window.selectedPlayerIndex];
            const btn = row.querySelector('button');
            if (btn) btn.onmousedown();

            // Hiệu ứng nháy vàng khi nhấn / (để không bị chọn ngầm)
            row.classList.add('clicked');
            setTimeout(() => row.classList.remove('clicked'), 200);
        }
        e.preventDefault();
    } else {
        return;
    }

    // Tự động ẩn viền sau 1 giây
    clearTimeout(hideTimer);
    hideTimer = setTimeout(() => {
        window.selectedPlayerIndex = -1;
    }, 3000);
};// 1. Xử lý thả phím Z để ngừng Zoom
window.addEventListener('keyup', (e) => {
    const keyL = e.key.toLowerCase();

    // Kiểm tra: Nếu nhả phím zoom mặc định (Z)
    // HOẶC nhả phím F (khi tính năng Zoom phím F đang được bật trong menu)
    if (keyL === espConfig.zoomKey.toLowerCase() || (keyL === 'f' && espConfig.fZoomEnabled)) {
        espConfig.isZooming = false;

        // Cập nhật lại giao diện Menu (để nút "Đang Zoom" tự tắt đi)
        if (typeof gui !== 'undefined') {
            gui.controllersRecursive().forEach(c => c.updateDisplay());
        }
    }
});

// 2. KHẮC PHỤC LỖI: Chặn đổi vũ khí khi đang cuộn chuột để Zoom
window.addEventListener('wheel', (e) => {
    if (espConfig.isZooming) {
        // e.preventDefault() ở đây giúp chặn sự kiện mặc định (đổi vũ khí) của game
        e.preventDefault();

        if (e.deltaY < 0) {
            // Cuộn lên: Giảm FOV = Phóng to hơn
            espConfig.zoomLevel = Math.max(1, espConfig.zoomLevel - 3);
        } else {
            // Cuộn xuống: Tăng FOV = Nhìn rộng hơn
            espConfig.zoomLevel = Math.min(175, espConfig.zoomLevel + 3);
        }

        // Cập nhật con số trên Menu khi đang lăn chuột
        if (typeof gui !== 'undefined') {
            gui.controllersRecursive().forEach(c => {
                if (c.property === 'zoomLevel') c.updateDisplay();
            });
        }
    }
}, { passive: false }); // Cần passive: false để preventDefault hoạt động

// 3. Ghi đè FOV của Camera game
Object.defineProperty(Object.prototype, 'fov', {
    get: function() {
        // Nếu là Camera và người dùng đang giữ phím Z
        if (this.isPerspectiveCamera && espConfig.isZooming) {
            return espConfig.zoomLevel;
        }
        return this._fov;
    },
    set: function(v) {
        this._fov = v;
    },
    configurable: true
});