Xianware

Krunker.io assistant

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Xianware
// @namespace    http://tampermonkey.net/
// @version      8.2.1
// @description  Krunker.io assistant
// @author       simplexian
// @match        *://krunker.io/*
// @exclude      *://krunker.io/social*
// @exclude      *://krunker.io/editor*
// @exclude      *://krunker.io/viewer*
// @icon         https://iili.io/qNroOue.png
// @grant        none
// @run-at       document-start
// @license      MIT
// @require      https://unpkg.com/[email protected]/build/three.min.js
// ==/UserScript==

(function () {
  "use strict";

  let THREE, MathUtils;
  try {
    THREE = window.THREE;
    MathUtils = THREE.MathUtils;
    delete window.THREE;
  } catch (e) {
    console.error("[Xianware] Error initializing THREE.js:", e);
  }

  class ConfigManager {
    constructor() {
      this.defaultSettings = {
        aimbotEnabled: true,
        aimbotOnRightMouse: false,
        smoothAim: true,
        horizontalFreeAim: true,
        verticalFreeAim: true,
        aimOffset: 0.75,
        aimHorizontal: true,
        aimVertical: true,
        wallCheck: true,
        aimSpeedXMin: 0.15,
        aimSpeedXMax: 0.3,
        aimSpeedYMin: 0.15,
        aimSpeedYMax: 0.3,
        aimPrediction: false,
        predictionFactor: 1.0,
        fovCheck: true,
        fovRadius: 150,
        fovCircleColor: "#ffffff",
        drawFov: true,
        targetIndicator: true,
        indicatorShape: "Arrow",
        indicatorColor: "#ff0000",
        indicatorSize: 15,
        indicatorOpacity: 1.0,
        indicatorImageUrl: "",
        espEnabled: true,
        espRainbow: false,
        espColor: "#fbc02d",
        espSnaplines: false,
        snaplineColor: "#ffffff",
        snaplineOrigin: "Bottom",
        espDistance: false,
        espDistanceColor: "#ffffff",
        chamsEnabled: false,
        chamsRainbow: false,
        chamsColor: "#ff0000",
        menuTheme: "Dark",
        menuScale: 1.0,
        menuColor: "#fbc02d",
        showNotifications: true,
        showHud: true,
        hudScale: 1.0,
        hudPosition: "Top Left",
        customCssText: "",
      };

      this.defaultKeybinds = {
        menuToggle: "Insert",
        aimbotEnabled: "KeyB",
        drawFov: "KeyG",
        espEnabled: "KeyV",
        espSnaplines: "",
        chamsEnabled: "",
        wallCheck: "",
        reloadScript: "KeyZ",
        showNotifications: "",
        showHud: "",
      };

      this.settings = JSON.parse(JSON.stringify(this.defaultSettings));
      this.keybinds = JSON.parse(JSON.stringify(this.defaultKeybinds));

      this.profiles = {
        Default: {
          settings: JSON.parse(JSON.stringify(this.defaultSettings)),
          keybinds: JSON.parse(JSON.stringify(this.defaultKeybinds)),
          hotkey: "",
        },
      };
      this.activeProfile = "Default";
      this.autoLoadProfile = "Default";

      this.reverseKeyMap = {};
      this.profileKeyMap = {};

      this.load();
    }

    updateKeyMaps() {
      this.reverseKeyMap = {};
      for (const [feat, key] of Object.entries(this.keybinds)) {
        if (key) this.reverseKeyMap[key] = feat;
      }
      this.profileKeyMap = {};
      for (const [pName, pData] of Object.entries(this.profiles)) {
        if (pData.hotkey) this.profileKeyMap[pData.hotkey] = pName;
      }
    }

    save() {
      try {
        localStorage.setItem(
          "xian_profiles_v82",
          JSON.stringify(this.profiles),
        );
        localStorage.setItem("xian_autoload_profile_v82", this.autoLoadProfile);
        this.updateKeyMaps();
      } catch (e) {}
    }

    load() {
      try {
        let savedProfiles =
          JSON.parse(localStorage.getItem("xian_profiles_v82")) ||
          JSON.parse(localStorage.getItem("xian_profiles_v81")) ||
          JSON.parse(localStorage.getItem("xian_profiles_v8")) ||
          JSON.parse(localStorage.getItem("xian_profiles_v75"));

        if (savedProfiles) {
          this.profiles = savedProfiles;
        }

        this.autoLoadProfile =
          localStorage.getItem("xian_autoload_profile_v82") ||
          localStorage.getItem("xian_autoload_profile_v81") ||
          localStorage.getItem("xian_autoload_profile_v8") ||
          localStorage.getItem("xian_autoload_profile_v75") ||
          "Default";
        this.activeProfile = this.autoLoadProfile;

        if (!this.profiles[this.activeProfile]) {
          this.activeProfile = "Default";
          this.autoLoadProfile = "Default";
        }

        this.migrateProfiles();
        this.applyProfile(this.activeProfile);
      } catch (e) {}
    }

    migrateProfiles() {
      for (const pName of Object.keys(this.profiles)) {
        const prof = this.profiles[pName];
        if (prof.hotkey === undefined) prof.hotkey = "";
        if (!prof.settings) prof.settings = {};
        if (!prof.keybinds) prof.keybinds = {};
        for (const key of Object.keys(this.defaultSettings)) {
          if (prof.settings[key] === undefined)
            prof.settings[key] = this.defaultSettings[key];
        }
        for (const key of Object.keys(this.defaultKeybinds)) {
          if (prof.keybinds[key] === undefined)
            prof.keybinds[key] = this.defaultKeybinds[key];
        }
      }
    }

    applyProfile(name) {
      if (!this.profiles[name]) return;
      this.activeProfile = name;
      const prof = this.profiles[name];

      Object.keys(this.defaultSettings).forEach((k) => {
        this.settings[k] = prof.settings.hasOwnProperty(k)
          ? prof.settings[k]
          : this.defaultSettings[k];
      });
      Object.keys(this.defaultKeybinds).forEach((k) => {
        this.keybinds[k] = prof.keybinds.hasOwnProperty(k)
          ? prof.keybinds[k]
          : this.defaultKeybinds[k];
      });
      this.updateKeyMaps();
    }

    resetToDefault() {
      Object.keys(this.defaultSettings).forEach((k) => {
        this.settings[k] = this.defaultSettings[k];
      });
      Object.keys(this.defaultKeybinds).forEach((k) => {
        this.keybinds[k] = this.defaultKeybinds[k];
      });
    }
  }

  class UIManager {
    constructor(app) {
      this.app = app;
      this.config = app.config;
      this.canvas = null;
      this.ctx = null;
      this.gui = null;
      this.welcomeGui = null;
      this.hudEl = null;
      this.notificationEl = null;
      this.toastQueue = [];
      this.toastActive = false;
      this.toastTimer = null;
      this.bindingFeature = null;
      this.saveDebounce = null;

      this.menuStructure = this.buildMenuStructure();
    }

    init() {
      this.createCanvas();
      this.injectCSS();
      this.createDOM();
      this.attachEventListeners();
      this.refreshUI();

      window.addEventListener("resize", () => {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
      });
    }

    createCanvas() {
      this.canvas = document.createElement("canvas");
      this.canvas.style.position = "fixed";
      this.canvas.style.top = "0";
      this.canvas.style.left = "0";
      this.canvas.style.width = "100%";
      this.canvas.style.height = "100%";
      this.canvas.style.pointerEvents = "none";
      this.canvas.style.zIndex = "999";
      document.documentElement.appendChild(this.canvas);
      this.ctx = this.canvas.getContext("2d", { alpha: true });
      this.canvas.width = window.innerWidth;
      this.canvas.height = window.innerHeight;
    }

    cleanKeyName(code) {
      return !code || code === ""
        ? "NONE"
        : code.replace("Key", "").replace("Digit", "");
    }

    showToast(text, stateText = "", duration = 1500) {
      if (!this.config.settings.showNotifications) return;
      this.toastQueue.push({ text, stateText, duration });
      if (!this.toastActive) this.processToastQueue();
    }

    processToastQueue() {
      if (this.toastQueue.length === 0) {
        this.toastActive = false;
        return;
      }
      this.toastActive = true;
      const { text, stateText, duration } = this.toastQueue.shift();
      this.notificationEl.innerHTML = `${text} <span>${stateText}</span>`;
      this.notificationEl.classList.add("show");
      clearTimeout(this.toastTimer);
      this.toastTimer = setTimeout(() => {
        this.notificationEl.classList.remove("show");
        setTimeout(() => this.processToastQueue(), 300);
      }, duration);
    }

    queueSave() {
      clearTimeout(this.saveDebounce);
      this.saveDebounce = setTimeout(() => {
        this.config.profiles[this.config.activeProfile].settings = JSON.parse(
          JSON.stringify(this.config.settings),
        );
        this.config.profiles[this.config.activeProfile].keybinds = JSON.parse(
          JSON.stringify(this.config.keybinds),
        );
        this.config.save();
      }, 2000);
    }

    applyTheme() {
      document.documentElement.setAttribute(
        "data-theme",
        this.config.settings.menuTheme.toLowerCase(),
      );
      document.documentElement.style.setProperty(
        "--accent-color",
        this.config.settings.menuColor,
      );
    }

    refreshUI() {
      this.applyTheme();
      this.updateHud();

      if (this.gui)
        this.gui.style.transform = `scale(${this.config.settings.menuScale})`;
      if (this.welcomeGui)
        this.welcomeGui.style.transform = `scale(${this.config.settings.menuScale})`;

      this.menuStructure.forEach((cat) =>
        cat.groups.forEach((group) =>
          group.items.forEach((item) => {
            const chk = document.getElementById(`chk-${item.id}`);
            const sld = document.getElementById(`sld-${item.id}`);
            const col = document.getElementById(`col-${item.id}`);
            const sel = document.getElementById(`sel-${item.id}`);
            const txt = document.getElementById(`txt-${item.id}`);
            const bind = document.getElementById(
              `bind-${item.bind || item.id}`,
            );

            if (chk) chk.checked = this.config.settings[item.id];
            if (sld) {
              sld.value = this.config.settings[item.id];
              const valEl = document.getElementById(`val-${item.id}`);
              if (valEl) valEl.innerText = this.config.settings[item.id];
            }
            if (col) col.value = this.config.settings[item.id];
            if (sel && item.id !== "profileSelect")
              sel.value = this.config.settings[item.id];
            if (txt && item.id !== "profileName" && item.id !== "configData")
              txt.value = this.config.settings[item.id] || "";

            if (
              item.type === "bind" &&
              this.config.keybinds[item.bind || item.id] !== undefined
            ) {
              if (bind)
                bind.innerText = this.cleanKeyName(
                  this.config.keybinds[item.bind || item.id] || "",
                );
            } else if (
              item.type === "customBind" &&
              item.id === "profileHotkey"
            ) {
              if (bind)
                bind.innerText = this.cleanKeyName(
                  this.config.profiles[this.config.activeProfile]?.hotkey || "",
                );
            }
          }),
        ),
      );

      this.updateProfileDropdown();
      this.app.engine.updateMaterials();
    }

    updateProfileDropdown() {
      const sel = document.getElementById("sel-profileSelect");
      if (!sel) return;
      sel.innerHTML = Object.keys(this.config.profiles)
        .map((p) => {
          const isActive = p === this.config.activeProfile ? " [ACTIVE]" : "";
          const isAuto = p === this.config.autoLoadProfile ? " [AUTO]" : "";
          const hk = this.config.profiles[p].hotkey
            ? ` [${this.cleanKeyName(this.config.profiles[p].hotkey)}]`
            : "";
          return `<option value="${p}">${p}${isActive}${isAuto}${hk}</option>`;
        })
        .join("");
      sel.value = this.config.activeProfile;
    }

    updateHud() {
      if (!this.hudEl) return;
      const s = this.config.settings;

      if (!s.showHud) {
        this.hudEl.style.display = "none";
        return;
      }

      this.hudEl.style.display = "flex";
      this.hudEl.style.transform = `scale(${s.hudScale})`;
      this.hudEl.style.top = "auto";
      this.hudEl.style.bottom = "auto";
      this.hudEl.style.left = "auto";
      this.hudEl.style.right = "auto";
      this.hudEl.style.alignItems = "flex-start";

      switch (s.hudPosition) {
        case "Top Left":
          this.hudEl.style.top = "15px";
          this.hudEl.style.left = "15px";
          this.hudEl.style.transformOrigin = "top left";
          break;
        case "Top Right":
          this.hudEl.style.top = "15px";
          this.hudEl.style.right = "15px";
          this.hudEl.style.alignItems = "flex-end";
          this.hudEl.style.transformOrigin = "top right";
          break;
        case "Bottom Left":
          this.hudEl.style.bottom = "15px";
          this.hudEl.style.left = "15px";
          this.hudEl.style.transformOrigin = "bottom left";
          break;
        case "Bottom Right":
          this.hudEl.style.bottom = "15px";
          this.hudEl.style.right = "15px";
          this.hudEl.style.alignItems = "flex-end";
          this.hudEl.style.transformOrigin = "bottom right";
          break;
      }

      const activeFeatures = [
        { name: "XianWare", active: true, color: s.menuColor, isTitle: true },
        { name: "Profile", state: `[${this.config.activeProfile}]` },
      ];

      if (s.aimbotEnabled)
        activeFeatures.push({
          name: "Aimbot",
          state: s.aimbotOnRightMouse ? "[ADS]" : "[ON]",
        });
      if (s.aimPrediction)
        activeFeatures.push({ name: "Prediction", state: "[ON]" });
      let freeAimState =
        s.horizontalFreeAim && s.verticalFreeAim
          ? "[H+V]"
          : s.horizontalFreeAim
            ? "[HORIZ]"
            : s.verticalFreeAim
              ? "[VERT]"
              : "";
      if (freeAimState)
        activeFeatures.push({ name: "Free-Aim", state: freeAimState });
      if (s.espEnabled)
        activeFeatures.push({
          name: "ESP",
          state: s.espRainbow ? "[RAINBOW]" : "[ON]",
        });
      if (s.espSnaplines)
        activeFeatures.push({ name: "Snaplines", state: "[ON]" });
      if (s.espDistance)
        activeFeatures.push({ name: "Distance", state: "[ON]" });
      if (s.chamsEnabled)
        activeFeatures.push({
          name: "Chams",
          state: s.chamsRainbow ? "[RAINBOW]" : "[ON]",
        });
      if (s.drawFov) activeFeatures.push({ name: "Draw FOV", state: "[ON]" });
      if (s.targetIndicator)
        activeFeatures.push({ name: "Target Ind.", state: "[ON]" });
      if (s.wallCheck)
        activeFeatures.push({ name: "Wall Check", state: "[ON]" });

      this.hudEl.innerHTML = activeFeatures
        .map((f) => {
          if (f.isTitle)
            return `<div class="hud-item title" style="color:${f.color}; font-size: 20px;">${f.name} <span style="font-size:12px;color:#fff;">V8.2.1</span></div>`;
          return `<div class="hud-item">${f.name} <span>${f.state}</span></div>`;
        })
        .join("");
    }

    injectCustomCss() {
      const existing = document.getElementById("xian-custom-css");
      if (existing) existing.remove();
      if (
        !this.config.settings.customCssText ||
        this.config.settings.customCssText.trim() === ""
      )
        return;
      const styleBlock = document.createElement("style");
      styleBlock.id = "xian-custom-css";
      styleBlock.innerHTML = this.config.settings.customCssText;
      document.documentElement.appendChild(styleBlock);
      this.showToast("Custom CSS", "APPLIED");
    }

    confirmAction(message) {
      return new Promise((resolve) => {
        const overlay = document.createElement("div");
        overlay.style.cssText =
          "position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.6);z-index:10000001;display:flex;justify-content:center;align-items:center;font-family:'Rajdhani',sans-serif;";
        overlay.innerHTML = `<div style="background:var(--bg-main);border:2px solid var(--border-light);border-radius:8px;padding:24px 32px;text-align:center;color:var(--text-main);min-width:300px;box-shadow:0 10px 40px rgba(0,0,0,0.8);"><div style="font-size:16px;font-weight:700;margin-bottom:20px;text-transform:uppercase;">${message}</div><div style="display:flex;gap:12px;justify-content:center;"><button class="xian-btn" id="xian-confirm-yes" style="width:auto;padding:8px 24px;border-color:#ff4444;color:#ff4444;">YES</button><button class="xian-btn" id="xian-confirm-no" style="width:auto;padding:8px 24px;">CANCEL</button></div></div>`;
        document.documentElement.appendChild(overlay);
        document.getElementById("xian-confirm-yes").onclick = () => {
          overlay.remove();
          resolve(true);
        };
        document.getElementById("xian-confirm-no").onclick = () => {
          overlay.remove();
          resolve(false);
        };
      });
    }

    btnSaveProfile() {
      this.config.profiles[this.config.activeProfile].settings = JSON.parse(
        JSON.stringify(this.config.settings),
      );
      this.config.profiles[this.config.activeProfile].keybinds = JSON.parse(
        JSON.stringify(this.config.keybinds),
      );
      this.config.save();
      this.updateProfileDropdown();
      this.showToast("Profile Saved", this.config.activeProfile.toUpperCase());
    }

    btnLoadProfile() {
      const sel = document.getElementById("sel-profileSelect");
      if (sel && this.config.profiles[sel.value]) {
        this.config.applyProfile(sel.value);
        this.refreshUI();
        this.injectCustomCss();
        this.showToast("Profile Loaded", sel.value.toUpperCase());
      }
    }

    btnCreateProfile() {
      const name = document.getElementById("txt-profileName").value.trim();
      if (!name) return this.showToast("Error", "NAME EMPTY");
      if (this.config.profiles[name]) return this.showToast("Error", "EXISTS");

      this.config.activeProfile = name;
      this.config.profiles[name] = {
        settings: JSON.parse(JSON.stringify(this.config.settings)),
        keybinds: JSON.parse(JSON.stringify(this.config.keybinds)),
        hotkey: "",
      };
      this.config.save();
      document.getElementById("txt-profileName").value = "";
      this.updateProfileDropdown();
      this.showToast("Profile Created", name.toUpperCase());
    }

    async btnDeleteProfile() {
      const sel = document.getElementById("sel-profileSelect");
      if (sel.value === "Default")
        return this.showToast("Error", "CANNOT DELETE DEFAULT");
      const confirmed = await this.confirmAction(
        `Delete profile "${sel.value}"?`,
      );
      if (!confirmed) return;
      delete this.config.profiles[sel.value];
      if (this.config.activeProfile === sel.value)
        this.config.applyProfile("Default");
      if (this.config.autoLoadProfile === sel.value)
        this.config.autoLoadProfile = "Default";
      this.config.save();
      this.updateProfileDropdown();
      this.refreshUI();
      this.showToast("Profile Deleted", sel.value.toUpperCase());
    }

    btnSetAutoLoad() {
      const sel = document.getElementById("sel-profileSelect");
      this.config.autoLoadProfile = sel.value;
      this.config.save();
      this.updateProfileDropdown();
      this.showToast("Auto-Load Set", sel.value.toUpperCase());
    }

    btnExportConfig() {
      const jsonStr = JSON.stringify(
        { settings: this.config.settings, keybinds: this.config.keybinds },
        null,
        2,
      );
      const txt = document.getElementById("txt-configData");
      if (txt) {
        txt.value = jsonStr;
        txt.select();
      }
      if (navigator.clipboard)
        navigator.clipboard
          .writeText(jsonStr)
          .catch(() => document.execCommand("copy"));
      else document.execCommand("copy");
      this.showToast("Config Exported", "COPIED TO CLIPBOARD");
    }

    btnImportConfig() {
      const txt = document.getElementById("txt-configData");
      try {
        const parsed = JSON.parse(txt.value.trim());
        if (parsed.settings)
          Object.assign(this.config.settings, parsed.settings);
        if (parsed.keybinds)
          Object.assign(this.config.keybinds, parsed.keybinds);
        this.refreshUI();
        this.showToast("Config Imported", "UNSAVED");
      } catch (e) {
        this.showToast("Import Error", "INVALID JSON");
      }
    }

    async btnResetSettings() {
      const confirmed = await this.confirmAction(
        "Reset all settings to default?",
      );
      if (!confirmed) return;
      this.config.resetToDefault();
      this.refreshUI();
      this.showToast("Current Profile Reset", "DEFAULT");
    }

    injectCSS() {
      const style = document.createElement("style");
      style.innerHTML = `
                @import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@500;700&display=swap');
                :root { --accent-color: ${this.config.settings.menuColor}; }
                :root[data-theme="dark"] { --bg-main: rgba(20, 20, 20, 0.95); --text-main: #ffffff; --bg-header: rgba(10, 10, 10, 0.8); --text-muted: #dddddd; --bg-tab-hover: rgba(255, 255, 255, 0.08); --bg-group: rgba(0, 0, 0, 0.25); --border-light: rgba(255, 255, 255, 0.08); --bg-row: rgba(255, 255, 255, 0.03); --bg-row-hover: rgba(255, 255, 255, 0.07); --bg-input: #2a2a2a; --bg-input-dark: #1f1f1f; --border-input: #555; --text-input: #ffffff; --bg-tooltip: #1a1a1a; --bg-slider-track: #555; }
                :root[data-theme="light"] { --bg-main: rgba(240, 240, 240, 0.95); --text-main: #111111; --bg-header: rgba(220, 220, 220, 0.9); --text-muted: #333333; --bg-tab-hover: rgba(0, 0, 0, 0.05); --bg-group: rgba(255, 255, 255, 0.6); --border-light: rgba(0, 0, 0, 0.1); --bg-row: rgba(255, 255, 255, 0.4); --bg-row-hover: rgba(255, 255, 255, 0.8); --bg-input: #e8e8e8; --bg-input-dark: #dddddd; --border-input: #aaaaaa; --text-input: #111111; --bg-tooltip: #f5f5f5; --bg-slider-track: #bbbbbb; }
                #xian-ui { position: fixed; width: 440px; background: var(--bg-main); border-radius: 8px; box-shadow: 0 10px 40px rgba(0,0,0,0.8); color: var(--text-main); font-family: 'Rajdhani', sans-serif; z-index: 1000000; user-select: none; transform-origin: top left; transform: scale(${this.config.settings.menuScale}); border: 2px solid var(--border-light); }
                #xian-welcome { position: fixed; top: 20px; right: 20px; width: 320px; background: var(--bg-main); border-radius: 8px; box-shadow: 0 10px 40px rgba(0,0,0,0.8); color: var(--text-main); font-family: 'Rajdhani', sans-serif; z-index: 1000001; border: 2px solid var(--border-light); transform-origin: top right; transform: scale(${this.config.settings.menuScale}); }
                #xian-welcome a { color: var(--accent-color); text-decoration: underline; font-weight: bold; transition: 0.2s; }
                #xian-welcome a:hover { filter: brightness(1.2); }
                .xian-header { display: flex; flex-direction: column; background: var(--bg-header); border-radius: 8px 8px 0 0; cursor: grab; border-bottom: 2px solid var(--border-light); }
                .xian-header:active { cursor: grabbing; }
                .xian-header-top { display: flex; justify-content: space-between; align-items: flex-start; width: 100%; }
                .xian-brand-container { display: flex; align-items: center; gap: 10px; padding: 12px 15px 4px 15px; pointer-events: none; }
                .xian-brand-logo { width: 24px; height: 24px; border-radius: 4px; filter: drop-shadow(0 0 2px rgba(0,0,0,0.5)); }
                .xian-brand-text { font-size: 20px; font-weight: 700; color: var(--text-main); text-transform: uppercase; letter-spacing: 1px; }
                .xian-brand-text span { color: var(--accent-color); }
                .xian-tabs { display: flex; width: 100%; overflow: hidden; }
                .xian-tab { padding: 10px 10px; font-size: 14px; font-weight: 700; color: var(--text-muted); cursor: pointer; transition: 0.2s; border-bottom: 3px solid transparent; text-transform: uppercase; text-align: center; flex: 1; white-space: nowrap; }
                .xian-tab.active { color: var(--text-main); background: var(--bg-row); border-bottom: 3px solid var(--accent-color); }
                .xian-tab:hover:not(.active) { background: var(--bg-tab-hover); color: var(--text-main); }
                .xian-close { padding: 12px 16px; font-weight: 700; cursor: pointer; color: #ff4444; font-size: 15px; transition: 0.2s; border-radius: 0 8px 0 0; }
                .xian-close:hover { color: #ff6666; background: rgba(255,0,0,0.1); }
                .xian-content { padding: 15px; max-height: 500px; overflow-y: auto; }
                .xian-content::-webkit-scrollbar { width: 6px; }
                .xian-content::-webkit-scrollbar-thumb { background: var(--bg-input); border-radius: 3px; }
                .xian-tab-pane { display: none; }
                .xian-tab-pane.active { display: block; animation: fadeIn 0.2s; }
                @keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
                .xian-group { margin-bottom: 12px; background: var(--bg-group); border-radius: 6px; border: 1px solid var(--border-light); }
                .xian-group-header { padding: 8px 14px; background: transparent; font-size: 14px; font-weight: 700; color: var(--accent-color); text-transform: uppercase; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border-light); }
                .xian-group-content { padding: 8px 10px; }
                .xian-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 10px; margin-bottom: 4px; background: var(--bg-row); border-radius: 4px; transition: background 0.2s; }
                .xian-row:hover { background: var(--bg-row-hover); }
                .xian-label { font-size: 15px; color: var(--text-main); font-weight: 600; text-transform: uppercase; display: flex; align-items: center; }
                .xian-info { display: inline-flex; justify-content: center; align-items: center; width: 16px; height: 16px; background: var(--border-light); border-radius: 50%; font-size: 11px; font-weight: bold; margin-left: 8px; cursor: help; color: var(--text-muted); position: relative; transition: 0.2s; }
                .xian-info:hover { background: var(--accent-color); color: #000; }
                .xian-info::after { content: attr(data-tooltip); position: absolute; bottom: 130%; left: 50%; transform: translateX(-50%); background: var(--bg-tooltip); color: var(--text-main); padding: 8px 12px; border-radius: 4px; font-size: 13px; white-space: normal; width: max-content; max-width: 220px; text-align: center; line-height: 1.3; opacity: 0; visibility: hidden; pointer-events: none; transition: 0.2s; border: 1px solid var(--border-input); box-shadow: 0 4px 10px rgba(0,0,0,0.5); z-index: 1000; font-weight: 600; font-family: sans-serif; text-transform: none; }
                .xian-info:hover::after { opacity: 1; visibility: visible; }
                .xian-controls { display: flex; align-items: center; gap: 12px; }
                .xian-switch { position: relative; display: inline-block; width: 40px; height: 22px; }
                .xian-switch input { opacity: 0; width: 0; height: 0; }
                .xian-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: var(--bg-input-dark); transition: .3s; border-radius: 4px; }
                .xian-slider:before { position: absolute; content: ""; height: 14px; width: 14px; left: 4px; bottom: 4px; background-color: var(--text-muted); transition: .3s; border-radius: 2px; }
                input:checked + .xian-slider { background-color: var(--border-light); }
                input:checked + .xian-slider:before { transform: translateX(18px); background-color: var(--accent-color); box-shadow: 0 0 8px var(--accent-color); }
                .xian-bind { background: var(--bg-input); border: 1px solid var(--border-input); color: var(--text-main); padding: 4px 10px; border-radius: 4px; font-size: 13px; cursor: pointer; min-width: 60px; text-align: center; font-weight: 700; }
                .xian-bind:hover { border-color: var(--accent-color); color: var(--accent-color); }
                .xian-bind.active { border-color: var(--accent-color); color: var(--accent-color); animation: pulse 1s infinite; }
                .xian-btn { background: var(--bg-input); border: 1px solid var(--border-input); color: var(--text-main); padding: 6px 15px; border-radius: 4px; font-family: inherit; font-size: 14px; font-weight: 700; cursor: pointer; transition: 0.2s; width: 100%; text-transform: uppercase; }
                .xian-btn:hover { border-color: var(--accent-color); color: var(--accent-color); background: var(--bg-row-hover); }
                .xian-select { background: var(--bg-input); color: var(--text-input); border: 1px solid var(--border-input); padding: 4px 8px; border-radius: 4px; font-family: inherit; font-size: 14px; font-weight: 600; outline: none; cursor: pointer; }
                .xian-textarea, .xian-text { background: var(--bg-input-dark); border: 1px solid var(--border-input); color: var(--text-input); padding: 8px 12px; border-radius: 4px; font-family: monospace; font-size: 12px; outline: none; margin-top: 8px; box-sizing: border-box; width: 100%; }
                .xian-textarea { height: 120px; resize: vertical; white-space: pre-wrap; overflow-wrap: break-word; overflow-y: auto; }
                .xian-textarea:focus, .xian-text:focus, .xian-select:focus { border-color: var(--accent-color); }
                .xian-search { background: var(--bg-input-dark); border: 1px solid var(--border-input); color: var(--text-input); padding: 8px 12px; border-radius: 4px; font-family: 'Rajdhani', sans-serif; font-size: 14px; font-weight: 600; outline: none; box-sizing: border-box; width: 100%; margin-bottom: 10px; }
                .xian-search:focus { border-color: var(--accent-color); }
                .xian-search::placeholder { color: var(--text-muted); opacity: 0.6; }
                .range-container { width: 100%; padding: 5px 0; }
                input[type=range] { -webkit-appearance: none; width: 100%; background: transparent; }
                input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; height: 16px; width: 16px; border-radius: 4px; background: var(--accent-color); cursor: pointer; margin-top: -6px; }
                input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 4px; cursor: pointer; background: var(--bg-slider-track); border-radius: 2px; }
                input[type="color"] { -webkit-appearance: none; border: none; width: 26px; height: 26px; background: none; cursor: pointer; }
                input[type="color"]::-webkit-color-swatch { border: 2px solid var(--border-input); border-radius: 4px; }
                .xian-footer { padding: 10px 15px; background: var(--bg-header); border-top: 2px solid var(--border-light); border-radius: 0 0 8px 8px; font-size: 13px; font-weight: 700; color: var(--text-muted); text-align: center; text-transform: uppercase; cursor: default; }
                .xian-footer span { color: var(--accent-color); }
                .xian-notification { position: fixed; top: 15px; left: 50%; transform: translateX(-50%) translateY(-20px) scale(0.9); background: var(--bg-main); color: var(--text-main); padding: 10px 24px; border-radius: 6px; font-size: 14px; font-weight: 700; pointer-events: none; opacity: 0; z-index: 1000000; display: flex; align-items: center; box-shadow: 0 5px 20px rgba(0,0,0,0.5); border: 1px solid var(--border-light); transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); text-transform: uppercase; }
                .xian-notification.show { transform: translateX(-50%) translateY(0) scale(1); opacity: 1; }
                .xian-notification span { color: var(--accent-color); margin-left: 8px; }
                #xian-hud { position: fixed; pointer-events: none; z-index: 999999; display: flex; flex-direction: column; gap: 4px; font-family: 'Rajdhani', sans-serif; text-transform: uppercase; font-weight: 700; text-shadow: 1px 1px 2px #000, -1px -1px 2px #000, 1px -1px 2px #000, -1px 1px 2px #000; }
                .hud-item { color: #fff; font-size: 16px; }
                .hud-item.title { margin-bottom: 5px; }
                .hud-item span { color: var(--accent-color); }
                @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } }
            `;
      document.documentElement.appendChild(style);
    }

    createDOM() {
      this.gui = document.createElement("div");
      this.gui.id = "xian-ui";
      this.gui.style.left = `${window.innerWidth / 2 - 220}px`;
      this.gui.style.top = `${window.innerHeight / 2 - 200}px`;

      let tabsHtml = "",
        panesHtml = "";
      this.menuStructure.forEach((cat, index) => {
        const isActive = index === 0 ? "active" : "";
        const safeId = cat.title.replace(/\s+/g, "");
        tabsHtml += `<div class="xian-tab ${isActive}" data-target="${safeId}">${cat.title}</div>`;
        panesHtml += `<div class="xian-tab-pane ${isActive}" id="pane-${safeId}">`;

        cat.groups.forEach((group) => {
          panesHtml += `<div class="xian-group" data-group-name="${group.name}"><div class="xian-group-header">${group.name}</div><div class="xian-group-content">`;

          group.items.forEach((item) => {
            let labelHtml = item.tooltip
              ? `<span class="xian-label">${item.name}<span class="xian-info" data-tooltip="${item.tooltip}">?</span></span>`
              : `<span class="xian-label">${item.name}</span>`;
            let controlsHtml = "";

            if (item.type === "slider") {
              panesHtml += `<div class="xian-row" data-item-id="${item.id}" data-search-name="${item.name.toLowerCase()}" data-search-tip="${(item.tooltip || "").toLowerCase()}" style="flex-direction: column; align-items: flex-start;"><div style="display:flex; justify-content:space-between; width:100%; margin-bottom:5px;">${labelHtml}<span class="xian-label" id="val-${item.id}" style="color:var(--accent-color);">${this.config.settings[item.id]}</span></div><div class="range-container"><input type="range" id="sld-${item.id}" min="${item.min}" max="${item.max}" step="${item.step}" value="${this.config.settings[item.id]}"></div></div>`;
            } else if (item.type === "textarea" || item.type === "text") {
              const tag = item.type === "textarea" ? "textarea" : "input";
              const typeAttr = item.type === "text" ? 'type="text"' : "";
              panesHtml += `<div class="xian-row" data-item-id="${item.id}" data-search-name="${item.name.toLowerCase()}" data-search-tip="${(item.tooltip || "").toLowerCase()}" style="flex-direction: column; align-items: flex-start;">${labelHtml}<${tag} ${typeAttr} id="txt-${item.id}" class="xian-${item.type}" placeholder="${item.placeholder || ""}">${item.type === "textarea" ? this.config.settings[item.id] || "" : ""}</${tag}></div>`;
            } else if (item.type === "button") {
              const bindHtml = item.bind
                ? `<div class="xian-bind" id="bind-${item.bind}" data-key="${item.bind}">${this.cleanKeyName(this.config.keybinds[item.bind])}</div>`
                : "";
              panesHtml += `<div class="xian-row" data-item-id="${item.id}" data-search-name="${item.name.toLowerCase()}" data-search-tip="${(item.tooltip || "").toLowerCase()}">${labelHtml}<div class="xian-controls">${bindHtml}<button class="xian-btn" id="btn-${item.id}" style="width: auto; padding: 4px 12px; margin-left: ${item.bind ? "5px" : "0"};">EXECUTE</button></div></div>`;
            } else {
              if (item.type === "checkbox") {
                const bindHtml = item.bind
                  ? `<div class="xian-bind" id="bind-${item.bind}" data-key="${item.bind}">${this.cleanKeyName(this.config.keybinds[item.bind])}</div>`
                  : "";
                controlsHtml = `${bindHtml}<label class="xian-switch"><input type="checkbox" id="chk-${item.id}" ${this.config.settings[item.id] ? "checked" : ""}><span class="xian-slider"></span></label>`;
              } else if (item.type === "color") {
                controlsHtml = `<input type="color" id="col-${item.id}" value="${this.config.settings[item.id]}">`;
              } else if (item.type === "select") {
                controlsHtml = `<select class="xian-select" id="sel-${item.id}">${item.options.map((opt) => `<option value="${opt}" ${this.config.settings[item.id] === opt ? "selected" : ""}>${opt}</option>`).join("")}</select>`;
              } else if (item.type === "bind" || item.type === "customBind") {
                controlsHtml = `<div class="xian-bind" id="bind-${item.bind || item.id}" data-key="${item.bind || item.id}">${this.cleanKeyName(item.id === "profileHotkey" ? this.config.profiles[this.config.activeProfile]?.hotkey : this.config.keybinds[item.bind])}</div>`;
              }
              panesHtml += `<div class="xian-row" data-item-id="${item.id}" data-search-name="${item.name.toLowerCase()}" data-search-tip="${(item.tooltip || "").toLowerCase()}">${labelHtml}<div class="xian-controls">${controlsHtml}</div></div>`;
            }
          });
          panesHtml += `</div></div>`;
        });
        panesHtml += `</div>`;
      });

      this.gui.innerHTML = `<div class="xian-header"><div class="xian-header-top"><div class="xian-brand-container"><img src="https://iili.io/qNroOue.png" class="xian-brand-logo" alt="logo"><div class="xian-brand-text">Xian<span>ware</span></div></div><div class="xian-close">✖</div></div><div class="xian-tabs">${tabsHtml}</div></div><div class="xian-content"><input type="text" id="xian-search" class="xian-search" placeholder="Search settings...">${panesHtml}</div><div class="xian-footer">Xianware V8.2.1 • Made with love by <span>simplexian</span></div>`;
      document.documentElement.appendChild(this.gui);

      this.gui.addEventListener("mousedown", (e) => e.stopPropagation());
      this.gui.addEventListener("wheel", (e) => e.stopPropagation());

      this.welcomeGui = document.createElement("div");
      this.welcomeGui.id = "xian-welcome";
      this.welcomeGui.innerHTML = `<div class="xian-header" style="justify-content: space-between; padding: 10px 14px; cursor: default; flex-direction: row; align-items: center;"><span style="font-weight: 700; color: var(--accent-color); text-transform: uppercase;">Xianware Announcement</span><div id="xian-welcome-close" class="xian-close" style="padding: 5px 10px; border-radius: 4px; font-size: 14px;">✖</div></div><div style="padding: 16px; font-size: 15px; line-height: 1.5; color: var(--text-main); font-weight: 600;">Join the official discord server at <a href="https://discord.gg/Jg6V3e6euj" target="_blank">https://discord.gg/Jg6V3e6euj</a> for the latest version / early access of the new update!<div id="xian-welcome-timer" style="margin-top: 14px; font-size: 13px; color: var(--text-muted); text-align: right; font-weight: 700; text-transform: uppercase;">Auto-closing in 30s...</div></div>`;
      document.documentElement.appendChild(this.welcomeGui);

      this.hudEl = document.createElement("div");
      this.hudEl.id = "xian-hud";
      document.documentElement.appendChild(this.hudEl);

      this.notificationEl = document.createElement("div");
      this.notificationEl.className = "xian-notification";
      document.documentElement.appendChild(this.notificationEl);
    }

    attachEventListeners() {
      const head = this.gui.querySelector(".xian-header");
      let initLeft, initTop, startX, startY;
      const onDragMove = (e) => {
        let newLeft = initLeft + (e.clientX - startX);
        let newTop = initTop + (e.clientY - startY);
        const scale = this.config.settings.menuScale;
        const h = 40 * scale;
        newLeft = Math.max(-360, Math.min(window.innerWidth - 80, newLeft));
        newTop = Math.max(0, Math.min(window.innerHeight - h, newTop));
        this.gui.style.left = `${newLeft}px`;
        this.gui.style.top = `${newTop}px`;
      };
      const onDragUp = () => {
        window.removeEventListener("mousemove", onDragMove);
        window.removeEventListener("mouseup", onDragUp);
        head.style.cursor = "grab";
      };
      head.addEventListener("mousedown", (e) => {
        if (
          e.target.classList.contains("xian-tab") ||
          e.target.classList.contains("xian-close")
        )
          return;
        startX = e.clientX;
        startY = e.clientY;
        initLeft = this.gui.offsetLeft;
        initTop = this.gui.offsetTop;
        head.style.cursor = "grabbing";
        window.addEventListener("mousemove", onDragMove);
        window.addEventListener("mouseup", onDragUp);
      });

      this.gui.querySelector(".xian-close").onclick = () =>
        (this.gui.style.display = "none");
      const tabs = this.gui.querySelectorAll(".xian-tab"),
        panes = this.gui.querySelectorAll(".xian-tab-pane");
      tabs.forEach((tab) =>
        tab.addEventListener("click", () => {
          tabs.forEach((t) => t.classList.remove("active"));
          panes.forEach((p) => p.classList.remove("active"));
          tab.classList.add("active");
          document
            .getElementById(`pane-${tab.dataset.target}`)
            .classList.add("active");
        }),
      );

      let wTimeLeft = 30;
      const wTimerText = document.getElementById("xian-welcome-timer");
      const wInterval = setInterval(() => {
        wTimeLeft--;
        if (wTimerText)
          wTimerText.innerText = `Auto-closing in ${wTimeLeft}s...`;
        if (wTimeLeft <= 0) {
          clearInterval(wInterval);
          if (this.welcomeGui) this.welcomeGui.remove();
        }
      }, 1000);
      document.getElementById("xian-welcome-close").onclick = () => {
        clearInterval(wInterval);
        this.welcomeGui.remove();
      };

      const searchInput = document.getElementById("xian-search");
      if (searchInput) {
        searchInput.addEventListener("input", (e) => {
          const query = e.target.value.toLowerCase().trim();
          const allRows = this.gui.querySelectorAll(".xian-row[data-item-id]");
          const allGroups = this.gui.querySelectorAll(".xian-group");

          allRows.forEach((row) => {
            const name = row.getAttribute("data-search-name") || "";
            const tip = row.getAttribute("data-search-tip") || "";
            const matches =
              !query || name.includes(query) || tip.includes(query);
            row.style.display = matches ? "" : "none";
          });

          allGroups.forEach((group) => {
            const visibleRows = group.querySelectorAll(
              '.xian-row[data-item-id]:not([style*="display: none"])',
            );
            group.style.display =
              !query || visibleRows.length > 0 ? "" : "none";
          });

          if (query) {
            panes.forEach((p) => p.classList.add("active"));
            tabs.forEach((t) => t.classList.add("active"));
          } else {
            panes.forEach((p) => p.classList.remove("active"));
            tabs.forEach((t) => t.classList.remove("active"));
            tabs[0].classList.add("active");
            panes[0].classList.add("active");
          }
        });
      }

      this.menuStructure.forEach((cat) =>
        cat.groups.forEach((group) =>
          group.items.forEach((item) => {
            const el =
              document.getElementById(`chk-${item.id}`) ||
              document.getElementById(`sld-${item.id}`) ||
              document.getElementById(`col-${item.id}`) ||
              document.getElementById(`sel-${item.id}`) ||
              document.getElementById(`txt-${item.id}`);
            if (el) {
              el.addEventListener(
                item.type === "slider" ||
                  item.type === "color" ||
                  item.type === "textarea" ||
                  item.type === "text"
                  ? "input"
                  : "change",
                (e) => {
                  if (
                    item.id === "configData" ||
                    item.id === "profileSelect" ||
                    item.id === "profileName"
                  )
                    return;
                  this.config.settings[item.id] =
                    item.type === "checkbox"
                      ? e.target.checked
                      : item.type === "slider"
                        ? parseFloat(e.target.value)
                        : e.target.value;
                  if (item.type === "slider") {
                    const valEl = document.getElementById(`val-${item.id}`);
                    if (valEl) valEl.innerText = this.config.settings[item.id];
                  }
                  if (item.id === "menuScale") {
                    this.gui.style.transform = `scale(${this.config.settings.menuScale})`;
                    if (this.welcomeGui)
                      this.welcomeGui.style.transform = `scale(${this.config.settings.menuScale})`;
                  }
                  if (item.id === "menuTheme" || item.id === "menuColor")
                    this.applyTheme();
                  if (item.type !== "textarea" && item.type !== "text")
                    this.updateHud();
                  this.app.engine.updateMaterials();
                  this.queueSave();
                },
              );
            }

            const btnMap = {
              loadCss: () => this.injectCustomCss(),
              reloadScript: () => this.app.engine.reset(),
              resetSettings: () => this.btnResetSettings(),
              loadProfile: () => this.btnLoadProfile(),
              saveProfile: () => this.btnSaveProfile(),
              deleteProfile: () => this.btnDeleteProfile(),
              setAutoLoad: () => this.btnSetAutoLoad(),
              createProfile: () => this.btnCreateProfile(),
              exportConfig: () => this.btnExportConfig(),
              importConfig: () => this.btnImportConfig(),
            };
            if (item.type === "button" && btnMap[item.id]) {
              const btnEl = document.getElementById(`btn-${item.id}`);
              if (btnEl) btnEl.onclick = btnMap[item.id];
            }

            if (
              item.bind ||
              item.type === "bind" ||
              item.type === "customBind"
            ) {
              const bindBtn = document.getElementById(
                `bind-${item.bind || item.id}`,
              );
              if (bindBtn)
                bindBtn.addEventListener("click", () => {
                  if (this.bindingFeature) return;
                  this.bindingFeature = item.bind || item.id;
                  bindBtn.classList.add("active");
                  bindBtn.innerText = "...";
                  this.showToast("Press key to bind", item.name);
                });
            }
          }),
        ),
      );
    }

    buildMenuStructure() {
      return [
        {
          title: "Combat",
          groups: [
            {
              name: "Aimbot",
              items: [
                {
                  id: "aimbotEnabled",
                  name: "Enable Aimbot",
                  type: "checkbox",
                  bind: "aimbotEnabled",
                  tooltip: "Automatically aims at enemies.",
                },
                {
                  id: "aimbotOnRightMouse",
                  name: "Aim on ADS",
                  type: "checkbox",
                  tooltip: "Only aims when holding the right mouse button.",
                },
                {
                  id: "aimOffset",
                  name: "Aim Height",
                  type: "slider",
                  min: 0.0,
                  max: 1.0,
                  step: 0.05,
                  tooltip:
                    "Where to aim. 0 = Feet, 0.5 = Chest, 0.75 = Head, 1.0 = Top of Head.",
                },
                {
                  id: "wallCheck",
                  name: "Visible Only",
                  type: "checkbox",
                  bind: "wallCheck",
                  tooltip: "Won't aim at enemies hidden behind walls.",
                },
                {
                  id: "aimPrediction",
                  name: "Aim Prediction",
                  type: "checkbox",
                  tooltip: "Leads targets based on their movement velocity.",
                },
                {
                  id: "predictionFactor",
                  name: "Prediction Strength",
                  type: "slider",
                  min: 0.1,
                  max: 3.0,
                  step: 0.1,
                  tooltip: "How far ahead to predict movement.",
                },
              ],
            },
            {
              name: "Aim Smoothing",
              items: [
                {
                  id: "smoothAim",
                  name: "Smooth Aiming",
                  type: "checkbox",
                  tooltip: "Makes aiming look more natural and human-like.",
                },
                {
                  id: "aimHorizontal",
                  name: "Horizontal Lock",
                  type: "checkbox",
                  tooltip: "Allows the aimbot to move left and right.",
                },
                {
                  id: "aimSpeedXMin",
                  name: "Min X Speed",
                  type: "slider",
                  min: 0.01,
                  max: 1.0,
                  step: 0.01,
                  tooltip: "Minimum horizontal aiming speed.",
                },
                {
                  id: "aimSpeedXMax",
                  name: "Max X Speed",
                  type: "slider",
                  min: 0.01,
                  max: 1.0,
                  step: 0.01,
                  tooltip: "Maximum horizontal aiming speed.",
                },
                {
                  id: "aimVertical",
                  name: "Vertical Lock",
                  type: "checkbox",
                  tooltip: "Allows the aimbot to move up and down.",
                },
                {
                  id: "aimSpeedYMin",
                  name: "Min Y Speed",
                  type: "slider",
                  min: 0.01,
                  max: 1.0,
                  step: 0.01,
                  tooltip: "Minimum vertical aiming speed.",
                },
                {
                  id: "aimSpeedYMax",
                  name: "Max Y Speed",
                  type: "slider",
                  min: 0.01,
                  max: 1.0,
                  step: 0.01,
                  tooltip: "Maximum vertical aiming speed.",
                },
              ],
            },
            {
              name: "Free-Aim Tracking",
              items: [
                {
                  id: "horizontalFreeAim",
                  name: "Horiz. Free-Aim",
                  type: "checkbox",
                  tooltip: "Allows manual horizontal movement while locked on.",
                },
                {
                  id: "verticalFreeAim",
                  name: "Vert. Free-Aim",
                  type: "checkbox",
                  tooltip:
                    "Allows manual vertical movement without breaking lock.",
                },
              ],
            },
            {
              name: "FOV Rules",
              items: [
                {
                  id: "drawFov",
                  name: "Show FOV Circle",
                  type: "checkbox",
                  bind: "drawFov",
                  tooltip: "Draws a circle to show the aimbot range.",
                },
                {
                  id: "fovRadius",
                  name: "FOV Size",
                  type: "slider",
                  min: 50,
                  max: 500,
                  step: 10,
                  tooltip: "How large the aimbot detection area is.",
                },
                {
                  id: "fovCircleColor",
                  name: "FOV Color",
                  type: "color",
                  tooltip: "Color of the FOV circle.",
                },
                {
                  id: "fovCheck",
                  name: "Require FOV",
                  type: "checkbox",
                  tooltip: "Only aims at enemies inside the FOV circle.",
                },
              ],
            },
          ],
        },
        {
          title: "Visuals",
          groups: [
            {
              name: "Player ESP",
              items: [
                {
                  id: "espEnabled",
                  name: "Enable ESP",
                  type: "checkbox",
                  bind: "espEnabled",
                  tooltip: "Draws a box around enemies so you can see them.",
                },
                {
                  id: "espRainbow",
                  name: "Rainbow ESP",
                  type: "checkbox",
                  tooltip: "Makes the ESP box constantly change colors.",
                },
                {
                  id: "espColor",
                  name: "ESP Color",
                  type: "color",
                  tooltip: "Choose a specific color for the ESP box.",
                },
                {
                  id: "espSnaplines",
                  name: "Snaplines",
                  type: "checkbox",
                  bind: "espSnaplines",
                  tooltip: "Draws lines from your screen to each enemy.",
                },
                {
                  id: "snaplineColor",
                  name: "Line Color",
                  type: "color",
                  tooltip: "Color of the snaplines.",
                },
                {
                  id: "snaplineOrigin",
                  name: "Line Origin",
                  type: "select",
                  options: ["Bottom", "Center", "Crosshair"],
                  tooltip: "Where the snapline starts on your screen.",
                },
                {
                  id: "espDistance",
                  name: "Show Distance",
                  type: "checkbox",
                  tooltip: "Displays the distance to each enemy in meters.",
                },
                {
                  id: "espDistanceColor",
                  name: "Distance Color",
                  type: "color",
                  tooltip: "Color of the distance text.",
                },
              ],
            },
            {
              name: "Player Chams",
              items: [
                {
                  id: "chamsEnabled",
                  name: "Enable Chams",
                  type: "checkbox",
                  bind: "chamsEnabled",
                  tooltip:
                    "Colors the enemy models so they are visible through walls.",
                },
                {
                  id: "chamsRainbow",
                  name: "Rainbow Chams",
                  type: "checkbox",
                  tooltip: "Makes the enemy models constantly change colors.",
                },
                {
                  id: "chamsColor",
                  name: "Chams Color",
                  type: "color",
                  tooltip: "Choose a specific color for the enemy models.",
                },
              ],
            },
            {
              name: "Target Indicator",
              items: [
                {
                  id: "targetIndicator",
                  name: "Show Marker",
                  type: "checkbox",
                  tooltip: "Draws a marker above the enemy currently targeted.",
                },
                {
                  id: "indicatorShape",
                  name: "Marker Shape",
                  type: "select",
                  options: ["Arrow", "Triangle", "Dot", "Image"],
                  tooltip: "The design of the target marker.",
                },
                {
                  id: "indicatorColor",
                  name: "Marker Color",
                  type: "color",
                  tooltip: "The color of the target marker.",
                },
                {
                  id: "indicatorSize",
                  name: "Marker Size",
                  type: "slider",
                  min: 5,
                  max: 100,
                  step: 1,
                  tooltip: "How big the target marker should be.",
                },
                {
                  id: "indicatorOpacity",
                  name: "Marker Opacity",
                  type: "slider",
                  min: 0.1,
                  max: 1.0,
                  step: 0.05,
                  tooltip:
                    "How transparent the target marker is (1 = solid, 0.1 = faint).",
                },
                {
                  id: "indicatorImageUrl",
                  name: "Custom Image URL",
                  type: "text",
                  placeholder: "https://example.com/image.png",
                  tooltip:
                    "Paste a direct link to an image (png/jpg) to use as the marker.",
                },
              ],
            },
          ],
        },
        {
          title: "System",
          groups: [
            {
              name: "Interface",
              items: [
                {
                  id: "menuToggle",
                  name: "Menu Key",
                  type: "bind",
                  bind: "menuToggle",
                  tooltip: "The keyboard key to open or close this menu.",
                },
                {
                  id: "menuTheme",
                  name: "UI Theme",
                  type: "select",
                  options: ["Dark", "Light"],
                  tooltip: "Switches the menu between Dark and Light mode.",
                },
                {
                  id: "menuColor",
                  name: "Menu Accent",
                  type: "color",
                  tooltip: "The main color used for buttons and active tabs.",
                },
                {
                  id: "menuScale",
                  name: "Menu Size",
                  type: "slider",
                  min: 0.5,
                  max: 1.5,
                  step: 0.1,
                  tooltip: "Adjusts how big the menu appears on your screen.",
                },
                {
                  id: "showHud",
                  name: "Enable HUD",
                  type: "checkbox",
                  bind: "showHud",
                  tooltip:
                    "Shows a small info box on screen with active features.",
                },
                {
                  id: "hudScale",
                  name: "HUD Size",
                  type: "slider",
                  min: 0.5,
                  max: 2.0,
                  step: 0.1,
                  tooltip: "Adjusts how big the HUD info box is.",
                },
                {
                  id: "hudPosition",
                  name: "HUD Location",
                  type: "select",
                  options: [
                    "Top Left",
                    "Top Right",
                    "Bottom Left",
                    "Bottom Right",
                  ],
                  tooltip: "Where the HUD is placed on your screen.",
                },
                {
                  id: "showNotifications",
                  name: "Notifications",
                  type: "checkbox",
                  bind: "showNotifications",
                  tooltip: "Shows small popup alerts when you toggle features.",
                },
              ],
            },
            {
              name: "Customization & Fixes",
              items: [
                {
                  id: "customCssText",
                  name: "Custom CSS",
                  type: "textarea",
                  placeholder:
                    "/* Paste CSS code here */\n.headerBar { display: none; }",
                  tooltip:
                    "Write your own CSS code to change how the game looks.",
                },
                {
                  id: "loadCss",
                  name: "Apply CSS",
                  type: "button",
                  tooltip: "Activates your custom CSS code immediately.",
                },
                {
                  id: "reloadScript",
                  name: "Reload Cheat",
                  type: "button",
                  bind: "reloadScript",
                  tooltip:
                    "Refreshes the cheat if it glitches or stops working.",
                },
                {
                  id: "resetSettings",
                  name: "Factory Reset",
                  type: "button",
                  tooltip:
                    "Restores all settings in the current profile back to default.",
                },
              ],
            },
          ],
        },
        {
          title: "Config",
          groups: [
            {
              name: "Profiles",
              items: [
                {
                  id: "profileSelect",
                  name: "Select Profile",
                  type: "select",
                  options: ["Default"],
                  tooltip: "Choose a saved profile from the list.",
                },
                {
                  id: "loadProfile",
                  name: "Load Profile",
                  type: "button",
                  tooltip: "Loads the settings of the selected profile.",
                },
                {
                  id: "saveProfile",
                  name: "Save Profile",
                  type: "button",
                  tooltip: "Saves your current settings to the active profile.",
                },
                {
                  id: "deleteProfile",
                  name: "Delete Profile",
                  type: "button",
                  tooltip: "Permanently removes the selected profile.",
                },
                {
                  id: "setAutoLoad",
                  name: "Set Startup Profile",
                  type: "button",
                  tooltip:
                    "Makes the selected profile load automatically when you join a game.",
                },
                {
                  id: "profileHotkey",
                  name: "Profile Hotkey",
                  type: "customBind",
                  bind: "profileHotkey",
                  tooltip:
                    "Assign a key to instantly switch to the active profile.",
                },
                {
                  id: "profileName",
                  name: "New Profile Name",
                  type: "text",
                  placeholder: "Enter new name...",
                  tooltip: "Type a name for your new settings profile.",
                },
                {
                  id: "createProfile",
                  name: "Create Profile",
                  type: "button",
                  tooltip:
                    "Creates a new profile with the name you typed above.",
                },
              ],
            },
            {
              name: "Data Management",
              items: [
                {
                  id: "configData",
                  name: "Config Data",
                  type: "textarea",
                  placeholder: "Paste config JSON here to import...",
                  tooltip:
                    "The raw text data of your settings. Use this to share or backup.",
                },
                {
                  id: "exportConfig",
                  name: "Copy to Clipboard",
                  type: "button",
                  tooltip:
                    "Copies your settings text so you can save it elsewhere.",
                },
                {
                  id: "importConfig",
                  name: "Load from Text",
                  type: "button",
                  tooltip: "Applies settings from the text you pasted above.",
                },
              ],
            },
          ],
        },
      ];
    }
  }

  class InputManager {
    constructor(app) {
      this.app = app;
      this.rightMouseDown = false;
    }

    init() {
      window.addEventListener("pointerdown", (e) => {
        if (e.button === 2) this.rightMouseDown = true;
      });
      window.addEventListener("pointerup", (e) => {
        if (e.button === 2) this.rightMouseDown = false;
      });

      window.addEventListener("keydown", (e) => {
        const ui = this.app.ui;
        const config = this.app.config;

        if (ui.bindingFeature) {
          e.preventDefault();
          e.stopPropagation();
          if (ui.bindingFeature === "profileHotkey") {
            const code = e.code === "Escape" ? "" : e.code;
            if (code !== "")
              for (let p in config.profiles)
                if (config.profiles[p].hotkey === code)
                  config.profiles[p].hotkey = "";
            config.profiles[config.activeProfile].hotkey = code;
            config.save();
            const btn = document.getElementById("bind-profileHotkey");
            if (btn) {
              btn.innerText = ui.cleanKeyName(code);
              btn.classList.remove("active");
            }
            ui.showToast("Profile Hotkey bound to", ui.cleanKeyName(code));
          } else {
            if (e.code === "Escape") {
              config.keybinds[ui.bindingFeature] = "";
              const btn = document.getElementById(`bind-${ui.bindingFeature}`);
              if (btn) {
                btn.innerText = "NONE";
                btn.classList.remove("active");
              }
              ui.showToast("Unbound feature", "");
            } else {
              config.keybinds[ui.bindingFeature] = e.code;
              const btn = document.getElementById(`bind-${ui.bindingFeature}`);
              if (btn) {
                btn.innerText = ui.cleanKeyName(e.code);
                btn.classList.remove("active");
              }
              ui.showToast("Bound to", ui.cleanKeyName(e.code));
            }
          }
          config.updateKeyMaps();
          ui.bindingFeature = null;
          return;
        }

        if (
          document.activeElement &&
          (document.activeElement.tagName === "INPUT" ||
            document.activeElement.tagName === "TEXTAREA")
        )
          return;

        if (config.profileKeyMap[e.code]) {
          const targetProfile = config.profileKeyMap[e.code];
          if (config.activeProfile !== targetProfile) {
            config.applyProfile(targetProfile);
            ui.refreshUI();
            ui.showToast("Profile Loaded", targetProfile.toUpperCase());
          }
          return;
        }

        const feature = config.reverseKeyMap[e.code];
        if (feature) {
          if (feature === "menuToggle") {
            if (ui.gui)
              ui.gui.style.display =
                ui.gui.style.display === "none" ? "block" : "none";
          } else if (feature === "reloadScript") {
            this.app.engine.reset();
          } else if (typeof config.settings[feature] === "boolean") {
            config.settings[feature] = !config.settings[feature];
            ui.updateHud();
            const chk = document.getElementById(`chk-${feature}`);
            if (chk) chk.checked = config.settings[feature];

            let displayName = feature;
            ui.menuStructure.forEach((cat) =>
              cat.groups.forEach((g) => {
                const item = g.items.find((i) => i.id === feature);
                if (item) displayName = item.name;
              }),
            );
            ui.showToast(displayName, config.settings[feature] ? "ON" : "OFF");
            ui.queueSave();
          }
        }
      });
    }
  }

  class Engine {
    constructor(app) {
      this.app = app;

      this.scene = null;
      this.gameCamera = null;
      this.cachedPlayers = [];
      this.mapMeshes = [];
      this.myPlayerCache = null;
      this.lastMapScrape = 0;
      this.lastNonPlayerCount = -1;
      this.lockedTarget = null;
      this.lastFrameTime = performance.now();
      this.canvasNeedsClear = false;

      this.hue = 0;
      this.cachedIndicatorImage = new Image();
      this.currentImageUrl = "";

      this.aimCandidates = [];
      this.nonPlayerObjects = [];

      this._pool = null;

      this.geometry = null;
      this.espMaterial = null;
      this.chamsMaterial = null;
      this.line = null;
      this.linePositions = null;

      this.originalPush = Array.prototype.push;
    }

    init() {
      if (!THREE) return;

      this._pool = {
        tempVec: new THREE.Vector3(),
        tempObj: new THREE.Object3D(),
        originVec: new THREE.Vector3(),
        destVec: new THREE.Vector3(),
        dirVec: new THREE.Vector3(),
        camPos: new THREE.Vector3(),
        centerVec: new THREE.Vector3(),
        topVec: new THREE.Vector3(),
        botVec: new THREE.Vector3(),
        targetVec: new THREE.Vector3(),
        raycaster: new THREE.Raycaster(),
        tempBox: new THREE.Box3(),
        localPos: new THREE.Vector3(),
        worldPos: new THREE.Vector3(),
        fallbackOffset: new THREE.Vector3(0, 5.5, 0),
        fallbackSize: new THREE.Vector3(3.5, 11, 3.5),
      };
      this._pool.tempObj.rotation.order = "YXZ";

      this.geometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(1, 1, 1));

      this.espMaterial = new THREE.RawShaderMaterial({
        uniforms: {
          uColor: { value: new THREE.Color(this.app.config.settings.espColor) },
        },
        vertexShader:
          "precision mediump float; attribute vec3 position; uniform mat4 projectionMatrix; uniform mat4 modelViewMatrix; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }",
        fragmentShader:
          "precision mediump float; uniform vec3 uColor; void main() { gl_FragColor = vec4(uColor, 1.0); }",
      });
      this.espMaterial.depthTest = false;
      this.espMaterial.depthWrite = false;
      this.espMaterial.transparent = true;

      this.line = new THREE.LineSegments(
        new THREE.BufferGeometry(),
        this.espMaterial,
      );
      this.line.frustumCulled = false;
      this.linePositions = new THREE.BufferAttribute(
        new Float32Array(100 * 2 * 3),
        3,
      );
      this.line.geometry.setAttribute("position", this.linePositions);

      this.chamsMaterial = new THREE.MeshBasicMaterial({
        color: new THREE.Color(this.app.config.settings.chamsColor),
        depthTest: false,
        depthWrite: false,
        transparent: true,
        opacity: 0.8,
      });

      this.hookArrayPush();
      this.renderLoop();
    }

    updateMaterials() {
      if (this.espMaterial)
        this.espMaterial.uniforms.uColor.value.set(
          this.app.config.settings.espColor,
        );
      if (this.chamsMaterial)
        this.chamsMaterial.color.set(this.app.config.settings.chamsColor);
    }

    hookArrayPush() {
      const self = this;
      const origPush = this.originalPush;
      if (Array.prototype.push === origPush) {
        Object.defineProperty(Array.prototype, "push", {
          configurable: true,
          enumerable: false,
          writable: true,
          value: function () {
            for (let i = 0; i < arguments.length; i++) {
              const obj = arguments[i];
              if (
                !self.scene &&
                obj &&
                typeof obj === "object" &&
                obj.parent &&
                obj.parent.type === "Scene" &&
                obj.parent.name === "Main"
              ) {
                self.scene = obj.parent;
                Object.defineProperty(Array.prototype, "push", {
                  configurable: true,
                  enumerable: false,
                  writable: true,
                  value: origPush,
                });
              }
            }
            return origPush.apply(this, arguments);
          },
        });
      }
    }

    reset() {
      this.scene = null;
      this.gameCamera = null;
      this.cachedPlayers.length = 0;
      this.mapMeshes.length = 0;
      this.myPlayerCache = null;
      this.lastMapScrape = 0;
      this.lastNonPlayerCount = -1;
      this.lockedTarget = null;
      this.hookArrayPush();
      this.app.ui.showToast("Cheat Reloaded", "SCANNING");
    }

    getPlayerHead(player) {
      try {
        const c0 = player?.children?.[0];
        const c00 = c0?.children?.[0];
        return c00 || null;
      } catch (e) {
        return null;
      }
    }

    renderLoop = () => {
      x.requestAnimationFrame.call(x.window, this.renderLoop);
      if (!THREE || !this.app.ui.ctx || !this._pool) return;

      const s = this.app.config.settings;
      const ctx = this.app.ui.ctx;
      const canvas = this.app.ui.canvas;

      let currentTime = performance.now();
      let delta = currentTime - this.lastFrameTime;
      if (delta > 100) delta = 16;
      this.lastFrameTime = currentTime;

      this.updateRainbow(s);
      this.updateIndicatorImage(s);

      let needs2D =
        s.drawFov || s.targetIndicator || s.espSnaplines || s.espDistance;
      if (needs2D || this.canvasNeedsClear) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        this.canvasNeedsClear = needs2D;
      }

      if (s.drawFov) {
        ctx.beginPath();
        ctx.arc(
          canvas.width / 2,
          canvas.height / 2,
          s.fovRadius,
          0,
          2 * Math.PI,
        );
        ctx.strokeStyle = s.espRainbow
          ? `hsl(${this.hue}, 100%, 50%)`
          : s.fovCircleColor;
        ctx.lineWidth = 1.5;
        ctx.stroke();
      }

      if (!this.scene || !this.scene.children) return;

      this.scanScene(currentTime);

      if (!this.myPlayerCache || !this.gameCamera) return;

      const centerX = canvas.width / 2;
      const centerY = canvas.height / 2;
      const fovRadiusSq = s.fovRadius * s.fovRadius;

      const myHead = this.getPlayerHead(this.myPlayerCache);
      if (myHead) myHead.updateMatrixWorld(true);

      if (s.espEnabled)
        this._pool.tempObj.matrix.copy(this.myPlayerCache.matrix).invert();

      this.aimCandidates.length = 0;
      let counter = 0;

      for (let i = 0; i < this.cachedPlayers.length; i++) {
        const player = this.cachedPlayers[i];

        if (!player.box) {
          const box = new THREE.LineSegments(this.geometry, this.espMaterial);
          box.frustumCulled = false;
          player.add(box);
          player.box = box;
        }

        if (
          player.position.x === this.myPlayerCache.position.x &&
          player.position.z === this.myPlayerCache.position.z
        ) {
          player.box.visible = false;
          if (this.line.parent !== player) player.add(this.line);
          continue;
        }

        player.visible = s.espEnabled || player.visible || s.chamsEnabled;
        player.box.visible = s.espEnabled;

        if (!player._xBox) player._xBox = new THREE.Box3();
        if (!player._xSize) player._xSize = new THREE.Vector3();
        if (!player._xCenter) player._xCenter = new THREE.Vector3();
        if (!player._xRawSize) player._xRawSize = new THREE.Vector3();
        if (!player._xRawCenter) player._xRawCenter = new THREE.Vector3();
        if (!player._xLastPos)
          player._xLastPos = new THREE.Vector3().copy(player.position);
        if (!player._xVelocity) player._xVelocity = new THREE.Vector3();

        player._xVelocity.copy(player.position).sub(player._xLastPos);
        player._xLastPos.copy(player.position);

        player._xBox.makeEmpty();
        let hasMesh = false;
        const applyChams = s.chamsEnabled;

        player.traverse((obj) => {
          if (!obj.isMesh || obj === player.box) return;
          if (!obj.visible) return;

          if (obj.geometry) {
            if (obj.geometry.boundingBox === null)
              obj.geometry.computeBoundingBox();
            this._pool.tempBox.copy(obj.geometry.boundingBox);
            this._pool.tempBox.applyMatrix4(obj.matrixWorld);
            player._xBox.union(this._pool.tempBox);
            hasMesh = true;
          }

          if (applyChams) {
            if (!obj.userData.originalMaterial)
              obj.userData.originalMaterial = obj.material;
            obj.material = this.chamsMaterial;
          } else if (
            obj.userData.originalMaterial &&
            obj.material === this.chamsMaterial
          ) {
            obj.material = obj.userData.originalMaterial;
          }
        });

        if (!hasMesh) {
          this._pool.tempVec
            .copy(player.position)
            .add(this._pool.fallbackOffset);
          player._xBox.setFromCenterAndSize(
            this._pool.tempVec,
            this._pool.fallbackSize,
          );
        }

        player._xBox.getSize(player._xRawSize);
        player._xBox.getCenter(player._xRawCenter);

        let tightBodyWidth = 3.5;
        player._xSize.set(tightBodyWidth, player._xRawSize.y, tightBodyWidth);
        player._xCenter.set(
          player.position.x,
          player._xRawCenter.y,
          player.position.z,
        );

        if (s.espEnabled) {
          player.box.scale.copy(player._xSize);
          this._pool.localPos.copy(player._xCenter);
          player.worldToLocal(this._pool.localPos);
          player.box.position.copy(this._pool.localPos);

          this.linePositions.setXYZ(counter++, 0, 10, -5);
          this._pool.tempVec
            .copy(player._xCenter)
            .applyMatrix4(this._pool.tempObj.matrix);
          this.linePositions.setXYZ(
            counter++,
            this._pool.tempVec.x,
            this._pool.tempVec.y,
            this._pool.tempVec.z,
          );
        }

        if (s.espSnaplines && this.gameCamera) {
          const head = this.getPlayerHead(player);
          if (head) {
            head.getWorldPosition(this._pool.worldPos);
            this._pool.worldPos.project(this.gameCamera);

            if (this._pool.worldPos.z < 1) {
              const sx = (this._pool.worldPos.x + 1) * centerX;
              const sy = (-this._pool.worldPos.y + 1) * centerY;

              let originX = centerX,
                originY;
              switch (s.snaplineOrigin) {
                case "Bottom":
                  originY = canvas.height;
                  break;
                case "Center":
                  originY = centerY;
                  break;
                case "Crosshair":
                  originY = centerY;
                  break;
                default:
                  originY = canvas.height;
              }

              ctx.beginPath();
              ctx.moveTo(originX, originY);
              ctx.lineTo(sx, sy);
              ctx.strokeStyle = s.espRainbow
                ? `hsl(${this.hue}, 100%, 50%)`
                : s.snaplineColor;
              ctx.lineWidth = 1;
              ctx.stroke();
            }
          }
        }

        if (s.espDistance && this.gameCamera) {
          this._pool.worldPos.copy(player._xCenter);
          this._pool.worldPos.y = player._xBox.min.y;
          this._pool.worldPos.project(this.gameCamera);

          if (this._pool.worldPos.z < 1) {
            const sx = (this._pool.worldPos.x + 1) * centerX;
            const sy = (-this._pool.worldPos.y + 1) * centerY;
            const dist = Math.sqrt(
              player.position.distanceToSquared(this.myPlayerCache.position),
            ).toFixed(0);

            ctx.font = "bold 13px Rajdhani";
            ctx.textAlign = "center";
            ctx.fillStyle = "#000";
            ctx.fillText(`${dist}m`, sx + 1, sy + 16);
            ctx.fillStyle = s.espDistanceColor;
            ctx.fillText(`${dist}m`, sx, sy + 15);
          }
        }

        if (s.aimbotEnabled) {
          let inFov = true;
          let distToCenterSq = 0;

          if (s.fovCheck) {
            const head = this.getPlayerHead(player);
            if (head) {
              head.getWorldPosition(this._pool.tempVec);
              this._pool.tempVec.project(this.gameCamera);
              const dx = (this._pool.tempVec.x + 1) * centerX - centerX;
              const dy = (-this._pool.tempVec.y + 1) * centerY - centerY;
              distToCenterSq = dx * dx + dy * dy;
              if (this._pool.tempVec.z > 1 || distToCenterSq > fovRadiusSq)
                inFov = false;
            } else {
              inFov = false;
            }
          }

          if (inFov) {
            this.aimCandidates.push({
              player: player,
              dist3D: player.position.distanceToSquared(
                this.myPlayerCache.position,
              ),
              distFOV: distToCenterSq,
            });
          }
        }
      }

      if (s.espEnabled) {
        this.linePositions.needsUpdate = true;
        this.line.geometry.setDrawRange(0, counter);
        this.line.visible = false;
      }

      const shouldAimbot =
        s.aimbotEnabled &&
        (!s.aimbotOnRightMouse || this.app.input.rightMouseDown);
      let targetPlayer = null;

      if (shouldAimbot && this.aimCandidates.length > 0) {
        if (s.fovCheck)
          this.aimCandidates.sort((a, b) => a.distFOV - b.distFOV);
        else this.aimCandidates.sort((a, b) => a.dist3D - b.dist3D);

        const checkWall = (candidate) => {
          if (!s.wallCheck || this.mapMeshes.length === 0) return true;
          if (
            candidate.lastWallCheckTime &&
            currentTime - candidate.lastWallCheckTime < 100
          )
            return candidate.isWallVisible;

          const myHead = this.getPlayerHead(this.myPlayerCache);
          const enemyHead = this.getPlayerHead(candidate);
          if (!myHead || !enemyHead) return false;

          myHead.getWorldPosition(this._pool.originVec);
          enemyHead.getWorldPosition(this._pool.destVec);

          const distToEnemy = this._pool.originVec.distanceTo(
            this._pool.destVec,
          );
          this._pool.dirVec
            .copy(this._pool.destVec)
            .sub(this._pool.originVec)
            .normalize();

          this._pool.raycaster.set(this._pool.originVec, this._pool.dirVec);
          this._pool.raycaster.far = distToEnemy;

          const intersects = this._pool.raycaster.intersectObjects(
            this.mapMeshes,
            false,
          );
          let isVisible = true;

          for (let j = 0; j < intersects.length; j++) {
            const hit = intersects[j].object;
            if (hit.visible && (!hit.material || hit.material.opacity > 0.5)) {
              isVisible = false;
              break;
            }
          }

          candidate.lastWallCheckTime = currentTime;
          candidate.isWallVisible = isVisible;
          return isVisible;
        };

        let lockedCandidate = this.lockedTarget
          ? this.aimCandidates.find((c) => c.player === this.lockedTarget)
          : null;

        if (lockedCandidate && checkWall(lockedCandidate.player)) {
          targetPlayer = this.lockedTarget;
        } else {
          for (let i = 0; i < this.aimCandidates.length; i++) {
            if (checkWall(this.aimCandidates[i].player)) {
              targetPlayer = this.aimCandidates[i].player;
              break;
            }
          }
          this.lockedTarget = targetPlayer;
        }
      } else {
        this.lockedTarget = null;
      }

      if (targetPlayer && s.targetIndicator && this.gameCamera) {
        this._pool.tempVec.set(
          targetPlayer._xCenter.x,
          targetPlayer._xBox.max.y + 1.5,
          targetPlayer._xCenter.z,
        );
        this._pool.tempVec.project(this.gameCamera);

        if (this._pool.tempVec.z < 1) {
          const screenX = (this._pool.tempVec.x + 1) * centerX;
          const screenY = (-this._pool.tempVec.y + 1) * centerY;
          ctx.globalAlpha = s.indicatorOpacity;
          const isz = s.indicatorSize;

          if (s.indicatorShape === "Image") {
            if (
              this.cachedIndicatorImage.complete &&
              this.cachedIndicatorImage.naturalWidth > 0
            ) {
              ctx.drawImage(
                this.cachedIndicatorImage,
                screenX - isz,
                screenY - isz * 2,
                isz * 2,
                isz * 2,
              );
            }
          } else {
            ctx.beginPath();
            if (s.indicatorShape === "Dot") {
              ctx.arc(screenX, screenY - isz * 0.6, isz * 0.5, 0, Math.PI * 2);
            } else if (s.indicatorShape === "Triangle") {
              ctx.moveTo(screenX, screenY);
              ctx.lineTo(screenX - isz * 0.6, screenY - isz * 1.2);
              ctx.lineTo(screenX + isz * 0.6, screenY - isz * 1.2);
            } else {
              ctx.moveTo(screenX, screenY);
              ctx.lineTo(screenX - isz * 0.5, screenY - isz * 0.6);
              ctx.lineTo(screenX - isz * 0.2, screenY - isz * 0.6);
              ctx.lineTo(screenX - isz * 0.2, screenY - isz * 1.2);
              ctx.lineTo(screenX + isz * 0.2, screenY - isz * 1.2);
              ctx.lineTo(screenX + isz * 0.2, screenY - isz * 0.6);
              ctx.lineTo(screenX + isz * 0.5, screenY - isz * 0.6);
            }
            ctx.closePath();
            ctx.fillStyle = s.indicatorColor;
            ctx.fill();
            ctx.strokeStyle = "rgba(0,0,0,0.8)";
            ctx.lineWidth = 1.5;
            ctx.stroke();
          }
          ctx.globalAlpha = 1.0;
        }
      }

      if (!targetPlayer) return;

      this.executeAimbot(s, targetPlayer);
    };

    updateRainbow(s) {
      if (s.espRainbow || s.chamsRainbow) this.hue = (this.hue + 1) % 360;
      if (s.espEnabled && s.espRainbow)
        this.espMaterial.uniforms.uColor.value.setHSL(this.hue / 360, 1, 0.5);
      if (s.chamsEnabled && this.chamsMaterial) {
        if (s.chamsRainbow)
          this.chamsMaterial.color.setHSL(this.hue / 360, 1, 0.5);
        else this.chamsMaterial.color.set(s.chamsColor);
      }
    }

    updateIndicatorImage(s) {
      if (
        s.indicatorShape === "Image" &&
        s.indicatorImageUrl !== this.currentImageUrl
      ) {
        this.currentImageUrl = s.indicatorImageUrl;
        if (this.currentImageUrl)
          this.cachedIndicatorImage.src = this.currentImageUrl;
      }
    }

    scanScene(currentTime) {
      this.cachedPlayers.length = 0;
      this.myPlayerCache = null;
      this.nonPlayerObjects.length = 0;

      for (let i = 0; i < this.scene.children.length; i++) {
        const child = this.scene.children[i];
        let isPlayer = false;
        if (child.type === "Object3D") {
          try {
            const c0 = child.children[0];
            if (c0 && c0.children && c0.children[0]) {
              if (c0.children[0].type === "PerspectiveCamera") {
                this.myPlayerCache = child;
                this.gameCamera = c0.children[0];
                isPlayer = true;
              } else {
                this.cachedPlayers.push(child);
                isPlayer = true;
              }
            }
          } catch (err) {}
        }
        if (!isPlayer) this.nonPlayerObjects.push(child);
      }

      if (this.nonPlayerObjects.length !== this.lastNonPlayerCount) {
        if (
          currentTime - this.lastMapScrape > 1000 ||
          this.lastNonPlayerCount === -1
        ) {
          this.mapMeshes.length = 0;
          for (let i = 0; i < this.nonPlayerObjects.length; i++) {
            const child = this.nonPlayerObjects[i];
            if (child.material) child.material.wireframe = false;
            child.traverse((node) => {
              if (node.isMesh && node.visible) {
                if (
                  node.material &&
                  (node.material.transparent ||
                    node.material.opacity < 0.9 ||
                    node.material.depthWrite === false)
                )
                  return;
                if (node.geometry) {
                  if (node.geometry.boundingSphere === null)
                    node.geometry.computeBoundingSphere();
                  if (node.geometry.boundingBox === null)
                    node.geometry.computeBoundingBox();
                }
                this.mapMeshes.push(node);
              }
            });
          }
          this.lastNonPlayerCount = this.nonPlayerObjects.length;
          this.lastMapScrape = currentTime;
        }
      }
    }

    executeAimbot(s, targetPlayer) {
      this.gameCamera.getWorldPosition(this._pool.camPos);

      let headTopY = targetPlayer._xBox.max.y;
      let feetY = targetPlayer._xBox.min.y;
      let dynamicHeight = targetPlayer._xSize.y;

      const getPitchYaw = (vec) => {
        this._pool.tempObj.position.copy(this._pool.camPos);
        this._pool.tempObj.lookAt(vec);
        let p = Math.max(
          -Math.PI / 2,
          Math.min(Math.PI / 2, -this._pool.tempObj.rotation.x),
        );
        let y = this._pool.tempObj.rotation.y + Math.PI;
        return { pitch: p, yaw: y };
      };

      this._pool.centerVec.set(
        targetPlayer._xCenter.x,
        targetPlayer.position.y,
        targetPlayer._xCenter.z,
      );

      if (s.aimPrediction && targetPlayer._xVelocity) {
        const dist = this._pool.camPos.distanceTo(targetPlayer.position);
        const travelTime = dist / 600;
        const leadFactor = travelTime * s.predictionFactor * 60;
        this._pool.centerVec.x += targetPlayer._xVelocity.x * leadFactor;
        this._pool.centerVec.z += targetPlayer._xVelocity.z * leadFactor;
      }

      let targetYaw = getPitchYaw(this._pool.centerVec).yaw;

      let angleDiff = targetYaw - this.myPlayerCache.rotation.y;
      while (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
      while (angleDiff < -Math.PI) angleDiff += Math.PI * 2;

      if (s.aimHorizontal && s.horizontalFreeAim) {
        let distToTarget = this._pool.camPos.distanceTo(targetPlayer.position);
        let deadzoneAngle = Math.atan2(targetPlayer._xSize.x / 2, distToTarget);
        if (Math.abs(angleDiff) <= deadzoneAngle) angleDiff = 0;
        else
          angleDiff =
            angleDiff > 0
              ? angleDiff - deadzoneAngle
              : angleDiff + deadzoneAngle;
      }

      let currentPitch = this.myPlayerCache.children[0].rotation.x;
      let pitchDiff = 0;

      if (s.aimVertical) {
        let predOffsetX = 0,
          predOffsetZ = 0;
        if (s.aimPrediction && targetPlayer._xVelocity) {
          const dist = this._pool.camPos.distanceTo(targetPlayer.position);
          const travelTime = dist / 600;
          const leadFactor = travelTime * s.predictionFactor * 60;
          predOffsetX = targetPlayer._xVelocity.x * leadFactor;
          predOffsetZ = targetPlayer._xVelocity.z * leadFactor;
        }

        if (s.verticalFreeAim) {
          this._pool.topVec.set(
            targetPlayer._xCenter.x + predOffsetX,
            headTopY,
            targetPlayer._xCenter.z + predOffsetZ,
          );
          this._pool.botVec.set(
            targetPlayer._xCenter.x + predOffsetX,
            feetY,
            targetPlayer._xCenter.z + predOffsetZ,
          );

          let topPitch = getPitchYaw(this._pool.topVec).pitch;
          let botPitch = getPitchYaw(this._pool.botVec).pitch;

          let maxPitch = Math.max(topPitch, botPitch);
          let minPitch = Math.min(topPitch, botPitch);

          if (currentPitch < minPitch) pitchDiff = minPitch - currentPitch;
          else if (currentPitch > maxPitch) pitchDiff = maxPitch - currentPitch;
          else pitchDiff = 0;
        } else {
          let targetY = feetY + dynamicHeight * s.aimOffset;
          this._pool.targetVec.set(
            targetPlayer._xCenter.x + predOffsetX,
            targetY,
            targetPlayer._xCenter.z + predOffsetZ,
          );
          let targetPitch = getPitchYaw(this._pool.targetVec).pitch;
          pitchDiff = targetPitch - currentPitch;
        }
      }

      let targetPitchOffset = currentPitch + pitchDiff;
      targetPitchOffset = Math.max(
        -Math.PI / 2,
        Math.min(Math.PI / 2, targetPitchOffset),
      );

      if (s.smoothAim) {
        const minX = Math.min(s.aimSpeedXMin, s.aimSpeedXMax);
        const maxX = Math.max(s.aimSpeedXMin, s.aimSpeedXMax);
        const minY = Math.min(s.aimSpeedYMin, s.aimSpeedYMax);
        const maxY = Math.max(s.aimSpeedYMin, s.aimSpeedYMax);

        const speedX = (minX + Math.random() * (maxX - minX)) * 0.4;
        const speedY = (minY + Math.random() * (maxY - minY)) * 0.4;

        if (s.aimVertical && pitchDiff !== 0) {
          this.myPlayerCache.children[0].rotation.x = MathUtils.lerp(
            this.myPlayerCache.children[0].rotation.x,
            targetPitchOffset,
            speedY,
          );
        }
        if (s.aimHorizontal && angleDiff !== 0) {
          this.myPlayerCache.rotation.y += angleDiff * speedX;
        }
      } else {
        if (s.aimVertical && pitchDiff !== 0)
          this.myPlayerCache.children[0].rotation.x = targetPitchOffset;
        if (s.aimHorizontal && angleDiff !== 0)
          this.myPlayerCache.rotation.y += angleDiff;
      }
    }
  }

  class Xianware {
    constructor() {
      this.config = new ConfigManager();
      this.ui = new UIManager(this);
      this.input = new InputManager(this);
      this.engine = new Engine(this);
      this.isInitialized = false;
    }

    init() {
      if (this.isInitialized) return;
      this.isInitialized = true;

      this.ui.init();
      this.input.init();
      this.engine.init();

      if (this.config.settings.customCssText) this.ui.injectCustomCss();
      console.log("[Xianware] V8.2.1 Initialized Successfully.");
    }
  }

  const x = {
    window: window,
    document: document,
    requestAnimationFrame: window.requestAnimationFrame,
  };
  const app = new Xianware();

  if (
    document.readyState === "complete" ||
    document.readyState === "interactive"
  ) {
    app.init();
  } else {
    window.addEventListener("DOMContentLoaded", () => app.init());
    window.addEventListener("load", () => app.init());
  }
})();