Cube Engine New Options

The ultimate enhancement for your Drawaria.online experience. Redefining possibilities!

// ==UserScript==
// @name         Cube Engine New Options
// @version      12.0.3
// @description  The ultimate enhancement for your Drawaria.online experience. Redefining possibilities!
// @namespace    drawaria.modded.fullspec
// @homepage     https://drawaria-cubic-engine.netlify.app
// @author       ≺ᴄᴜʙᴇ³≻ And YouTubeDrawaria
// @match        https://drawaria.online/
// @match        https://*.drawaria.online/*
// @match        https://drawaria.online/test
// @match        https://drawaria.online/room/*
// @match        https://*.drawaria.online/avatar/builder/
// @grant        GM_xmlhttpRequest
// @icon         https://drawaria.online/avatar/cache/e53693c0-18b1-11ec-b633-b7649fa52d3f.jpg
// @license      GNU GPLv3
// @run-at       document-end
// @require      https://update.greasyforks.org/scripts/546193/1643721/Auto%20Drawing%20Library%20with%20100%2B%20Word%20Presets.js
// @require      https://update.greasyforks.org/scripts/546196/1643733/Avatar%20IDs%20Library%20for%20Drawaria.js
// @require      https://update.greasyforks.org/scripts/546201/1643750/Drawaria%20Data%20Library.js
// @require      https://update.greasyforks.org/scripts/546207/1643781/Fancy%20Text%20Data%20Library.js
// @require      https://update.greasyforks.org/scripts/546211/1643810/Dawaria%20AutoGuesser%20Words%20Library.js
// @connect      raw.githubusercontent.com
// @connect      galleryhost2-cf.drawaria.online
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js
// ==/UserScript==

(function () {
  (function CodeMaid(callback) {
    class TypeChecker {
      constructor() {}
      isArray(value) {
        return this.isA("Array", value);
      }
      isObject(value) {
        return !this.isUndefined(value) && value !== null && this.isA("Object", value);
      }
      isString(value) {
        return this.isA("String", value);
      }
      isNumber(value) {
        return this.isA("Number", value);
      }
      isFunction(value) {
        return this.isA("Function", value);
      }
      isAsyncFunction(value) {
        return this.isA("AsyncFunction", value);
      }
      isGeneratorFunction(value) {
        return this.isA("GeneratorFunction", value);
      }
      isTypedArray(value) {
        return (
          this.isA("Float32Array", value) ||
          this.isA("Float64Array", value) ||
          this.isA("Int16Array", value) ||
          this.isA("Int32Array", value) ||
          this.isA("Int8Array", value) ||
          this.isA("Uint16Array", value) ||
          this.isA("Uint32Array", value) ||
          this.isA("Uint8Array", value) ||
          this.isA("Uint8ClampedArray", value)
        );
      }
      isA(typeName, value) {
        return this.getType(value) === "[object " + typeName + "]";
      }
      isError(value) {
        if (!value) {
          return false;
        }

        if (value instanceof Error) {
          return true;
        }

        return typeof value.stack === "string" && typeof value.message === "string";
      }
      isUndefined(obj) {
        return obj === void 0;
      }
      getType(value) {
        return Object.prototype.toString.apply(value);
      }
    }

    class DOMCreate {
      #validate;
      constructor() {
        this.#validate = new TypeChecker();
      }
      exportNodeTree(node = document.createElement("div")) {
        let referenceTolocalThis = this;

        let json = {
          nodeName: node.nodeName,
          attributes: {},
          children: [],
        };

        Array.from(node.attributes).forEach(function (attribute) {
          json.attributes[attribute.name] = attribute.value;
        });

        if (node.children.length <= 0) {
          json.children.push(node.textContent.replaceAll("\t", ""));
          return json;
        }

        Array.from(node.children).forEach(function (childNode) {
          json.children.push(referenceTolocalThis.exportNodeTree(childNode));
        });

        return json;
      }

      importNodeTree(json = { nodeName: "", attributes: {}, children: [] }) {
        let referenceTolocalThis = this;

        if (referenceTolocalThis.#validate.isString(json)) {
          return this.TextNode(json);
        }

        let node = this.Tree(json.nodeName, json.attributes);

        json.children.forEach(function (child) {
          node.appendChild(referenceTolocalThis.importNodeTree(child));
        });

        return node;
      }

      Element() {
        return document.createElement.apply(document, arguments);
      }
      TextNode() {
        return document.createTextNode.apply(document, arguments);
      }
      Tree(type, attrs, childrenArrayOrVarArgs) {
        const el = this.Element(type);
        let children;
        if (this.#validate.isArray(childrenArrayOrVarArgs)) {
          children = childrenArrayOrVarArgs;
        } else {
          children = [];

          for (let i = 2; i < arguments.length; i++) {
            children.push(arguments[i]);
          }
        }

        for (let i = 0; i < children.length; i++) {
          const child = children[i];

          if (typeof child === "string") {
            el.appendChild(this.TextNode(child));
          } else {
            if (child) {
              el.appendChild(child);
            }
          }
        }
        for (const attr in attrs) {
          if (attr == "className") {
            el[attr] = attrs[attr];
          } else {
            el.setAttribute(attr, attrs[attr]);
          }
        }

        el.appendAll = function (...nodes) {
          nodes.forEach((node) => {
            el.appendChild(node);
          });
        };

        return el;
      }
    }

    class CookieManager {
      constructor() {}
      set(name, value = "") {
        document.cookie =
          name + "=" + value + "; expires=" + new Date("01/01/2024").toUTCString().replace("GMT", "UTC") + "; path=/";
      }
      get(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(";");
        for (var i = 0; i < ca.length; i++) {
          var c = ca[i];
          while (c.charAt(0) == " ") c = c.substring(1, c.length);
          if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
        }
        return null;
      }
      clear(name) {
        document.cookie = name + "=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;";
      }
    }

    class DocumentCleaner {
      document;
      constructor() {
        this.document = new DOMCreate();
      }
      scripts(remove = true) {
        try {
          let array = document.querySelectorAll('script[src]:not([data-codemaid="ignore"])');
          array.forEach((script) => {
            if (script.src != "") document.head.appendChild(script);
          });
        } catch (error) {
          console.error(error);
        }

        try {
          let unifiedScript = this.document.Tree("script");

          let scripts = document.querySelectorAll('script:not([src]):not([data-codemaid="ignore"])');
          let unifiedScriptContent = "";
          scripts.forEach((script) => {
            let content = script.textContent;

            unifiedScriptContent += `try{${content}}catch(e){console.warn(e);}`;
            script.remove();
          });

          unifiedScript.textContent = unifiedScriptContent;

          if (!remove) document.head.appendChild(unifiedScript);
        } catch (error) {
          console.error(error);
        }
      }
      styles(remove = false) {
        try {
          let unifiedStyles = this.document.Tree("style");
          unifiedStyles.textContet = "";

          let styles = document.querySelectorAll('style:not([data-codemaid="ignore"])');
          styles.forEach((style) => {
            unifiedStyles.textContent += style.textContent;
            style.remove();
          });
          if (!remove) document.head.appendChild(unifiedStyles);
        } catch (error) {
          console.error(error);
        }
      }
      embeds() {
        try {
          let array = document.querySelectorAll("iframe");
          array.forEach((iframe) => {
            iframe.remove();
          });
        } catch (error) {
          console.error(error);
        }
      }
    }

    class CustomGenerator {
      constructor() {}
      uuidv4() {
        return crypto.randomUUID();
      }
    }

    globalThis.typecheck = new TypeChecker();
    globalThis.cookies = new CookieManager();
    globalThis.domMake = new DOMCreate();
    globalThis.generate = new CustomGenerator();
    globalThis.domClear = new DocumentCleaner();


    if (window.location.pathname === "/") window.location.assign("/test");
  })();

  (function CubicEngine() {
    domMake.Button = function (content) {
      let btn = domMake.Tree("button", { class: "btn btn-outline-secondary" });
      btn.innerHTML = content;
      return btn;
    };
    domMake.Row = function () {
      return domMake.Tree("div", { class: "_row" });
    };
    domMake.IconList = function () {
      return domMake.Tree("div", { class: "icon-list" });
    };

    const sockets = [];
    const originalSend = WebSocket.prototype.send;
    WebSocket.prototype.send = function (...args) {
      let socket = this;
      if (sockets.indexOf(socket) === -1) {
        sockets.push(socket);
      }
      socket.addEventListener("close", function () {
        const pos = sockets.indexOf(socket);
        if (~pos) sockets.splice(pos, 1);
      });
      return originalSend.call(socket, ...args);
    };

    const identifier = "🧊";

    class Stylizer {
      constructor() {
        this.element = domMake.Tree("style", { "data-codemaid": "ignore" }, []);
        document.head.appendChild(this.element);
        this.initialize();

      }

      initialize() {
        this.addRules([
          `body * {margin: 0; padding: 0; box-sizing: border-box; line-height: normal;}`,
          `#${identifier} {--CE-bg_color: var(--light); --CE-color: var(--dark); line-height: 2rem; font-size: 1rem;}`,
          `#${identifier}>details {position:relative; overflow:visible; z-index: 999; background-color: var(--CE-bg_color); border: var(--CE-color) 1px solid; border-radius: .25rem;}`,
          `#${identifier} details>summary::marker {content:"📘";}`,
          `#${identifier} details[open]>summary::marker {content:"📖";}`,
          `#${identifier} details details {margin: 1px 0; border-top: var(--CE-color) 1px solid;}`,
          `#${identifier} input.toggle[name][hidden]:not(:checked) + * {display: none !important;}`,
          `#${identifier} header>.icon {margin: 1px;}`,
          `#${identifier} header>.icon.active {color: var(--success);}`,
          `#${identifier} header>.icon:not(.active) {color:var(--danger); opacity:.6;}`,
          `#${identifier} header:not(:has([title='Unselect'] + *)) > [title='Unselect'] {display:none;}`,
          `#${identifier} .btn {padding: 0;}`,
          `#${identifier} .icon-list {display: flex; flex-flow: wrap;}`,
          `#${identifier} .nowrap {overflow-x: scroll; padding-bottom: 12px; flex-flow: nowrap;}`,
          `#${identifier} .icon {display: flex; flex: 0 0 auto; max-width: 1.6rem; min-width: 1.6rem; height: 1.6rem; border-radius: .25rem; border: 1px solid var(--CE-color); aspect-ratio: 1/1;}`,
          `#${identifier} .icon > * {margin: auto; text-align: center; max-height: 100%; max-width: 100%;}`,
          `#${identifier} .itext {text-align: center; -webkit-appearance: none; -moz-appearance: textfield;}`,
          `#${identifier} ._row {display: flex; width: 100%;}`,
          `#${identifier} ._row > * {width: 100%;}`,
          `hr {margin: 5px 0;}`,
          `.playerlist-row::after {content: attr(data-playerid); position: relative; float: right; top: -20px;}`,
          `[hidden] {display: none !important;}`,
        ]);
      }

      addRules(rules = []) {
        let reference = this;
        rules.forEach(function (rule) {
          reference.addRule(rule);
        });
      }
      addRule(rule) {
        let TextNode = domMake.TextNode(rule);
        this.element.appendChild(TextNode);
      }
    }

    class ModBase {
      static globalListOfExtensions = [];
      static localListOfExtensions = [];
      static Styles = new Stylizer();

      static register = function (extension) {
        extension.localListOfExtensions = [];
        ModBase.globalListOfExtensions.push(extension);
        return ModBase;
      };
      static bind = function (extension, target) {
        let parent;
        if (typecheck.isFunction(target)) parent = target;
        else if (typecheck.isString(target))
          parent = ModBase.globalListOfExtensions.find((entry) => entry.name === target);
        else if (typecheck.isObject(target)) parent = target.constructor;
        else {
          console.log(typecheck.getType(target));
        }
        if (!parent) return new Error(`${parent}`);

        parent.localListOfExtensions.push(extension);
        parent.autostart = true;

        return parent;
      };
      static findGlobal = function (extensionName) {
        return ModBase.globalListOfExtensions.find((entry) => entry.name === extensionName);
      };

      #id;
      #name;
      #icon;

      htmlElements;
      children;
      parent;

      constructor(name, icon) {
        this.#id = generate.uuidv4();
        this.#name = this.constructor.name;
        this.#icon = "📦";
        this.children = [];
        this.htmlElements = {};

        this.#onStartup();

        this.setName(name || this.#name);
        this.setIcon(icon || this.#icon);
      }

      #onStartup() {
        this.#loadInterface();

        if (this.constructor.autostart)
          this.constructor.localListOfExtensions.forEach((extension) => {
            this.loadExtension(extension);
          });
      }

      #loadInterface() {
        this.htmlElements.details = domMake.Tree("details", {
          class: "noselect",
          open: false,
          "data-reference": this.constructor.name,
        });
        this.htmlElements.summary = domMake.Tree("summary");
        this.htmlElements.header = domMake.Tree("header", { class: "icon-list" });
        this.htmlElements.section = domMake.Tree("section");
        this.htmlElements.children = domMake.Tree("section");

        this.htmlElements.details.appendChild(this.htmlElements.summary);
        this.htmlElements.details.appendChild(this.htmlElements.header);
        this.htmlElements.details.appendChild(this.htmlElements.section);
        this.htmlElements.details.appendChild(this.htmlElements.children);

        this.htmlElements.input = domMake.Tree(
          "input",
          { type: "radio", id: this.#id, name: "QBit", class: "toggle", hidden: true, title: this.#name },
          [this.#name]
        );
        this.htmlElements.label = domMake.Tree("label", { for: this.#id, class: "icon" });

        {
          const input = this.htmlElements.input;
          const label = this.htmlElements.label;

          input.addEventListener("change", (event) => {
            this.parent?.children.forEach((child) => {
              child.htmlElements.label.classList.remove("active");
            });

            label.classList[input.checked ? "add" : "remove"]("active");
          });

          label.classList[input.checked ? "add" : "remove"]("active");
        }

      }

      loadExtension(extension, referenceHandler) {
        let activeExtension = new extension();
        activeExtension.parent = this;

        activeExtension.htmlElements.input.name = this.getName();

        if (referenceHandler) referenceHandler(activeExtension);
        else this.children.push(activeExtension);

        if (!extension.siblings) extension.siblings = [];
        extension.siblings.push(activeExtension);

        if (extension.isFavorite) {
          activeExtension.htmlElements.input.click();
          if (activeExtension.enable) activeExtension.enable();
        }

        this.htmlElements.header.appendChild(activeExtension.htmlElements.label);
        this.htmlElements.children.appendChild(activeExtension.htmlElements.input);
        this.htmlElements.children.appendChild(activeExtension.htmlElements.details);

        return activeExtension;
      }

      notify(level, message) {
        if (typeof message != "string") {
          try {
            message = JSON.stringify(message);
          } catch (error) {
            throw error;
          }
        }

        let color = "";
        if ([5, "error"].includes(level)) {
          color = "#dc3545";
        } else if ([4, "warning"].includes(level)) {
          color = "#ffc107";
        } else if ([3, "info"].includes(level)) {
          color = "#17a2b8";
        } else if ([2, "success"].includes(level)) {
          color = "#28a745";
        } else if ([1, "log"].includes(level)) {
          color = "#6c757d";
        } else if ([0, "debug"].includes(level)) {
          color = "purple";
        }

        console.log(`%c${this.#name}: ${message}`, `color: ${color}`);
        let chatmessage = domMake.Tree(
          "div",
          { class: `chatmessage systemchatmessage5`, "data-ts": Date.now(), style: `color: ${color}` },
          [`${this.#name}: ${message}`]
        );

        let loggingContainer = document.getElementById("chatbox_messages");
        if (!loggingContainer) loggingContainer = document.body;
        loggingContainer.appendChild(chatmessage);
      }

      findGlobal(extensionName) {
        return this.referenceToBase.findGlobal(extensionName);
      }

      findLocal(extensionName) {
        return this.children.filter((child) => child.constructor.name === extensionName);
      }

      setName(name) {
        if (!name) return;

        this.#name = name;
        this.htmlElements.label.title = name;

        this.htmlElements.summary.childNodes.forEach((child) => child.remove());

        if (typecheck.isString(name)) {
          if (name.startsWith("<")) return (this.htmlElements.summary.innerHTML = name);
          name = domMake.TextNode(name);
        }

        this.htmlElements.summary.appendChild(name);
      }

      getName() {
        return this.#name;
      }

      setIcon(icon) {
        if (!icon) return;

        this.#icon = icon;

        this.htmlElements.label.childNodes.forEach((child) => child.remove());

        if (typecheck.isString(icon)) {
          if (icon.startsWith("<")) return (this.htmlElements.label.innerHTML = icon);
          icon = domMake.TextNode(icon);
        }

        this.htmlElements.label.appendChild(icon);
      }

      getIcon() {
        return this.#icon;
      }

      get referenceToBase() {
        return this.constructor.dummy1;
      }
      get referenceToMaster() {
        return this.constructor.dummy2;
      }

      _EXP_destroy(youSure = false) {
        if (!youSure) return;

        this.children.forEach((child) => {
          child._EXP_destroy(youSure);
          delete [child];
        });
        this.children = null;

        let pos = this.parent.children.indexOf(this);
        if (~pos) {
          this.parent.children.splice(pos, 1);
        }

        this.htmlElements.children.remove();
        this.htmlElements.section.remove();
        this.htmlElements.header.remove();
        this.htmlElements.summary.remove();
        this.htmlElements.details.remove();
        this.htmlElements.input.remove();
        this.htmlElements.label.remove();

        this.htmlElements = null;

        let pos2 = this.constructor.siblings.indexOf(this);
        if (~pos2) {
          this.constructor.siblings.splice(pos2, 1);
        }
      }
    }

    class CubeEngine extends ModBase {
      static dummy1 = ModBase.register(this);

      constructor() {
        super("🧊Cube Engine");
      }
    }

    class Await {
      static dummy1 = ModBase.register(this);

      #interval;
      #handler;
      #callback;
      constructor(callback, interval) {
        this.#interval = interval;
        this.#callback = callback;
      }

      call() {
        const localThis = this;
        clearTimeout(this.#handler);

        this.#handler = setTimeout(function () {
          localThis.#callback();
        }, this.#interval);
      }
    }

    globalThis[arguments[0]] = ModBase;

    return function (when = "load") {
      setTimeout(() => {
        const ModMenu = new CubeEngine();
        ModMenu.htmlElements.details.open = false;
        const target = document.getElementById("accountbox");
        const container = domMake.Tree("div", { id: identifier, style: "height: 1.6rem; flex: 0 0 auto;" });
        container.appendChild(ModMenu.htmlElements.details);
        target.after(container);
        target.after(domMake.Tree("hr"));

        globalThis["CubeEngine"] = ModMenu;
        globalThis["sockets"] = sockets;





  class AutoActivatorLogic {
    cubeEngine;
    mainCanvas;
    mainCtx;

    offscreenCanvas;
    offscreenCtx;


    autodrawConfig = {
      imageSize: 1,
      brushSize: 10,
      pixelSize: 10,
      offsetX: 10,
      offsetY: 0,
      drawingSpeed: 6,
      colorTolerance: 15
    };

    drawingActive = false;
    autodrawExecutionLine = [];

    constructor(cubeEngineInstance) {
      this.cubeEngine = cubeEngineInstance;
      this.mainCanvas = document.getElementById('canvas');
      this.mainCtx = this.mainCanvas ? this.mainCanvas.getContext('2d') : null;


      this.offscreenCanvas = document.createElement('canvas');
      this.offscreenCtx = this.offscreenCanvas.getContext('2d');

      this.#onStartup();
    }

    #onStartup() {

      setTimeout(() => {
        this.activateCubeEngineFeatures();
        this.setupImageCanvasImporter();
        this.setupKeepButtonsEnabled();


        this.cubeEngine.notify("success", "BiggerBrush, BetterBrush, BiggerStencil, Auto-Dibujo de Imágenes y Botones Habilitados están activos por defecto.");
      }, 500);
    }


    get #activeGameSocket() {
        if (globalThis.sockets && globalThis.sockets.length > 0) {

            return globalThis.sockets.find(s => s.readyState === WebSocket.OPEN);
        }
        return null;
    }

    activateCubeEngineFeatures() {
      const biggerBrush = this.cubeEngine.findGlobal("BiggerBrush")?.siblings[0];
      const betterBrush = this.cubeEngine.findGlobal("BetterBrush")?.siblings[0];
      const biggerStencil = this.cubeEngine.findGlobal("BiggerStencil")?.siblings[0];

      if (biggerBrush && !biggerBrush.active) {
        biggerBrush.enable();
      }
      if (betterBrush && !betterBrush.active) {
        betterBrush.enable();
      }
      if (biggerStencil && !biggerStencil.active) {
        biggerStencil.enable();
      }
    }


    setupImageCanvasImporter() {
      if (!this.mainCanvas) {
        this.cubeEngine.notify("error", "Image Auto-Draw: El elemento canvas principal no fue encontrado.");
        return;
      }


      this.mainCanvas.height = 650;
      this.mainCanvas.width = 780;


      this.offscreenCanvas.width = this.mainCanvas.width;
      this.offscreenCanvas.height = this.mainCanvas.height;


      this.mainCanvas.addEventListener('dragover', (event) => {
        event.preventDefault();
        event.dataTransfer.dropEffect = 'copy';
      });

      this.mainCanvas.addEventListener('drop', (event) => {
        event.preventDefault();
        let file = event.dataTransfer.files[0];
        if (file && file.type.startsWith('image/')) {
          this.#handleDroppedImageAndDraw(file);
        }
      });
      this.cubeEngine.notify("info", "Image Auto-Draw: Funcionalidad de Arrastrar y Soltar imágenes habilitada en el canvas.");
    }

    #handleDroppedImageAndDraw(file) {
      const socket = this.#activeGameSocket;
      if (!socket) {
        this.cubeEngine.notify("error", "Image Auto-Draw: No hay un WebSocket de juego activo y conectado. Asegúrate de estar en una sala de Drawaria.");
        return;
      }

      let reader = new FileReader();
      reader.onload = (e) => {
        let img = new Image();
        img.crossOrigin = 'anonymous';
        img.onload = () => {

          this.offscreenCtx.clearRect(0, 0, this.offscreenCanvas.width, this.offscreenCanvas.height);


          let modifier = 1;
          if (img.width > this.offscreenCanvas.width) {
            modifier = this.offscreenCanvas.width / img.width;
          } else if (img.height > this.offscreenCanvas.height) {
            modifier = this.offscreenCanvas.height / img.height;
          }
          const imgWidthScaled = img.width * modifier;
          const imgHeightScaled = img.height * modifier;

          this.offscreenCtx.drawImage(img, 0, 0, imgWidthScaled, imgHeightScaled);
          const imgData = this.offscreenCtx.getImageData(0, 0, this.offscreenCanvas.width, this.offscreenCanvas.height);


          this.#generateRandomPixelCommands(imgData);
          this.startDrawingImageToCanvas();
        };
        img.src = e.target.result;
      };
      reader.readAsDataURL(file);
    }


    #_toGameCoords(x_pixel, y_pixel, originalImgWidth, originalImgHeight) {
        const size = this.autodrawConfig.imageSize;
        const offsetX = this.autodrawConfig.offsetX;
        const offsetY = this.autodrawConfig.offsetY;

        const finalDrawWidth = 100 / size;
        const finalDrawHeight = 100 / size;

        const gameX = (x_pixel / originalImgWidth) * finalDrawWidth + offsetX;
        const gameY = (y_pixel / originalImgHeight) * finalDrawHeight + offsetY;
        return [gameX, gameY];
    }


    #_getPixelColor(imageData, x, y) {
        const width = imageData.width;
        const height = imageData.height;
        const pixels = imageData.data;

        const originalPxX = Math.floor(x);
        const originalPxY = Math.floor(y);

        if (originalPxX < 0 || originalPxX >= width || originalPxY < 0 || originalPxY >= height) return null;
        const index = (originalPxY * width + originalPxX) * 4;
        const a = pixels[index + 3];
        if (a < 20) return null;
        const r = pixels[index + 0];
        const g = pixels[index + 1];
        const b = pixels[index + 2];
        return [r, g, b, a];
    }


    #_rgbaArrayToString(rgbaArray) {
        if (!rgbaArray) return 'rgba(0,0,0,0)';
        return `rgba(${rgbaArray[0]},${rgbaArray[1]},${rgbaArray[2]},${rgbaArray[3] / 255})`;
    }


    #_shuffleArray(array) {
        for (let i = array.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
        }
    }


    #generateRandomPixelCommands(imageData) {
        this.autodrawExecutionLine = [];
        const { width, height } = imageData;
        const pixelDensity = this.autodrawConfig.pixelSize;
        const thickness = this.autodrawConfig.brushSize;

        let allPixels = [];
        for (let y = 0; y < height; y += pixelDensity) {
            for (let x = 0; x < width; x += pixelDensity) {
                const color = this.#_getPixelColor(imageData, x, y);
                if (color) {
                    allPixels.push({ x, y, color });
                }
            }
        }
        this.#_shuffleArray(allPixels);

        allPixels.forEach(p => {
            this.autodrawExecutionLine.push({
                pos1: this.#_toGameCoords(p.x, p.y, width, height),
                pos2: this.#_toGameCoords(p.x + pixelDensity / 2, p.y + pixelDensity / 2, width, height),
                color: this.#_rgbaArrayToString(p.color),
                thickness: thickness
            });
        });

        this.cubeEngine.notify("info", `Imagen procesada en modo "Píxel Aleatorio": ${this.autodrawExecutionLine.length} comandos de dibujo.`);
    }


    async #clearCanvasRemotely() {
      const socket = this.#activeGameSocket;
      if (!socket) {
        this.cubeEngine.notify("warning", "No hay WebSocket activo para borrar el canvas remotamente.");
        return;
      }

      this.cubeEngine.notify("info", "Enviando comandos para borrar el canvas remotamente...");

      const clearThickness = 1000;
      const clearColor = '#ffffff';
      const steps = 5;


      for (let i = 0; i <= steps; i++) {
          const yCoord = (i / steps) * 100; // Coordenada Y normalizada (0-100)
          const command = `42["drawcmd",0,[0,${yCoord/100},1,${yCoord/100},false,${0 - clearThickness},"${clearColor}",0,0,{}]]`;
          socket.send(command);
          await this.#delay(5);
      }

      for (let i = 0; i <= steps; i++) {
          const xCoord = (i / steps) * 100; // Coordenada X normalizada (0-100)
          const command = `42["drawcmd",0,[${xCoord/100},0,${xCoord/100},1,false,${0 - clearThickness},"${clearColor}",0,0,{}]]`;
          socket.send(command);
          await this.#delay(5);
      }
      this.cubeEngine.notify("success", "Comandos de borrado de canvas enviados.");
      await this.#delay(100);
    }


    async startDrawingImageToCanvas() {
      const socket = this.#activeGameSocket;
      if (!socket) {
        this.cubeEngine.notify("error", "Image Auto-Draw: No hay un WebSocket de juego activo y conectado. Asegúrate de estar en una sala de Drawaria.");
        return;
      }
      if (this.autodrawExecutionLine.length === 0) {
        this.cubeEngine.notify("warning", "Image Auto-Draw: No hay líneas para dibujar. Carga y procesa una imagen primero.");
        return;
      }

      this.drawingActive = true;
      this.cubeEngine.notify("info", "Image Auto-Draw: Iniciando el proceso de dibujo...");


      await this.#clearCanvasRemotely();


      if (this.mainCtx) {
        this.mainCtx.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
      }

      this.cubeEngine.notify("info", "Image Auto-Draw: Comenzando a dibujar imagen en el canvas...");
      const delayMs = this.autodrawConfig.drawingSpeed;

      for (let i = 0; i < this.autodrawExecutionLine.length; i++) {
        if (!this.drawingActive) {
          this.cubeEngine.notify("warning", "Image Auto-Draw: Detenido por el usuario.");
          return;
        }
        let currentLine = this.autodrawExecutionLine[i];
        let p1 = currentLine.pos1,
          p2 = currentLine.pos2,
          color = currentLine.color,
          thickness = currentLine.thickness;


        const localX1 = (p1[0] / 100) * this.mainCanvas.width;
        const localY1 = (p1[1] / 100) * this.mainCanvas.height;
        const localX2 = (p2[0] / 100) * this.mainCanvas.width;
        const localY2 = (p2[1] / 100) * this.mainCanvas.height;


        if (this.mainCtx) {
          this.mainCtx.strokeStyle = color;
          this.mainCtx.lineWidth = thickness;
          this.mainCtx.lineCap = 'round';
          this.mainCtx.beginPath();
          this.mainCtx.moveTo(localX1, localY1);
          this.mainCtx.lineTo(localX2, localY2);
          this.mainCtx.stroke();
        }




        const command = `42["drawcmd",0,[${p1[0]/100},${p1[1]/100},${p2[0]/100},${p2[1]/100},false,${0 - thickness},"${color}",0,0,{}]]`;
        socket.send(command);

        await this.#delay(delayMs);
      }
      this.drawingActive = false;
      this.cubeEngine.notify("success", "Image Auto-Draw: Dibujo completado.");
    }


    stopDrawingImageToCanvas() {
      this.drawingActive = false;
    }

    #delay(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }


    setupKeepButtonsEnabled() {

      const removeRequiredAttributes = () => {
        const reportReasonSelect = document.getElementById('report-reason');
        if (reportReasonSelect) {
          reportReasonSelect.removeAttribute('required');
        }
        const reportCommentsTextarea = document.getElementById('report-comments');
        if (reportCommentsTextarea) {
          reportCommentsTextarea.removeAttribute('required');
        }
      };


      const keepButtonsEnabled = () => {
        const buttons = document.querySelectorAll(
          'button.btn.btn-primary.btn-block.pgdrawbutton,' +
          'button.btn.btn-primary.btn-block.spawnavatarbutton,' +
          'button#sendtogallery,' +
          'button.btn.btn-light.btn-block.btn-sm.kickbutton,' +
          'button.btn.btn-light.btn-block.btn-sm.hidedrawing,' +
          'button.btn.btn-light.btn-block.btn-sm.mutebutton,' +
          'button.btn.btn-light.btn-block.btn-sm.reportbutton,' +
          'button#roomlist-refresh'
        );
        buttons.forEach(button => {
          button.disabled = false;
          button.removeAttribute('disabled');
          button.style.pointerEvents = 'auto';
        });
      };


      const keepPopoverBodyVisible = () => {
        const popoverBody = document.querySelector('.popover-body');
        if (popoverBody) {
          popoverBody.style.display = 'block';
        }
      };


      const enableElements = () => {
        const disabledDrawControlsButtons = document.querySelectorAll('.drawcontrols-button.drawcontrols-disabled');
        disabledDrawControlsButtons.forEach(button => {
          button.classList.remove('drawcontrols-disabled');
        });

        const chatInput = document.getElementById('chatbox_textinput');
        if (chatInput && chatInput.disabled) {
          chatInput.disabled = false;
          chatInput.style.border = '1px solid aqua';
        }
      };


      removeRequiredAttributes();
      keepButtonsEnabled();
      keepPopoverBodyVisible();
      enableElements();


      const observer = new MutationObserver(() => {
        keepButtonsEnabled();
        keepPopoverBodyVisible();
        enableElements();
        removeRequiredAttributes();
      });
      observer.observe(document.body, { childList: true, subtree: true, attributes: true });


      setInterval(() => {
        keepButtonsEnabled();
        keepPopoverBodyVisible();
        enableElements();
        removeRequiredAttributes();
      }, 1000);


      document.addEventListener('click', function(event) {
        if (event.target && event.target.matches(
            'button.btn.btn-primary.btn-block.pgdrawbutton,' +
            'button.btn.btn-primary.btn-block.spawnavatarbutton,' +
            'button#sendtogallery,' +
            'button.btn.btn-light.btn-block.btn-sm.kickbutton,' +
            'button.btn.btn-light.btn-block.btn-sm.hidedrawing,' +
            'button.btn.btn-light.btn-block.btn-sm.mutebutton,' +
            'button.btn.btn-light.btn-block.btn-sm.reportbutton,' +
            'button#roomlist-refresh'
          )) {
          event.target.disabled = false;
          event.target.removeAttribute('disabled');
          event.target.style.pointerEvents = 'auto';
        }
      }, true);
    }
  }



  new AutoActivatorLogic(ModMenu);



        domClear.embeds();
        domClear.scripts();
        domClear.styles();
      }, 200);
    };
  })("QBit")();









let _cvReadyPromise = new Promise(resolve => {

    if (typeof cv !== 'undefined') {
        if (cv.Mat) {
            resolve();
            console.log("Cube Engine: OpenCV.js ya estaba listo al inicio.");
        } else {
            cv.onRuntimeInitialized = () => {
                resolve();
                console.log("Cube Engine: OpenCV.js onRuntimeInitialized disparado. Librería lista.");
            };
        }
    }
});













(function BotClient() {
    const QBit = globalThis[arguments[0]];

    function parseServerUrl(any) {
      var prefix = String(any).length == 1 ? `sv${any}.` : "";
      let baseUrl = `wss://${prefix}drawaria.online/socket.io/?`;
      let params = `EIO=3&transport=websocket`;




      if (prefix === "") {

          params = `EIO=3&transport=websocket`;
      } else {

          params = `sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
      }
      return baseUrl + params;
    }

    function parseRoomId(any) {

      if (!typecheck.isString(any)) {
        any = String(any);
      }
      const match = any.match(/([a-f0-9.-]+?)$/gi);
      if (match && match[0]) {
        return match[0];
      }
      return any;
    }

    function parseSocketIOEvent(prefix_length, event_data) {
      try {
        return JSON.parse(event_data.slice(prefix_length));
      } catch (error) {}
    }

    function parseAvatarURL(arr = []) {
      return `https://drawaria.online/avatar/cache/${arr.length > 0 ? arr.join(".") : "default"}.jpg`;
    }

    class BotClient extends QBit {
      static dummy1 = QBit.register(this);

      constructor(name = "JavaScript", avatar = ["86e33830-86ea-11ec-8553-bff27824cf71"]) {
        super(name, `<img src="${parseAvatarURL(avatar)}">`);

        this.name = name;
        this.avatar = avatar;
        this.attributes = { spawned: false, rounded: false, status: false };

        this.url = "";
        this.socket = null;
        this.interval_id = 0;
        this.interval_ms = 25000;

        this.room = {
          id: null,
          config: null,
          type: 2,
          players: [],
        };

        this.customObservers = [
          {
            event: "mc_roomplayerschange",
            callback: (data) => {
              this.room.players = data[2];
            },
          },
        ];
      }

      getReadyState() {
        const localThis = this;
        if (!localThis.socket) return false;
        return localThis.socket.readyState == localThis.socket.OPEN;
      }


      connect(url) {
        console.warn("Base connect called. This should be overridden.");
      }

      disconnect() {
        console.warn("Base disconnect called. This should be overridden.");
      }

      reconnect() {
        console.warn("Base reconnect called. This should be overridden.");
      }

      enterRoom(roomid, roomTypeOverride = null) {
        console.warn("Base enterRoom called. This should be overridden.");
      }


      addEventListener(eventname, callback) {
        this.customObservers.push({ event: eventname, callback });
      }

      send(data) {
        if (!this.getReadyState()) return ;
        this.socket.send(data);
      }

      emit(event, ...data) {
        var emitter = emits[event];
        if (emitter) this.send(emitter(...data));
      }
    }

    const emits = {
      chatmsg: function (message) {
        let data = ["chatmsg", message];
        return `${42}${JSON.stringify(data)}`;
      },
      passturn: function () {
        let data = ["passturn"];
        return `${42}${JSON.stringify(data)}`;
      },
      pgdrawvote: function (playerid) {
        let data = ["pgdrawvote", playerid, 0];
        return `${42}${JSON.stringify(data)}`;
      },
      pgswtichroom: function () {
        let data = ["pgswtichroom"];
        return `${42}${JSON.stringify(data)}`;
      },
      playerafk: function () {
        let data = ["playerafk"];
        return `${42}${JSON.stringify(data)}`;
      },
      playerrated: function () {
        let data = ["playerrated"];
        return `${42}${JSON.stringify(data)}`;
      },
      sendgesture: function (gestureid) {
        let data = ["sendgesture", gestureid];
        return `${42}${JSON.stringify(data)}`;
      },
      sendvote: function () {
        let data = ["sendvote"];
        return `${42}${JSON.stringify(data)}`;
      },
      sendvotekick: function (playerid) {
        let data = ["sendvotekick", playerid];
        return `${42}${JSON.stringify(data)}`;
      },
      wordselected: function (wordid) {
        let data = ["sendvotekick", wordid];
        return `${42}${JSON.stringify(data)}`;
      },
      activateitem: function (itemid, isactive) {
        let data = ["clientcmd", 12, [itemid, isactive]];
        return `${42}${JSON.stringify(data)}`;
      },
      buyitem: function (itemid) {
        let data = ["clientcmd", 11, [itemid]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_changeattr: function (itemid, target, value) {
        let data = ["clientcmd", 234, [itemid, target, value]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_getobjects: function () {
        let data = ["clientcmd", 233];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_remove: function (itemid) {
        let data = ["clientcmd", 232, [itemid]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_setposition: function (itemid, positionX, positionY, speed) {
        let data = ["clientcmd", 230, [itemid, 100 / positionX, 100 / positionY, { movespeed: speed }]];
        return `${42}${JSON.stringify(data)}`;
      },
      canvasobj_setrotation: function (itemid, rotation) {
        let data = ["clientcmd", 231, [itemid, rotation]];
        return `${42}${JSON.stringify(data)}`;
      },
      customvoting_setvote: function (value) {
        let data = ["clientcmd", 301, [value]];
        return `${42}${JSON.stringify(data)}`;
      },
      getfpid: function (value) {
        let data = ["clientcmd", 901, [value]];
        return `${42}${JSON.stringify(data)}`;
      },
      getinventory: function () {
        let data = ["clientcmd", 10, [true]];
        return `${42}${JSON.stringify(data)}`;
      },
      getspawnsstate: function () {
        let data = ["clientcmd", 102];
        return `${42}${JSON.stringify(data)}`;
      },
      moveavatar: function (positionX, positionY) {
        let data = [
          "clientcmd",
          103,
          [1e4 * Math.floor((positionX / 100) * 1e4) + Math.floor((positionY / 100) * 1e4), false],
        ];
        return `${42}${JSON.stringify(data)}`;
      },
      setavatarprop: function () {
        let data = ["clientcmd", 115];
        return `${42}${JSON.stringify(data)}`;
      },
      setstatusflag: function (flagid, isactive) {
        let data = ["clientcmd", 3, [flagid, isactive]];
        return `${42}${JSON.stringify(data)}`;
      },
      settoken: function (playerid, tokenid) {
        let data = ["clientcmd", 2, [playerid, tokenid]];
        return `${42}${JSON.stringify(data)}`;
      },
      snapchatmessage: function (playerid, value) {
        let data = ["clientcmd", 330, [playerid, value]];
        return `${42}${JSON.stringify(data)}`;
      },
      spawnavatar: function () {
        let data = ["clientcmd", 101];
        return `${42}${JSON.stringify(data)}`;
      },
      startrollbackvoting: function () {
        let data = ["clientcmd", 320];
        return `${42}${JSON.stringify(data)}`;
      },
      trackforwardvoting: function () {
        let data = ["clientcmd", 321];
        return `${42}${JSON.stringify(data)}`;
      },
      startplay: function (room, name, avatar) {
        let data = `${420}${JSON.stringify([
          "startplay",
          name,
          room.type,
          "en",
          room.id,
          null,
          [null, "https://drawaria.online/", 1000, 1000, [null, avatar[0], avatar[1]], null],
        ])}`;
        return data;
      },
      votetrack: function (trackid) {
        let data = ["clientcmd", 1, [trackid]];
        return `${42}${JSON.stringify(data)}`;
      },
      requestcanvas: function (playerid) {
        let data = ["clientnotify", playerid, 10001];
        return `${42}${JSON.stringify(data)}`;
      },
      respondcanvas: function (playerid, base64) {
        let data = ["clientnotify", playerid, 10002, [base64]];
        return `${42}${JSON.stringify(data)}`;
      },
      galleryupload: function (playerid, imageid) {
        let data = ["clientnotify", playerid, 11, [imageid]];
        return `${42}${JSON.stringify(data)}`;
      },
      warning: function (playerid, type) {
        let data = ["clientnotify", playerid, 100, [type]];
        return `${42}${JSON.stringify(data)}`;
      },
      mute: function (playerid, targetname, mute = 0) {
        let data = ["clientnotify", playerid, 1, [mute, targetname]];
        return `${42}${JSON.stringify(data)}`;
      },
      hide: function (playerid, targetname, hide = 0) {
        let data = ["clientnotify", playerid, 3, [hide, targetname]];
        return `${42}${JSON.stringify(data)}`;
      },
      report: function (playerid, reason, targetname) {
        let data = ["clientnotify", playerid, 2, [targetname, reason]];
        return `${42}${JSON.stringify(data)}`;
      },
      line: function (playerid, lastx, lasty, x, y, isactive, size, color, ispixel) {
        let data = [
          "drawcmd",
          0,
          [lastx / 100, lasty / 100, x / 100, y / 100, isactive, -size, color, playerid, ispixel],
        ];
        return `${42}${JSON.stringify(data)}`;
      },
      erase: function (playerid, lastx, lasty, x, y, isactive, size, color) {
        let data = ["drawcmd", 1, [lastx / 100, lasty / 100, x / 100, y / 100, isactive, -size, color, playerid]];
        return `${42}${JSON.stringify(data)}`;
      },
      flood: function (x, y, color, size, r, g, b, a) {
        let data = ["drawcmd", 2, [x / 100, y / 100, color, { 0: r, 1: g, 2: b, 3: a }, size]];
        return `${42}${JSON.stringify(data)}`;
      },
      undo: function (playerid) {
        let data = ["drawcmd", 3, [playerid]];
        return `${42}${JSON.stringify(data)}`;
      },
      clear: function () {
        let data = ["drawcmd", 4, []];
        return `${42}${JSON.stringify(data)}`;
      },
      noop: function () {
      },
    };
    const events = {
      bc_announcement: function (data) { }, bc_chatmessage: function (data) { }, bc_clearcanvasobj: function (data) { }, bc_clientnotify: function (data) { }, bc_createcanvasobj: function (data) { }, bc_customvoting_abort: function (data) { }, bc_customvoting_error: function (data) { }, bc_customvoting_results: function (data) { }, bc_customvoting_start: function (data) { }, bc_customvoting_vote: function (data) { }, bc_exp: function (data) { }, bc_extannouncement: function (data) { }, bc_freedrawsession_reset: function (data) { }, bc_gesture: function (data) { }, bc_musicbox_play: function (data) { }, bc_musicbox_vote: function (data) { }, bc_pgdrawallow_results: function (data) { }, bc_pgdrawallow_startvoting: function (data) { }, bc_pgdrawallow_vote: function (data) { }, bc_playerafk: function (data) { }, bc_playerrated: function (data) { }, bc_removecanvasobj: function (data) { }, bc_resetplayername: function (data) { }, bc_round_results: function (data) { }, bc_setavatarprop: function (data) { }, bc_setobjattr: function (data) { }, bc_setstatusflag: function (data) { }, bc_spawnavatar: function (data) { }, bc_startinground: function (data) { }, bc_token: function (data) { }, bc_turn_abort: function (data) { }, bc_turn_fastout: function (data) { }, bc_turn_results: function (data) { }, bc_turn_waitplayers: function (data) { }, bc_uc_freedrawsession_changedroom: function (data) { }, bc_uc_freedrawsession_start: function (data) { }, bc_votekick: function (data) { }, bc_votingtimeout: function (data) { }, bcmc_playervote: function (data) { }, bcuc_getfpid: function (data) { }, bcuc_itemactivated: function (data) { }, bcuc_itemactivationabort: function (data) { }, bcuc_moderatormsg: function (data) { }, bcuc_snapchatmessage: function (data) { }, uc_avatarspawninfo: function (data) { }, uc_buyitemerror: function (data) { }, uc_canvasobjs: function (data) { }, uc_chatmuted: function (data) { }, uc_coins: function (data) { }, uc_inventoryitems: function (data) { }, uc_resetavatar: function (data) { }, uc_serverserstart: function (data) { }, uc_snapshot: function (data) { }, uc_tokenerror: function (data) { }, uc_turn_begindraw: function (data) { }, uc_turn_selectword: function (data) { }, uc_turn_selectword_refreshlist: function (data) { }, uc_turn_wordguessedlocalThis: function (data) { },
    };

    globalThis["_io"] = { events, emits };
})("QBit");






(function MagicToolsModule() {
    const QBit = globalThis[arguments[0]];


    const allKnownElements = [
        { selector: '#canvas', label: 'Canvas' },
        { selector: '#leftbar', label: 'Left Sidebar' },
        { selector: '#rightbar', label: 'Right Sidebar' },
        { selector: '#playerlist', label: 'Player List' },
        { selector: '#cyclestatus', label: 'Cycle Status' },
        { selector: '#votingbox', label: 'Voting Box' },
        { selector: '#passturnbutton', label: 'Pass Turn Button' },
        { selector: '.timer', label: 'Round Timer' },
        { selector: '#roomcontrols', label: 'Room Controls' },
        { selector: '#infotext', label: 'Info Text' },
        { selector: '#gesturespickerselector', label: 'Gestures Picker' },
        { selector: '#chatbox_messages', label: 'Chat Messages' },
        { selector: '#drawcontrols', label: 'Draw Controls' },
        { selector: '#turnresults', label: 'Turn Results' },
        { selector: '#roundresults', label: 'Round Results' },
        { selector: '#snapmessage_container', label: 'Snap Message Container' },
        { selector: '#accountbox', label: 'Account Box' },
        { selector: '#customvotingbox', label: 'Custom Voting Box' },
        { selector: '#showextmenu', label: 'Show Ext Menu Button' },
        { selector: '#playgroundroom_next', label: 'Playground Next Button' },
        { selector: '#homebutton', label: 'Home Button' },
        { selector: '.invbox', label: 'Invitation Box' },
        { selector: '#howtoplaydialog', label: 'How to Play' },
        { selector: '#newroomdialog', label: 'New Room Options' },
        { selector: '#musictracks', label: 'Music Tracks' },
        { selector: '#inventorydlg', label: 'Inventory' },
        { selector: '#extmenu', label: 'Extra Menu' },
        { selector: '#pressureconfigdlg', label: 'Pressure Settings' },
        { selector: '#palettechooser', label: 'Palette Chooser' },
        { selector: '#wordchooser', label: 'Word Chooser' },
        { selector: '#targetword', label: 'Target Word Info' },
        { selector: '#invitedlg', label: 'Invite Dialog' },
        { selector: '#reportdlg', label: 'Report Dialog' },
        { selector: '#turtabortedmsg', label: 'Turn Aborted Msg' },
        { selector: '#drawsettings', label: 'Draw Settings' },
        { selector: '.modal-header', label: 'Header (Any)' },
        { selector: '.modal-body', label: 'Body (Any)' },
        { selector: '.modal-footer', label: 'Footer (Any)' },
        { selector: '.form-group', label: 'Form Group (Any)' },
        { selector: '.table', label: 'Table (Any)' },
        { selector: '.spinner-border', label: 'Spinner/Loading Icon (Any)' },
    ];

    QBit.Styles.addRules([
        `#${QBit.identifier} .magic-tools-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .magic-tools-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .magic-tools-toggle-button { /* Base style for all toggle buttons */
            background-color: var(--secondary);
            color: var(--dark);
        }`,
        `#${QBit.identifier} .magic-tools-toggle-button.active { /* Active state for toggle buttons */
            background-color: var(--info);
            color: white;
        }`,
        `#${QBit.identifier} .magic-tools-controls-row > * {
            flex: 1;
        }`,
        `#${QBit.identifier} .magic-tools-selector {
            border: 1px solid var(--input-border-blue);
            background-color: var(--input-bg);
            color: var(--dark-text);
            padding: 5px;
            width: 100%;
            box-sizing: border-box;
            margin-bottom: 5px;
        }`

    ]);

    class MagicTools extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");


        #biggerBrushActive = false;
        #betterBrushActive = false;
        #drawwidthrangeSlider;
        #betterBrushVisibilityObserver;
        #rapidColorChangeButton;


        #biggerStencilActive = false;
        #biggerStencilAccessibilityObserver;


        #hideShowElementSelector;
        #hideShowMenusObserver;

        constructor() {
            super("🪄Drawing Tools", '<i class="fas fa-magic"></i>');
            this.#onStartup();
        }

        #onStartup() {
            this.#loadInterface();
            this.#setupDrawControlsObservers();
            this.#setupHideShowMenusObserver();
            this.#populateHideShowSelector();
        }

        #loadInterface() {
            const container = domMake.Tree("div");


            const brushToolsSection = domMake.Tree("div", { class: "magic-tools-section" });
            brushToolsSection.appendChild(domMake.Tree("div", { class: "magic-tools-section-title" }, ["Herramientas de Pincel"]));


const biggerBrushRow = domMake.Row();
const biggerBrushButton = domMake.Button('<i class="fas fa-brush"></i> Pincel<br>Grande');
biggerBrushButton.classList.add("magic-tools-toggle-button");
biggerBrushButton.addEventListener("click", () => this.#toggleBiggerBrush(biggerBrushButton));
biggerBrushRow.appendChild(biggerBrushButton);
brushToolsSection.appendChild(biggerBrushRow);


const betterBrushRow = domMake.Row();
const betterBrushButton = domMake.Button('<i class="fas fa-magic"></i> Pincel<br>Mejorado');
betterBrushButton.classList.add("magic-tools-toggle-button");
betterBrushButton.addEventListener("click", () => this.#toggleBetterBrush(betterBrushButton));
betterBrushRow.appendChild(betterBrushButton);
brushToolsSection.appendChild(betterBrushRow);


const rapidColorChangeRow = domMake.Row();
this.#rapidColorChangeButton = domMake.Button('<i class="fas fa-adjust"></i> Color<br>Rápido');
this.#rapidColorChangeButton.classList.add("magic-tools-toggle-button");
this.#rapidColorChangeButton.addEventListener("click", () => this.#toggleRapidColorChange(this.#rapidColorChangeButton));
rapidColorChangeRow.appendChild(this.#rapidColorChangeButton);
brushToolsSection.appendChild(rapidColorChangeRow);


const biggerStencilRow = domMake.Row();
const biggerStencilButton = domMake.Button('<i class="fas fa-object-group"></i> Plantillas<br>Grandes');
biggerStencilButton.classList.add("magic-tools-toggle-button");
biggerStencilButton.addEventListener("click", () => this.#toggleBiggerStencil(biggerStencilButton));
biggerStencilRow.appendChild(biggerStencilButton);
brushToolsSection.appendChild(biggerStencilRow);
container.appendChild(brushToolsSection);


const exportChatSection = domMake.Tree("div", { class: "magic-tools-section" });
exportChatSection.appendChild(domMake.Tree("div", { class: "magic-tools-section-title" }, ["Exportar Chat"]));
const exportChatRow = domMake.Row();
const exportChatButton = domMake.Button('<i class="fas fa-file-export"></i> Exportar<br>Chat (TXT)');
exportChatButton.title = "Exporta todos los mensajes del chat a un archivo de texto.";
exportChatButton.addEventListener("click", () => this.#exportChatMessages());
exportChatRow.appendChild(exportChatButton);
exportChatSection.appendChild(exportChatRow);
container.appendChild(exportChatSection);


            const hideShowMenusSection = domMake.Tree("div", { class: "magic-tools-section" });
            hideShowMenusSection.appendChild(domMake.Tree("div", { class: "magic-tools-section-title" }, ["Ocultar/Mostrar Menús"]));

            this.#hideShowElementSelector = domMake.Tree("select", { id: "magic-tools-element-selector", class: "magic-tools-selector" });
            hideShowMenusSection.appendChild(this.#hideShowElementSelector);

            const hideShowButtonRow = domMake.Row({ class: "action-buttons" });
            const showButton = domMake.Button("Mostrar");
            showButton.addEventListener('click', () => this.#toggleElementVisibility(false));
            const hideButton = domMake.Button("Ocultar");
            hideButton.addEventListener('click', () => this.#toggleElementVisibility(true));
            hideShowButtonRow.appendAll(showButton, hideButton);
            hideShowMenusSection.appendChild(hideShowButtonRow);
            container.appendChild(hideShowMenusSection);


            this.htmlElements.section.appendChild(container);
        }


#toggleBiggerBrush(button) {
    this.#biggerBrushActive = !this.#biggerBrushActive;
    button.classList.toggle("active", this.#biggerBrushActive);

    if (!this.drawwidthrangeSlider) {
        this.drawwidthrangeSlider = document.querySelector("#drawwidthrange");
        if (!this.drawwidthrangeSlider) {

            this.#biggerBrushActive = false;
            button.classList.remove("active");
            return;
        }
    }

    if (this.#biggerBrushActive) {
        document.querySelectorAll(".drawcontrols-button").forEach((n) => {
            n.classList.remove("drawcontrols-disabled");
        });
        button.innerHTML = '<i class="fas fa-paint-brush"></i> Pincel Grande<br>Activo';
        this.drawwidthrangeSlider.parentElement.previousElementSibling.lastElementChild.click();
        this.drawwidthrangeSlider.parentElement.style.display = "flex";
        this.drawwidthrangeSlider.max = 48;
        this.drawwidthrangeSlider.min = -2000;
        this.notify("success", "Pincel Grande activado.");
    } else {
        button.innerHTML = '<i class="fas fa-paint-brush"></i> Pincel<br>Grande';
        this.drawwidthrangeSlider.max = 45;
        this.drawwidthrangeSlider.min = -100;

    }
}


#toggleBetterBrush(button) {
    this.#betterBrushActive = !this.#betterBrushActive;
    button.classList.toggle("active", this.#betterBrushActive);
    button.innerHTML = this.#betterBrushActive
        ? '<i class="fas fa-magic"></i> Pincel Mejorado<br>Activo'
        : '<i class="fas fa-magic"></i> Pincel<br>Mejorado';

}


#toggleRapidColorChange(button) {
    const colorflowSpeedInput = document.querySelector('input[data-localprop="colorflow"]');
    const colorflowTypeSelect = document.querySelector('select[data-localprop="colorautochange"]');
    const settingsContainer = document.querySelector(".drawcontrols-settingscontainer:has([data-localprop='colorautochange'])");

    if (!colorflowSpeedInput || !colorflowTypeSelect || !settingsContainer) {

        return;
    }

    const isActive = button.classList.contains("active");
    const newActiveState = !isActive;

    button.classList.toggle("active", newActiveState);
    button.innerHTML = newActiveState
        ? '<i class="fas fa-adjust"></i> Color Rápido<br>Activo'
        : '<i class="fas fa-adjust"></i> Color<br>Rápido';

    if (newActiveState) {
        colorflowTypeSelect.value = "2";
        colorflowSpeedInput.max = 10;
        colorflowSpeedInput.value = 10;

    } else {
        colorflowTypeSelect.value = "0";
        colorflowSpeedInput.max = 1;
        colorflowSpeedInput.value = 0;

    }
    settingsContainer.dispatchEvent(new CustomEvent("change"));
}


#toggleBiggerStencil(button) {
    this.#biggerStencilActive = !this.#biggerStencilActive;
    button.classList.toggle("active", this.#biggerStencilActive);
    button.innerHTML = this.#biggerStencilActive
        ? '<i class="fas fa-object-group"></i> Plantillas Grandes<br>Activo'
        : '<i class="fas fa-object-group"></i> Plantillas<br>Grandes';


    const targetStencilButton = document.querySelector(".fa-parachute-box")?.parentElement;
    if (!targetStencilButton) {

        return;
    }

    if (this.#biggerStencilActive) {
        if (targetStencilButton.disabled) {
            targetStencilButton.disabled = "";
        }
    }
}

        #setupDrawControlsObservers() {
            const betterBrushTarget = document.querySelector(".drawcontrols-popuplist");
            if (betterBrushTarget) {
                this.#betterBrushVisibilityObserver = new MutationObserver((mutations) => {
                    if (this.#betterBrushActive) {
                        if (mutations[0].target.style.display !== "none") {
                            mutations[0].target.querySelectorAll("div").forEach((n) => {
                                n.removeAttribute("style");
                            });
                        }
                    }
                });
                this.#betterBrushVisibilityObserver.observe(betterBrushTarget, { attributes: true, attributeFilter: ['style'] });
            } else {

            }

            const biggerStencilTarget = document.querySelector(".fa-parachute-box")?.parentElement;
            if (biggerStencilTarget) {
                this.#biggerStencilAccessibilityObserver = new MutationObserver((mutations) => {
                    if (this.#biggerStencilActive) {
                        if (mutations[0].target.disabled) {
                            mutations[0].target.disabled = "";
                        }
                    }
                });
                this.#biggerStencilAccessibilityObserver.observe(biggerStencilTarget, { attributes: true, attributeFilter: ['disabled'] });
            } else {

            }

            this.drawwidthrangeSlider = document.querySelector("#drawwidthrange");
            if (!this.drawwidthrangeSlider) {

            }
        }



        #exportChatMessages() {
            const chatbox = document.getElementById('chatbox_messages');
            if (!chatbox) {

                return;
            }

            const messages = chatbox.querySelectorAll('div.chatmessage');
            let exportedMessages = [];

            messages.forEach(message => {
                let timestamp = message.dataset.ts ? new Date(parseInt(message.dataset.ts)).toLocaleTimeString() : 'N/A';
                if (message.classList.contains('systemchatmessage') || message.classList.contains('systemchatmessage5')) {
                    exportedMessages.push(`[${timestamp}] [Sistema] ${message.textContent.trim().replace(/^System: /, '')}`);
                } else if (message.classList.contains('playerchatmessage-highlightable') || message.classList.contains('chatmessage')) {
                    const playerName = message.querySelector('.playerchatmessage-name')?.textContent?.trim() || 'Desconocido';
                    const playerMessage = message.querySelector('.playerchatmessage-text')?.textContent?.trim() || '';
                    exportedMessages.push(`[${timestamp}] ${playerName}: ${playerMessage}`);
                }
            });

            if (exportedMessages.length === 0) {

                return;
            }

            const blob = new Blob([exportedMessages.join('\n')], { type: 'text/plain;charset=utf-8' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `drawaria_chat_${new Date().toISOString().slice(0, 10)}.txt`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
            this.notify("success", "Chat exportado exitosamente.");
        }


        #populateHideShowSelector() {
            const currentSelectedValue = this.#hideShowElementSelector.value;
            this.#hideShowElementSelector.innerHTML = '';
            const addedSelectors = new Set();

            const placeholderOption = domMake.Tree('option', { value: '' }, ['-- Selecciona un elemento --']);
            this.#hideShowElementSelector.appendChild(placeholderOption);

            allKnownElements.forEach(item => {
                try {
                    if (document.querySelector(item.selector) && !addedSelectors.has(item.selector)) {
                        const option = domMake.Tree('option', { value: item.selector }, [item.label]);
                        this.#hideShowElementSelector.appendChild(option);
                        addedSelectors.add(item.selector);
                    }
                } catch (e) {
                    console.warn(`[MagicTools - HideShow] Selector inválido: ${item.selector}. Error: ${e.message}`);
                }
            });

            if (currentSelectedValue && Array.from(this.#hideShowElementSelector.options).some(opt => opt.value === currentSelectedValue)) {
                this.#hideShowElementSelector.value = currentSelectedValue;
            } else {
                this.#hideShowElementSelector.value = '';
            }
        }

        #toggleElementVisibility(hide) {
            const selectedValue = this.#hideShowElementSelector.value;
            if (!selectedValue) {

                return;
            }

            try {
                document.querySelectorAll(selectedValue).forEach(el => {
                    if (hide) {
                        if (el.style.display && el.style.display !== 'none') {
                            el.dataset.originalDisplay = el.style.display;
                        }
                        el.style.display = 'none';
                        el.style.visibility = 'hidden';
                        if (selectedValue.includes('.modal-backdrop')) {
                             el.remove();
                        }

                    } else {
                        if (el.dataset.originalDisplay) {
                            el.style.display = el.dataset.originalDisplay;
                            delete el.dataset.originalDisplay;
                        } else {
                            el.style.display = '';
                        }
                        el.style.visibility = '';

                    }
                });
            } catch (e) {

            }
        }

        #setupHideShowMenusObserver() {
            if (this.#hideShowMenusObserver) {
                this.#hideShowMenusObserver.disconnect();
            }

            this.#hideShowMenusObserver = new MutationObserver(() => {
                clearTimeout(this.#hideShowMenusObserver._timer);
                this.#hideShowMenusObserver._timer = setTimeout(() => {
                    this.#populateHideShowSelector();
                }, 500);
            });

            this.#hideShowMenusObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
        }
    }
})("QBit");





(function GhostCanvas() {
    const QBit = globalThis[arguments[0]];
    const Await = QBit.findGlobal("Await");

    QBit.Styles.addRule(
      ".ghostimage { position:fixed; top:50%; left:50%; opacity:.6; box-shadow: 0 0 1px 1px cornflowerblue inset; }"
    );

    function getBoundingClientRect(htmlElement) {
      let { top, right, bottom, left, width, height, x, y } = htmlElement.getBoundingClientRect();

      top = Number(top).toFixed();
      right = Number(right).toFixed();
      bottom = Number(bottom).toFixed();
      left = Number(left).toFixed();
      width = Number(width).toFixed();
      height = Number(height).toFixed();
      x = Number(x).toFixed();
      y = Number(y).toFixed();

      return { top, right, bottom, left, width, height, x, y };
    }

    function makeDragable(draggableElement, update) {
      var pos1 = 0,
        pos2 = 0,
        pos3 = 0,
        pos4 = 0;
      draggableElement.onmousedown = dragMouseDown;

      function dragMouseDown(e) {
        e = e || window.event;
        e.preventDefault();

        pos3 = e.clientX;
        pos4 = e.clientY;
        document.onmouseup = closeDragElement;

        document.onmousemove = elementDrag;
      }

      function elementDrag(e) {
        e = e || window.event;
        e.preventDefault();

        pos1 = pos3 - e.clientX;
        pos2 = pos4 - e.clientY;
        pos3 = e.clientX;
        pos4 = e.clientY;

        draggableElement.style.top = Number(draggableElement.offsetTop - pos2).toFixed() + "px";
        draggableElement.style.left = Number(draggableElement.offsetLeft - pos1).toFixed() + "px";
        update();
      }

      function closeDragElement() {

        document.onmouseup = null;
        document.onmousemove = null;
      }
    }

    const radios = [];

    class GhostCanvas extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "CubeEngine");
      static isFavorite = true;

      GameCanvas;
      DrawCanvas;
      DrawCanvasContext;
      DrawCanvasRect;
      loadedImages;
      drawingManager;

      constructor() {
        super("🖼️Ghost Canvas", '<i class="fas fa-images"></i>');
        this.GameCanvas = document.body.querySelector("canvas#canvas");
        this.DrawCanvas = document.createElement("canvas");
        this.DrawCanvasRect = {};
        this.loadedImages = [];
        this.DrawCanvasContext = this.DrawCanvas.getContext("2d");
        this.drawingManager = new TaskManager(this);

        this.#onStartup();
        this.resetAllSettings();
      }

      #onStartup() {
        this.#loadInterface();
        this.DrawCanvas.width = this.GameCanvas.width;
        this.DrawCanvas.height = this.GameCanvas.height;
        this.DrawCanvas.style =
          "position:fixed; opacity:.6; box-shadow: 0 0 1px 1px firebrick inset; pointer-events: none;";

        const onResize = new Await(this.alignDrawCanvas.bind(this), 500);
        window.addEventListener("resize", (event) => {
          onResize.call();
        });

        this.htmlElements.input.addEventListener("change", (event) => {
          if (this.htmlElements.input.checked) this.alignDrawCanvas();
        });
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
        this.#row3();
        this.#row4();
        this.#row5();
      }

      #row1() {
        const row = domMake.Row();
        {
          const resetSettingsButton = domMake.Button("Reset");
          const showCanvasInput = domMake.Tree("input", { type: "checkbox", title: "Toggle Canvas", class: "icon" });
          const clearCanvasButton = domMake.Button("Clear");

          resetSettingsButton.title = "Reset Settings";
          clearCanvasButton.title = "Clear GameCanvas";

          resetSettingsButton.addEventListener("click", (event) => {
            this.resetAllSettings();
          });

          showCanvasInput.addEventListener("change", () => {
            this.DrawCanvas.style.display = showCanvasInput.checked ? "block" : "none";
          });

          clearCanvasButton.addEventListener("click", (event) => {
            let data = ["drawcmd", 0, [0.5, 0.5, 0.5, 0.5, !0, -2000, "#FFFFFF", -1, !1]];
            this.findGlobal("BotClientInterface").siblings[0].bot.send(`${42}${JSON.stringify(data)}`);
          });

          document.body.appendChild(this.DrawCanvas);
          row.appendAll(resetSettingsButton, showCanvasInput, clearCanvasButton);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.Row();
        {
          const loadPixelDataButton = domMake.Button("Load");
          const pixelsLeftToDraw = domMake.Tree("input", {
            type: "text",
            readonly: true,
            style: "text-align: center;",
            value: "0",
          });
          const clearPixelListButton = domMake.Button("Clear");

          this.htmlElements.pixelsLeftToDraw = pixelsLeftToDraw;
          loadPixelDataButton.title = "Load Pixels to draw";
          clearPixelListButton.title = "Clear Pixels to draw";

          loadPixelDataButton.addEventListener("click", (event) => {
            this.saveCanvas();
          });

          clearPixelListButton.addEventListener("click", (event) => {
            this.setPixelList([]);
          });

          row.appendAll(loadPixelDataButton, pixelsLeftToDraw, clearPixelListButton);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row3() {
        const row = domMake.Row();
        {
          const startDrawingButton = domMake.Button("Start");
          const stopDrawingButton = domMake.Button("Stop");

          startDrawingButton.addEventListener("click", (event) => {
            this.drawingManager.startDrawing();
          });
          stopDrawingButton.addEventListener("click", (event) => {
            this.drawingManager.stopDrawing();
          });

          row.appendAll(startDrawingButton, stopDrawingButton);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row4() {
        const row = domMake.Row();
        {
          const brushSizeInput = domMake.Tree("input", { type: "number", min: 2, value: 2, max: 200, step: 1 });
          const singleColorModeInput = domMake.Tree("input", { type: "checkbox", class: "icon" });
          const brushColorInput = domMake.Tree("input", { type: "text", value: "blue" });

          brushSizeInput.addEventListener("change", (event) => {
            this.drawingManager.brushSize = Number(brushSizeInput.value);
          });
          singleColorModeInput.addEventListener("change", (event) => {
            this.drawingManager.singleColor = Boolean(singleColorModeInput.checked);
          });
          brushColorInput.addEventListener("change", (event) => {
            this.drawingManager.brushColor = brushColorInput.value;
          });

          row.appendAll(brushSizeInput, singleColorModeInput, brushColorInput);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row5() {
        const row = domMake.IconList();
        {
          const id = generate.uuidv4();
          const chooseGhostlyImageInput = domMake.Tree("input", { type: "file", id: id, hidden: true });
          const chooseGhostlyImageLabel = domMake.Tree("label", { for: id, class: "icon", title: "Add Image" }, [
            domMake.Tree("i", { class: "fas fa-plus" }),
          ]);

          const localThis = this;
          function onChange() {
            if (!this.files || !this.files[0]) return;
            const myFileReader = new FileReader();
            myFileReader.addEventListener("load", (event) => {
              const base64 = event.target.result.replace("image/gif", "image/png");
              localThis.createGhostImage(base64, row);
            });
            myFileReader.readAsDataURL(this.files[0]);
          }
          chooseGhostlyImageInput.addEventListener("change", onChange);

          row.appendAll(chooseGhostlyImageLabel, chooseGhostlyImageInput);
        }
        {
          const resetImageSelectionLabel = domMake.Tree("div", { class: "icon", title: "Unselect" }, [
            domMake.Tree("i", { class: "fas fa-chevron-left" }),
          ]);
          resetImageSelectionLabel.addEventListener("click", () => {
            document.body.querySelectorAll('input[name="ghostimage"]').forEach((node) => {
              node.checked = false;
            });
          });
          row.appendChild(resetImageSelectionLabel);
        }
        this.htmlElements.section.appendChild(row);
      }

      createGhostImage(imageSource, row) {
        this.alignDrawCanvas();
        const image = this.loadExtension(GhostImage, (reference) => {
          this.loadedImages.push(reference);
        });
        row.appendChild(image.htmlElements.label);
        image.setImageSource(imageSource);
      }

      clearCanvas() {
        this.DrawCanvasContext.clearRect(0, 0, this.DrawCanvas.width, this.DrawCanvas.height);
      }

      saveCanvas() {
        this.getAllPixels();
      }

      resetAllSettings() {
        this.clearCanvas();
        this.loadedImages.forEach((image, index) => {

          if (image && typeof image.reduceToAtoms === 'function') {
            setTimeout(() => {
              image.reduceToAtoms();
            }, 10 * index);
          }
        });
        this.loadedImages = [];
        this.drawingManager.stopDrawing();
        this.setPixelList([]);
        this.alignDrawCanvas(true);
      }

      alignDrawCanvas() {
        if (arguments[0]) {
          this.DrawCanvas.width = this.GameCanvas.width;
          this.DrawCanvas.height = this.GameCanvas.height;
        }

        const GameCanvasRect = getBoundingClientRect(this.GameCanvas);

        this.DrawCanvas.style.top = `${GameCanvasRect.top}px`;
        this.DrawCanvas.style.left = `${GameCanvasRect.left}px`;
        this.DrawCanvas.style.width = `${GameCanvasRect.width}px`;
        this.DrawCanvas.style.height = `${GameCanvasRect.height}px`;

        const DrawCanvasRect = getBoundingClientRect(this.DrawCanvas);

        if (DrawCanvasRect.width <= 0 || DrawCanvasRect.height <= 0)
          return Object.assign(this.DrawCanvasRect, DrawCanvasRect);



        DrawCanvasRect.drawModifierX = 100 / DrawCanvasRect.width;
        DrawCanvasRect.drawModifierY = 100 / DrawCanvasRect.height;
        Object.assign(this.DrawCanvasRect, DrawCanvasRect);
      }

      getAllPixels() {
        const image = this.DrawCanvasContext.getImageData(
          0,
          0,
          this.DrawCanvasContext.canvas.width,
          this.DrawCanvasContext.canvas.height
        );
        const pixels = [];

        for (let index = 0; index < image.data.length; index += 4) {
          const x = (index / 4) % image.width;
          const y = Math.floor((index / 4) / image.width);

          const r = image.data[index + 0];
          const g = image.data[index + 1];
          const b = image.data[index + 2];
          const a = image.data[index + 3];

          pixels.push({ x1: x, y1: y, x2: x, y2: y, color: [r, g, b, a] });
        }

        this.setPixelList(pixels);

      }

      getNoneTransparentPixels() {




        this.getAllPixels();

        const newPixelArray = this.drawingManager.pixelList.filter((pixel) => {


          return pixel.color[3] > 0;
        });

        this.setPixelList(newPixelArray);

      }

      setPixelList(pixelArray) {
        this.drawingManager.pixelList = pixelArray;
        this.htmlElements.pixelsLeftToDraw.value = pixelArray.length;
      }
    }

    class GhostImage extends QBit {
      image;
      rect;

      constructor() {
        super("GhostImage", '<i class="fas fa-image-polaroid"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();

        this.image = domMake.Tree("img", { class: "ghostimage" });
        this.image.addEventListener("mousedown", (event) => {
          this.htmlElements.label.click();
        });

        this.htmlElements.input.type = "radio";
        this.htmlElements.input.name = "ghostimage";

        radios.push(this.htmlElements.input);
        this.htmlElements.input.addEventListener("change", (event) => {
          radios.forEach(function (radio) {
            document.body.querySelector(`label[for="${radio.id}"]`).classList.remove("active");
          });
          this.htmlElements.label.classList.add("active");
        });

        document.body.appendChild(this.image);
        makeDragable(this.image, this.updatePosition.bind(this));
        this.updatePosition();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.Row();
        {
          const paintCanvasButton = domMake.Button("Place");

          paintCanvasButton.addEventListener("click", (event) => {
            this.drawImage();
          });

          row.appendAll(paintCanvasButton);
        }
        {
          const enableButton = domMake.Button("Delete");

          enableButton.addEventListener("click", (event) => {
            this.reduceToAtoms();
          });
          row.appendChild(enableButton);
          this.htmlElements.toggleStatusButton = enableButton;
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.Row();
        {
          const scaleInput = domMake.Tree("input", {
            type: "number",
            title: "scale",
            min: 0.1,
            max: 10,
            value: 1,
            step: 0.02,
          });

          scaleInput.addEventListener("change", () => {
            this.image.style.scale = scaleInput.value;
          });

          this.htmlElements.scaleInput = scaleInput;

          row.appendAll(scaleInput);
        }
        {
          const rotationInput = domMake.Tree("input", { type: "number", title: "rotation", value: 0, step: 1 });

          rotationInput.addEventListener("change", () => {
            this.image.style.rotate = `${rotationInput.value}deg`;
          });

          this.htmlElements.rotationInput = rotationInput;

          row.appendChild(rotationInput);
        }
        this.htmlElements.section.appendChild(row);
      }

      drawImage() {
        this.updatePosition();
        const ctx = this.parent.DrawCanvasContext;

        const offsetTop = Number(this.rect.top) - Number(this.parent.DrawCanvasRect.top);
        const offsetLeft = Number(this.rect.left) - Number(this.parent.DrawCanvasRect.left);

        const angle = (Math.PI / 180) * Number(this.htmlElements.rotationInput.value);
        const scale = Number(this.htmlElements.scaleInput.value);

        const imageWidth = this.image.width * scale;
        const imageHeight = this.image.height * scale;
        const imgHalfWidth = imageWidth * 0.5;
        const imgHalfHeight = imageHeight * 0.5;

        ctx.save();
        ctx.translate(offsetLeft + imgHalfWidth, offsetTop + imgHalfHeight);
        ctx.rotate(angle);
        ctx.translate(-imgHalfWidth, -imgHalfHeight);
        ctx.drawImage(this.image, 0, 0, imageWidth, imageHeight);
        ctx.restore();
        this.parent.notify("success", "Imagen colocada en el lienzo fantasma.");
      }

      setImageSource(imageSource) {
        this.image.src = imageSource;
        this.setIcon(`<img src="${this.image.src}">`);
      }

      updatePosition() {
        this.rect = getBoundingClientRect(this.image);
      }

      reduceToAtoms() {
        this.image.remove();
        const pos = radios.indexOf(this.htmlElements.input);
        if (~pos) radios.splice(pos, 1);




        let pos2 = this.parent.loadedImages.indexOf(this);
        if (~pos2) {
          this.parent.loadedImages.splice(pos2, 1);
        }

        this._EXP_destroy(!0);
        this.parent.notify("info", "Imagen fantasma eliminada.");
      }
    }

    class TaskManager {
      isRunning;
      pixelList;
      parent;
      BotClientManager;
      singleColor;
      brushColor;
      brushSize;

      constructor(parent) {
        this.pixelList = [];
        this.singleColor = false;
        this.brushColor = "blue";
        this.brushSize = 2;
        this.parent = parent;
      }

      startDrawing() {
        this.BotClientManager = this.parent.findGlobal("BotClientManager")?.siblings[0];
        if (!this.BotClientManager || this.BotClientManager.children.length <= 0) {
            this.parent.notify("error", "No hay bots disponibles. Crea y conecta un bot primero.");
            return;
        }
        if (this.pixelList.length === 0) {
            this.parent.notify("warning", "La lista de píxeles para dibujar está vacía. Carga una imagen primero.");
            return;
        }

        this.isRunning = true;
        this.doTasks();
        this.parent.notify("info", "Dibujo iniciado.");
      }

      stopDrawing() {
        this.isRunning = false;
      }

      doTasks() {
        if (!this.BotClientManager || this.BotClientManager.children.length <= 0) {
            this.stopDrawing();
            this.parent.notify("warning", "Bots no disponibles, deteniendo dibujo.");
            return;
        }
        if (!this.isRunning) {
            this.parent.notify("info", "Dibujo detenido.");
            return;
        }
        if (this.pixelList.length <= 0) {
            this.stopDrawing();
            this.parent.notify("success", "Lista de píxeles completada. Dibujo finalizado.");
            return;
        }


        this.BotClientManager.children.forEach((botClientInterface, index) => {
          this.parseAndSendPixel(botClientInterface, index);
        });

        setTimeout(() => {
          this.doTasks();
        }, 1);
      }

      parseAndSendPixel(botClientInterface, index) {
        if (this.pixelList.length <= 0) return;
        if (!botClientInterface.bot || !botClientInterface.bot.getReadyState()) return;


        const task = index % 2 === 0 ? this.pixelList.shift() : this.pixelList.pop();
        if (task) {
            botClientInterface.bot.send(this.convertTasks(task));
            this.parent.htmlElements.pixelsLeftToDraw.value = this.pixelList.length;
        }
      }

      convertTasks(pixel) {
        const playerid = -1;



        if (!this.parent.DrawCanvasRect.drawModifierX || !this.parent.DrawCanvasRect.drawModifierY) {
            this.parent.notify("error", "Error: DrawCanvasRect no inicializado correctamente para escalar píxeles.");
            return null;
        }

        const x1_game = pixel.x1 * this.parent.DrawCanvasRect.drawModifierX;
        const y1_game = pixel.y1 * this.parent.DrawCanvasRect.drawModifierY;
        const x2_game = pixel.x2 * this.parent.DrawCanvasRect.drawModifierX;
        const y2_game = pixel.y2 * this.parent.DrawCanvasRect.drawModifierY;

        const isactive = true;
        const size = pixel.size ?? this.brushSize;
        const pxColor = pixel.color;

        const color = this.singleColor
          ? this.brushColor
          : `rgba(${pxColor[0]},${pxColor[1]},${pxColor[2]},${parseFloat(pxColor[3] / 255).toFixed(2)})`;

        const ispixel = false;



        let data = [
          "drawcmd",
          0,
          [x1_game / 100, y1_game / 100, x2_game / 100, y2_game / 100, isactive, -size, color, playerid, ispixel],
        ];

        return `${42}${JSON.stringify(data)}`;
      }
    }
})("QBit");


(function GhostCanvasAlgorithms() {
    const QBit = globalThis[arguments[0]];

    function sortByColor(pixel1, pixel2) {

      const color1 = (pixel1.color[0] << 24) | (pixel1.color[1] << 16) | (pixel1.color[2] << 8) | pixel1.color[3];
      const color2 = (pixel2.color[0] << 24) | (pixel2.color[1] << 16) | (pixel2.color[2] << 8) | pixel2.color[3];
      return color1 - color2;
    }


    function intToHex(number) {
      return number.toString(16).padStart(2, "0");
    }

    function rgbaArrayToHex(rgbaArray) {
      const r = intToHex(rgbaArray[0]);
      const g = intToHex(rgbaArray[1]);
      const b = intToHex(rgbaArray[2]);
      const a = intToHex(rgbaArray[3]);
      return `#${r}${g}${b}${a}`;
    }


    function areSameColor(colorArray1, colorArray2, allowedDifference = 8) {

      if (Math.abs(colorArray1[3] - colorArray2[3]) > allowedDifference) return false;

      var red = Math.abs(colorArray1[0] - colorArray2[0]);
      var green = Math.abs(colorArray1[1] - colorArray2[1]);
      var blue = Math.abs(colorArray1[2] - colorArray2[2]);

      if (blue > allowedDifference || green > allowedDifference || red > allowedDifference) return false;
      return true;
    }

    class GhostCanvasMinify extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "GhostCanvas");

      constructor() {
        super("Minify", '<i class="fas fa-compress-arrows-alt"></i>');
        this.minOpacity = 20;
        this.maxFuzzyness = 32;
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();


      }

      #row1() {
        const row = domMake.Row();
        {
          const fuzzynessInput = domMake.Tree("input", {
            type: "number",
            title: "Fuzzyness",
            step: 1,
            min: 0,
            max: 255,
            value: this.maxFuzzyness,
          });
          const opacityInput = domMake.Tree("input", {
            type: "number",
            title: "Min Opacity",
            step: 1,
            min: 0,
            max: 255,
            value: this.minOpacity,
          });

          fuzzynessInput.addEventListener("change", () => {
            this.maxFuzzyness = Number(fuzzynessInput.value);
            this.parent.notify("info", `Fuzzyness de color ajustada a: ${this.maxFuzzyness}`);
          });

          opacityInput.addEventListener("change", () => {
            this.minOpacity = Number(opacityInput.value);
            this.parent.notify("info", `Opacidad mínima para considerar sólido ajustada a: ${this.minOpacity}`);
          });

          row.appendAll(fuzzynessInput, opacityInput);
        }
        this.htmlElements.section.appendChild(row);
      }
      #row2() {
        const row = domMake.Row();
        {
          const minifyPixelsArrayButton = domMake.Button("Minify");

          minifyPixelsArrayButton.addEventListener("click", (event) => {
            this.minifyPixelsArray();
          });

          row.appendAll(minifyPixelsArrayButton);
        }
        this.htmlElements.section.appendChild(row);
      }



        minifyPixelsArray() {
            const pixelArray = this.parent.drawingManager.pixelList;
            if (pixelArray.length === 0) {
                this.parent.notify("info", "No hay píxeles para minificar. Carga una imagen en el lienzo fantasma primero.");
                return;
            }






            const newPixelArray = [];
            let currentSegmentStartPixel = null;
            let currentSegmentColor = null;

            for (let i = 0; i < pixelArray.length; i++) {
                const currentPixel = pixelArray[i];
                const pixelColor = currentPixel.color;
                const isTransparent = pixelColor[3] < this.minOpacity;

                if (isTransparent) {

                    if (currentSegmentStartPixel !== null) {
                        newPixelArray.push({
                            x1: currentSegmentStartPixel.x1,
                            y1: currentSegmentStartPixel.y1,
                            x2: pixelArray[i - 1].x1,
                            y2: currentSegmentStartPixel.y1,
                            color: currentSegmentColor,
                        });
                        currentSegmentStartPixel = null;
                        currentSegmentColor = null;
                    }
                    continue;
                }


                if (currentSegmentStartPixel === null) {

                    currentSegmentStartPixel = currentPixel;
                    currentSegmentColor = pixelColor;
                } else {


                    const prevPixelInList = pixelArray[i - 1];





                    const isConsecutiveX = currentPixel.x1 === (prevPixelInList.x1 + 1);
                    const sameRow = currentPixel.y1 === currentSegmentStartPixel.y1;
                    const similarColor = areSameColor(currentSegmentColor, pixelColor, this.maxFuzzyness);

                    if (!isConsecutiveX || !sameRow || !similarColor) {

                        newPixelArray.push({
                            x1: currentSegmentStartPixel.x1,
                            y1: currentSegmentStartPixel.y1,
                            x2: prevPixelInList.x1,
                            y2: currentSegmentStartPixel.y1,
                            color: currentSegmentColor,
                        });


                        currentSegmentStartPixel = currentPixel;
                        currentSegmentColor = pixelColor;
                    }

                }
            }


            if (currentSegmentStartPixel !== null) {
                newPixelArray.push({
                    x1: currentSegmentStartPixel.x1,
                    y1: currentSegmentStartPixel.y1,
                    x2: pixelArray[pixelArray.length - 1].x1,
                    y2: currentSegmentStartPixel.y1,
                    color: currentSegmentColor,
                });
            }

            this.parent.setPixelList(newPixelArray);
            this.parent.notify("success", `Minificación completada. Líneas reducidas de ${pixelArray.length} a ${newPixelArray.length}.`);
        }


      minifyPixelsArray_alt() {
        const pixelArray = this.parent.drawingManager.pixelList;
        if (pixelArray.length === 0) {
            this.parent.notify("info", "No hay píxeles para minificar (alt).");
            this.parent.setPixelList([]);
            return;
        }

        const newPixelArray = [];
        let lastPixel = pixelArray[0];
        let currentLine = {
            x1: lastPixel.x1,
            y1: lastPixel.y1,
            x2: lastPixel.x2,
            y2: lastPixel.y2,
            color: lastPixel.color
        };
        const stepsize = this.parent.stepsize ?? 1;

        for (let i = 0; i < pixelArray.length; i += stepsize) {
          const currentPixel = pixelArray[i];


          if (
              currentPixel.y1 !== currentLine.y1 ||
              !areSameColor(currentPixel.color, currentLine.color, this.maxFuzzyness)
          ) {

            currentLine.x2 = lastPixel.x2;
            if (currentLine.color[3] >= this.minOpacity) {
                newPixelArray.push(currentLine);
            }

            currentLine = {
                x1: currentPixel.x1,
                y1: currentPixel.y1,
                x2: currentPixel.x2,
                y2: currentPixel.y2,
                color: currentPixel.color
            };
          } else {

              currentLine.x2 = currentPixel.x2;
          }
          lastPixel = currentPixel;
        }

        if (currentLine.color[3] >= this.minOpacity) {
            newPixelArray.push(currentLine);
        }

        this.parent.setPixelList(newPixelArray);
        this.parent.notify("success", `Minificación (alt) completada. Líneas reducidas de ${pixelArray.length} a ${newPixelArray.length}.`);
      }
    }

    class GhostCanvasSort extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "GhostCanvas");

      constructor() {
        super("Sort", '<i class="fas fa-sort-numeric-down"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();



      }

      #row1() {
        const row = domMake.Row();
        {
          const sortPixelsArrayButton = domMake.Button("Sort");

          sortPixelsArrayButton.addEventListener("click", (event) => {
            this.sortPixelsArray();
          });

          row.appendAll(sortPixelsArrayButton);
        }
        this.htmlElements.section.appendChild(row);
      }


      sortPixelsArray() {
        const pixelArray = this.parent.drawingManager.pixelList;
        if (pixelArray.length === 0) {
            this.parent.notify("info", "No hay píxeles para ordenar.");
            return;
        }


        const newPixelArray = [...pixelArray].sort((a, b) => {
            if (a.y1 !== b.y1) {
                return a.y1 - b.y1;
            }
            if (a.x1 !== b.x1) {
                return a.x1 - b.x1;
            }

            return sortByColor(a, b);
        });

        this.parent.setPixelList(newPixelArray);
        this.parent.notify("success", `Píxeles ordenados. Total: ${newPixelArray.length}.`);
      }
    }
})("QBit");

  (function BotClientInterface() {
    const QBit = globalThis[arguments[0]];
    const BotClient = QBit.findGlobal("BotClient");

    let botcount = 0;
    const radios = [];

    function getMasterId() {
      return document.querySelector(".playerlist-name-self")?.parentElement.dataset.playerid || 0;
    }

    function parseAvatarURL(arr = []) {
      return `https://drawaria.online/avatar/cache/${arr.length > 0 ? arr.join(".") : "default"}.jpg`;
    }

    class BotClientManager extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "CubeEngine");

      constructor() {
        super(`🤖Bot Client Manager`, '<i class="fas fa-robot"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.IconList();
        {
          const id = generate.uuidv4();
          const createBotClientInterfaceInput = domMake.Tree("input", { type: "button", id: id, hidden: true });
          const createBotClientInterfaceLabel = domMake.Tree("label", { for: id, class: "icon", title: "Add Image" }, [
            domMake.Tree("i", { class: "fas fa-plus" }),
          ]);

          createBotClientInterfaceInput.addEventListener("click", () => {
            this.createBotClientInterface();
          });

          row.appendAll(createBotClientInterfaceLabel);
          row.appendAll(createBotClientInterfaceInput);
        }
        {
          const id = generate.uuidv4();
          const removeBotClientInterfaceInput = domMake.Tree("input", { type: "button", id: id, hidden: true });
          const removeBotClientInterfaceLabel = domMake.Tree("label", { for: id, class: "icon", title: "Add Image" }, [
            domMake.Tree("i", { class: "fas fa-minus" }),
          ]);

          removeBotClientInterfaceInput.addEventListener("click", () => {
            this.deleteBotClientInterface();
          });

          row.appendAll(removeBotClientInterfaceLabel);
          row.appendAll(removeBotClientInterfaceInput);
        }
        this.htmlElements.header.before(row);
      }

      createBotClientInterface() {
        const instance = this.loadExtension(BotClientInterface);
        instance.htmlElements.input.type = "radio";
        instance.htmlElements.input.name = "botClient";

        radios.push(instance.htmlElements.input);
        instance.htmlElements.input.addEventListener("change", (event) => {
          radios.forEach(function (radio) {
            document.body.querySelector(`label[for="${radio.id}"]`).classList.remove("active");
          });
          instance.htmlElements.label.classList.add("active");
        });

        return instance;
      }

      deleteBotClientInterface() {
        const input = document.body.querySelector(`input[name="botClient"]:checked`);

        const matches = this.children.filter((child) => {
          return child.htmlElements.input === input;
        });
        if (matches.length <= 0) return;

        const instance = matches[0];

        instance.bot.disconnect();

        const labelPos = radios.indexOf(instance.htmlElements.input);
        if (~labelPos) radios.splice(labelPos, 1);

        instance._EXP_destroy(!0);
      }
    }

    class BotClientInterface extends QBit {
      static dummy1 = QBit.register(this);



      constructor() {
        super(`Bot${botcount}`, '<i class="fas fa-robot"></i>');
        this.bot = new BotClient();
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
        this.setClientName(this.bot.name);
        this.setClientIcon(this.bot.avatar);
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.Row();
        {
          let join_button = domMake.Button("Enter");
          let leave_button = domMake.Button("Leave");

          join_button.addEventListener("click", (event) => {
            this.bot.enterRoom(document.querySelector("#invurl").value);
          });

          leave_button.addEventListener("click", (event) => {
            this.bot.disconnect();
          });

          row.appendAll(join_button, leave_button);
        }
        this.htmlElements.section.appendChild(row);
      }

      setClientName(name) {
        this.setName(name);
        this.bot.name = name;
      }

      setClientIcon(icon) {
        this.setIcon(`<img src="${parseAvatarURL(this.bot.avatar)}">`);
        this.bot.avatar = icon;
      }
    }
  })("QBit");

  (function BotClientInteractions() {
    const QBit = globalThis[arguments[0]];

    class BotPersonality extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "BotClientInterface");

      constructor() {
        super("Personality", '<i class="fas fa-user-cog"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.Row();
        {
          let botName = domMake.Tree("input", { type: "text", placeholder: "Your Bots name" });
          let botNameAccept = domMake.Tree("button", { class: "icon" }, [domMake.Tree("i", { class: "fas fa-check" })]);

          botNameAccept.addEventListener("click", (event) => {
            this.parent.setClientName(botName.value);
          });

          row.appendAll(botName, botNameAccept);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        let id = generate.uuidv4();
        const row = domMake.Row();
        {
          let botAvatarUpload = domMake.Tree("input", { type: "file", id: id, hidden: true });
          let botAvatarAccept = domMake.Tree("label", { for: id, class: "btn btn-outline-secondary" }, [
            "Upload BotAvatar",
          ]);

          const localThis = this;
          function onChange() {
            if (!this.files || !this.files[0]) return;
            let myFileReader = new FileReader();
            myFileReader.addEventListener("load", (e) => {
              let a = e.target.result.replace("image/gif", "image/png");
              fetch("https://drawaria.online/uploadavatarimage", {
                method: "POST",
                body: "imagedata=" + encodeURIComponent(a) + "&fromeditor=true",
                headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
              }).then((res) =>
                res.text().then((body) => {
                  localThis.parent.setClientIcon(body.split("."));
                })
              );
            });
            myFileReader.readAsDataURL(this.files[0]);
          }
          botAvatarUpload.addEventListener("change", onChange);

          row.appendAll(botAvatarUpload, botAvatarAccept);
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    class BotSozials extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "BotClientInterface");

      constructor() {
        super("Socialize", '<i class="fas fa-comment-dots"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.Row();
        {
          let messageClear_button = domMake.Button('<i class="fas fa-strikethrough"></i>');
          let messageSend_button = domMake.Button('<i class="fas fa-paper-plane"></i>');
          let message_input = domMake.Tree("input", { type: "text", placeholder: "message..." });

          messageClear_button.classList.add("icon");
          messageSend_button.classList.add("icon");

          messageClear_button.addEventListener("click", (event) => {
            message_input.value = "";
          });

          messageSend_button.addEventListener("click", (event) => {
            this.parent.bot.emit("chatmsg", message_input.value);
          });

          message_input.addEventListener("keypress", (event) => {
            if (event.keyCode != 13) return;
            this.parent.bot.emit("chatmsg", message_input.value);
          });

          row.appendAll(messageClear_button, message_input, messageSend_button);
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.IconList();

        {
          document
            .querySelectorAll("#gesturespickerselector .gesturespicker-container .gesturespicker-item")
            .forEach((node, index) => {
              let clone = node.cloneNode(true);
              clone.classList.add("icon");
              clone.addEventListener("click", (event) => {
                this.parent.bot.emit("sendgesture", index);
              });
              row.appendChild(clone);
            });
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    class BotTokenGiver extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "BotClientInterface");

      constructor() {
        super("Tokken", '<i class="fas fa-thumbs-up"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
        this.#row2();
      }

      #row1() {
        const row = domMake.IconList();

        {
          let listOfTokens = [
            '<i class="fas fa-thumbs-up"></i>',
            '<i class="fas fa-heart"></i>',
            '<i class="fas fa-paint-brush"></i>',
            '<i class="fas fa-cocktail"></i>',
            '<i class="fas fa-hand-peace"></i>',
            '<i class="fas fa-feather-alt"></i>',
            '<i class="fas fa-trophy"></i>',
            '<i class="fas fa-mug-hot"></i>',
            '<i class="fas fa-gift"></i>',
          ];
          listOfTokens.forEach((token, index) => {
            let tokenSend_button = domMake.Button(token);
            tokenSend_button.classList.add("icon");
            tokenSend_button.addEventListener("click", () => {
              this.parent.bot.room.players.forEach((player) => {
                this.parent.bot.emit("settoken", player.id, index);
              });
            });
            row.appendChild(tokenSend_button);
          });
        }
        this.htmlElements.section.appendChild(row);
      }

      #row2() {
        const row = domMake.Row();
        {
          let toggleStatus_button = domMake.Button("Toggle Status");
          toggleStatus_button.addEventListener("click", () => {
            this.parent.bot.attributes.status = !this.parent.bot.attributes.status;
            let status = this.parent.bot.attributes.status;
            toggleStatus_button.classList[status ? "add" : "remove"]("active");
            this.parent.bot.emit("setstatusflag", 0, status);
            this.parent.bot.emit("setstatusflag", 1, status);
            this.parent.bot.emit("setstatusflag", 2, status);
            this.parent.bot.emit("setstatusflag", 3, status);
            this.parent.bot.emit("setstatusflag", 4, status);
          });
          row.appendChild(toggleStatus_button);
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    class BotCanvasAvatar extends QBit {
      static dummy1 = QBit.register(this);
      static dummy2 = QBit.bind(this, "BotClientInterface");

      constructor() {
        super("SpawnAvatar", '<i class="fas fa-user-circle"></i>');
        this.#onStartup();
      }

      #onStartup() {
        this.#loadInterface();
      }

      #loadInterface() {
        this.#row1();
      }

      #row1() {
        const row = domMake.Row();
        {

          let avatarPosition = { x: 0, y: 0 };

          let avatarSpawn_button = domMake.Button('<i class="fas fa-exchange-alt"></i>');
          let avatarChange_button = domMake.Button('<i class="fas fa-retweet"></i>');
          let avatarPositionX_button = domMake.Tree("input", {
            type: "number",
            value: 2,
            min: 2,
            max: 98,
            title: "Left",
          });
          let avatarPositionY_button = domMake.Tree("input", {
            type: "number",
            value: 2,
            min: 2,
            max: 98,
            title: "Top",
          });

          avatarSpawn_button.addEventListener("click", (event) => {
            this.parent.bot.emit("spawnavatar");
            this.parent.bot.attributes.spawned = !this.parent.bot.attributes.spawned;
          });

          avatarChange_button.addEventListener("click", (event) => {
            this.parent.bot.emit("setavatarprop");
            this.parent.bot.attributes.rounded = !this.parent.bot.attributes.rounded;
          });

          avatarPositionX_button.addEventListener("change", (event) => {
            avatarPosition.x = avatarPositionX_button.value;
            this.parent.bot.emit("moveavatar", avatarPosition.x, avatarPosition.y);
          });

          avatarPositionY_button.addEventListener("change", (event) => {
            avatarPosition.y = avatarPositionY_button.value;
            this.parent.bot.emit("moveavatar", avatarPosition.x, avatarPosition.y);
          });

          avatarSpawn_button.title = "Spawn Avatar";
          avatarChange_button.title = "Toggle Round Avatar";

          row.appendAll(avatarSpawn_button, avatarPositionX_button, avatarPositionY_button, avatarChange_button);
        }
        this.htmlElements.section.appendChild(row);
      }
    }

    function getMyId() {
      return document.querySelector(".playerlist-name-self")?.parentElement.dataset.playerid || 0;
    }

    function parseAvatarURL(arr = []) {
      return `https://drawaria.online/avatar/cache/${arr.length > 0 ? arr.join(".") : "default"}.jpg`;
    }
  })("QBit");






(function BotSentinel_PersonalityAI_Module() {
    const QBit = globalThis[arguments[0]];


    function createToggle(labelText, callback, initialChecked = false) {
        const row = domMake.Row();
        row.style.alignItems = 'center';
        row.style.justifyContent = 'space-between';

        const labelSpan = domMake.Tree("span", {}, [labelText]);

        const checkboxId = "sentinel-toggle-" + (Math.random() * 1e9 | 0);
        const checkbox = domMake.Tree("input", { type: "checkbox", id: checkboxId, hidden: true });

        if (initialChecked) {
            checkbox.checked = true;
        }

        const indicatorLabel = domMake.Tree("label", {
            for: checkboxId,
            class: "icon",
            style: `
                width: 24px;
                height: 24px;
                min-width: unset;
                min-height: unset;
                border: 1px solid var(--CE-color);
                border-radius: .25rem;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: background-color 0.2s ease, border-color 0.2s ease;
                background-color: ${initialChecked ? 'var(--info)' : 'var(--secondary)'};
            `
        });


        indicatorLabel.innerHTML = initialChecked
            ? '<i class="fas fa-check-square" style="font-size: 1.2em; color: var(--success);"></i>'
            : '<i class="fas fa-square" style="font-size: 1.2em;"></i>';

        checkbox.addEventListener('change', (event) => {
            const checked = event.target.checked;
            if (checked) {
                indicatorLabel.innerHTML = '<i class="fas fa-check-square" style="font-size: 1.2em; color: var(--success);"></i>';
                indicatorLabel.style.backgroundColor = 'var(--info)';
            } else {
                indicatorLabel.innerHTML = '<i class="fas fa-square" style="font-size: 1.2em;"></i>';
                indicatorLabel.style.backgroundColor = 'var(--secondary)';
            }
            if (callback && typeof callback === 'function') {
                callback(checked);
            }
        });

        row.appendAll(labelSpan, domMake.Tree("div", {style: "flex-shrink: 0;"}, [checkbox, indicatorLabel]));
        return row;
    }

    QBit.Styles.addRules([
        `#${QBit.identifier} .sentinel-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .sentinel-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .sentinel-control-group {
            display: flex;
            gap: 10px;
            margin-top: 8px;
            align-items: center;
        }`,
        `#${QBit.identifier} .sentinel-control-group > select,
         #${QBit.identifier} .sentinel-control-group > input[type="number"],
         #${QBit.identifier} .sentinel-control-group > button {
            flex-grow: 1;
            padding: 5px;
            box-sizing: border-box;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
        }`,
        `#${QBit.identifier} .sentinel-control-group label {
             flex-shrink: 0;
             margin-right: 5px;
        }`,
        `#${QBit.identifier} .sentinel-follow-button.active {
             background-color: var(--warning);
             color: white;
        }`,

        `#${QBit.identifier} .bad-girl-section-title {
            color: var(--danger);
        }`,
        `#${QBit.identifier} .bad-girl-toggle-button.active {
            background-color: var(--danger);
            color: white;
        }`,
        `#${QBit.identifier} .bad-girl-textarea {
            width: 100%;
            min-height: 80px;
            margin-top: 5px;
            background-color: var(--input-bg);
            color: var(--dark-text);
            border: 1px solid var(--input-border-blue);
            padding: 5px;
            box-sizing: border-box;
        }`,
         `#${QBit.identifier} .bad-girl-controls {
            display: flex;
            gap: 10px;
            align-items: center;
            margin-top: 5px;
        }`,
        `#${QBit.identifier} .bad-girl-controls label {
             margin-right: 5px;
        }`
    ]);

    class BotSentinel extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");


        _bots = [];
        _botManagerInstance = null;
        _gameCanvas = null;
        _followTarget = { id: null, interval: null };
        _naturalMovementInterval = null;
        _activeToggles = {
            naturalMovement: false,
            reactiveChat: false,
            smartGestures: false,
            badGirlSpam: false
        };
        _chatCooldown = new Map();
        _gestureCooldown = new Map();


        _ui = {
            playerDropdown: null,
            followButton: null,
            naturalMovementToggleCheckbox: null,
            reactiveChatToggleCheckbox: null,
            smartGesturesToggleCheckbox: null,
            personalitySelect: null,

            badGirlToggleButton: null,
            messageTextarea: null,
            intervalInput: null,
            saveMessagesButton: null,
        };


        _spamInterval = null;
        _intervalTime = 700;
        _messageList = [
            "Eres muy lento", "Novato", "Jaja, qué mal", "Inténtalo de nuevo",
            "¿Eso es todo lo que tienes?", "Aburrido...", "Me duermo", "Puedes hacerlo mejor",
            "...", "Casi, pero no"
        ];


        _personalities = {
            Amigable: {
                spanish_greetings: ["¡Hola a todos!", "¡Buenas!", "¿Qué tal?", "Hey! Un gusto estar aquí 😊"],
                spanish_acknowledgements: ["Si!", "Claro!", "Entendido!", "Asi es!"],
                spanish_questions: ["Como estás?", "Y tu?", "¿Que tal?"],
                spanish_laughter: ["XD", "Jaja", "LOL"],
                spanish_general: ["Que?", "Bueno...", "Pero..."],
                spanish_congrats: ["¡Bien hecho, {player}!", "¡Excelente!", "¡Esa era!", "Felicidades, {player}!"],
                spanish_farewell: ["¡Adiós!", "Nos vemos", "¡Hasta la próxima!", "Chao 👋"],
                spanish_playerJoin: ["¡Bienvenido, {player}!", "Hola {player}!", "Mira quién llegó, {player} 👋"],
                spanish_playerLeave: ["Adiós, {player}!", "{player} se fue 😔", "Chao {player}"],

                english_greetings: ["Hi!", "Hello!", "Hey there!", "Nice to see you 😊"],
                english_acknowledgements: ["Yes!", "Got it!", "Right!"],
                english_questions: ["How are you?", "And you?", "What's up?"],
                english_laughter: ["LOL", "Haha", "XD", "Omg!"],
                english_general: ["What?", "Well...", "But..."],
                english_congrats: ["Good job, {player}!", "Excellent!", "That was it!", "Congrats, {player}!"],
                english_farewell: ["Bye!", "See ya!", "Later!", "So long 👋"],
                english_playerJoin: ["Welcome, {player}!", "Hi {player}!", "Look who's here, {player} 👋"],
                english_playerLeave: ["Bye, {player}!", "{player} left 😔", "See ya {player}"],

                gestures: {
                    greeting: 5,
                    acknowledgement: 11,
                    question: 10,
                    laughter: 7,
                    general: 17,
                    congrats: 19,
                    playerJoin: 5,
                    playerLeave: 3,
                    drawing: 4,
                    goodjob_drawing: 0
                }
            },
            Competitivo: {
                spanish_greetings: ["He llegado.", "Prepárense para dibujar.", "A ver quién gana."],
                spanish_acknowledgements: ["Si.", "Ok.", "Correcto."],
                spanish_questions: ["¿Estás listo?", "Quién sigue?", "¿Qué dibujas?"],
                spanish_laughter: ["Jaja.", "Easy."],
                spanish_general: ["..."],
                spanish_congrats: ["Nada mal, {player}.", "Correcto.", "Uno menos.", "Ok, adivinaste."],
                spanish_farewell: ["Me retiro.", "Suficiente por hoy.", "GG."],
                spanish_playerJoin: ["Otro rival...", "Llegó {player}...", "Hola {player}."],
                spanish_playerLeave: ["Uno menos.", "{player} se fue.", "OK, {player}."],

                english_greetings: ["I'm here.", "Get ready to draw.", "Who's next?"],
                english_acknowledgements: ["Yes.", "Ok.", "Correct."],
                english_questions: ["You ready?", "Who's drawing?", "What is it?"],
                english_laughter: ["Haha.", "Easy."],
                english_general: ["..."],
                english_congrats: ["Not bad, {player}.", "Correct.", "One less.", "Okay, you got it."],
                english_farewell: ["I'm out.", "Enough for today.", "GG."],
                english_playerJoin: ["Another rival...", "{player} arrived...", "Hi {player}."],
                english_playerLeave: ["One less.", "{player} left.", "Okay {player}."],

                gestures: {
                    greeting: 1,
                    acknowledgement: 12,
                    question: 10,
                    laughter: 6,
                    general: 16,
                    congrats: 0,
                    playerJoin: 13,
                    playerLeave: 3,
                    drawing: 12,
                    goodjob_drawing: 0
                }
            },
            Neutral: {
                spanish_greetings: ["Hola.", "Saludos."],
                spanish_acknowledgements: ["Si.", "Ok."],
                spanish_questions: ["?", "Cómo?"],
                spanish_laughter: ["Jeje."],
                spanish_general: ["..."],
                spanish_congrats: ["Bien, {player}.", "Correcto."],
                spanish_farewell: ["Adiós."],
                spanish_playerJoin: ["{player} se unió."],
                spanish_playerLeave: ["{player} se fue."],

                english_greetings: ["Hi.", "Greetings."],
                english_acknowledgements: ["Yes.", "Ok."],
                english_questions: ["?", "How?"],
                english_laughter: ["Hehe."],
                english_general: ["..."],
                english_congrats: ["Good, {player}.", "Correct."],
                english_farewell: ["Bye."],
                english_playerJoin: ["{player} joined."],
                english_playerLeave: ["{player} left."],

                gestures: {
                    greeting: 11,
                    acknowledgement: 11,
                    question: 10,
                    laughter: 5,
                    general: 17,
                    congrats: 11,
                    playerJoin: 11,
                    playerLeave: 11,
                    drawing: 8,
                    goodjob_drawing: 11
                }
            }
        };


        _toggleNaturalMovement = this._toggleNaturalMovement.bind(this);
        _toggleSmartFollow = this._toggleSmartFollow.bind(this);
        _updatePlayerDropdown = this._updatePlayerDropdown.bind(this);
        _handleReactiveChat = this._handleReactiveChat.bind(this);
        _handleCorrectGuess = this._handleCorrectGuess.bind(this);
        _handlePlayerJoin = this._handlePlayerJoin.bind(this);
        _handlePlayerLeave = this._handlePlayerLeave.bind(this);
        _handleTurnEnd = this._handleTurnEnd.bind(this);
        _executeNaturalMovement = this._executeNaturalMovement.bind(this);
        _followLogic = this._followLogic.bind(this);
        _moveBotSmoothly = this._moveBotSmoothly.bind(this);
        _handleTurnBeginDraw = this._handleTurnBeginDraw.bind(this);
        _handleWordSelected = this._handleWordSelected.bind(this);
        _toggleBadGirlSpam = this._toggleBadGirlSpam.bind(this);
        _updateMessageList = this._updateMessageList.bind(this);

        constructor() {
            super("⚙️1 Bot Commander", '<i class="fas fa-cog"></i>');
            this._loadInterface();
            this._initializeBotListeners();
            this._gameCanvas = document.getElementById('canvas');

            this._ui.messageTextarea.value = this._messageList.join("\n");
            this._ui.intervalInput.value = this._intervalTime;
        }

        _initializeBotListeners() {
            const botManagerClass = this.findGlobal("BotClientManager");

            if (!botManagerClass || !botManagerClass.siblings || botManagerClass.siblings.length === 0) {
                setTimeout(() => this._initializeBotListeners(), 500);
                return;
            }

            this._botManagerInstance = botManagerClass.siblings[0];



            setTimeout(() => {
                 this._botManagerInstance.children.forEach(botInterface => {
                      if (botInterface.bot) {
                           this._addBot(botInterface.bot);
                      } else {

                      }
                 });

                 if (this._bots.length === 0) {

                 } else {

                      this._updatePlayerDropdown();
                 }
            }, 1000);


            const playerListElement = document.getElementById("playerlist");
             if (playerListElement) {
                 let playerListObserverTimer;
                 const playerListObserver = new MutationObserver(() => {
                     clearTimeout(playerListObserverTimer);
                     playerListObserverTimer = setTimeout(() => {
                         this._updatePlayerDropdown();
                     }, 200);
                 });
                 playerListObserver.observe(playerListElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-playerid', 'data-loggedin', 'style'] });

             } else {

             }


        }

        _loadInterface() {
            const container = domMake.Tree("div");


            const movementSection = domMake.Tree("div", { class: "sentinel-section" });
            movementSection.appendChild(domMake.Tree("div", { class: "sentinel-section-title" }, ["Movimiento Avanzado"]));

            const naturalMovementToggleRow = createToggle("Movimiento Natural", (checked) => this._toggleNaturalMovement(checked));
            this._ui.naturalMovementToggleCheckbox = naturalMovementToggleRow.querySelector('input[type="checkbox"]');
            movementSection.appendChild(naturalMovementToggleRow);

            const followGroup = domMake.Tree("div", { class: "sentinel-control-group" });
            this._ui.playerDropdown = domMake.Tree("select", {style: "flex-grow: 1;"});
            followGroup.appendChild(this._ui.playerDropdown);

            this._ui.followButton = domMake.Button("Seguir");
            this._ui.followButton.classList.add("sentinel-follow-button");
            this._ui.followButton.addEventListener("click", this._toggleSmartFollow);
            followGroup.appendChild(this._ui.followButton);
            movementSection.appendChild(followGroup);
            container.appendChild(movementSection);


            const interactionSection = domMake.Tree("div", { class: "sentinel-section" });
            interactionSection.appendChild(domMake.Tree("div", { class: "sentinel-section-title" }, ["Interacción Inteligente"]));

            const reactiveChatToggle = createToggle("Chat Reactivo", (checked) => this._activeToggles.reactiveChat = checked);
            this._ui.reactiveChatToggleCheckbox = reactiveChatToggle.querySelector('input[type="checkbox"]');
            interactionSection.appendChild(reactiveChatToggle);

            const smartGesturesToggle = createToggle("Gestos Inteligentes", (checked) => this._activeToggles.smartGestures = checked);
            this._ui.smartGesturesToggleCheckbox = smartGesturesToggle.querySelector('input[type="checkbox"]');
            interactionSection.appendChild(smartGesturesToggle);

            const personalityGroup = domMake.Tree("div", { class: "sentinel-control-group" });
            personalityGroup.appendChild(domMake.Tree("label", {}, ["Personalidad:"]));
            this._ui.personalitySelect = domMake.Tree("select", {style: "flex-grow: 1;"});
            Object.keys(this._personalities).forEach(p => {
                this._ui.personalitySelect.appendChild(domMake.Tree("option", { value: p }, [p]));
            });
            personalityGroup.appendChild(this._ui.personalitySelect);
            interactionSection.appendChild(personalityGroup);
            container.appendChild(interactionSection);


            const badGirlSection = domMake.Tree("div", { class: "sentinel-section" });
            badGirlSection.appendChild(domMake.Tree("div", { class: "sentinel-section-title bad-girl-section-title" }, ["Modo 'Chica Mala' (Spam)"]));

            this._ui.badGirlToggleButton = domMake.Button('<i class="fas fa-play-circle"></i> Iniciar Spam');
            this._ui.badGirlToggleButton.classList.add("bad-girl-toggle-button");
            this._ui.badGirlToggleButton.addEventListener("click", () => this._toggleBadGirlSpam());
            badGirlSection.appendChild(this._ui.badGirlToggleButton);

            badGirlSection.appendChild(domMake.Tree("label", { style: "margin-top: 10px; display: block;" }, ["Lista de mensajes (uno por línea):"]));
            this._ui.messageTextarea = domMake.Tree("textarea", {
                class: "bad-girl-textarea",
                placeholder: "Escribe aquí tus mensajes, uno por línea."
            });
            badGirlSection.appendChild(this._ui.messageTextarea);

            const controlsDiv = domMake.Tree("div", { class: "bad-girl-controls" });
            const intervalLabel = domMake.Tree("label", { for: "bad-girl-interval" }, ["Intervalo (ms):"]);
            this._ui.intervalInput = domMake.Tree("input", {
                type: "number",
                id: "bad-girl-interval",
                value: this._intervalTime,
                min: "100",
                step: "50",
                style: "width: 80px;"
            });
            this._ui.intervalInput.addEventListener("change", (e) => {
                const newTime = parseInt(e.target.value);
                if (newTime >= 100) {
                    this._intervalTime = newTime;

                    if (this._spamInterval) {
                        this._toggleBadGirlSpam();
                        this._toggleBadGirlSpam();
                    }
                }
            });

            this._ui.saveMessagesButton = domMake.Button("Guardar Lista");
            this._ui.saveMessagesButton.addEventListener("click", this._updateMessageList);

            controlsDiv.appendAll(intervalLabel, this._ui.intervalInput, this._ui.saveMessagesButton);
            badGirlSection.appendChild(controlsDiv);
            container.appendChild(badGirlSection);

            this.htmlElements.section.appendChild(container);
        }

        _addBot(botInstance) {
            if (!botInstance || this._bots.some(b => b === botInstance)) {
                return;
            }

            const isManagedBot = this._botManagerInstance?.children.some(bi => bi.bot === botInstance);
            if (!isManagedBot) {

                return;
            }

            this._bots.push(botInstance);
            this.notify("log", `Bot Sentinel ahora vigila a: ${botInstance.name}`);

            const sentinelListeners = [
                { event: "bc_chatmessage", callback: (data) => this._handleReactiveChat(botInstance, { id: data[0], name: data[1], message: data[2] }) },
                { event: "uc_turn_wordguessedlocalThis", callback: (data) => this._handleCorrectGuess(data) },
                { event: "bc_playernew", callback: (data) => this._handlePlayerJoin({ id: data[0], name: data[1] }) },
                { event: "bc_playerleft", callback: (data) => this._handlePlayerLeave({ id: data[0], name: data[1] }) },
                { event: "bc_turn_results", callback: (data) => this._handleTurnEnd(botInstance, data) },
                { event: "uc_turn_begindraw", callback: (data) => this._handleTurnBeginDraw(botInstance, data) },
                { event: "uc_turn_selectword", callback: (data) => this._handleWordSelected(botInstance, data) },
            ];

            sentinelListeners.forEach(listener => {
                 const exists = botInstance.customObservers.some(obs => obs.event === listener.event && obs.callback === listener.callback);
                 if (!exists) {
                      botInstance.customObservers.push(listener);
                 }
            });
        }

        _removeBot(botInstance) {
            const initialCount = this._bots.length;
            this._bots = this._bots.filter(b => b !== botInstance);
            if (this._bots.length < initialCount) {
                this.notify("log", `Bot Sentinel ya no vigila a: ${botInstance.name}`);
                 if (this._followTarget.id === botInstance.id) {

                      this._toggleSmartFollow();
                 }
                 if (this._bots.length === 0) {


                      this._toggleNaturalMovement(false);
                      this._activeToggles.reactiveChat = false;
                      this._activeToggles.smartGestures = false;
                      this._toggleBadGirlSpam(false);

                      if(this._ui.naturalMovementToggleCheckbox) this._ui.naturalMovementToggleCheckbox.checked = false;
                      if(this._ui.reactiveChatToggleCheckbox) this._ui.reactiveChatToggleCheckbox.checked = false;
                      if(this._ui.smartGesturesToggleCheckbox) this._ui.smartGesturesToggleCheckbox.checked = false;
                      if(this._ui.badGirlToggleButton) this._ui.badGirlToggleButton.classList.remove("active");
                      this._ui.badGirlToggleButton.innerHTML = '<i class="fas fa-play-circle"></i> Iniciar Spam';
                 }
            }
        }


        _getBot(requireConnected = true, skipNotification = false) {
            if (!this._botManagerInstance) {

                return null;
            }

            const botClientInterfaces = this._botManagerInstance.children;
            let activeBotClientInterface = null;

            const selectedBotInput = document.querySelector('input[name="botClient"]:checked');
            if (selectedBotInput) {
                activeBotClientInterface = botClientInterfaces.find(bci => bci.htmlElements.input === selectedBotInput);
            }

            if (!activeBotClientInterface && botClientInterfaces.length > 0) {
                activeBotClientInterface = botClientInterfaces[0];

            }

            if (!activeBotClientInterface) {

                 return null;
            }

            if (requireConnected && (!activeBotClientInterface.bot || !activeBotClientInterface.bot.getReadyState())) {

                return null;
            }

            return activeBotClientInterface.bot;
        }

        _updatePlayerDropdown() {
            if (!this._ui.playerDropdown) return;

            if (this._botManagerInstance) {
                 this._botManagerInstance.children.forEach(botInterface => {
                      if (botInterface.bot) {
                           this._addBot(botInterface.bot);
                      }
                 });

                 const managedBotInstances = new Set(this._botManagerInstance.children.map(bi => bi.bot).filter(b => b !== undefined));
                 this._bots.filter(bot => !managedBotInstances.has(bot)).forEach(botToRemove => this._removeBot(botToRemove));
            }

            const anyConnectedBot = this._bots.find(bot => bot.getReadyState());
            const players = anyConnectedBot?.room?.players || [];

            const myBotIds = new Set(this._bots.map(b => b.id));
            const humanPlayers = players.filter(p => !myBotIds.has(p.id) && p.id !== 0 && p.name && p.id !== undefined);

            const currentSelection = this._ui.playerDropdown.value;
            this._ui.playerDropdown.innerHTML = "";

             if (humanPlayers.length === 0) {
                 this._ui.playerDropdown.appendChild(domMake.Tree("option", { value: "" }, ["No hay jugadores"]));
                 this._ui.playerDropdown.disabled = true;
                 this._ui.followButton.disabled = true;
                 if (this._followTarget.id !== null && !humanPlayers.some(p => String(p.id) === currentSelection)) {

                     this._toggleSmartFollow();
                 }
                 return;
             }

            this._ui.playerDropdown.disabled = false;
            this._ui.followButton.disabled = false;

            humanPlayers.forEach(p => {
                const option = domMake.Tree("option", { value: p.id }, [p.name]);
                this._ui.playerDropdown.appendChild(option);
            });

            if (humanPlayers.some(p => String(p.id) === currentSelection)) {
                this._ui.playerDropdown.value = currentSelection;
            } else {
                 this._ui.playerDropdown.selectedIndex = 0;
                 if (this._followTarget.id !== null && !humanPlayers.some(p => String(p.id) === currentSelection)) {

                     this._toggleSmartFollow();
                 }
            }

             if (this._activeToggles.naturalMovement && this._followTarget.id === null) {
                  this._toggleNaturalMovement(true);
             }
        }

        _toggleNaturalMovement(isActive) {
            this._activeToggles.naturalMovement = isActive;

             if (isActive && this._followTarget.id !== null) {

                 this._toggleSmartFollow();
             }

            const connectedBots = this._bots.filter(bot => bot.getReadyState());
            const canRun = isActive && connectedBots.length > 0 && this._followTarget.id === null;

            if (canRun && !this._naturalMovementInterval) {

                this._naturalMovementInterval = setInterval(this._executeNaturalMovement, 3000 + Math.random() * 2000);
                this._executeNaturalMovement();
            } else if (!canRun && this._naturalMovementInterval) {

                clearInterval(this._naturalMovementInterval);
                this._naturalMovementInterval = null;
            } else if (isActive && connectedBots.length === 0) {

                 if(this._ui.naturalMovementToggleCheckbox) this._ui.naturalMovementToggleCheckbox.checked = false;
                 this._activeToggles.naturalMovement = false;
            }
        }

        _executeNaturalMovement() {
            if (this._followTarget.id || !this._activeToggles.naturalMovement || !this._gameCanvas) return;

             const connectedBots = this._bots.filter(bot => bot.getReadyState());
             if (connectedBots.length === 0) {

                  this._toggleNaturalMovement(false);
                  return;
             }

            const centerX = 50;
            const centerY = 50;
            const driftAmount = 15;

            connectedBots.forEach(bot => {
                 if (bot.attributes?.spawned === false) {
                      this.notify("debug", `Bot ${bot.name} no generado para movimiento natural. Intentando generar.`);
                      bot.emit("spawnavatar");
                      return;
                 }

                let targetX = centerX + (Math.random() - 0.5) * driftAmount;
                let targetY = centerY + (Math.random() - 0.5) * driftAmount;

                targetX = Math.max(5, Math.min(95, targetX));
                targetY = Math.max(5, Math.min(95, targetY));

                this._moveBotSmoothly(bot, targetX, targetY);
            });
        }

        _toggleSmartFollow() {
            const targetIdString = this._ui.playerDropdown.value;
            const targetId = parseInt(targetIdString);
            const targetName = this._ui.playerDropdown.querySelector(`option[value="${targetIdString}"]`)?.textContent || 'jugador desconocido';

            const anyConnectedBot = this._bots.find(bot => bot.getReadyState());
            const currentPlayerList = anyConnectedBot?.room?.players || [];
            const targetPlayerExists = currentPlayerList.some(p => String(p.id) === targetIdString);

             if (isNaN(targetId) || !targetPlayerExists) {

                 return;
             }

            if (String(this._followTarget.id) === targetIdString) {
                clearInterval(this._followTarget.interval);
                this._followTarget = { id: null, interval: null };
                this._ui.followButton.textContent = "Seguir";
                this._ui.followButton.classList.remove("active");

                if (this._activeToggles.naturalMovement) {
                    this._toggleNaturalMovement(true);
                }
            } else {
                const connectedBots = this._bots.filter(b => b.getReadyState());
                if (connectedBots.length === 0) {

                     return;
                }

                if (this._followTarget.interval) {
                    clearInterval(this._followTarget.interval);
                }
                if (this._naturalMovementInterval) {
                    this._toggleNaturalMovement(false);
                    if(this._ui.naturalMovementToggleCheckbox) this._ui.naturalMovementToggleCheckbox.checked = false;
                }

                this._followTarget.id = targetId;
                this._ui.followButton.textContent = `Siguiendo: ${targetName}`;
                this._ui.followButton.classList.add("active");


                this._followTarget.interval = setInterval(this._followLogic, 500);
                this._followLogic();
            }
        }

        _followLogic() {
            if (this._followTarget.id === null || !this._gameCanvas) return;

            const connectedBots = this._bots.filter(bot => bot.getReadyState());
            if (connectedBots.length === 0) {

                  this._toggleSmartFollow();
                  return;
             }

            const targetPlayerElement = document.querySelector(`.spawnedavatar[data-playerid="${this._followTarget.id}"]`);

            if (!targetPlayerElement) {

                this._toggleSmartFollow();
                return;
            }

            const canvasRect = this._gameCanvas.getBoundingClientRect();
            const avatarRect = targetPlayerElement.getBoundingClientRect();

            let targetXGameCoords = ((avatarRect.left + (avatarRect.width / 2) - canvasRect.left) / canvasRect.width) * 100;
            let targetYGameCoords = ((avatarRect.top + (avatarRect.height / 2) - canvasRect.top) / canvasRect.height) * 100;

            targetXGameCoords = Math.max(0, Math.min(100, targetXGameCoords));
            targetYGameCoords = Math.max(0, Math.min(100, targetYGameCoords));

            connectedBots.forEach((bot, index) => {
                 if (bot.attributes?.spawned === false) {
                      this.notify("debug", `Bot ${bot.name} no generado para seguir. Intentando generar.`);
                      bot.emit("spawnavatar");
                      return;
                 }

                const offsetDistance = 10 + index * 5;
                const offsetAngle = (index * 1.5 + Math.random()) * Math.PI * 2 / connectedBots.length;

                const offsetX = offsetDistance * Math.cos(offsetAngle);
                const offsetY = offsetDistance * Math.sin(offsetAngle);

                let moveX = targetXGameCoords + offsetX;
                let moveY = targetYGameCoords + offsetY;

                moveX = Math.max(5, Math.min(95, moveX));
                moveY = Math.max(5, Math.min(95, moveY));

                this._moveBotSmoothly(bot, moveX, moveY);
            });
        }

        _moveBotSmoothly(bot, targetX, targetY) {
            if (!bot || !bot.getReadyState() || typeof bot.emit !== 'function' || bot.attributes?.spawned === false) {
                this.notify("debug", `Saltando movimiento suave para bot ${bot?.name || 'Desconocido'}: No está listo o no ha sido generado.`);
                return;
            }

             bot._lastCommandedX = bot._lastCommandedX ?? 50;
             bot._lastCommandedY = bot._lastCommandedY ?? 50;
             const currentX = bot._lastCommandedX;
             const currentY = bot._lastCommandedY;

             const steps = 10;
             const stepDelay = 50;
             for (let i = 1; i <= steps; i++) {
                 setTimeout(() => {
                     if (bot.getReadyState()) {
                        const interX = currentX + (targetX - currentX) * (i / steps);
                        const interY = currentY + (targetY - currentY) * (i / steps);
                         bot.emit("moveavatar", interX, interY);
                         bot._lastCommandedX = interX;
                         bot._lastCommandedY = interY;
                     }
                 }, i * stepDelay);
             }
        }

        _canBotChat(bot) {
            const now = Date.now();
            const lastChat = this._chatCooldown.get(bot.id) || 0;
            const canChat = this._activeToggles.reactiveChat && (now - lastChat > 7000);
            if (canChat) {
                 this._chatCooldown.set(bot.id, now);
            }
            return canChat;
        }

         _sendBotChat(bot, message) {
             if (bot && bot.getReadyState() && this._activeToggles.reactiveChat) {
                 setTimeout(() => {
                      if (bot.getReadyState() && this._activeToggles.reactiveChat) {
                          bot.emit("chatmsg", message);
                          this.notify("log", `${bot.name} (${this._ui.personalitySelect.value}): "${message}"`);
                      }
                 }, 500 + Math.random() * 500);
             }
         }

         _canBotGesture(bot) {
             const now = Date.now();
             const lastGesture = this._gestureCooldown.get(bot.id) || 0;
             const canGesture = this._activeToggles.smartGestures && (now - lastGesture > 500);
             if (canGesture) {
                 this._gestureCooldown.set(bot.id, now);
             }
             return canGesture;
         }

         _sendBotGesture(bot, gestureId) {
             if (gestureId === undefined || gestureId === null) {
                 this.notify("debug", `Saltando gesto: ID inválido para bot ${bot.name}.`);
                 return;
             }

             if (bot && bot.getReadyState() && this._activeToggles.smartGestures && this._canBotGesture(bot)) {
                 setTimeout(() => {
                     if (bot.getReadyState() && this._activeToggles.smartGestures) {
                          bot.emit("sendgesture", gestureId);
                          this.notify("log", `${bot.name} usó el gesto ${gestureId}.`);
                     }
                 }, 100 + Math.random() * 200);
             }
         }

        _getMessageLanguage(message) {
            const lowerCaseMsg = message.toLowerCase();

            const spanishKeywords = [
                'hola', 'buenas', 'que', 'qué', 'tal', 'como', 'cómo', 'estás', 'estas', 'estoy', 'bien', 'mal', 'sí', 'si', 'no', 'por favor', 'gracias', 'adiós', 'chao',
                'tú', 'vos', 'usted', 'nosotros', 'ustedes', 'ellos', 'ellas', 'mi', 'mí', 'tu', 'su', 'nuestro', 'vuestro', 'suya',
                'es', 'son', 'está', 'están', 'ser', 'estar', 'tener', 'hacer', 'ir', 'ver', 'decir', 'poder', 'saber', 'querer', 'hay',
                'un', 'una', 'unos', 'unas', 'el', 'la', 'los', 'las',
                'y', 'o', 'pero', 'mas', 'más', 'también', 'aún', 'así', 'solo', 'mucho', 'poco', 'nada', 'siempre', 'nunca', 'quizás', 'tal vez', 'claro', 'verdad',
                'ahora', 'después', 'antes', 'hoy', 'mañana', 'ayer', 'aquí', 'allí', 'donde', 'dónde', 'cuando', 'cuándo', 'cómo', 'cuánto',
                'jaja', 'jeje', 'xd', 'lol', 'emoji', 'dibujo', 'dibujar', 'jugador', 'adivinar', 'palabra', 'ronda', 'juego', 'ganar', 'perder', 'score', 'puntos',
                'genial', 'excelente', 'increíble', 'bonito', 'lindo', 'feo', 'divertido', 'aburrido', 'perfecto', 'error', 'ayuda', 'ayúdame',
                'amigo', 'amiga', 'gente', 'persona', 'puedes', 'quieres', 'sabes', 'entiendes', 'entiendo', 'no sé', 'no entiendo', 'por qué', 'porque',
                'listo', 'lista', 'empezar', 'terminar', 'rápido', 'lento', 'espera', 'esperar', 'ya', 'ya voy', 'llegar', 'llegué', 'fuiste', 'saludos', 'bienvenido', 'bienvenida',
                'jugar', 'ganaste', 'perdiste', 'buen', 'mala', 'mal', 'muy', 'gracioso', 'divertida', 'pista', 'pistas', 'qué tal', 'qué onda', 'a ver', 'dale', 'vamos', 'va',
                'eso', 'esto', 'aquello', 'acá', 'allá', 'dentro', 'fuera', 'arriba', 'abajo', 'cerca', 'lejos', 'antes', 'después', 'durante', 'mientras', 'entonces', 'luego', 'así que',
                'para', 'con', 'sin', 'sobre', 'bajo', 'entre', 'hacia', 'hasta', 'desde', 'según', 'contra', 'tras', 'mediante', 'durante',
                'yo', 'tú', 'él', 'ella', 'usted', 'nosotros', 'vosotros', 'ustedes', 'ellos', 'ellas', 'me', 'te', 'se', 'lo', 'la', 'le', 'les', 'nos', 'os',
                'mi', 'tu', 'su', 'nuestro', 'vuestro', 'sus', 'mis', 'tus', 'sus', 'nuestros', 'vuestros',
                'este', 'esta', 'estos', 'estas', 'ese', 'esa', 'esos', 'esas', 'aquel', 'aquella', 'aquellos', 'aquellas',
                'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve', 'diez',
                'cero', 'cien', 'mil', 'millón', 'primero', 'segundo', 'tercero', 'último'
            ];


            const englishKeywords = [
                'hi', 'hello', 'hey', 'what', 'how', 'are', 'you', 'i', 'am', 'fine', 'good', 'bad', 'yes', 'no', 'please', 'thank', 'thanks', 'bye', 'goodbye', 'see', 'ya',
                'me', 'my', 'your', 'his', 'her', 'its', 'our', 'their', 'we', 'us', 'they', 'them',
                'is', 'are', 'am', 'be', 'been', 'was', 'were', 'have', 'has', 'had', 'do', 'does', 'did', 'make', 'go', 'see', 'say', 'can', 'could', 'would', 'should', 'will', 'must',
                'a', 'an', 'the',
                'and', 'or', 'but', 'also', 'too', 'still', 'even', 'so', 'then', 'as', 'if', 'when', 'where', 'why', 'how', 'much', 'many', 'little', 'none', 'always', 'never', 'maybe', 'perhaps', 'of course', 'true', 'really',
                'now', 'later', 'before', 'after', 'today', 'tomorrow', 'yesterday', 'here', 'there', 'where', 'when', 'how', 'why',
                'lol', 'lmao', 'rofl', 'xd', 'emoji', 'draw', 'drawing', 'drawer', 'player', 'guess', 'word', 'round', 'game', 'win', 'lose', 'score', 'points', 'xp', 'level',
                'great', 'excellent', 'amazing', 'beautiful', 'ugly', 'funny', 'boring', 'perfect', 'error', 'help', 'help me', 'bug',
                'friend', 'people', 'person', 'can you', 'do you want', 'do you know', 'understand', 'i know', 'i dont know', 'i dont understand', 'why', 'because',
                'ready', 'start', 'end', 'finish', 'quick', 'slow', 'wait', 'waiting', 'already', 'almost', 'coming', 'arrived', 'left', 'welcome',
                'play', 'won', 'lost', 'nice', 'bad', 'very', 'clue', 'hint', 'whatsup', 'whats up', 'go ahead', 'come on',
                'this', 'that', 'these', 'those', 'it', 'them', 'they', 'here', 'there', 'inside', 'outside', 'up', 'down', 'near', 'far',
                'for', 'with', 'without', 'on', 'under', 'between', 'towards', 'until', 'from', 'according', 'against', 'behind', 'through', 'during',
                'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
                'zero', 'hundred', 'thousand', 'million', 'first', 'second', 'third', 'last'
            ];


            let spanishMatches = spanishKeywords.filter(k => lowerCaseMsg.includes(k)).length;
            let englishMatches = englishKeywords.filter(k => lowerCaseMsg.includes(k)).length;

            if (spanishMatches > englishMatches && spanishMatches > 0) return 'spanish';
            if (englishMatches > spanishMatches && englishMatches > 0) return 'english';
            return 'neutral';
        }

        _handleReactiveChat(bot, msg) {
            if (msg.id === bot.id || msg.id === 0 || !this._activeToggles.reactiveChat) return;

            const personality = this._personalities[this._ui.personalitySelect.value];
            const lowerCaseMsg = msg.message.toLowerCase().trim();
            const lang = this._getMessageLanguage(msg.message);

            let responseType = null;
            let responseMessage = null;

            if (/\b(hola|buenas|hey|hi|hello)\b/.test(lowerCaseMsg)) {
                responseType = 'greeting';
                responseMessage = lang === 'english' ? personality.english_greetings : personality.spanish_greetings;
            } else if (/\b(si|yes|ok|claro|yeah)\b/.test(lowerCaseMsg)) {
                responseType = 'acknowledgement';
                responseMessage = lang === 'english' ? personality.english_acknowledgements : personality.spanish_acknowledgements;
            } else if (/\b(como\sestas|y\stu|how\sare\syou|what's\sup|what)\b/.test(lowerCaseMsg)) {
                responseType = 'question';
                responseMessage = lang === 'english' ? personality.english_questions : personality.spanish_questions;
            } else if (/\b(xd|lol|jaja|haha|omg)\b/.test(lowerCaseMsg)) {
                responseType = 'laughter';
                responseMessage = lang === 'english' ? personality.english_laughter : personality.spanish_laughter;
            } else if (/\b(que|but|well|pero|bueno)\b/.test(lowerCaseMsg)) {
                responseType = 'general';
                responseMessage = lang === 'english' ? personality.english_general : personality.spanish_general;
            } else if (/\b(lindo|hermoso|dibujas\sbien|buen\sdibujo|buen\strabajo|good\sjob|nice\sdraw)\b/.test(lowerCaseMsg)) {
                responseType = 'goodjob_drawing';
                responseMessage = lang === 'english' ? [`Thanks, {player}!`, `Glad you liked it, {player}!`] : [`Gracias, {player}!`, `Me alegro que te guste, {player}!`];
            }

            if (responseType && responseMessage) {
                const selectedResponse = responseMessage[Math.floor(Math.random() * responseMessage.length)];
                if (this._canBotChat(bot)) {
                    this._sendBotChat(bot, selectedResponse.replace("{player}", msg.name));
                }

                if (personality.gestures[responseType]) {
                    this._sendBotGesture(bot, personality.gestures[responseType]);
                }
            }
        }

        _handleCorrectGuess(data) {
            const player = { id: data[0], name: data[1] };
            if (this._bots.some(b => b.id === player.id)) return;

            const personality = this._personalities[this._ui.personalitySelect.value];
            const response = personality.spanish_congrats[Math.floor(Math.random() * personality.spanish_congrats.length)].replace("{player}", player.name);

            this._bots.forEach((bot) => {
                if (bot.getReadyState()) {
                     if (this._activeToggles.reactiveChat && Math.random() < 0.7 && this._canBotChat(bot)) {
                         this._sendBotChat(bot, response);
                     }
                    if (this._activeToggles.smartGestures && personality.gestures.congrats) {
                         this._sendBotGesture(bot, personality.gestures.congrats);
                    }
                }
            });
        }

         _handlePlayerJoin(player) {
             if (this._bots.some(b => b.id === player.id) || player.id === 0) return;

             const personality = this._personalities[this._ui.personalitySelect.value];
             const response = personality.spanish_playerJoin[Math.floor(Math.random() * personality.spanish_playerJoin.length)].replace("{player}", player.name);

             this._bots.forEach((bot) => {
                 if (bot.getReadyState()) {
                      if (this._activeToggles.reactiveChat && Math.random() < 0.6 && this._canBotChat(bot)) {
                           this._sendBotChat(bot, response);
                      }
                      if (this._activeToggles.smartGestures && personality.gestures.playerJoin) {
                          this._sendBotGesture(bot, personality.gestures.playerJoin);
                      }
                 }
             });
         }

         _handlePlayerLeave(player) {
             if (this._bots.some(b => b.id === player.id) || player.id === 0) return;

              if (this._followTarget.id === player.id) {

                  this._toggleSmartFollow();
              }

             const personality = this._personalities[this._ui.personalitySelect.value];
             const response = personality.spanish_playerLeave[Math.floor(Math.random() * personality.spanish_playerLeave.length)].replace("{player}", player.name);

             this._bots.forEach((bot) => {
                 if (bot.getReadyState()) {
                      if (this._activeToggles.reactiveChat && Math.random() < 0.5 && this._canBotChat(bot)) {
                              this._sendBotChat(bot, response);
                          }
                      if (this._activeToggles.smartGestures && personality.gestures.playerLeave) {
                           this._sendBotGesture(bot, personality.gestures.playerLeave);
                      }
                 }
             });
         }

         _handleTurnEnd(botInstance, data) {
             if (!this._activeToggles.reactiveChat) return;


             if (!this._bots.some(b => b.id === botInstance.id)) return;

             if (Math.random() < 0.3 && this._canBotChat(botInstance)) {
                  const generalEndMessages = ["Turno terminado.", "Bien jugado.", "A ver qué sigue."];
                  const message = generalEndMessages[Math.floor(Math.random() * generalEndMessages.length)];
                  this._sendBotChat(botInstance, message);
             }
         }

         _handleTurnBeginDraw(botInstance, data) {
             const drawingPlayerId = data[0];
             const drawingPlayerName = botInstance.room?.players?.find(p => p.id === drawingPlayerId)?.name || 'alguien';

             if (this._bots.some(b => b.id === drawingPlayerId)) {
                 return;
             }

             const personality = this._personalities[this._ui.personalitySelect.value];
             if (this._activeToggles.smartGestures && personality.gestures.drawing) {
                 this._bots.forEach(bot => {
                     this._sendBotGesture(bot, personality.gestures.drawing);
                 });
             }
             if (this._activeToggles.reactiveChat && Math.random() < 0.4 && this._canBotChat(botInstance)) {
                 const chatMessage = `¡Buena suerte, ${drawingPlayerName}!`;
                 this._sendBotChat(botInstance, chatMessage);
             }
         }

         _handleWordSelected(botInstance, data) {
             const drawingPlayerElement = document.querySelector('.playerlist-row[data-turn="true"]');
             const drawingPlayerId = drawingPlayerElement ? parseInt(drawingPlayerElement.dataset.playerid) : null;

             if (this._bots.some(b => b.id === drawingPlayerId)) {
                 return;
             }

             const personality = this._personalities[this._ui.personalitySelect.value];
             if (this._activeToggles.smartGestures && personality.gestures.acknowledgement) {
                 this._bots.forEach(bot => {
                     this._sendBotGesture(bot, personality.gestures.acknowledgement);
                 });
             }
         }


        _updateMessageList() {
            const newMessages = this._ui.messageTextarea.value.split('\n').filter(msg => msg.trim() !== '');
            if (newMessages.length > 0) {
                this._messageList = newMessages;
                this.notify("success", `Lista de mensajes actualizada con ${this._messageList.length} mensajes.`);
            } else {

            }
        }

        _toggleBadGirlSpam() {
            this._activeToggles.badGirlSpam = !this._activeToggles.badGirlSpam;

            if (this._spamInterval) {

                clearInterval(this._spamInterval);
                this._spamInterval = null;
                this._ui.badGirlToggleButton.classList.remove("active");
                this._ui.badGirlToggleButton.innerHTML = '<i class="fas fa-play-circle"></i> Iniciar Spam';

            } else {

                const bot = this._getBot();
                if (!bot) {

                    this._activeToggles.badGirlSpam = false;
                    return;
                }

                if (this._messageList.length === 0) {

                    this._activeToggles.badGirlSpam = false;
                    return;
                }

                this._ui.badGirlToggleButton.classList.add("active");
                this._ui.badGirlToggleButton.innerHTML = '<i class="fas fa-stop-circle"></i> Detener Spam';


                this._spamInterval = setInterval(() => {
                    const currentBot = this._getBot();
                    if (!currentBot) {

                        this._toggleBadGirlSpam();
                        return;
                    }

                    const randomMessage = this._messageList[Math.floor(Math.random() * this._messageList.length)];
                    currentBot.emit("chatmsg", randomMessage);

                }, this._intervalTime);
            }
        }
    }
})("QBit");





(function IntelligentSwarmModule() {
    const QBit = globalThis[arguments[0]];


    const _personalities = {
        Amigable: {
            spanish_greetings: ["¡Hola a todos!", "¡Buenas!", "¿Qué tal?", "Hey! Un gusto estar aquí 😊"],
            spanish_acknowledgements: ["Si!", "Claro!", "Entendido!", "Asi es!"],
            spanish_questions: ["Como estás?", "Y tu?", "¿Que tal?"],
            spanish_laughter: ["XD", "Jaja", "LOL"],
            spanish_general: ["Que?", "Bueno...", "Pero..."],
            spanish_congrats: ["¡Bien hecho, {player}!", "¡Excelente!", "¡Esa era!", "Felicidades, {player}!"],
            spanish_playerJoin: ["¡Bienvenido, {player}!", "Hola {player}!", "Mira quién llegó, {player} 👋"],
            spanish_playerLeave: ["Adiós, {player}!", "{player} se fue 😔", "Chao {player}"],

            english_greetings: ["Hi!", "Hello!", "Hey there!", "Nice to see you 😊"],
            english_acknowledgements: ["Yes!", "Got it!", "Right!"],
            english_questions: ["How are you?", "And you?", "What's up?"],
            english_laughter: ["LOL", "Haha", "XD", "Omg!"],
            english_general: ["What?", "Well...", "But..."],
            english_congrats: ["Good job, {player}!", "Excellent!", "That was it!", "Congrats, {player}!"],
            english_farewell: ["Bye!", "See ya!", "Later!", "So long 👋"],
            english_playerJoin: ["Welcome, {player}!", "Hi {player}!", "Look who's here, {player} 👋"],
            english_playerLeave: ["Bye, {player}!", "{player} left 😔", "See ya {player}"],

            gestures: {
                greeting: 5,
                acknowledgement: 11,
                question: 10,
                laughter: 7,
                general: 17,
                congrats: 19,
                playerJoin: 5,
                playerLeave: 3,
                drawing: 4,
                goodjob_drawing: 0
            }
        },
        Competitivo: {
            spanish_greetings: ["He llegado.", "Prepárense para dibujar.", "A ver quién gana."],
            spanish_acknowledgements: ["Si.", "Ok.", "Correcto."],
            spanish_questions: ["¿Estás listo?", "Quién sigue?", "¿Qué dibujas?"],
            spanish_laughter: ["Jaja.", "Easy."],
            spanish_general: ["..."],
            spanish_congrats: ["Nada mal, {player}.", "Correcto.", "Uno menos.", "Ok, adivinaste."],
            spanish_farewell: ["Me retiro.", "Suficiente por hoy.", "GG."],
            spanish_playerJoin: ["Otro rival...", "Llegó {player}...", "Hola {player}."],
            spanish_playerLeave: ["Uno menos.", "{player} se fue.", "OK, {player}."],

            english_greetings: ["I'm here.", "Get ready to draw.", "Who's next?"],
            english_acknowledgements: ["Yes.", "Ok.", "Correct."],
            english_questions: ["You ready?", "Who's drawing?", "What is it?"],
            english_laughter: ["Haha.", "Easy."],
            english_general: ["..."],
            english_congrats: ["Not bad, {player}.", "Correct.", "One less.", "Okay, you got it."],
            english_farewell: ["I'm out.", "Enough for today.", "GG."],
            english_playerJoin: ["Another rival...", "{player} arrived...", "Hi {player}."],
            english_playerLeave: ["One less.", "{player} left.", "Okay {player}."],

            gestures: {
                greeting: 1,
                acknowledgement: 12,
                question: 10,
                laughter: 6,
                general: 16,
                congrats: 0,
                playerJoin: 13,
                playerLeave: 3,
                drawing: 12,
                goodjob_drawing: 0
            }
        },
        Neutral: {
            spanish_greetings: ["Hola.", "Saludos."],
            spanish_acknowledgements: ["Si.", "Ok."],
            spanish_questions: ["?", "Cómo?"],
            spanish_laughter: ["Jeje."],
            spanish_general: ["..."],
            spanish_congrats: ["Bien, {player}.", "Correct."],
            spanish_farewell: ["Adiós."],
            spanish_playerJoin: ["{player} se unió."],
            spanish_playerLeave: ["{player} se fue."],

            english_greetings: ["Hi.", "Greetings."],
            english_acknowledgements: ["Yes.", "Ok."],
            english_questions: ["?", "How?"],
            english_laughter: ["Hehe."],
            english_general: ["..."],
            english_congrats: ["Good, {player}.", "Correct."],
            english_farewell: ["Bye."],
            english_playerJoin: ["{player} joined."],
            english_playerLeave: ["{player} left."],

            gestures: {
                greeting: 11,
                acknowledgement: 11,
                question: 10,
                laughter: 5,
                general: 17,
                congrats: 11,
                playerJoin: 11,
                playerLeave: 11,
                drawing: 8,
                goodjob_drawing: 11
            }
        }
    };


    function createToggle(labelText, callback, initialChecked = false) {
        const row = domMake.Row();
        row.style.alignItems = 'center';
        row.style.justifyContent = 'space-between';

        const labelSpan = domMake.Tree("span", {}, [labelText]);

        const checkboxId = "intelligent-swarm-toggle-" + (Math.random() * 1e9 | 0);
        const checkbox = domMake.Tree("input", { type: "checkbox", id: checkboxId, hidden: true });
        if (initialChecked) {
            checkbox.checked = true;
        }

        const indicatorLabel = domMake.Tree("label", {
            for: checkboxId,
            class: "icon",
            style: `
                width: 24px;
                height: 24px;
                min-width: unset;
                min-height: unset;
                border: 1px solid var(--CE-color);
                border-radius: .25rem;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: background-color 0.2s ease, border-color 0.2s ease;
                background-color: ${initialChecked ? 'var(--info)' : 'var(--secondary)'};
            `
        });

        indicatorLabel.innerHTML = initialChecked
            ? '<i class="fas fa-check-square" style="font-size: 1.2em; color: var(--success);"></i>'
            : '<i class="fas fa-square" style="font-size: 1.2em;"></i>';

        checkbox.addEventListener('change', (event) => {
            const checked = event.target.checked;
            if (checked) {
                indicatorLabel.innerHTML = '<i class="fas fa-check-square" style="font-size: 1.2em; color: var(--success);"></i>';
                indicatorLabel.style.backgroundColor = 'var(--info)';
            } else {
                indicatorLabel.innerHTML = '<i class="fas fa-square" style="font-size: 1.2em;"></i>';
                indicatorLabel.style.backgroundColor = 'var(--secondary)';
            }
            if (callback && typeof callback === 'function') {
                callback(checked);
            }
        });

        row.appendAll(labelSpan, domMake.Tree("div", {style: "flex-shrink: 0;"}, [checkbox, indicatorLabel]));
        return row;
    }

    QBit.Styles.addRules([
        `#${QBit.identifier} .intelligent-swarm-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .intelligent-swarm-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .intelligent-swarm-control-group {
            display: flex;
            gap: 10px;
            margin-top: 8px;
            align-items: center;
        }`,
        `#${QBit.identifier} .intelligent-swarm-control-group > select,
         #${QBit.identifier} .intelligent-swarm-control-group > input[type="number"],
         #${QBit.identifier} .intelligent-swarm-control-group > button {
            flex-grow: 1;
            padding: 5px;
            box-sizing: border-box;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
        }`,
        `#${QBit.identifier} .intelligent-swarm-control-group label {
             flex-shrink: 0;
             margin-right: 5px;
        }`,
        `#${QBit.identifier} .intelligent-swarm-follow-button.active {
             background-color: var(--warning);
             color: white;
        }`,
        `#${QBit.identifier} .intelligent-swarm-interaction-toggle.active {
             background-color: var(--info);
             color: white;
        }`,
        `#${QBit.identifier} .bot-group-card {
            border: 1px dashed var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 5px;
            background-color: rgba(0,0,0,0.05);
            display: flex;
            flex-direction: column;
            gap: 5px;
        }`,
        `#${QBit.identifier} .bot-group-card summary {
            font-weight: bold;
            color: var(--dark-blue-title);
            cursor: pointer;
            padding: 2px 0;
        }`,
        `#${QBit.identifier} .bot-group-card summary::-webkit-details-marker { display: none; }`,
        `#${QBit.identifier} .bot-group-card summary::before { content: '► '; color: var(--info); }`,
        `#${QBit.identifier} .bot-group-card details[open] > summary::before { content: '▼ '; }`,
        `#${QBit.identifier} .group-content-area {
            display: flex;
            flex-direction: column;
            gap: 5px;
            padding-top: 5px;
        }`,
        `#${QBit.identifier} .group-row { display: flex; gap: 5px; align-items: center; }`,
        `#${QBit.identifier} .group-row input[type="text"],
         #${QBit.identifier} .group-row input[type="number"] {
            flex: 1; min-width: 70px; padding: 5px; box-sizing: border-box;
            border: 1px solid var(--CE-color); border-radius: .25rem;
            background-color: var(--CE-bg_color); color: var(--CE-color); text-align: center;
        }`,
        `#${QBit.identifier} .group-row label {
            flex-shrink: 0; font-size: 0.9em; font-weight: bold;
            color: var(--CE-color); white-space: nowrap;
        }`,
        `#${QBit.identifier} .group-actions {
            display: flex; gap: 5px; justify-content: space-between; flex-wrap: wrap;
        }`,
        `#${QBit.identifier} .group-actions .btn {
            flex: 1 1 auto; min-width: 80px; padding: 5px;
            font-size: 0.8em; height: auto;
        }`,
        `#${QBit.identifier} .group-actions .btn i { margin-right: 5px; }`
    ]);

    class IntelligentSwarm extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        _bots = [];
        _botManagerInstance = null;
        _gameCanvas = null;
        _maxTotalBots = 10;

        _activeToggles = {
            naturalMovement: false, reactiveChat: false, smartGestures: false,
            botToBotChat: false, botToBotGestures: false, botConversation: false,
            autoSendGroups: false
        };

        _followTarget = { id: null, interval: null, name: null };
        _naturalMovementInterval = null;

        _chatCooldown = new Map();
        _gestureCooldown = new Map();
        _selectedPersonality = "Amigable";

        _conversationInterval = null;
        _conversationBots = [];
        _currentSpeakerIndex = -1;
        _currentConversationTopic = null;
        _currentTopicPhraseIndex = -1;
        _conversationTopicsData = {
             Juegos: ["¿Has jugado a algo interesante últimamente?", "Me encantan los juegos de estrategia, ¿y a ti?", "El último parche de mi juego favorito fue genial.", "¿Prefieres juegos online o para un solo jugador?", "A veces me pierdo en mundos virtuales por horas."],
             Playa: ["Nada como la brisa marina para relajarse.", "Me gustaría teletransportarme a una playa ahora mismo.", "¿Castillos de arena o nadar en el mar?", "El sol en la piel es tan agradable.", "Espero que no haya medusas hoy."],
             Cine: ["¿Viste alguna buena película recientemente?", "Soy más de documentales, la realidad supera la ficción.", "Me encanta la banda sonora de las películas.", "¿Qué género te gusta más en el cine?", "La última de superhéroes fue un poco predecible."],
             Diversion: ["¿Qué haces para divertirte cuando no estás dibujando?", "Me encanta explorar nuevos lugares, incluso virtuales.", "La risa es el mejor pasatiempo, ¿no crees?", "A veces simplemente me gusta observar y aprender.", "¿Hay algo que te apasione hacer?"],
             Exploracion: ["El mundo es tan grande, me gustaría verlo todo.", "Me pregunto qué secretos esconde cada esquina del mapa.", "Explorar lo desconocido siempre es emocionante.", "¿Hay algún lugar remoto que te gustaría visitar?", "Siempre hay algo nuevo por descubrir si prestas atención."],
             General: ["El tiempo está agradable hoy, ¿verdad?", "¿Qué opinas del dibujo actual?", "Es un día interesante.", "Me gusta el ambiente de esta sala.", "A veces me pregunto sobre el significado de todo."]
        };

        _botGroupsData = [[], [], []];
        _autoSendGroupsInterval = null;
        _autoSendSpeedInput = null;

        _ui = {
            playerDropdown: null, followButton: null, naturalMovementToggleCheckbox: null,
            reactiveChatToggleCheckbox: null, smartGesturesToggleCheckbox: null, botToBotChatToggleCheckbox: null,
            botToBotGesturesToggleCheckbox: null, botConversationToggleCheckbox: null, personalitySelect: null,
            movementSpeedInput: null, chatCooldownInput: null, gestureCooldownInput: null,
            groupInputs: [], autoSendGroupsToggle: null, autoSendSpeedInput: null
        };

        _handlePlayerListChange = this._handlePlayerListChange.bind(this);
        _handleBotClientManagerChange = this._handleBotClientManagerChange.bind(this);
        _toggleNaturalMovement = this._toggleNaturalMovement.bind(this);
        _toggleSmartFollow = this._toggleSmartFollow.bind(this);
        _executeNaturalMovement = this._executeNaturalMovement.bind(this);
        _followLogic = this._followLogic.bind(this);
        _moveBotSmoothly = this._moveBotSmoothly.bind(this);
        _handleChatMessage = this._handleChatMessage.bind(this);
        _handleCorrectGuess = this._handleCorrectGuess.bind(this);
        _handlePlayerJoin = this._handlePlayerJoin.bind(this);
        _handlePlayerLeave = this._handlePlayerLeave.bind(this);
        _handleTurnBeginDraw = this._handleTurnBeginDraw.bind(this);
        _handleWordSelected = this._handleWordSelected.bind(this);
        _onPersonalityChange = this._onPersonalityChange.bind(this);
        _toggleBotConversation = this._toggleBotConversation.bind(this);
        _startNewConversationTopic = this._startNewConversationTopic.bind(this);
        _continueConversation = this._continueConversation.bind(this);
        _joinBotsToGroup = this._joinBotsToGroup.bind(this);
        _sendGroupMessage = this._sendGroupMessage.bind(this);
        _handleGroupAvatarUpload = this._handleGroupAvatarUpload.bind(this);
        _toggleAutoSendGroups = this._toggleAutoSendGroups.bind(this);
        _performAutoSendGroups = this._performAutoSendGroups.bind(this);
        _disconnectGroupBots = this._disconnectGroupBots.bind(this);
        _confirmGroupBaseName = this._confirmGroupBaseName.bind(this);
        _spawnGroupAvatars = this._spawnGroupAvatars.bind(this);

        constructor() {
            super("🧠Bots Group Control", '<i class="fas fa-brain"></i>');
            this._gameCanvas = document.getElementById('canvas');
            this._loadInterface();
            this._initializeObservers();

        }

        _loadInterface() {
            const container = domMake.Tree("div");


            const behaviorSection = domMake.Tree("div", { class: "intelligent-swarm-section" });
            behaviorSection.appendChild(domMake.Tree("div", { class: "intelligent-swarm-section-title" }, ["Comportamiento General"]));

            const personalityGroup = domMake.Tree("div", { class: "intelligent-swarm-control-group" });
            personalityGroup.appendChild(domMake.Tree("label", {}, ["Personalidad:"]));
            this._ui.personalitySelect = domMake.Tree("select", {style: "flex-grow: 1;"});
            Object.keys(_personalities).forEach(p => this._ui.personalitySelect.appendChild(domMake.Tree("option", { value: p }, [p])));
            this._ui.personalitySelect.value = this._selectedPersonality;
            this._ui.personalitySelect.addEventListener("change", this._onPersonalityChange);
            personalityGroup.appendChild(this._ui.personalitySelect);
            behaviorSection.appendChild(personalityGroup);

            const reactiveChatToggle = createToggle("Chat Reactivo (Humanos)", (checked) => this._activeToggles.reactiveChat = checked);
            this._ui.reactiveChatToggleCheckbox = reactiveChatToggle.querySelector('input[type="checkbox"]');
            behaviorSection.appendChild(reactiveChatToggle);

            const smartGesturesToggle = createToggle("Gestos Inteligentes (Humanos)", (checked) => this._activeToggles.smartGestures = checked);
            this._ui.smartGesturesToggleCheckbox = smartGesturesToggle.querySelector('input[type="checkbox"]');
            behaviorSection.appendChild(smartGesturesToggle);

            const botToBotChatToggle = createToggle("Chat entre Bots", (checked) => this._activeToggles.botToBotChat = checked);
            this._ui.botToBotChatToggleCheckbox = botToBotChatToggle.querySelector('input[type="checkbox"]');
            behaviorSection.appendChild(botToBotChatToggle);

            const botToBotGesturesToggle = createToggle("Gestos entre Bots", (checked) => this._activeToggles.botToBotGestures = checked);
            this._ui.botToBotGesturesToggleCheckbox = botToBotGesturesToggle.querySelector('input[type="checkbox"]');
            behaviorSection.appendChild(botToBotGesturesToggle);
            container.appendChild(behaviorSection);


            const movementSection = domMake.Tree("div", { class: "intelligent-swarm-section" });
            movementSection.appendChild(domMake.Tree("div", { class: "intelligent-swarm-section-title" }, ["Control de Movimiento"]));

            const naturalMovementToggleRow = createToggle("Movimiento Natural", this._toggleNaturalMovement);
            this._ui.naturalMovementToggleCheckbox = naturalMovementToggleRow.querySelector('input[type="checkbox"]');
            movementSection.appendChild(naturalMovementToggleRow);

            const followGroup = domMake.Tree("div", { class: "intelligent-swarm-control-group" });
            this._ui.playerDropdown = domMake.Tree("select", {style: "flex-grow: 1;"});
            followGroup.appendChild(this._ui.playerDropdown);
            this._ui.followButton = domMake.Button("Seguir Jugador");
            this._ui.followButton.classList.add("intelligent-swarm-follow-button");
            this._ui.followButton.addEventListener("click", this._toggleSmartFollow);
            followGroup.appendChild(this._ui.followButton);
            movementSection.appendChild(followGroup);

            const movementSpeedGroup = domMake.Tree("div", { class: "intelligent-swarm-control-group" });
            movementSpeedGroup.appendChild(domMake.Tree("label", {}, ["Vel. Movimiento (ms):"]));
            this._ui.movementSpeedInput = domMake.Tree("input", { type: "number", value: "3000", min: "500", max: "10000", step: "100" });
            movementSpeedGroup.appendChild(this._ui.movementSpeedInput);
            movementSpeedGroup.appendChild(domMake.Tree("label", {}, ["Movimiento Suave (ms):"]));
            movementSpeedGroup.appendChild(domMake.Tree("input", { type: "number", value: "300", min: "50", max: "1000", step: "50" }));
            this._ui.movementSpeedInput.addEventListener("change", () => {
                if (this._activeToggles.naturalMovement) {
                    this._toggleNaturalMovement(false);
                    this._toggleNaturalMovement(true);
                }
                if (this._followTarget.interval) {
                    clearInterval(this._followTarget.interval);
                    this._followTarget.interval = setInterval(this._followLogic, (parseInt(this._ui.movementSpeedInput.value) || 3000) / 3);
                }
            });
            movementSection.appendChild(movementSpeedGroup);
            container.appendChild(movementSection);


            const botConversationSection = domMake.Tree("div", { class: "intelligent-swarm-section" });
            botConversationSection.appendChild(domMake.Tree("div", { class: "intelligent-swarm-section-title" }, ["Conversaciones entre Bots"]));
            const botConversationToggle = createToggle("Empezar a hablar y conversar", this._toggleBotConversation);
            this._ui.botConversationToggleCheckbox = botConversationToggle.querySelector('input[type="checkbox"]');
            botConversationSection.appendChild(botConversationToggle);
            container.appendChild(botConversationSection);


            const groupManagementSection = domMake.Tree("div", { class: "intelligent-swarm-section" });
            groupManagementSection.appendChild(domMake.Tree("div", { class: "intelligent-swarm-section-title" }, ["Gestión de Grupos de Bots"]));

            for (let i = 0; i < 3; i++) {
                const groupId = i;
                const avatarFileId = `group${i}AvatarFile`;

                const groupDetails = domMake.Tree("details", { class: "bot-group-card", open: (i === 0) });
                const groupSummary = domMake.Tree("summary", {}, [`Grupo ${i + 1}:`]);
                groupDetails.appendChild(groupSummary);
                const groupInnerContent = domMake.Tree("div", { class: "group-content-area" });
                groupDetails.appendChild(groupInnerContent);

                const baseNameRow = domMake.Row({ class: "group-row" });
                const initialRandomName = this._generateRandomBotName();
                const baseNameInput = domMake.Tree("input", { type: "text", placeholder: `Nombre del grupo`, value: initialRandomName });
                const confirmNameButton = domMake.Button('<i class="fas fa-check"></i>', { title: "Confirmar nombre base para bots" });
                confirmNameButton.addEventListener("click", () => this._confirmGroupBaseName(groupId, baseNameInput.value));
                baseNameRow.appendAll(baseNameInput, confirmNameButton);
                groupInnerContent.appendChild(baseNameRow);

                groupInnerContent.appendChild(domMake.Tree("label", {}, [`Nombres de Bots:`]));
                const botNameInputs = [];
                for (let j = 0; j < 3; j++) {
                    const individualBotNameInput = domMake.Tree("input", { type: "text", placeholder: `Bot ${j + 1} (Grupo ${i + 1})` });
                    botNameInputs.push(individualBotNameInput);
                    groupInnerContent.appendChild(domMake.Row({ class: "group-row" }, [domMake.Tree("label", {}, `Bot ${j + 1}:`), individualBotNameInput]));
                }
                this._ui.groupInputs[i] = { baseNameInput: baseNameInput, nameInputs: botNameInputs };

                const messageInput = domMake.Tree("input", { type: "text", placeholder: `Mensaje Grupo ${i + 1}` });
                const sendButton = domMake.Button('<i class="fas fa-paper-plane"></i> Enviar', { title: "Enviar mensaje del grupo" });
                sendButton.addEventListener("click", () => this._sendGroupMessage(groupId));
                groupInnerContent.appendAll(domMake.Row({ class: "group-row" }, [messageInput, sendButton]));
                this._ui.groupInputs[i].messageInput = messageInput;

                const actionsRow = domMake.Row({ class: "group-actions" });
                const joinBotsButton = domMake.Button('<i class="fas fa-user-plus"></i> Unir Bots (3)', { title: "Unir 3 bots a este grupo" });
                joinBotsButton.addEventListener("click", () => this._joinBotsToGroup(groupId));
                actionsRow.appendChild(joinBotsButton);

                const avatarButtonLabel = domMake.Tree("label", { class: "btn", title: "Subir avatar para bots del grupo", for: avatarFileId });
                avatarButtonLabel.appendAll(domMake.Tree("i", { class: "fas fa-upload" }), domMake.TextNode(" Avatar"));
                const avatarFileInput = domMake.Tree("input", { type: "file", id: avatarFileId, hidden: true, accept: "image/png, image/jpeg" });
                avatarFileInput.addEventListener("change", (e) => this._handleGroupAvatarUpload(groupId, e.target.files[0]));
                groupInnerContent.appendChild(avatarFileInput);
                this._ui.groupInputs[i].avatarFileInput = avatarFileInput;
                actionsRow.appendChild(avatarButtonLabel);

                const spawnGroupAvatarsButton = domMake.Button('<i class="fas fa-user-circle"></i> Spawn (3)', { title: "Spawnear los avatares de los 3 bots de este grupo en el canvas" });
                spawnGroupAvatarsButton.addEventListener("click", () => this._spawnGroupAvatars(groupId));
                actionsRow.appendChild(spawnGroupAvatarsButton);

                const disconnectGroupButton = domMake.Button('<i class="fas fa-user-times"></i> Desconectar (3)', { title: "Desconectar todos los bots de este grupo" });
                disconnectGroupButton.addEventListener("click", () => this._disconnectGroupBots(groupId));
                actionsRow.appendChild(disconnectGroupButton);
                groupInnerContent.appendChild(actionsRow);

                groupManagementSection.appendChild(groupDetails);
            }

            const globalGroupActionsRow = domMake.Row({ class: "intelligent-swarm-control-group", style: "margin-top: 10px;" });
            const sendAllGroupsButton = domMake.Button('<i class="fas fa-paper-plane"></i> Enviar Todo');
            sendAllGroupsButton.title = "Enviar el mensaje configurado de cada grupo.";
            sendAllGroupsButton.addEventListener("click", () => { for (let i = 0; i < 3; i++) this._sendGroupMessage(i); });


            this._ui.autoSendGroupsToggle = createToggle("Auto Enviar Grupos", this._toggleAutoSendGroups);



            this._ui.autoSendSpeedInput = domMake.Tree("input", { type: "number", value: "5000", min: "1000", max: "60000", step: "500" });
            this._ui.autoSendSpeedInput.addEventListener("change", () => {
                if (this._activeToggles.autoSendGroups) {
                    this._toggleAutoSendGroups(false);
                    this._toggleAutoSendGroups(true);
                }
            });


            groupManagementSection.appendChild(globalGroupActionsRow);
            container.appendChild(groupManagementSection);


            const cooldownsSection = domMake.Tree("div", { class: "intelligent-swarm-section" });


            const chatCooldownGroup = domMake.Tree("div", { class: "intelligent-swarm-control-group" });
            chatCooldownGroup.appendChild(domMake.Tree("label", {}, ["Cooldown Chat (ms):"]));
            this._ui.chatCooldownInput = domMake.Tree("input", { type: "number", value: "2000", min: "1000", max: "30000", step: "500" });
            chatCooldownGroup.appendChild(this._ui.chatCooldownInput);
            cooldownsSection.appendChild(chatCooldownGroup);

            const gestureCooldownGroup = domMake.Tree("div", { class: "intelligent-swarm-control-group" });
            gestureCooldownGroup.appendChild(domMake.Tree("label", {}, ["Cooldown Gesto (ms):"]));
            this._ui.gestureCooldownInput = domMake.Tree("input", { type: "number", value: "500", min: "100", max: "5000", step: "100" });
            gestureCooldownGroup.appendChild(this._ui.gestureCooldownInput);
            cooldownsSection.appendChild(gestureCooldownGroup);

            container.appendChild(cooldownsSection);
            this.htmlElements.section.appendChild(container);
        }

        _generateRandomBotName() {
            const adjectives = ["Carlos", "Olivia", "Pablo", "Sofia", "Daniel", "Valentina", "Javier", "Camila"];
            const nouns = [""];
            const randomAdj = adjectives[Math.floor(Math.random() * adjectives.length)];
            const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
            return `${randomAdj} ${randomNoun}`;
        }

        _initializeObservers() {
            const botManagerClass = this.findGlobal("BotClientManager");
            if (!botManagerClass || !botManagerClass.siblings || !botManagerClass.siblings.length === 0) {
                setTimeout(() => this._initializeObservers(), 500);
                return;
            }
            this._botManagerInstance = botManagerClass.siblings[0];

            let botManagerObserverTimer;
            const botManagerObserver = new MutationObserver(() => {
                clearTimeout(botManagerObserverTimer);
                botManagerObserverTimer = setTimeout(this._handleBotClientManagerChange, 100);
            });
            botManagerObserver.observe(this._botManagerInstance.htmlElements.children, { childList: true, subtree: false });
            this._handleBotClientManagerChange();

            const playerListElement = document.getElementById("playerlist");
            if (playerListElement) {
                let playerListObserverTimer;
                const playerListObserver = new MutationObserver(() => {
                    clearTimeout(playerListObserverTimer);
                    playerListObserverTimer = setTimeout(this._handlePlayerListChange, 100);
                });
                playerListObserver.observe(playerListElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-playerid', 'data-loggedin', 'style'] });
                this._handlePlayerListChange();
            } else {

            }
        }

        _handleBotClientManagerChange() {
            const prevManagedBotsSet = new Set(this._bots);
            const currentBotsInManager = this._botManagerInstance.children
                .map(bci => bci.bot)
                .filter(bot => bot && bot.getReadyState());

            this._bots = currentBotsInManager;
            const currentManagedBotsSet = new Set(this._bots);

            this._bots.forEach(bot => {
                if (!prevManagedBotsSet.has(bot)) {
                    this._attachBotListeners(bot);
                    this.notify("log", `Intelligent Swarm ahora gestiona a: ${bot.name} (recién conectado).`);
                }
            });

            this._botGroupsData.forEach((group, groupIdx) => {
                this._botGroupsData[groupIdx] = group.filter(bot => currentManagedBotsSet.has(bot));
            });


            this._updatePlayerDropdown();

            if (this._activeToggles.botConversation) {
                this._toggleBotConversation(false);
                this._toggleBotConversation(true);
            }
            if (this._activeToggles.autoSendGroups) {
                this._toggleAutoSendGroups(false);
                this._toggleAutoSendGroups(true);
            }
        }

        _attachBotListeners(botInstance) {
            const isListenerAttached = botInstance.customObservers.some(obs => obs.callback.name === "_handleChatMessage");
            if (isListenerAttached) {
                this.notify("debug", `Listeners ya adjuntos a ${botInstance.name}.`);
                return;
            }

            this.notify("log", `Adjuntando listeners para: ${botInstance.name}`);
            const listeners = [
                { event: "bc_chatmessage", callback: (data) => this._handleChatMessage(botInstance, { id: data[0], name: data[1], message: data[2] }) },
                { event: "uc_turn_wordguessedlocalThis", callback: (data) => this._handleCorrectGuess(data) },
                { event: "bc_playernew", callback: (data) => this._handlePlayerJoin({ id: data[0], name: data[1] }) },
                { event: "bc_playerleft", callback: (data) => this._handlePlayerLeave({ id: data[0], name: data[1] }) },
                { event: "uc_turn_begindraw", callback: (data) => this._handleTurnBeginDraw(botInstance, data) },
                { event: "uc_turn_selectword", callback: (data) => this._handleWordSelected(botInstance, data) },
            ];

            listeners.forEach(listener => botInstance.customObservers.push(listener));
        }


        _removeBot(botInstance) {
            this._bots = this._bots.filter(b => b !== botInstance);
            this.notify("log", `Intelligent Swarm ya no gestiona a: ${botInstance.name}`);
            if (this._followTarget.id === botInstance.id) {

                this._toggleSmartFollow();
            }
        }

        _handlePlayerListChange() {
            this._updatePlayerDropdown();
            if (this._activeToggles.naturalMovement && !this._naturalMovementInterval && this._bots.length > 0) {
                this._toggleNaturalMovement(true);
            } else if (this._followTarget.id && !this._followTarget.interval && this._bots.length > 0) {
                this._followLogic();
            }
        }

        _updatePlayerDropdown() {
            if (!this._ui.playerDropdown) return;

            const anyConnectedBot = this._bots.find(bot => bot.getReadyState());
            const currentRoomPlayers = anyConnectedBot?.room?.players || [];
            const myBotIds = new Set(this._bots.map(b => b.id));
            const humanPlayers = currentRoomPlayers.filter(p => !myBotIds.has(p.id) && p.id !== 0 && p.name && p.id !== undefined);

            const currentSelection = this._ui.playerDropdown.value;
            this._ui.playerDropdown.innerHTML = "";

            if (humanPlayers.length === 0) {
                this._ui.playerDropdown.appendChild(domMake.Tree("option", { value: "" }, ["No hay jugadores (humanos)"]));
                this._ui.playerDropdown.disabled = true;
                this._ui.followButton.disabled = true;
                if (this._followTarget.id !== null && !humanPlayers.some(p => String(p.id) === currentSelection)) {

                    this._toggleSmartFollow();
                }
                return;
            }

            this._ui.playerDropdown.disabled = false;
            this._ui.followButton.disabled = false;
            humanPlayers.forEach(p => this._ui.playerDropdown.appendChild(domMake.Tree("option", { value: p.id }, [p.name])));

            if (humanPlayers.some(p => String(p.id) === currentSelection)) {
                this._ui.playerDropdown.value = currentSelection;
            } else {
                this._ui.playerDropdown.selectedIndex = 0;
            }
        }

        _getBots(requireConnected = true, skipNotification = false) {
            const connectedBots = this._bots.filter(bot => bot.getReadyState());
            if (connectedBots.length === 0 && !skipNotification) {

            }
            return connectedBots;
        }

        _toggleNaturalMovement(isActive) {
            this._activeToggles.naturalMovement = isActive;
            const toggleCheckbox = this._ui.naturalMovementToggleCheckbox;
            if (toggleCheckbox) toggleCheckbox.checked = isActive;

            if (isActive && this._followTarget.id !== null) {

                this._toggleSmartFollow();
            }

            const connectedBots = this._getBots(true);
            const canRun = isActive && connectedBots.length > 0 && this._followTarget.id === null;

            if (canRun && !this._naturalMovementInterval) {
                const moveSpeed = parseInt(this._ui.movementSpeedInput.value) || 3000;

                this._naturalMovementInterval = setInterval(this._executeNaturalMovement, moveSpeed + Math.random() * 1000);
                this._executeNaturalMovement();
            } else if (!canRun && this._naturalMovementInterval) {

                clearInterval(this._naturalMovementInterval);
                this._naturalMovementInterval = null;
            } else if (isActive && connectedBots.length === 0) {

                 if(toggleCheckbox) toggleCheckbox.checked = false;
                 this._activeToggles.naturalMovement = false;
            }
        }

        _executeNaturalMovement() {
            if (this._followTarget.id || !this._activeToggles.naturalMovement || !this._gameCanvas) return;
            const connectedBots = this._getBots(true);
            if (connectedBots.length === 0) {

                this._toggleNaturalMovement(false);
                return;
            }
            const centerX = 50, centerY = 50, driftAmount = 15;

            connectedBots.forEach(bot => {
                 if (bot.attributes?.spawned === false) {
                      this.notify("debug", `Bot ${bot.name} no generado para movimiento natural. Intentando generar.`);
                      bot.emit("spawnavatar");
                      bot.attributes.spawned = true;
                      return;
                 }
                let targetX = centerX + (Math.random() - 0.5) * driftAmount * 2;
                let targetY = centerY + (Math.random() - 0.5) * driftAmount * 2;
                targetX = Math.max(5, Math.min(95, targetX));
                targetY = Math.max(5, Math.min(95, targetY));
                this._moveBotSmoothly(bot, targetX, targetY);
            });
        }

        _toggleSmartFollow() {
            const targetIdString = this._ui.playerDropdown.value;
            const targetId = parseInt(targetIdString);
            const targetName = this._ui.playerDropdown.querySelector(`option[value="${targetIdString}"]`)?.textContent || 'jugador desconocido';
            const connectedBots = this._getBots(true);
            if (connectedBots.length === 0) {

                 return;
            }
            const anyConnectedBot = connectedBots[0];
            const currentPlayerList = anyConnectedBot?.room?.players || [];
            const targetPlayerExists = currentPlayerList.some(p => String(p.id) === targetIdString);

            if (isNaN(targetId) || !targetPlayerExists) {

                return;
            }

            if (String(this._followTarget.id) === targetIdString) {
                clearInterval(this._followTarget.interval);
                this._followTarget = { id: null, interval: null, name: null };
                this._ui.followButton.textContent = "Seguir Jugador";
                this._ui.followButton.classList.remove("active");

                if (this._activeToggles.naturalMovement) this._toggleNaturalMovement(true);
            } else {
                if (this._followTarget.interval) clearInterval(this._followTarget.interval);
                if (this._naturalMovementInterval) {
                    this._toggleNaturalMovement(false);
                    if(this._ui.naturalMovementToggleCheckbox) this._ui.naturalMovementToggleCheckbox.checked = false;
                }
                this._followTarget = { id: targetId, name: targetName, interval: null };
                this._ui.followButton.textContent = `Siguiendo: ${targetName}`;
                this._ui.followButton.classList.add("active");

                const moveSpeed = parseInt(this._ui.movementSpeedInput.value) || 3000;
                this._followTarget.interval = setInterval(this._followLogic, moveSpeed / 3);
                this._followLogic();
            }
        }

        _followLogic() {
            if (this._followTarget.id === null || !this._gameCanvas) return;
            const connectedBots = this._getBots(true);
            if (connectedBots.length === 0) {

                this._toggleSmartFollow();
                return;
            }
            const targetPlayerElement = document.querySelector(`.spawnedavatar[data-playerid="${this._followTarget.id}"]`);
            if (!targetPlayerElement) {

                this._toggleSmartFollow();
                return;
            }
            const canvasRect = this._gameCanvas.getBoundingClientRect();
            const avatarRect = targetPlayerElement.getBoundingClientRect();
            let targetXGameCoords = ((avatarRect.left + (avatarRect.width / 2) - canvasRect.left) / canvasRect.width) * 100;
            let targetYGameCoords = ((avatarRect.top + (avatarRect.height / 2) - canvasRect.top) / canvasRect.height) * 100;
            targetXGameCoords = Math.max(0, Math.min(100, targetXGameCoords));
            targetYGameCoords = Math.max(0, Math.min(100, targetYGameCoords));

            connectedBots.forEach((bot, index) => {
                 if (bot.attributes?.spawned === false) {
                      this.notify("debug", `Bot ${bot.name} no generado para seguir. Intentando generar.`);
                      bot.emit("spawnavatar");
                      bot.attributes.spawned = true;
                      return;
                 }
                const offsetDistance = 10 + index * 5;
                const offsetAngle = (index * 1.5 + Math.random()) * Math.PI * 2 / connectedBots.length;
                const offsetX = offsetDistance * Math.cos(offsetAngle);
                const offsetY = offsetDistance * Math.sin(offsetAngle);
                let moveX = targetXGameCoords + offsetX;
                let moveY = targetYGameCoords + offsetY;
                moveX = Math.max(5, Math.min(95, moveX));
                moveY = Math.max(5, Math.min(95, moveY));
                this._moveBotSmoothly(bot, moveX, moveY);
            });
        }

        _moveBotSmoothly(bot, targetX, targetY) {
            if (!bot || !bot.getReadyState() || typeof bot.emit !== 'function' || bot.attributes?.spawned === false) {
                this.notify("debug", `Saltando movimiento suave para bot ${bot?.name || 'Desconocido'}: No está listo o no ha sido generado.`);
                return;
            }
             bot._lastCommandedX = bot._lastCommandedX ?? 50;
             bot._lastCommandedY = bot._lastCommandedY ?? 50;
             const currentX = bot._lastCommandedX;
             const currentY = bot._lastCommandedY;
             const steps = 10;
             const stepDelay = (parseInt(this._ui.movementSpeedInput.value) || 3000) / (steps * 3);
             for (let i = 1; i <= steps; i++) {
                 setTimeout(() => {
                     if (bot.getReadyState()) {
                        const interX = currentX + (targetX - currentX) * (i / steps);
                        const interY = currentY + (targetY - currentY) * (i / steps);
                         bot.emit("moveavatar", interX, interY);
                         bot._lastCommandedX = interX;
                         bot._lastCommandedY = interY;
                     }
                 }, i * stepDelay);
             }
        }

        _onPersonalityChange() {
            this._selectedPersonality = this._ui.personalitySelect.value;

        }

        _canChat(bot) {
            const now = Date.now();
            const lastChat = this._chatCooldown.get(bot.id) || 0;
            const chatCooldownMs = parseInt(this._ui.chatCooldownInput.value) || 2000;
            const canChat = (now - lastChat > chatCooldownMs);
            if (canChat) this._chatCooldown.set(bot.id, now);
            return canChat;
        }

         _sendChat(bot, message) {
             if (bot && bot.getReadyState() && this._canChat(bot)) {
                 setTimeout(() => {
                      if (bot.getReadyState()) {
                          bot.emit("chatmsg", message);
                          this.notify("log", `${bot.name} (Pers: ${this._selectedPersonality}): "${message}"`);
                      }
                 }, 500 + Math.random() * 500);
             }
         }

         _canGesture(bot) {
             const now = Date.now();
             const lastGesture = this._gestureCooldown.get(bot.id) || 0;
             const gestureCooldownMs = parseInt(this._ui.gestureCooldownInput.value) || 500;
             const canGesture = (now - lastGesture > gestureCooldownMs);
             if (canGesture) this._gestureCooldown.set(bot.id, now);
             return canGesture;
         }

         _sendGesture(bot, gestureId) {
             if (gestureId === undefined || gestureId === null) {
                 this.notify("debug", `Saltando gesto: ID inválido para bot ${bot.name}.`);
                 return;
             }
             if (bot && bot.getReadyState() && this._canGesture(bot)) {
                 setTimeout(() => {
                     if (bot.getReadyState()) {
                          bot.emit("sendgesture", gestureId);
                          this.notify("log", `${bot.name} usó el gesto ${gestureId}.`);
                     }
                 }, 100 + Math.random() * 200);
             }
         }

        _getMessageLanguage(message) {
            const lowerCaseMsg = message.toLowerCase();
            const spanishKeywords = ['hola', 'buenas', 'que', 'qué', 'tal', 'como', 'cómo', 'estás', 'estas', 'estoy', 'bien', 'mal', 'sí', 'si', 'no', 'por favor', 'gracias', 'adiós', 'chao', 'jaja', 'xd', 'dibujo', 'jugador', 'adivinar', 'palabra'];
            const englishKeywords = ['hi', 'hello', 'hey', 'what', 'how', 'are', 'you', 'i', 'am', 'fine', 'good', 'bad', 'yes', 'no', 'please', 'thank', 'thanks', 'bye', 'goodbye', 'lol', 'haha', 'draw', 'player', 'guess', 'word'];
            let spanishMatches = spanishKeywords.filter(k => lowerCaseMsg.includes(k)).length;
            let englishMatches = englishKeywords.filter(k => lowerCaseMsg.includes(k)).length;
            if (spanishMatches > englishMatches && spanishMatches > 0) return 'spanish';
            if (englishMatches > spanishMatches && englishMatches > 0) return 'english';
            return 'neutral';
        }

        _handleChatMessage(receivingBot, msg) {
            const isSelfMessage = receivingBot.id === msg.id;
            const isFromOurBot = this._bots.some(b => b.id === msg.id);
            const isHumanMessage = !isFromOurBot && msg.id !== 0;

            if (isSelfMessage) return;

            let allowChat = (isHumanMessage && this._activeToggles.reactiveChat) || (isFromOurBot && this._activeToggles.botToBotChat);
            let allowGestures = (isHumanMessage && this._activeToggles.smartGestures) || (isFromOurBot && this._activeToggles.botToBotGestures);
            if (!allowChat && !allowGestures) return;

            const personality = _personalities[this._selectedPersonality];
            const lowerCaseMsg = msg.message.toLowerCase().trim();
            const lang = this._getMessageLanguage(msg.message);
            let responseType = null, responseMessages = null;

            if (/\b(hola|buenas|hey|hi|hello)\b/.test(lowerCaseMsg)) responseType = 'greeting';
            else if (/\b(si|yes|ok|claro|yeah)\b/.test(lowerCaseMsg)) responseType = 'acknowledgement';
            else if (/\b(como\sestas|y\stu|how\sare\syou|what's\sup|what)\b/.test(lowerCaseMsg)) responseType = 'question';
            else if (/\b(xd|lol|jaja|haha|omg)\b/.test(lowerCaseMsg)) responseType = 'laughter';
            else if (/\b(que|but|well|pero|bueno)\b/.test(lowerCaseMsg)) responseType = 'general';
            else if (/\b(lindo|hermoso|dibujas\sbien|buen\sdibujo|buen\strabajo|good\sjob|nice\sdraw)\b/.test(lowerCaseMsg)) responseType = 'goodjob_drawing';

            if (responseType) {
                responseMessages = personality[`${lang}_${responseType}`] || personality[`${lang}_general`] || personality.spanish_general;
                if (allowChat && responseMessages && Math.random() < 0.7) {
                    const selectedResponse = responseMessages[Math.floor(Math.random() * responseMessages.length)];
                    this._sendChat(receivingBot, selectedResponse.replace("{player}", msg.name));
                }
                if (allowGestures && personality.gestures[responseType] !== undefined) {
                    this._sendGesture(receivingBot, personality.gestures[responseType]);
                }
            }
        }

        _handleCorrectGuess(data) {
            const player = { id: data[0], name: data[1] };
            if (this._bots.some(b => b.id === player.id)) return;
            const personality = _personalities[this._selectedPersonality];
            const response = personality.spanish_congrats[Math.floor(Math.random() * personality.spanish_congrats.length)].replace("{player}", player.name);
            this._getBots(true).forEach(bot => {
                if (this._activeToggles.reactiveChat && Math.random() < 0.7) this._sendChat(bot, response);
                if (this._activeToggles.smartGestures && personality.gestures.congrats !== undefined) this._sendGesture(bot, personality.gestures.congrats);
            });
        }

        _handlePlayerJoin(player) {
            if (this._bots.some(b => b.id === player.id) || player.id === 0) return;
            const personality = _personalities[this._selectedPersonality];
            const response = personality.spanish_playerJoin[Math.floor(Math.random() * personality.spanish_playerJoin.length)].replace("{player}", player.name);
            this._getBots(true).forEach(bot => {
                if (this._activeToggles.reactiveChat && Math.random() < 0.6) this._sendChat(bot, response);
                if (this._activeToggles.smartGestures && personality.gestures.playerJoin !== undefined) this._sendGesture(bot, personality.gestures.playerJoin);
            });
        }

        _handlePlayerLeave(player) {
            if (this._bots.some(b => b.id === player.id) || player.id === 0) return;
            if (this._followTarget.id === player.id) {

                this._toggleSmartFollow();
            }
            const personality = _personalities[this._selectedPersonality];
            const response = personality.spanish_playerLeave[Math.floor(Math.random() * personality.spanish_playerLeave.length)].replace("{player}", player.name);
            this._getBots(true).forEach(bot => {
                if (this._activeToggles.reactiveChat && Math.random() < 0.5) this._sendChat(bot, response);
                if (this._activeToggles.smartGestures && personality.gestures.playerLeave !== undefined) this._sendGesture(bot, personality.gestures.playerLeave);
            });
        }

        _handleTurnBeginDraw(botInstance, data) {
            const drawingPlayerId = data[0];
            const drawingPlayerName = botInstance.room?.players?.find(p => p.id === drawingPlayerId)?.name || 'alguien';
            const isOurBotDrawing = this._bots.some(b => b.id === drawingPlayerId);
            this._getBots(true).forEach(bot => {
                if (bot.id === drawingPlayerId) return;
                let allowChat = (isOurBotDrawing && this._activeToggles.botToBotChat) || (!isOurBotDrawing && this._activeToggles.reactiveChat);
                let allowGestures = (isOurBotDrawing && this._activeToggles.botToBotGestures) || (!isOurBotDrawing && this._activeToggles.smartGestures);
                if (allowGestures) {
                    const personality = _personalities[this._selectedPersonality];
                    if (personality.gestures.drawing !== undefined) this._sendGesture(bot, personality.gestures.drawing);
                }
                if (allowChat && Math.random() < 0.4) this._sendChat(bot, `¡Buena suerte, ${drawingPlayerName}!`);
            });
        }

        _handleWordSelected(botInstance, data) {
            const drawingPlayerElement = document.querySelector('.playerlist-row[data-turn="true"]');
            const drawingPlayerId = drawingPlayerElement ? parseInt(drawingPlayerElement.dataset.playerid) : null;
            const isOurBotDrawing = this._bots.some(b => b.id === drawingPlayerId);
            this._getBots(true).forEach(bot => {
                if (bot.id === drawingPlayerId) return;
                let allowGestures = (isOurBotDrawing && this._activeToggles.botToBotGestures) || (!isOurBotDrawing && this._activeToggles.smartGestures);
                if (allowGestures) {
                    const personality = _personalities[this._selectedPersonality];
                    if (personality.gestures.acknowledgement !== undefined) this._sendGesture(bot, personality.gestures.acknowledgement);
                }
            });
        }

        _toggleBotConversation(isActive) {
            this._activeToggles.botConversation = isActive;
            const toggleCheckbox = this._ui.botConversationToggleCheckbox;
            if (toggleCheckbox) toggleCheckbox.checked = isActive;

            if (isActive) {
                this._conversationBots = this._getBots(true);
                if (this._conversationBots.length < 2) {

                    this._activeToggles.botConversation = false;
                    if (toggleCheckbox) toggleCheckbox.checked = false;
                    return;
                }
                for (let i = this._conversationBots.length - 1; i > 0; i--) {
                    const j = Math.floor(Math.random() * (i + 1));
                    [this._conversationBots[i], this._conversationBots[j]] = [this._conversationBots[j], this._conversationBots[i]];
                }
                this._currentSpeakerIndex = 0;
                this._startNewConversationTopic();
                const chatCooldownMs = parseInt(this._ui.chatCooldownInput.value) || 2000;

                this._conversationInterval = setInterval(this._continueConversation, chatCooldownMs);
            } else {
                clearInterval(this._conversationInterval);
                this._conversationInterval = null;
                this._conversationBots = [];
                this._currentSpeakerIndex = -1;
                this._currentConversationTopic = null;
                this._currentTopicPhraseIndex = -1;

            }
        }

        _startNewConversationTopic() {
            const topicKeys = Object.keys(this._conversationTopicsData).filter(key => key !== 'General');
            let selectedTopic = 'General';
            for (const topic of topicKeys) { if (Math.random() < 0.20) { selectedTopic = topic; break; } }
            if (selectedTopic === 'General' && Math.random() < 0.5) selectedTopic = topicKeys[Math.floor(Math.random() * topicKeys.length)];
            this._currentConversationTopic = selectedTopic;
            this._currentTopicPhraseIndex = 0;
            this.notify("log", `Nueva conversación iniciada. Tema: "${this._currentConversationTopic}".`);
        }

        _continueConversation() {
            if (!this._activeToggles.botConversation || this._conversationBots.length < 2) {

                this._toggleBotConversation(false);
                return;
            }
            const speakerBot = this._conversationBots[this._currentSpeakerIndex];
            const topicPhrases = this._conversationTopicsData[this._currentConversationTopic];
            if (!speakerBot || !speakerBot.getReadyState()) {
                this.notify("debug", `Bot ${speakerBot ? speakerBot.name : 'desconocido'} no está listo. Saltando turno.`);
                this._currentSpeakerIndex = (this._currentSpeakerIndex + 1) % this._conversationBots.length;
                return;
            }
            if (this._currentTopicPhraseIndex < topicPhrases.length) {
                const phrase = topicPhrases[this._currentTopicPhraseIndex];
                this._sendChat(speakerBot, phrase);
                this._currentTopicPhraseIndex++;
            } else {
                this._startNewConversationTopic();
                const phrase = this._conversationTopicsData[this._currentConversationTopic][0];
                this._sendChat(speakerBot, phrase);
                this._currentTopicPhraseIndex = 1;
            }
            this._currentSpeakerIndex = (this._currentSpeakerIndex + 1) % this._conversationBots.length;
        }

        _confirmGroupBaseName(groupId, baseName) {
            const botNameInputs = this._ui.groupInputs[groupId].nameInputs;
            const effectiveBaseName = baseName.trim() || `Grupo ${groupId + 1}`;
            botNameInputs.forEach((input, index) => input.value = `${effectiveBaseName}`);

        }

        async _joinBotsToGroup(groupId) {
            const botsToCreate = 3;
            const currentTotalBots = this._bots.length;
            if (currentTotalBots + botsToCreate > this._maxTotalBots) {

                return;
            }

            const joinedBots = [];
            const botNameInputs = this._ui.groupInputs[groupId].nameInputs;
            for (let i = 0; i < botsToCreate; i++) {
                const botName = botNameInputs[i].value.trim() || `Grupo ${groupId + 1} Bot ${i + 1}`;
                const botInterface = this._botManagerInstance.createBotClientInterface();
                const botInstance = botInterface.bot;
                if (!botInstance) {

                    continue;
                }
                botInterface.setClientName(botName);
                const avatarFileInput = this._ui.groupInputs[groupId].avatarFileInput;
                if (avatarFileInput && avatarFileInput.dataset.avatarUidParts) {
                    const avatarUidParts = JSON.parse(avatarFileInput.dataset.avatarUidParts);
                    botInstance.avatar = avatarUidParts;
                    botInterface.setClientIcon(avatarUidParts);
                }
                const roomUrlInput = document.querySelector("#invurl");
                const currentRoomId = roomUrlInput ? roomUrlInput.value : window.location.pathname.replace('/room/', '');
                botInstance.enterRoom(currentRoomId);
                joinedBots.push(botInstance);
                await new Promise(resolve => setTimeout(resolve, 500));
            }
            this._botGroupsData[groupId] = joinedBots;
            this.notify("success", `Grupo ${groupId + 1}: ${joinedBots.length} bots unidos y en sala.`);
            this._handleBotClientManagerChange();
        }

        _sendGroupMessage(groupId) {
            const groupBots = this._botGroupsData[groupId];
            if (!groupBots || groupBots.length === 0) {

                return;
            }
            const message = this._ui.groupInputs[groupId].messageInput.value.trim();
            if (!message) {

                return;
            }
            groupBots.forEach(bot => {
                if (bot.getReadyState()) this._sendChat(bot, message);

            });
        }

        async _handleGroupAvatarUpload(groupId, file) {
            if (!file) {

                return;
            }

            try {
                const reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onload = async () => {
                    const base64Image = reader.result;
                    const response = await fetch("https://drawaria.online/uploadavatarimage", {
                        method: "POST", body: "imagedata=" + encodeURIComponent(base64Image) + "&fromeditor=true",
                        headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
                    });
                    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                    const body = await response.text();
                    const avatarUidParts = body.split(".");
                    if (avatarUidParts.length < 2) throw new Error("Respuesta inválida del servidor al subir avatar.");
                    this._ui.groupInputs[groupId].avatarFileInput.dataset.avatarUidParts = JSON.stringify(avatarUidParts);
                    const groupBots = this._botGroupsData[groupId];
                    if (groupBots && groupBots.length > 0) {
                        for (const bot of groupBots) {
                            if (bot.getReadyState()) {
                                bot.avatar = avatarUidParts;
                                const botClientInterface = this._botManagerInstance.children.find(bci => bci.bot === bot);
                                if (botClientInterface) botClientInterface.setClientIcon(avatarUidParts);

                            }
                        }
                    }
                    this.notify("success", `Avatar para Grupo ${groupId + 1} subido y asignado.`);

                };
                reader.onerror = (error) => { throw new Error(`Error al leer el archivo: ${error.message}`); };
            } catch (error) {

                console.error("Avatar upload error:", error);
            }
        }

        _spawnGroupAvatars(groupId) {
            const groupBots = this._botGroupsData[groupId];
            if (!groupBots || groupBots.length === 0) {

                return;
            }

            groupBots.forEach(bot => {
                if (bot.getReadyState() && !bot.attributes.spawned) {
                    bot.emit("spawnavatar");
                    bot.attributes.spawned = true;
                    this.notify("log", `Avatar de ${bot.name} spawneado.`);
                } else if (bot.attributes.spawned) {
                     this.notify("log", `Avatar de ${bot.name} ya está spawneado.`);
                } else {

                }
            });
        }

        _disconnectGroupBots(groupId) {
            const groupBots = this._botGroupsData[groupId];
            if (!groupBots || groupBots.length === 0) {

                return;
            }

            groupBots.forEach(bot => { if (bot.getReadyState()) bot.disconnect(); });
            this.notify("success", `Bots del Grupo ${groupId + 1} desconectados.`);
            setTimeout(() => this._handleBotClientManagerChange(), 50);
        }

        _toggleAutoSendGroups(isActive) {
            this._activeToggles.autoSendGroups = isActive;
            const toggleCheckbox = this._ui.autoSendGroupsToggle.querySelector('input[type="checkbox"]');
            if (toggleCheckbox) toggleCheckbox.checked = isActive;

            if (isActive) {
                const autoSendSpeedMs = parseInt(this._ui.autoSendSpeedInput.value) || 5000;
                if (autoSendSpeedMs < 1000) {

                     this._activeToggles.autoSendGroups = false;
                     if (toggleCheckbox) toggleCheckbox.checked = false;
                     return;
                }

                this._autoSendGroupsInterval = setInterval(this._performAutoSendGroups, autoSendSpeedMs);
                this._performAutoSendGroups();
            } else {
                clearInterval(this._autoSendGroupsInterval);
                this._autoSendGroupsInterval = null;

            }
        }

        _performAutoSendGroups() {
            if (!this._activeToggles.autoSendGroups) return;
            for (let i = 0; i < 3; i++) {
                const groupBots = this._botGroupsData[i];
                if (groupBots && groupBots.length > 0) {
                    const message = this._ui.groupInputs[i].messageInput.value.trim();
                    if (message) {
                        const randomBot = groupBots[Math.floor(Math.random() * groupBots.length)];
                        if (randomBot.getReadyState()) this._sendChat(randomBot, message);
                    }
                }
            }
        }
    }
})("QBit");




(function SwarmCommanderModule() {
    const QBit = globalThis[arguments[0]];

    QBit.Styles.addRules([
        `#${QBit.identifier} .swarm-commander-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .swarm-commander-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .swarm-commander-toggle-button.active {
            background-color: var(--info);
            color: white;
        }`,
        `#${QBit.identifier} .swarm-commander-control-group {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            margin-top: 5px;
            padding-top: 5px;
            border-top: 1px solid rgba(0,0,0,0.1);
        }`,
        `#${QBit.identifier} .swarm-commander-control-group > div {
            flex: 1 1 48%; /* For responsiveness */
            display: flex;
            flex-direction: column;
            align-items: flex-start;
        }`,
        `#${QBit.identifier} .swarm-commander-control-group input,
         #${QBit.identifier} .swarm-commander-control-group select {
            width: 100%;
        }`
    ]);

    class SwarmCommander extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");


        #lagInterval = null;
        #secretSpamInterval = null;
        #bugExperienceInterval = null;
        #playerChaosInterval = null;
        #visualGlitchInterval = null;

        constructor() {
            super("🕹️Bots Manipulation", '<i class="fas fa-gamepad"></i>');
            this.#onStartup();
        }

        #onStartup() {
            this.#loadInterface();
        }

        #loadInterface() {
            const container = domMake.Tree("div");


            const swarmTacticsSection = domMake.Tree("div", { class: "swarm-commander-section" });
            swarmTacticsSection.appendChild(domMake.Tree("div", { class: "swarm-commander-section-title" }, ["Modos de bot"]));

            const collaborativeDrawRow = domMake.Row();
            const collaborativeDrawButton = domMake.Button('<i class="fas fa-palette"></i> Dibujo Colaborativo');
            collaborativeDrawButton.title = "Divide el lienzo en zonas para que cada bot dibuje una parte (usa Autodraw V2 para cargar imagen).";
            collaborativeDrawButton.addEventListener("click", () => this.#startCollaborativeDrawing());
            collaborativeDrawRow.appendChild(collaborativeDrawButton);
            swarmTacticsSection.appendChild(collaborativeDrawRow);

            const smartGuessRow = domMake.Row();
            const smartGuessButton = domMake.Button('<i class="fas fa-robot"></i> Bot de Adivinanza');
            smartGuessButton.title = "Un bot intentará adivinar la palabra (simulado).";
            smartGuessButton.addEventListener("click", () => this.#smartGuess());
            smartGuessRow.appendChild(smartGuessButton);
            swarmTacticsSection.appendChild(smartGuessRow);

            const personalityGroup = domMake.Tree("div", { class: "swarm-commander-control-group" });
            personalityGroup.appendChild(domMake.Tree("label", { style: "width: 100%; text-align: center; font-weight: bold; margin-bottom: 5px;" }, ["Personalidad del Bot"]));

            const drawingSpeedDiv = domMake.Tree("div");
            drawingSpeedDiv.appendChild(domMake.Tree("label", {}, ["Velocidad Dibujo (ms/línea):"]));
            const drawingSpeedInput = domMake.Tree("input", { type: "number", min: "1", max: "100", value: "10" });
            drawingSpeedInput.addEventListener("change", (e) => this.#setBotDrawingSpeed(parseInt(e.target.value)));
            drawingSpeedDiv.appendChild(drawingSpeedInput);
            personalityGroup.appendChild(drawingSpeedDiv);

            const verbosityDiv = domMake.Tree("div");
            verbosityDiv.appendChild(domMake.Tree("label", {}, ["Verbosidad Mensajes:"]));
            const verbositySelect = domMake.Tree("select");
            ['Silencioso', 'Normal', 'Charlatán'].forEach(level => {
                verbositySelect.appendChild(domMake.Tree("option", { value: level }, [level]));
            });
            verbositySelect.addEventListener("change", (e) => this.#setBotChatVerbosity(e.target.value));
            verbosityDiv.appendChild(verbositySelect);
            personalityGroup.appendChild(verbosityDiv);

            swarmTacticsSection.appendChild(personalityGroup);
            container.appendChild(swarmTacticsSection);


            const disruptionSection = domMake.Tree("div", { class: "swarm-commander-section" });
            disruptionSection.appendChild(domMake.Tree("div",  { class: "swarm-commander-section-title" }, ["Herramientas de Caos"]));

            const createToggleButton = (icon, text, toggleFunction) => {
                const row = domMake.Row();
                const button = domMake.Button(`<i class="fas ${icon}"></i> ${text}`);
                button.classList.add("swarm-commander-toggle-button");
                button.addEventListener("click", () => toggleFunction(button));
                row.appendChild(button);
                return row;
            };

            disruptionSection.appendChild(createToggleButton('fa-dizzy', 'Generar Lag', (btn) => this.#toggleLag(btn)));
            disruptionSection.appendChild(createToggleButton('fa-gamepad', 'Bugear Experiencia', (btn) => this.#toggleBugExperience(btn)));
            disruptionSection.appendChild(createToggleButton('fa-running', 'Caos de Jugador', (btn) => this.#togglePlayerChaos(btn)));
            disruptionSection.appendChild(createToggleButton('fa-ghost', 'Glitch Visual', (btn) => this.#toggleVisualGlitch(btn)));
            disruptionSection.appendChild(createToggleButton('fa-mask', 'Spam Visual Secreto', (btn) => this.#toggleSecretSpam(btn)));

            container.appendChild(disruptionSection);

            this.htmlElements.section.appendChild(container);
        }


        #getBot() {
            const botManagerClass = this.findGlobal("BotClientManager");
            if (!botManagerClass || !botManagerClass.siblings || botManagerClass.siblings.length === 0) {

                return null;
            }

            const botManagerInstance = botManagerClass.siblings[0];
            const botClientInterfaces = botManagerInstance.children;
            let activeBotClientInterface = null;

            const selectedBotInput = document.querySelector('input[name="botClient"]:checked');
            if (selectedBotInput) {
                activeBotClientInterface = botClientInterfaces.find(bci => bci.htmlElements.input === selectedBotInput);
            }

            if (!activeBotClientInterface && botClientInterfaces.length > 0) {
                activeBotClientInterface = botClientInterfaces[0];

            }

            if (!activeBotClientInterface) {

                 return null;
            }



            if (!activeBotClientInterface.bot || !activeBotClientInterface.bot.getReadyState()) {

                return null;
            }

            return activeBotClientInterface.bot;
        }


        #startCollaborativeDrawing() {
            const autodrawV2Class = this.findGlobal("AutodrawV2");
            if (!autodrawV2Class || !autodrawV2Class.siblings || autodrawV2Class.siblings.length === 0) {

                 return;
            }
            const autodrawV2Instance = autodrawV2Class.siblings[0];

            if (autodrawV2Instance && typeof autodrawV2Instance.startDrawing === 'function') {
                autodrawV2Instance.startDrawing();

            } else {

            }
        }

        #smartGuess() {
            const bot = this.#getBot();
            if (!bot) return;

            const commonWords = ["casa", "flor", "mesa", "sol", "perro", "gato", "arbol", "coche", "libro"];
            const randomWord = commonWords[Math.floor(Math.random() * commonWords.length)];

            bot.emit("chatmsg", randomWord);

        }

        #setBotProperty(propertyName, value, logMessage) {
            const botManagerClass = this.findGlobal("BotClientManager");
            if (botManagerClass && botManagerClass.siblings.length > 0) {
                botManagerClass.siblings.forEach(manager => {
                    if (manager && manager.children) {
                        manager.children.forEach(botInterface => {
                            if (botInterface.bot) {
                                botInterface.bot[propertyName] = value;
                                this.notify("log", logMessage(botInterface.getName(), value));
                            }
                        });
                    }
                });
            } else {

            }
        }

        #setBotDrawingSpeed(speed) {
            this.#setBotProperty("drawingDelay", speed, (name, val) => `Velocidad de dibujo de ${name}: ${val}ms/línea.`);
        }

        #setBotChatVerbosity(verbosity) {
             this.#setBotProperty("chatVerbosity", verbosity, (name, val) => `Verbosidad de ${name}: ${val}.`);
        }


        #toggleLag(button) {
            if (this.#lagInterval) {
                clearInterval(this.#lagInterval);
                this.#lagInterval = null;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-dizzy"></i> Generar Lag';

            } else {
                const bot = this.#getBot();
                if (!bot) return;

                button.classList.add("active");
                button.innerHTML = '<i class="fas fa-stop"></i> Detener Lag';


                let counter = 0;
                this.#lagInterval = setInterval(() => {
                    if (!bot.getReadyState()) {

                        this.#toggleLag(button);
                        return;
                    }
                    if (counter % 50 === 0) bot.emit("clear");

                    for (let i = 0; i < 5; i++) {
                        bot.emit("line", -1, Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100, true, Math.floor(Math.random() * 70) + 20, `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')}`, false);
                    }
                    counter++;
                }, 25);
            }
        }

        #toggleBugExperience(button) {
            if (this.#bugExperienceInterval) {
                clearInterval(this.#bugExperienceInterval);
                this.#bugExperienceInterval = null;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-gamepad"></i> Bugear Experiencia';

            } else {
                const bot = this.#getBot();
                if (!bot) return;

                button.classList.add("active");
                button.innerHTML = '<i class="fas fa-stop"></i> Detener Bug';


                this.#bugExperienceInterval = setInterval(() => {
                    if (!bot.getReadyState()) {

                        this.#toggleBugExperience(button);
                        return;
                    }
                    bot.emit("moveavatar", Math.random() * 100, Math.random() * 100);
                    bot.emit("sendgesture", Math.floor(Math.random() * 32));
                }, 100);
            }
        }

        #togglePlayerChaos(button) {
            if (this.#playerChaosInterval) {
                clearInterval(this.#playerChaosInterval);
                this.#playerChaosInterval = null;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-running"></i> Caos de Jugador';

            } else {
                const bot = this.#getBot();
                if (!bot) return;

                button.classList.add("active");
                button.innerHTML = '<i class="fas fa-stop"></i> Detener Caos';


                this.#playerChaosInterval = setInterval(() => {
                    if (!bot.getReadyState()) {

                        this.#togglePlayerChaos(button);
                        return;
                    }
                    bot.emit("moveavatar", Math.random() * 100, Math.random() * 100);
                    bot.emit("sendgesture", Math.floor(Math.random() * 32));
                    bot.emit("playerafk");
                    bot.emit("setstatusflag", Math.floor(Math.random() * 5), Math.random() < 0.5);
                }, 100);
            }
        }

        #toggleVisualGlitch(button) {
            if (this.#visualGlitchInterval) {
                clearInterval(this.#visualGlitchInterval);
                this.#visualGlitchInterval = null;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-ghost"></i> Glitch Visual';

            } else {
                const bot = this.#getBot();
                if (!bot) return;

                button.classList.add("active");
                button.innerHTML = '<i class="fas fa-stop"></i> Detener Glitch';


                const chatMessages = ["!! GLITCH DETECTED !!", "ERROR CODE 404: REALITY NOT FOUND", "SYSTEM OVERLOAD", "// VISUAL ANOMALY //", "PACKET CORRUPTION", "DISCONNECTING...", "RECALIBRATING... X_X"];

                this.#visualGlitchInterval = setInterval(() => {
                    if (!bot.getReadyState()) {

                        this.#toggleVisualGlitch(button);
                        return;
                    }
                    bot.emit("spawnavatar");
                    bot.emit("setavatarprop");
                    bot.emit("chatmsg", chatMessages[Math.floor(Math.random() * chatMessages.length)]);

                    const otherPlayers = (bot.room.players || []).filter(p => p.id !== bot.id && p.id !== 0);
                    if (otherPlayers.length > 0) {
                        const randomPlayer = otherPlayers[Math.floor(Math.random() * otherPlayers.length)];
                        bot.emit("sendvotekick", randomPlayer.id);
                        bot.emit("settoken", randomPlayer.id, Math.floor(Math.random() * 9));
                    }
                    bot.emit("sendvote");
                }, 200);
            }
        }

        #toggleSecretSpam(button) {
            if (this.#secretSpamInterval) {
                clearInterval(this.#secretSpamInterval);
                this.#secretSpamInterval = null;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-mask"></i> Spam Visual Secreto';

            } else {
                const bot = this.#getBot();
                if (!bot) return;

                button.classList.add("active");
                button.innerHTML = '<i class="fas fa-stop"></i> Detener Spam';


                this.#secretSpamInterval = setInterval(() => {
                    if (!bot.getReadyState()) {

                        this.#toggleSecretSpam(button);
                        return;
                    }
                    for (let i = 0; i < 10; i++) {
                        const x1 = Math.random() * 100, y1 = Math.random() * 100;
                        const x2 = x1 + (Math.random() * 2 - 1) * 5;
                        const y2 = y1 + (Math.random() * 2 - 1) * 5;
                        bot.emit("line", -1, x1, y1, x2, y2, true, Math.floor(Math.random() * 3) + 1, `#${Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0')}`, false);
                    }
                }, 100);
            }
        }
    }
})("QBit");





(function PlayerKickToolsModule() {
    const QBit = globalThis[arguments[0]];

    QBit.Styles.addRules([
        `#${QBit.identifier} .kick-tools-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .kick-tools-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .kick-tools-player-list {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            max-height: 150px; /* Limit height to show scrollbar if many players */
            overflow-y: auto;
            padding: 5px;
            border: 1px dashed var(--CE-color);
            border-radius: .25rem;
        }`,
        `#${QBit.identifier} .kick-tools-player-list .btn {
            flex: 0 0 auto; /* Prevent stretching */
            margin: 0; /* Remove default margin from btn class */
            padding: 3px 8px; /* Compact padding */
            font-size: 0.8em;
        }`,
        `#${QBit.identifier} .auto-action-toggle.active { /* Generic style for automation toggles */
            background-color: var(--info); /* Consistent active color */
            color: white;
        }`
    ]);

    class PlayerKickTools extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        _humanKickPlayerListContainer;
        _botKickPlayerListContainer;
        _cachedPlayers = [];


        _isAutoKickActive = false;
        _autoKickInterval = null;
        _kickedPlayersThisSession = new Set();


        _isAutoProhibitDrawingActive = false;
        _autoProhibitDrawingInterval = null;
        _prohibitedPlayersThisSession = new Set();

        constructor() {
            super("🔨Kick Panel", '<i class="fas fa-gavel"></i>');
            this._onStartup();
        }

        _onStartup() {
            this._loadInterface();
            this._setupObservers();
        }

        _loadInterface() {
            const container = domMake.Tree("div");


            const humanKickSection = domMake.Tree("div", { class: "kick-tools-section" });
            humanKickSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Voto para Expulsar (Tú)"]));
            this._humanKickPlayerListContainer = domMake.IconList({ class: "kick-tools-player-list" });
            humanKickSection.appendChild(this._humanKickPlayerListContainer);
            container.appendChild(humanKickSection);


            const botKickSection = domMake.Tree("div", { class: "kick-tools-section" });
            botKickSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Expulsar con Bot"]));
            this._botKickPlayerListContainer = domMake.IconList({ class: "kick-tools-player-list" });
            botKickSection.appendChild(this._botKickPlayerListContainer);
            container.appendChild(botKickSection);


            const automationSection = domMake.Tree("div", { class: "kick-tools-section" });
            automationSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Automatización de Acciones de Bots"]));


            const autoKickRow = domMake.Row();
            const autoKickButton = domMake.Button('<i class="fas fa-user-minus"></i> Auto Expulsar');
            autoKickButton.classList.add("auto-action-toggle");
            autoKickButton.addEventListener("click", () => this._toggleAutoKick(autoKickButton));
            autoKickRow.appendChild(autoKickButton);
            automationSection.appendChild(autoKickRow);


            const autoProhibitDrawingRow = domMake.Row();
            const autoProhibitDrawingButton = domMake.Button('<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo');
            autoProhibitDrawingButton.classList.add("auto-action-toggle");
            autoProhibitDrawingButton.addEventListener("click", () => this._toggleAutoProhibitDrawing(autoProhibitDrawingButton));
            autoProhibitDrawingRow.appendChild(autoProhibitDrawingButton);
            automationSection.appendChild(autoProhibitDrawingRow);

            container.appendChild(automationSection);

            this.htmlElements.section.appendChild(container);
        }

        _setupObservers() {
            const playerListElement = document.getElementById("playerlist");
            if (playerListElement) {
                const observer = new MutationObserver(() => {
                    this._updatePlayerLists();

                    if (this._isAutoKickActive) {
                        this._performAutoKick();
                    }
                    if (this._isAutoProhibitDrawingActive) {
                        this._performAutoProhibitDrawing();
                    }
                });

                observer.observe(playerListElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-playerid', 'style'] });
                this._updatePlayerLists();
            }
        }

        _updatePlayerLists() {
            this._humanKickPlayerListContainer.innerHTML = '';
            this._botKickPlayerListContainer.innerHTML = '';
            this._cachedPlayers = [];

            const playerRows = document.querySelectorAll("#playerlist .playerlist-row");
            const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement;
            const myPlayerId = myPlayerIdElement ? myPlayerIdElement.dataset.playerid : null;

            if (playerRows.length <= 1) {
                const noPlayersMessage = domMake.Tree("span", {}, ["No hay otros jugadores."]);
                this._humanKickPlayerListContainer.appendChild(noPlayersMessage.cloneNode(true));
                this._botKickPlayerListContainer.appendChild(noPlayersMessage.cloneNode(true));
                return;
            }

            playerRows.forEach(playerRow => {
                const playerId = playerRow.dataset.playerid;

                if (playerId === myPlayerId) {
                    return;
                }

                const playerName = playerRow.querySelector(".playerlist-name a")?.textContent || `Jugador ${playerId}`;
                const isDrawing = playerRow.querySelector(".playerlist-draw")?.style.display !== 'none';

                this._cachedPlayers.push({ id: parseInt(playerId), name: playerName, isDrawing: isDrawing });
            });

            this._renderHumanKickButtons();
            this._renderBotKickButtons();
        }

        _renderHumanKickButtons() {
            this._humanKickPlayerListContainer.innerHTML = '';
            if (this._cachedPlayers.length === 0) {
                this._humanKickPlayerListContainer.appendChild(domMake.TextNode("No hay jugadores."));
                return;
            }

            this._cachedPlayers.forEach(player => {
                const playerButton = domMake.Button(player.name);
                playerButton.title = `Votar para expulsar a ${player.name} (ID: ${player.id}).`;
                playerButton.addEventListener("click", () => this._sendHumanVoteKick(player.id, player.name));
                this._humanKickPlayerListContainer.appendChild(playerButton);
            });
        }

        _renderBotKickButtons() {
            this._botKickPlayerListContainer.innerHTML = '';
            if (this._cachedPlayers.length === 0) {
                this._botKickPlayerListContainer.appendChild(domMake.TextNode("No hay jugadores."));
                return;
            }

            this._cachedPlayers.forEach(player => {
                const playerButton = domMake.Button(player.name);
                playerButton.title = `Expulsar a ${player.name} (ID: ${player.id}) con el bot seleccionado.`;
                playerButton.addEventListener("click", () => this._sendBotKick(player.id, player.name));
                this._botKickPlayerListContainer.appendChild(playerButton);
            });
        }

        _sendHumanVoteKick(targetPlayerId, targetPlayerName) {
            if (globalThis.sockets && globalThis.sockets.length > 0) {
                const data = _io.emits.sendvotekick(targetPlayerId);
                globalThis.sockets[0].send(data);

            } else {

            }
        }

        _getBot(skipNotification = false) {
            const botManagerClass = this.findGlobal("BotClientManager");
            if (!botManagerClass || !botManagerClass.siblings || botManagerClass.siblings.length === 0) {

                return null;
            }

            const botManagerInstance = botManagerClass.siblings[0];
            const botClientInterfaces = botManagerInstance.children;
            let activeBotClientInterface = null;

            const selectedBotInput = document.querySelector('input[name="botClient"]:checked');
            if (selectedBotInput) {
                activeBotClientInterface = botClientInterfaces.find(bci => bci.htmlElements.input === selectedBotInput);
            }

            if (!activeBotClientInterface && botClientInterfaces.length > 0) {
                activeBotClientInterface = botClientInterfaces[0];

            }

            if (!activeBotClientInterface || !activeBotClientInterface.bot || !activeBotClientInterface.bot.getReadyState()) {

                return null;
            }
            return activeBotClientInterface.bot;
        }

        _sendBotKick(targetPlayerId, targetPlayerName) {
            const bot = this._getBot();
            if (!bot) return;

            const data = _io.emits.sendvotekick(targetPlayerId);
            bot.send(data);
            this.notify("success", `Bot "${bot.name}" envió solicitud de expulsión para ${targetPlayerName}.`);
        }


        _toggleAutoKick(button) {
            this._isAutoKickActive = !this._isAutoKickActive;
            button.classList.toggle("active", this._isAutoKickActive);
            button.innerHTML = this._isAutoKickActive
                ? '<i class="fas fa-user-minus"></i> Auto Expulsar Activo'
                : '<i class="fas fa-user-minus"></i> Auto Expulsar';

            if (this._isAutoKickActive) {
                const bot = this._getBot(true);
                const humanSocketReady = globalThis.sockets && globalThis.sockets.length > 0 && globalThis.sockets[0].readyState === WebSocket.OPEN;

                if (!bot && !humanSocketReady) {

                    this._isAutoKickActive = false;
                    button.classList.remove("active");
                    button.innerHTML = '<i class="fas fa-user-minus"></i> Auto Expulsar';
                    return;
                }
                this.notify("success", "Automatización 'Auto Expulsar' activada. Se intentará expulsar a todos los jugadores.");
                this._kickedPlayersThisSession.clear();
                this._performAutoKick();


            } else {
                clearInterval(this._autoKickInterval);
                this._autoKickInterval = null;

            }
        }

        _performAutoKick() {
            if (!this._isAutoKickActive) return;

            const bot = this._getBot(true);
            const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement;
            const myPlayerId = myPlayerIdElement ? parseInt(myPlayerIdElement.dataset.playerid) : null;
            const humanSocketReady = globalThis.sockets && globalThis.sockets.length > 0 && globalThis.sockets[0].readyState === WebSocket.OPEN;


            if (!bot && !humanSocketReady) {

                const button = document.querySelector('.auto-action-toggle:has(.fa-user-minus)');
                if (button) {
                    this._toggleAutoKick(button);
                }
                return;
            }


            const playersToTarget = this._cachedPlayers.filter(player => {
                return player.id !== myPlayerId && !this._kickedPlayersThisSession.has(player.id);
            });

            if (playersToTarget.length === 0) {
                return;
            }

            playersToTarget.forEach(player => {
                const data = _io.emits.sendvotekick(player.id);
                let kickSent = false;

                if (humanSocketReady) {
                    globalThis.sockets[0].send(data);

                    kickSent = true;
                }

                if (bot && bot.getReadyState()) {
                    bot.send(data);

                    kickSent = true;
                }

                if (kickSent) {
                    this._kickedPlayersThisSession.add(player.id);
                } else {

                }
            });
        }


        _toggleAutoProhibitDrawing(button) {
            this._isAutoProhibitDrawingActive = !this._isAutoProhibitDrawingActive;
            button.classList.toggle("active", this._isAutoProhibitDrawingActive);
            button.innerHTML = this._isAutoProhibitDrawingActive
                ? '<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo Activo'
                : '<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo';

            if (this._isAutoProhibitDrawingActive) {
                const bot = this._getBot();
                if (!bot) {

                    this._isAutoProhibitDrawingActive = false;
                    button.classList.remove("active");
                    button.innerHTML = '<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo';
                    return;
                }
                this.notify("success", "Automatización 'Auto Prohibir Dibujo' activada. Los jugadores que dibujen serán prohibidos periódicamente.");
                this._prohibitedPlayersThisSession.clear();
                this._performAutoProhibitDrawing();
                this._autoProhibitDrawingInterval = setInterval(() => {
                    this._performAutoProhibitDrawing();
                }, 5000);
            } else {
                clearInterval(this._autoProhibitDrawingInterval);
                this._autoProhibitDrawingInterval = null;

            }
        }

        _performAutoProhibitDrawing() {
            if (!this._isAutoProhibitDrawingActive) return;

            const bot = this._getBot();
            if (!bot || !bot.getReadyState()) {

                const button = document.querySelector('.auto-action-toggle:has(.fa-user-slash)');
                if (button) {
                    this._toggleAutoProhibitDrawing(button);
                }
                return;
            }

            const playersToProhibit = this._cachedPlayers.filter(player => {

                return player.isDrawing && !this._prohibitedPlayersThisSession.has(player.id);
            });

            if (playersToProhibit.length === 0) {
                return;
            }

            playersToProhibit.forEach(player => {

                const data = _io.emits.pgdrawvote(player.id, 0);
                bot.send(data);
                this._prohibitedPlayersThisSession.add(player.id);

            });
        }
    }
})("QBit");




const GLITCH_TEXT_STRINGS = {
    "welcome_glitch": "꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅꧅"
};



(function PlayerKickToolsModule() {
const QBit = globalThis[arguments[0]];



QBit.Styles.addRules([
    `#${QBit.identifier} .kick-tools-section {
        border: 1px solid var(--CE-color);
        border-radius: .25rem;
        padding: 5px;
        margin-bottom: 10px;
        background-color: var(--CE-bg_color);
    }`,
    `#${QBit.identifier} .kick-tools-section-title {
        font-weight: bold;
        margin-bottom: 5px;
        color: var(--dark-blue-title);
        text-align: center;
    }`,
    `#${QBit.identifier} .kick-tools-player-list {
        display: flex;
        flex-wrap: wrap;
        gap: 5px;
        max-height: 150px; /* Limit height to show scrollbar if many players */
        overflow-y: auto;
        padding: 5px;
        border: 1px dashed var(--CE-color);
        border-radius: .25rem;
        background-color: var(--CE-bg_color_alt);
        font-family: monospace;
        font-size: 0.8em;
        white-space: pre-wrap; /* For player lists, if applicable */
    }`,
    `#${QBit.identifier} .kick-tools-log-area .log-entry { /* These apply to entries *inside* the new divs */
        width: 100%; /* Each log entry on its own line */
        margin-bottom: 2px;
        color: var(--CE-text_color);
        overflow-wrap: anywhere; /* IMPORTANT: Ensures long text breaks to fit */
    }`,
    `#${QBit.identifier} .kick-tools-log-area .log-header { /* These apply to headers *inside* the new divs */
        font-weight: bold;
        color: var(--dark-blue-title);
        margin-top: 5px;
        border-bottom: 1px solid var(--CE-color);
        padding-bottom: 2px;
    }`,
    `#${QBit.identifier} .kick-tools-player-list .btn {
        flex: 0 0 auto; /* Prevent stretching */
        margin: 0; /* Remove default margin from btn class */
        padding: 3px 8px; /* Compact padding */
        font-size: 0.8em;
    }`,
    `#${QBit.identifier} .auto-action-toggle.active { /* Generic style for automation toggles */
        background-color: var(--info); /* Consistent active color */
        color: white;
    }`,
    `#${QBit.identifier} .log-success { color: #28a745; }`,
    `#${QBit.identifier} .log-info { color: #17a2b8; }`,
    `#${QBit.identifier} .log-warning { color: #ffc107; }`,
    `#${QBit.identifier} .log-error { color: #dc3545; }`
]);

class PlayerKickTools extends QBit {
    static dummy1 = QBit.register(this);
    static dummy2 = QBit.bind(this, "CubeEngine");

    _humanKickPlayerListContainer;
    _botKickPlayerListContainer;
    _cachedPlayers = [];


    _isAutoKickActive = false;
    _kickedPlayersThisSession = new Set();


    _isAutoProhibitDrawingActive = false;
    _prohibitedPlayersThisSession = new Set();


    _isAutoRoomSwitchActive = false;
    _autoRoomSwitchInterval = null;
    _botLastKnownRoomId = null;
    _isProcessingRoom = false;

    _activeBotInstance = null;
    _humanSocketInstance = null;
    _botRoomPlayers = [];


    _reportSpamInterval = null;
    _rulesSpamInterval = null;
    _afkSpamInterval = null;
    _glitchSpamInterval = null;


    _roomActionLog = {
        currentRoomId: null,
        sessions: []
    };
    _currentRoomLogEntry = null;
    _commandCounters = {
        spamTodoCount: 0,
        glitchTextCount: 0
    };
    _playerLogUIContainer;
    _commandCounterUIContainer;

    constructor() {
        super("⚡Bot Strike Force", '<i class="fas fa-bolt"></i>');
        this._onStartup();
    }

    _onStartup() {
        this._loadInterface();
        this._setupHumanPlayerListObserver();
        this._humanSocketInstance = this._getHumanSocket();
        this._activeBotInstance = this._getBot(true);
    }

    _getHumanSocket() {
        return globalThis.sockets && globalThis.sockets.length > 0 && globalThis.sockets[0].readyState === WebSocket.OPEN
            ? globalThis.sockets[0] : null;
    }

    _formatTimestamp(date) {
        return date.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
    }

    _loadInterface() {
        const container = domMake.Tree("div");


        const humanKickSection = domMake.Tree("div", { class: "kick-tools-section" });
        humanKickSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Voto para Expulsar (Tú)"]));
        this._humanKickPlayerListContainer = domMake.IconList({ class: "kick-tools-player-list" });
        humanKickSection.appendChild(this._humanKickPlayerListContainer);



        const botKickSection = domMake.Tree("div", { class: "kick-tools-section" });
        botKickSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Expulsar con Bot (Manual)"]));
        this._botKickPlayerListContainer = domMake.IconList({ class: "kick-tools-player-list" });
        botKickSection.appendChild(this._botKickPlayerListContainer);



        const automationSection = domMake.Tree("div", { class: "kick-tools-section" });



        const autoKickRow = domMake.Row();
        const autoKickButton = domMake.Button('<i class="fas fa-user-minus"></i> Auto Expulsar');
        autoKickButton.classList.add("auto-action-toggle");
        autoKickButton.addEventListener("click", () => this._toggleAutoKick(autoKickButton));

        automationSection.appendChild(autoKickRow);


        const autoProhibitDrawingRow = domMake.Row();
        const autoProhibitDrawingButton = domMake.Button('<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo');
        autoProhibitDrawingButton.classList.add("auto-action-toggle");
        autoProhibitDrawingButton.addEventListener("click", () => this._toggleAutoProhibitDrawing(autoProhibitDrawingButton));

        automationSection.appendChild(autoProhibitDrawingRow);


        const autoRoomSwitchRow = domMake.Row();
        const autoRoomSwitchButton = domMake.Button('<i class="fas fa-random"></i> Auto Cambiar Sala (Bot)');
        autoRoomSwitchButton.classList.add("auto-action-toggle");
        autoRoomSwitchButton.addEventListener("click", () => this._toggleAutoRoomSwitch(autoRoomSwitchButton));
        autoRoomSwitchRow.appendChild(autoRoomSwitchButton);
        automationSection.appendChild(autoRoomSwitchRow);


        const autoSpamTodoRow = domMake.Row();
        const autoSpamTodoButton = domMake.Button('<i class="fas fa-bullhorn"></i> Spam TODO (Bot)');
        autoSpamTodoButton.classList.add("auto-action-toggle");
        autoSpamTodoButton.addEventListener("click", () => this._toggleAllSpam(autoSpamTodoButton));
        autoSpamTodoRow.appendChild(autoSpamTodoButton);
        automationSection.appendChild(autoSpamTodoRow);


        const autoSpamGlitchRow = domMake.Row();
        const autoSpamGlitchButton = domMake.Button('<i class="fas fa-comments"></i> Spam Glitch Text (Bot)');
        autoSpamGlitchButton.classList.add("auto-action-toggle");
        autoSpamGlitchButton.addEventListener("click", () => this._toggleGlitchTextSpam(autoSpamGlitchButton));
        autoSpamGlitchRow.appendChild(autoSpamGlitchButton);
        automationSection.appendChild(autoSpamGlitchRow);

        container.appendChild(automationSection);


        const logSection = domMake.Tree("div", { class: "kick-tools-section" });
        logSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Registro de Acciones de Bot (Tú en sala principal)"]));



        this._playerLogUIContainer = document.getElementById("BotActions");
        this._commandCounterUIContainer = document.getElementById("CommandSent");


        logSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Jugadores Procesados por Sala (Bot)"]));
        if (this._playerLogUIContainer) {
            this._playerLogUIContainer.innerHTML = '';
            logSection.appendChild(this._playerLogUIContainer);
        } else {


            this._playerLogUIContainer = domMake.Tree("div", {
                class: "kick-tools-log-area",
                style: "height: 180px; width: 100%; background: rgb(238, 238, 238); overflow-wrap: anywhere; border: 4px solid rgb(238, 238, 238); border-radius: 2px; overflow-y: scroll; color: rgb(54, 117, 206);"
            });
            logSection.appendChild(this._playerLogUIContainer);
        }


        logSection.appendChild(domMake.Tree("div", { class: "kick-tools-section-title" }, ["Contadores de Comandos Enviados (Bot)"]));
        if (this._commandCounterUIContainer) {
            this._commandCounterUIContainer.innerHTML = '';
            logSection.appendChild(this._commandCounterUIContainer);
        } else {


            this._commandCounterUIContainer = domMake.Tree("div", {
                class: "kick-tools-log-area",
                style: "height: 180px; width: 100%; background: rgb(238, 238, 238); overflow-wrap: anywhere; border: 4px solid rgb(238, 238, 238); border-radius: 2px; overflow-y: scroll; color: rgb(54, 117, 206);"
            });
            logSection.appendChild(this._commandCounterUIContainer);
        }

        container.appendChild(logSection);
        this.htmlElements.section.appendChild(container);
        this._updateCommandCountersUI();
    }

    _setupHumanPlayerListObserver() {
        const playerListElement = document.getElementById("playerlist");
        if (playerListElement) {
            const observer = new MutationObserver(() => {
                this._updatePlayerLists();
            });
            observer.observe(playerListElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-playerid', 'style'] });
            this._updatePlayerLists();
        }
    }

    _updatePlayerLists() {
        this._humanKickPlayerListContainer.innerHTML = '';
        this._botKickPlayerListContainer.innerHTML = '';
        this._cachedPlayers = [];

        const playerRows = document.querySelectorAll("#playerlist .playerlist-row");
        const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement;
        const myPlayerId = myPlayerIdElement ? myPlayerIdElement.dataset.playerid : null;

        if (playerRows.length <= 1) {
            const noPlayersMessage = domMake.Tree("span", {}, ["No hay otros jugadores."]);
            this._humanKickPlayerListContainer.appendChild(noPlayersMessage.cloneNode(true));
            this._botKickPlayerListContainer.appendChild(noPlayersMessage.cloneNode(true));
            return;
        }

        playerRows.forEach(playerRow => {
            const playerId = playerRow.dataset.playerid;
            if (playerId === myPlayerId) {
                return;
            }

            const playerName = playerRow.querySelector(".playerlist-name a")?.textContent || `Jugador ${playerId}`;
            const isDrawing = playerRow.querySelector(".playerlist-draw")?.style.display !== 'none';

            this._cachedPlayers.push({ id: parseInt(playerId), name: playerName, isDrawing: isDrawing });
        });

        this._renderHumanKickButtons();
        this._renderBotKickButtons();
    }

    _renderHumanKickButtons() {
        this._humanKickPlayerListContainer.innerHTML = '';
        if (this._cachedPlayers.length === 0) {
            this._humanKickPlayerListContainer.appendChild(domMake.TextNode("No hay jugadores."));
            return;
        }

        this._cachedPlayers.forEach(player => {
            const playerButton = domMake.Button(player.name);
            playerButton.title = `Votar para expulsar a ${player.name} (ID: ${player.id}).`;
            playerButton.addEventListener("click", () => this._sendHumanVoteKick(player.id, player.name));
            this._humanKickPlayerListContainer.appendChild(playerButton);
        });
    }

    _renderBotKickButtons() {
        this._botKickPlayerListContainer.innerHTML = '';
        if (this._cachedPlayers.length === 0) {
            this._botKickPlayerListContainer.appendChild(domMake.TextNode("No hay jugadores."));
            return;
        }

        this._cachedPlayers.forEach(player => {
            const playerButton = domMake.Button(player.name);
            playerButton.title = `Expulsar a ${player.name} (ID: ${player.id}) con el bot seleccionado.`;
            playerButton.addEventListener("click", () => this._sendBotKick(player.id, player.name));
            this._botKickPlayerListContainer.appendChild(playerButton);
        });
    }

    _sendHumanVoteKick(targetPlayerId, targetPlayerName) {
        this._humanSocketInstance = this._getHumanSocket();
        if (this._humanSocketInstance) {
            const data = _io.emits.sendvotekick(targetPlayerId);
            this._humanSocketInstance.send(data);
            this.notify("info", `Voto para expulsar enviado para ${targetPlayerName} (ID: ${targetPlayerId}) desde tu cuenta.`);
        } else {
            this.notify("warning", "No hay conexión WebSocket activa (humana) para enviar el voto de expulsión.");
        }
    }

    _getBot(skipNotification = false) {
        const botManagerClass = this.findGlobal("BotClientManager");
        if (!botManagerClass || !botManagerClass.siblings || botManagerClass.siblings.length === 0) {
            if (!skipNotification) this.notify("warning", "No hay instancias activas de 'BotClientManager'. Por favor, crea uno desde 'CubeEngine'.");
            return null;
        }

        const botManagerInstance = botManagerClass.siblings[0];
        const botClientInterfaces = botManagerInstance.children;
        let activeBotClientInterface = null;

        const selectedBotInput = document.querySelector('input[name="botClient"]:checked');
        if (selectedBotInput) {
            activeBotClientInterface = botClientInterfaces.find(bci => bci.htmlElements.input === selectedBotInput);
        }

        if (!activeBotClientInterface && botClientInterfaces.length > 0) {
            activeBotClientInterface = botClientInterfaces[0];
            if (!skipNotification) this.notify("info", `No se seleccionó un bot. Usando el primer bot disponible: ${activeBotClientInterface.getName()}.`);
        }

        if (!activeBotClientInterface || !activeBotClientInterface.bot || !activeBotClientInterface.bot.getReadyState()) {
            if (!skipNotification) this.notify("warning", `El bot "${activeBotClientInterface ? activeBotClientInterface.getName() : 'desconocido'}" no está conectado y listo para enviar comandos.`);
            return null;
        }
        return activeBotClientInterface.bot;
    }

    _sendBotKick(targetPlayerId, targetPlayerName) {
        this._activeBotInstance = this._getBot();
        if (!this._activeBotInstance) return;

        const data = _io.emits.sendvotekick(targetPlayerId);
        this._activeBotInstance.send(data);
        this.notify("success", `Bot "${this._activeBotInstance.name}" envió solicitud de expulsión para ${targetPlayerName}.`);
    }

    _toggleAutoKick(button) {
        this._isAutoKickActive = !this._isAutoKickActive;
        button.classList.toggle("active", this._isAutoKickActive);
        button.innerHTML = this._isAutoKickActive
            ? '<i class="fas fa-user-minus"></i> Auto Expulsar Activo'
            : '<i class="fas fa-user-minus"></i> Auto Expulsar';

        if (this._isAutoKickActive) {
            this._activeBotInstance = this._getBot(true);
            if (!this._activeBotInstance) {
                this.notify("error", "Necesitas un bot activo y conectado para usar 'Auto Expulsar'.");
                this._isAutoKickActive = false;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-user-minus"></i> Auto Expulsar';
                return;
            }
            this.notify("success", "Automatización 'Auto Expulsar' activada (desde bot).");
        } else {
            this.notify("info", "Automatización 'Auto Expulsar' desactivada.");
        }
    }

_performAutoKick() {
    if (!this._isAutoKickActive || !this._isAutoRoomSwitchActive || !this._activeBotInstance || !this._activeBotInstance.getReadyState()) return;

    const botRoomPlayers = this._activeBotInstance.room?.players || [];
    const myBotId = this._activeBotInstance.id;


    const playersToTarget = botRoomPlayers.filter(player => {
        return player.id !== myBotId;
    });

    if (playersToTarget.length === 0) {
        return;
    }

    playersToTarget.forEach(player => {
        const data = _io.emits.sendvotekick(player.id);
        this._activeBotInstance.send(data);

        const now = new Date();
        this._currentRoomLogEntry.playersKicked.push({ id: player.id, name: player.name, timestamp: this._formatTimestamp(now) });
        this._updatePlayerLogUI();
    });
}

    _toggleAutoProhibitDrawing(button) {
        this._isAutoProhibitDrawingActive = !this._isAutoProhibitDrawingActive;
        button.classList.toggle("active", this._isAutoProhibitDrawingActive);
        button.innerHTML = this._isAutoProhibitDrawingActive
            ? '<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo Activo'
            : '<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo';

        if (this._isAutoProhibitDrawingActive) {
            this._activeBotInstance = this._getBot(true);
            if (!this._activeBotInstance) {
                this.notify("error", "Necesitas un bot activo y conectado para usar 'Auto Prohibir Dibujo'.");
                this._isAutoProhibitDrawingActive = false;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-user-slash"></i> Auto Prohibir Dibujo';
                return;
            }
            this.notify("success", "Automatización 'Auto Prohibir Dibujo' activada (desde bot).");
        } else {
            this.notify("info", "Automatización 'Auto Prohibir Dibujo' desactivada.");
        }
    }

_performAutoProhibitDrawing() {
    if (!this._isAutoProhibitDrawingActive || !this._isAutoRoomSwitchActive || !this._activeBotInstance || !this._activeBotInstance.getReadyState()) return;

    const botRoomPlayers = this._activeBotInstance.room?.players || [];
    const myBotId = this._activeBotInstance.id;


    const playersToProhibit = botRoomPlayers.filter(player => {
        return player.id !== myBotId;
    });

    if (playersToProhibit.length === 0) {
        return;
    }

    playersToProhibit.forEach(player => {
        const data = _io.emits.pgdrawvote(player.id, 0);
        this._activeBotInstance.send(data);

        const now = new Date();
        this._currentRoomLogEntry.playersProhibited.push({ id: player.id, name: player.name, timestamp: this._formatTimestamp(now) });
        this._updatePlayerLogUI();
    });
}

    _toggleAutoRoomSwitch(button) {
        this._isAutoRoomSwitchActive = !this._isAutoRoomSwitchActive;
        button.classList.toggle("active", this._isAutoRoomSwitchActive);
        button.innerHTML = this._isAutoRoomSwitchActive
            ? '<i class="fas fa-random"></i> Auto Cambiar Sala Activo'
            : '<i class="fas fa-random"></i> Auto Cambiar Sala';

        if (this._isAutoRoomSwitchActive) {
            this._activeBotInstance = this._getBot(true);
            if (!this._activeBotInstance || !this._activeBotInstance.getReadyState()) {
                this.notify("error", "Necesitas un bot activo y conectado para usar 'Auto Cambiar Sala'.");
                this._isAutoRoomSwitchActive = false;
                button.classList.remove("active");
                button.innerHTML = '<i class="fas fa-random"></i> Auto Cambiar Sala';
                return;
            }

            this.notify("success", "Automatización 'Auto Cambiar Sala' activada. El bot comenzará a navegar y procesar salas.");

            setTimeout(() => {
                const initialBotRoomId = this._activeBotInstance.room?.id;
                if (initialBotRoomId) {
                    this._botLastKnownRoomId = initialBotRoomId;
                    this._processBotRoom(initialBotRoomId, true);
                } else {
                    this.notify("warning", "El bot no está en una sala válida al activar 'Auto Cambiar Sala'.");
                }
            }, 500);

            this._autoRoomSwitchInterval = setInterval(() => {
                this._navigateNextRoomForBot();
            }, 3000);

        } else {
            clearInterval(this._autoRoomSwitchInterval);
            this._autoRoomSwitchInterval = null;
            this.notify("info", "Automatización 'Auto Cambiar Sala' desactivada.");
            this._kickedPlayersThisSession.clear();
            this._prohibitedPlayersThisSession.clear();
            this._isProcessingRoom = false;

            if (this._currentRoomLogEntry) {
                this._roomActionLog.sessions.push(this._currentRoomLogEntry);
                this._currentRoomLogEntry = null;
                this._updatePlayerLogUI();
            }
        }
    }

    _navigateNextRoomForBot() {


        if (this._roomActionsInterval) {
            clearInterval(this._roomActionsInterval);
            this._roomActionsInterval = null;
        }

        if (this._isProcessingRoom || !this._activeBotInstance || !this._activeBotInstance.getReadyState()) {
            return;
        }

        try {
            this.notify("info", `Bot "${this._activeBotInstance.name}" intentando cambiar a la siguiente sala.`);

            const data = _io.emits.pgswtichroom();
            this._activeBotInstance.send(data);

            setTimeout(() => {
                const currentBotRoomId = this._activeBotInstance.room?.id;
                if (currentBotRoomId && currentBotRoomId !== this._botLastKnownRoomId) {
                    this._processBotRoom(currentBotRoomId);
                    this._botLastKnownRoomId = currentBotRoomId;
                } else {
                    this.notify("info", `Bot se quedó en la sala ${this._botLastKnownRoomId} o falló el cambio. Re-procesando.`);
                    this._processBotRoom(this._botLastKnownRoomId);
                }
            }, 200);
        } catch (e) {
            this.notify("error", `Error al intentar que el bot cambie de sala: ${e.message}. Deteniendo automatización.`);
            const button = document.querySelector('.auto-action-toggle:has(.fa-random)');
            if (button) {
                this._toggleAutoRoomSwitch(button);
            }
        }
    }

    _processBotRoom(botRoomId, isInitialSetup = false) {
        if (!this._isAutoRoomSwitchActive || this._isProcessingRoom) {
            return;
        }

        this._isProcessingRoom = true;
        this._activeBotInstance = this._getBot(true);
        if (!this._activeBotInstance || !this._activeBotInstance.getReadyState()) {
            this.notify("warning", "Bot desconectado durante el procesamiento de sala. Deteniendo acciones automáticas.");
            this._isProcessingRoom = false;
            return;
        }

        if (this._currentRoomLogEntry && !isInitialSetup) {
            this._roomActionLog.sessions.push(this._currentRoomLogEntry);
        }

        const now = new Date();
        this._currentRoomLogEntry = {
            roomId: botRoomId,
            timestamp: this._formatTimestamp(now),
            playersKicked: [],
            playersProhibited: [],
            commandsSent: {
                spamTodoCount: 0,
                glitchTextCount: 0
            }
        };
        this._roomActionLog.currentRoomId = botRoomId;

        this._kickedPlayersThisSession.clear();
        this._prohibitedPlayersThisSession.clear();

        this.notify("info", `Bot "${this._activeBotInstance.name}" procesando sala: ${botRoomId}.`);
        this._updatePlayerLogUI();

        this._botRoomPlayers = this._activeBotInstance.room?.players || [];

        if (this._isAutoProhibitDrawingActive) {
            this._performAutoProhibitDrawing();
        }
        if (this._isAutoKickActive) {
            this._performAutoKick();
        }


    this._roomActionsInterval = setInterval(() => {
        if (this._isAutoProhibitDrawingActive) {
            this._performAutoProhibitDrawing();
        }
        if (this._isAutoKickActive) {
            this._performAutoKick();
        }
    }, 500);

    this._isProcessingRoom = false;
}


    _sendBotGameNotification(type) {
        if (!this._activeBotInstance || !this._activeBotInstance.getReadyState()) {
            return;
        }

        let data;
        if (type === "report") {
            data = `42["clientnotify",-1,2,["", "Generic Report"]]`;
        } else if (type === "rules") {
            data = `42["clientnotify",-1,100,[2]]`;
        } else if (type === "afk") {
            data = `42["playerafk"]`;
        }
        if (data) {
            this._activeBotInstance.send(data);
            if (this._currentRoomLogEntry) {
                this._currentRoomLogEntry.commandsSent.spamTodoCount++;
                this._commandCounters.spamTodoCount++;
                this._updateCommandCountersUI();
            }
        }
    }

    _toggleAllSpam(button) {
        const enable = !button.classList.contains("active");

        if (enable) {
            this._activeBotInstance = this._getBot(true);
            if (!this._activeBotInstance || !this._activeBotInstance.getReadyState()) {
                this.notify("error", "Necesitas un bot activo y conectado para usar 'Spam TODO'.");
                button.classList.remove('active');
                button.innerHTML = '<i class="fas fa-bullhorn"></i> Spam TODO (Bot)';
                return;
            }
            button.classList.add('active');
            button.innerHTML = '<i class="fas fa-bullhorn"></i> Spam TODO Activo (Bot)';
            this.notify("success", "Spam TODO activado (Reportes, Reglas, AFK) desde el bot.");

            const delay = 700;
            this._reportSpamInterval = setInterval(() => this._sendBotGameNotification("report"), delay);
            this._rulesSpamInterval = setInterval(() => this._sendBotGameNotification("rules"), delay);
            this._afkSpamInterval = setInterval(() => this._sendBotGameNotification("afk"), delay);
        } else {
            button.classList.remove('active');
            button.innerHTML = '<i class="fas fa-bullhorn"></i> Spam TODO (Bot)';
            this.notify("info", "Spam TODO desactivado.");

            clearInterval(this._reportSpamInterval); this._reportSpamInterval = null;
            clearInterval(this._rulesSpamInterval); this._rulesSpamInterval = null;
            clearInterval(this._afkSpamInterval); this._afkSpamInterval = null;
        }
    }

    _toggleGlitchTextSpam(button) {
        const enable = !button.classList.contains("active");

        if (enable) {
            this._activeBotInstance = this._getBot(true);
            if (!this._activeBotInstance || !this._activeBotInstance.getReadyState()) {
                this.notify("error", "Necesitas un bot activo y conectado para usar 'Spam Glitch Text'.");
                button.classList.remove('active');
                button.textContent = '<i class="fas fa-comments"></i> Spam Glitch Text (Bot)';
                return;
            }
            button.classList.add('active');
            button.innerHTML = '<i class="fas fa-comments"></i> Spam Glitch Text Activo (Bot)';
            this.notify("success", "Spam Glitch Text activado desde el bot.");

            const delay = 700;
            this._glitchSpamInterval = setInterval(() => {
                if (!this._activeBotInstance || !this._activeBotInstance.getReadyState()) {
                    this.notify("warning", "Bot desconectado, deteniendo Spam Glitch Text.");
                    this._toggleGlitchTextSpam(button);
                    return;
                }
                const glitchTexts = Object.values(GLITCH_TEXT_STRINGS);
                if (glitchTexts.length === 0) {
                    this.notify("warning", "No hay textos glitch definidos. Deteniendo Spam Glitch Text.");
                    this._toggleGlitchTextSpam(button);
                    return;
                }
                let messageToSend = glitchTexts[Math.floor(Math.random() * glitchTexts.length)];
                messageToSend = messageToSend.replace(/@Facemojikeyboard/g, '').trim();
                messageToSend = (messageToSend ? '\n' : '') + messageToSend;

                this._activeBotInstance.emit("chatmsg", messageToSend);

                if (this._currentRoomLogEntry) {
                    this._currentRoomLogEntry.commandsSent.glitchTextCount++;
                    this._commandCounters.glitchTextCount++;
                    this._updateCommandCountersUI();
                }

            }, delay);
        } else {
            button.classList.remove('active');
            button.innerHTML = '<i class="fas fa-comments"></i> Spam Glitch Text (Bot)';
            this.notify("info", "Spam Glitch Text desactivado.");
            clearInterval(this._glitchSpamInterval); this._glitchSpamInterval = null;
        }
    }


    _updatePlayerLogUI() {
        if (!this._playerLogUIContainer) {
            console.error("BotActions log container not found!");
            return;
        }
        this._playerLogUIContainer.innerHTML = '';

        const sessionsToDisplay = [...this._roomActionLog.sessions];
        if (this._currentRoomLogEntry) {
            sessionsToDisplay.push(this._currentRoomLogEntry);
        }

        sessionsToDisplay.forEach(session => {
            const roomHeader = domMake.Tree("div", { class: "log-header" }, [`Sala del Bot: ${session.roomId} (Inicio: ${session.timestamp})`]);
            this._playerLogUIContainer.appendChild(roomHeader);

            if (session.playersKicked.length === 0 && session.playersProhibited.length === 0 && session.commandsSent.spamTodoCount === 0 && session.commandsSent.glitchTextCount === 0) {
                this._playerLogUIContainer.appendChild(domMake.Tree("div", { class: "log-entry log-info" }, ["  (No hay acciones de bot en esta sala hasta ahora)"]));
            } else {
                session.playersKicked.forEach(p => {
                    this._playerLogUIContainer.appendChild(domMake.Tree("div", { class: "log-entry log-success" }, [`  Player ${p.name} (ID: ${p.id}) kickeado - ${p.timestamp}`]));
                });
                session.playersProhibited.forEach(p => {
                    this._playerLogUIContainer.appendChild(domMake.Tree("div", { class: "log-entry log-warning" }, [`  Player ${p.name} (ID: ${p.id}) prohibido dibujar - ${p.timestamp}`]));
                });
            }
        });

        this._playerLogUIContainer.scrollTop = this._playerLogUIContainer.scrollHeight;
    }

    _updateCommandCountersUI() {
        if (!this._commandCounterUIContainer) {
            console.error("CommandSent log container not found!");
            return;
        }
        this._commandCounterUIContainer.innerHTML = '';
        this._commandCounterUIContainer.appendChild(
            domMake.Tree("div", { class: "log-entry log-info" }, [`Spam TODO Sent (total por bot): ${this._commandCounters.spamTodoCount} veces`])
        );
        this._commandCounterUIContainer.appendChild(
            domMake.Tree("div", { class: "log-entry log-info" }, [`Glitch Text Sent (total por bot): ${this._commandCounters.glitchTextCount} veces`])
        );

        if (this._currentRoomLogEntry) {
            this._commandCounterUIContainer.appendChild(
                domMake.Tree("div", { class: "log-entry log-info" }, [`- Spam TODO (bot en sala ${this._currentRoomLogEntry.roomId}): ${this._currentRoomLogEntry.commandsSent.spamTodoCount}`])
            );
            this._commandCounterUIContainer.appendChild(
                domMake.Tree("div", { class: "log-entry log-info" }, [`- Glitch Text (bot en sala ${this._currentRoomLogEntry.roomId}): ${this._currentRoomLogEntry.commandsSent.glitchTextCount}`])
            );
        }
    }
}

})("QBit");



(function PlayerSocialsModule() {
    const QBit = globalThis[arguments[0]];


    const TOKEN_NAMES = {
        0: "Thumbs Up",
        1: "Heart",
        2: "Paint Brush",
        3: "Cocktail",
        4: "Peace Sign",
        5: "Feather",
        6: "Trophy",
        7: "Mug",
        8: "Gift"
    };

    QBit.Styles.addRules([
        `#${QBit.identifier} .player-socials-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .player-socials-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title); /* Use existing style var */
            text-align: center;
        }`,
        `#${QBit.identifier} .player-socials-afk-toggle,
         #${QBit.identifier} .player-socials-status-flags .status-flag-button,
         #${QBit.identifier} .player-socials-auto-friend-request-toggle { /* Added for new button */
            background-color: var(--secondary);
            color: var(--dark);
            width: 100%;
            padding: 5px 10px;
            box-sizing: border-box;
        }`,
        `#${QBit.identifier} .player-socials-afk-toggle.active,
         #${QBit.identifier} .player-socials-status-flags .status-flag-button.active,
         #${QBit.identifier} .player-socials-auto-friend-request-toggle.active { /* Added for new button */
            background-color: var(--info); /* Consistent active color */
            color: white;
        }`,
        `#${QBit.identifier} .player-socials-token-list .icon {
            width: 38px; /* Slightly larger icons */
            height: 38px;
            min-width: 38px;
            min-height: 38px;
            font-size: 1.2em; /* Larger icon itself */
        }`,

        `#${QBit.identifier} .manual-friend-request-list {
            max-height: 200px;
            overflow-y: auto;
            border: 1px dashed var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            display: flex;
            flex-direction: column;
            gap: 5px;
        }`,
        `#${QBit.identifier} .manual-friend-request-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            background-color: rgba(0,0,0,0.1);
            padding: 5px;
            border-radius: .15rem;
            font-size: 0.9em;
        }`,
        `#${QBit.identifier} .manual-friend-request-item .player-name {
            flex-grow: 1;
            font-weight: bold;
            margin-right: 5px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }`,
        `#${QBit.identifier} .manual-friend-request-item .action-buttons button {
            padding: 3px 8px;
            font-size: 0.8em;
            margin-left: 5px;
            white-space: nowrap;
        }`,
        `#${QBit.identifier} .manual-friend-request-item .status-text {
            color: var(--info); /* Consistent active color */
            font-weight: bold;
            margin-right: 5px;
            white-space: nowrap;
        }`,
        `#${QBit.identifier} .manual-friend-request-item .status-text.guest {
            color: var(--warning); /* Yellow for guests/non-logged */
        }`,
        `#${QBit.identifier} .manual-friend-request-item .status-text.already-friend {
            color: var(--success); /* Green for already friends */
        }`,
        `#${QBit.identifier} .manual-friend-request-item .status-text.pending {
            color: var(--orange); /* Orange for pending requests */
        }`
    ]);

    class PlayerSocials extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");


        #messageInput;


        #isAfkActive = false;
        #afkToggleButton;


        #playerList = [];


        #statusFlagsState = {};


        #isAutoFriendRequestActive = false;
        #autoFriendRequestToggleButton;
        #autoFriendRequestSentUids = new Set();
        #autoFriendRequestTimer = null;


        #manualFriendRequestListContainer;
        #manualFriendRequestSentUids = new Set();

        constructor() {
            super("🤝Community Panel", '<i class="fas fa-handshake"></i>');
            this.#onStartup();
        }

        #onStartup() {
            this.#loadInterface();
            this.#setupObservers();
        }

        #loadInterface() {
            const container = domMake.Tree("div");


            const chatSection = domMake.Tree("div", { class: "player-socials-section" });
            chatSection.appendChild(domMake.Tree("div", { class: "player-socials-section-title" }, ["Enviar Mensaje"]));
            const chatRow = domMake.Row();
            this.#messageInput = domMake.Tree("input", { type: "text", placeholder: "Tu mensaje..." });
            const sendButton = domMake.Button('<i class="fas fa-paper-plane"></i>');
            sendButton.classList.add("icon");
            sendButton.addEventListener("click", () => {
                this.#sendMessage(this.#messageInput.value);
                this.#messageInput.value = '';
            });
            this.#messageInput.addEventListener("keypress", (event) => {
                if (event.keyCode === 13) {
                    this.#sendMessage(this.#messageInput.value);
                    this.#messageInput.value = '';
                }
            });
            chatRow.appendAll(this.#messageInput, sendButton);
            chatSection.appendChild(chatRow);
            container.appendChild(chatSection);


            const afkSection = domMake.Tree("div", { class: "player-socials-section" });
            afkSection.appendChild(domMake.Tree("div", { class: "player-socials-section-title" }, ["Estado AFK"]));
            const afkRow = domMake.Row();
            this.#afkToggleButton = domMake.Button("Toggle AFK");
            this.#afkToggleButton.classList.add("player-socials-afk-toggle");
            this.#afkToggleButton.addEventListener("click", () => this.#toggleAfkStatus());
            afkRow.appendChild(this.#afkToggleButton);
            afkSection.appendChild(afkRow);
            container.appendChild(afkSection);


            const tokensSection = domMake.Tree("div", { class: "player-socials-section" });
            tokensSection.appendChild(domMake.Tree("div", { class: "player-socials-section-title" }, ["Dar Emblemas"]));
            const tokensRow = domMake.IconList({ class: "player-socials-token-list" });
            const tokens = [
                { id: 0, icon: '<i class="fas fa-thumbs-up"></i>' },
                { id: 1, icon: '<i class="fas fa-heart"></i>' },
                { id: 2, icon: '<i class="fas fa-paint-brush"></i>' },
                { id: 3, icon: '<i class="fas fa-cocktail"></i>' },
                { id: 4, icon: '<i class="fas fa-hand-peace"></i>' },
                { id: 5, icon: '<i class="fas fa-feather-alt"></i>' },
                { id: 6, icon: '<i class="fas fa-trophy"></i>' },
                { id: 7, icon: '<i class="fas fa-mug-hot"></i>' },
                { id: 8, icon: '<i class="fas fa-gift"></i>' }
            ];
            tokens.forEach(token => {
                const tokenButton = domMake.Button(token.icon);
                tokenButton.classList.add("icon");
                tokenButton.title = `Dar Emblema: ${TOKEN_NAMES[token.id]}`;
                tokenButton.addEventListener("click", () => this.#giveToken(token.id));
                tokensRow.appendChild(tokenButton);
            });
            tokensSection.appendChild(tokensRow);
            container.appendChild(tokensSection);


            const statusSection = domMake.Tree("div", { class: "player-socials-section" });
            statusSection.appendChild(domMake.Tree("div", { class: "player-socials-section-title" }, ["Establecer Estado"]));
            const flags = [
                { id: 0, name: "Música", icon: '<i class="fas fa-music"></i>' },
                { id: 1, name: "AFK 1", icon: '<i class="fas fa-bed"></i>' },
                { id: 2, name: "AFK 2", icon: '<i class="fas fa-couch"></i>' },
                { id: 3, name: "Inventario Abierto", icon: '<i class="fas fa-box-open"></i>' },
                { id: 4, name: "Lista Amigos Abierta", icon: '<i class="fas fa-user-friends"></i>' }
            ];
            flags.forEach(flag => {
                const flagRow = domMake.Row();
                const toggleButton = domMake.Button(flag.icon + " " + flag.name);
                toggleButton.classList.add("status-flag-button");
                toggleButton.dataset.flagId = flag.id;
                toggleButton.dataset.isActive = "false";
                this.#statusFlagsState[flag.id] = false;

                toggleButton.addEventListener("click", () => this.#toggleStatusFlag(flag.id, toggleButton));
                flagRow.appendChild(toggleButton);
                statusSection.appendChild(flagRow);
            });
            container.appendChild(statusSection);



            const manualFriendRequestSection = domMake.Tree("div", { class: "player-socials-section" });
            manualFriendRequestSection.appendChild(domMake.Tree("div", { class: "player-socials-section-title" }, ["Solicitudes de Amistad Automaticas"]));
            this.#manualFriendRequestListContainer = domMake.Tree("div", { class: "manual-friend-request-list" });
            manualFriendRequestSection.appendChild(this.#manualFriendRequestListContainer);
            container.appendChild(manualFriendRequestSection);

            this.htmlElements.section.appendChild(container);
        }

        #setupObservers() {

            const playerListElement = document.getElementById("playerlist");
            if (playerListElement) {
                const observer = new MutationObserver(() => {
                    this.#updatePlayerList();

                    if (this.#isAutoFriendRequestActive) {
                        this.#sendAutoFriendRequests();
                    }

                    this.#renderManualFriendRequestList();
                });
                observer.observe(playerListElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-playerid', 'data-loggedin'] });
                this.#updatePlayerList();
                this.#renderManualFriendRequestList();
            }
        }


        #sendMessage(message) {
            if (!message.trim()) return;
            if (globalThis.sockets && globalThis.sockets.length > 0) {
                const data = _io.emits.chatmsg(message);
                globalThis.sockets[0].send(data);

            } else {

            }
        }


        #toggleAfkStatus() {
            this.#isAfkActive = !this.#isAfkActive;
            if (globalThis.sockets && globalThis.sockets.length > 0) {
                const data = _io.emits.playerafk();
                globalThis.sockets[0].send(data);
                this.#afkToggleButton.classList[this.#isAfkActive ? "add" : "remove"]("active");
                this.#afkToggleButton.textContent = this.#isAfkActive ? "AFK Activo" : "Toggle AFK";

            } else {

            }
        }


        #updatePlayerList() {
            this.#playerList = [];
            const playerRows = document.querySelectorAll("#playerlist .playerlist-row");
            const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement;
            const myPlayerId = myPlayerIdElement ? myPlayerIdElement.dataset.playerid : null;

            playerRows.forEach(playerRow => {
                const playerId = playerRow.dataset.playerid;
                const playerNameElement = playerRow.querySelector(".playerlist-name a");
                const playerName = playerNameElement ? playerNameElement.textContent.trim() : `Jugador ${playerId}`;
                const avatarImg = playerRow.querySelector(".playerlist-avatar");
                const avatarUrl = avatarImg ? avatarImg.src : null;


                const avatarUidMatch = avatarUrl ? avatarUrl.match(/\/([a-f0-9-]+)\.jpg$/i) : null;
                const avatarUid = (avatarUidMatch && avatarUidMatch[1] && avatarUidMatch[1] !== 'default' && avatarUidMatch[1] !== 'undefined') ? avatarUidMatch[1] : null;


                const isLoggedIn = playerRow.dataset.loggedin === "true";

                if (playerId && playerId !== myPlayerId) {
                    this.#playerList.push({
                        id: parseInt(playerId),
                        name: playerName,
                        avatarUid: avatarUid,
                        isLoggedIn: isLoggedIn
                    });
                }
            });
        }

        #giveToken(tokenId) {
            if (globalThis.sockets && globalThis.sockets.length > 0) {
                if (this.#playerList.length > 0) {
                    this.#playerList.forEach(player => {
                        const data = _io.emits.settoken(player.id, tokenId);
                        globalThis.sockets[0].send(data);

                    });
                } else {

                }
            } else {

            }
        }


        #toggleStatusFlag(flagId, buttonElement) {
            const newActiveState = !this.#statusFlagsState[flagId];
            this.#statusFlagsState[flagId] = newActiveState;
            buttonElement.classList[newActiveState ? "add" : "remove"]("active");
            buttonElement.dataset.isActive = newActiveState;

            if (globalThis.sockets && globalGlobal.sockets.length > 0) {
                const data = _io.emits.setstatusflag(flagId, newActiveState);
                globalThis.sockets[0].send(data);

            } else {

            }
        }


        #toggleAutoFriendRequest(button) {
            this.#isAutoFriendRequestActive = !this.#isAutoFriendRequestActive;
            button.classList.toggle("active", this.#isAutoFriendRequestActive);
            button.innerHTML = this.#isAutoFriendRequestActive
                ? '<i class="fas fa-user-check"></i> Auto Solicitud de Amistad Activo'
                : '<i class="fas fa-user-plus"></i> Auto Solicitud de Amistad';

            if (this.#isAutoFriendRequestActive) {
                if (typeof window.LOGGEDIN === 'undefined' || !window.LOGGEDIN) {

                    this.#isAutoFriendRequestActive = false;
                    button.classList.remove("active");
                    button.innerHTML = '<i class="fas fa-user-plus"></i> Auto Solicitud de Amistad';
                    return;
                }


                if (typeof window.friendswg === 'undefined' || typeof window.friendswg.isfriend !== 'function') {

                    this.#isAutoFriendRequestActive = false;
                    button.classList.remove("active");
                    button.innerHTML = '<i class="fas fa-user-plus"></i> Auto Solicitud de Amistad';
                    return;
                }
                this.notify("success", "Solicitud de Amistad Automática activada. Se enviarán solicitudes a los jugadores logueados de la sala.");
                this.#autoFriendRequestSentUids.clear();
                this.#sendAutoFriendRequests();
            } else {
                clearInterval(this.#autoFriendRequestTimer);
                this.#autoFriendRequestTimer = null;

            }
        }

        async #_sendFriendRequest(playerUid) {
            if (!playerUid) {



                return { success: false, status: 'invalid_uid' };
            }

            try {
                const response = await fetch('/friendsapi/friendrequest', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    },
                    body: `action=outsend&uid=${playerUid}`,
                });

                if (!response.ok) {
                    throw new Error(`HTTP error! Status: ${response.status}`);
                }

                const data = await response.json();

                if (data && data.res === 'ok') {

                    return { success: true, status: 'ok' };
                } else if (data && data.error) {
                    let status = data.error;
                    let errorMessage = `Error al enviar solicitud a ${playerUid}: `;
                    switch (status) {
                        case 'alreadyinlist':
                            errorMessage += "Ya son amigos.";
                            break;
                        case 'requestduplicate':
                            errorMessage += "Solicitud pendiente.";
                            break;
                        case 'self':
                            errorMessage += "No puedes enviarte solicitud a ti mismo.";
                            break;
                        case 'friendlimit':
                            errorMessage += "Límite de amigos alcanzado.";
                            break;
                        case 'toofast':
                            errorMessage += "Demasiadas solicitudes, espera un momento.";
                            break;
                        default:
                            errorMessage += `Error desconocido (${status}).`;
                    }

                    return { success: false, status: status };
                } else {

                    return { success: false, status: 'unexpected_response' };
                }
            } catch (error) {

                console.error("Friend Request Fetch Error:", error);
                return { success: false, status: 'fetch_error' };
            }
        }

        async #sendAutoFriendRequests() {
            if (!this.#isAutoFriendRequestActive || !window.LOGGEDIN || !window.friendswg) {
                clearInterval(this.#autoFriendRequestTimer);
                this.#autoFriendRequestTimer = null;
                return;
            }



            const playersToProcess = this.#playerList.filter(player =>
                player.isLoggedIn &&
                player.avatarUid &&
                !this.#autoFriendRequestSentUids.has(player.avatarUid)
            );

            if (playersToProcess.length === 0) {
                return;
            }

            clearInterval(this.#autoFriendRequestTimer);
            this.#autoFriendRequestTimer = null;

            let index = 0;
            const processNextRequest = async () => {
                if (index < playersToProcess.length && this.#isAutoFriendRequestActive) {
                    const player = playersToProcess[index];



                    if (window.friendswg.isfriend(player.avatarUid)) {

                        this.#autoFriendRequestSentUids.add(player.avatarUid);
                    } else {

                        const result = await this.#_sendFriendRequest(player.avatarUid);
                        if (result.success || result.status === 'requestduplicate' || result.status === 'alreadyinlist') {
                            this.notify("success", `Solicitud auto a ${player.name} (${result.status}).`);
                            this.#autoFriendRequestSentUids.add(player.avatarUid);
                        } else {



                        }
                    }

                    index++;
                    this.#autoFriendRequestTimer = setTimeout(processNextRequest, 500);
                } else if (index >= playersToProcess.length) {

                    this.#autoFriendRequestTimer = null;
                }
            };


            this.#autoFriendRequestTimer = setTimeout(processNextRequest, 0);
        }


        #renderManualFriendRequestList() {
            this.#manualFriendRequestListContainer.innerHTML = '';

            if (typeof window.LOGGEDIN === 'undefined' || !window.LOGGEDIN) {
                this.#manualFriendRequestListContainer.appendChild(domMake.TextNode("Debes iniciar sesión para ver esta lista."));
                return;
            }


            if (typeof window.friendswg === 'undefined' || typeof window.friendswg.isfriend !== 'function') {
                this.#manualFriendRequestListContainer.appendChild(domMake.TextNode("El módulo de amigos de Drawaria no está cargado."));
                return;
            }


            const playersToList = [...this.#playerList];

            if (playersToList.length === 0) {
                this.#manualFriendRequestListContainer.appendChild(domMake.TextNode("No hay jugadores en la sala."));
                return;
            }

            playersToList.forEach(player => {
                const playerItem = domMake.Tree("div", { class: "manual-friend-request-item" });
                playerItem.appendChild(domMake.Tree("span", { class: "player-name", title: player.name }, [player.name]));

                const actionButtons = domMake.Tree("div", { class: "action-buttons" });

                let statusTextNode = null;

                let sendButtonInitialText = '<i class="fas fa-user-plus"></i> Enviar';
                let statusClass = '';


                if (!player.isLoggedIn || !player.avatarUid) {
                    statusTextNode = domMake.TextNode('Invitado');
                    statusClass = 'guest';

                } else if (window.friendswg.isfriend(player.avatarUid)) {
                    statusTextNode = domMake.TextNode('Ya Amigo');
                    statusClass = 'already-friend';

                } else if (this.#autoFriendRequestSentUids.has(player.avatarUid)) {
                    statusTextNode = domMake.TextNode('Auto Enviada');
                    statusClass = 'pending';

                } else if (this.#manualFriendRequestSentUids.has(player.avatarUid)) {
                    statusTextNode = domMake.TextNode('Manual Enviada');
                    statusClass = 'pending';

                }

                if (statusTextNode) {
                    const statusSpan = domMake.Tree("span", { class: `status-text ${statusClass}` }, [statusTextNode]);
                    playerItem.appendChild(statusSpan);
                }

                const sendRequestButton = domMake.Button(sendButtonInitialText);
                sendRequestButton.title = `Enviar solicitud de amistad a ${player.name}`;

                sendRequestButton.addEventListener('click', async (e) => {
                    const button = e.currentTarget;
                    button.disabled = true;
                    button.innerHTML = '<i class="fas fa-hourglass-half"></i>';


                    const result = await this.#_sendFriendRequest(player.avatarUid);

                    if (result.success || result.status === 'requestduplicate' || result.status === 'alreadyinlist') {
                        this.notify("success", `Solicitud de amistad manual a: ${player.name} (${result.status}).`);
                        this.#manualFriendRequestSentUids.add(player.avatarUid);

                        this.#renderManualFriendRequestList();
                    } else {

                        button.disabled = false;
                        button.innerHTML = '<i class="fas fa-user-plus"></i> Reintentar';
                    }
                });
                actionButtons.appendChild(sendRequestButton);

                const viewProfileButton = domMake.Button('<i class="fas fa-user-circle"></i> Perfil');
                viewProfileButton.title = `Ver perfil de ${player.name}`;

                viewProfileButton.addEventListener('click', () => {
                    if (player.avatarUid) {
                        window.open(`https://drawaria.online/profile/?uid=${player.avatarUid}`, '_blank');
                    } else {

                    }
                });
                actionButtons.appendChild(viewProfileButton);

                playerItem.appendChild(actionButtons);
                this.#manualFriendRequestListContainer.appendChild(playerItem);
            });
        }
    }
})("QBit");






(function BotClientModifications() {
    const QBit = globalThis[arguments[0]];


    function parseServerUrl(any) {
        var prefix = String(any).length == 1 ? `sv${any}.` : "";
        let baseUrl = `wss://${prefix}drawaria.online/socket.io/?`;
        let params = `EIO=3&transport=websocket`;

        if (prefix === "") {
            params = `EIO=3&transport=websocket`;
        } else {
            params = `sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
        }
        return baseUrl + params;
    }

    function parseRoomId(any) {
        if (!typecheck.isString(any)) {
          any = String(any);
        }
        const match = any.match(/([a-f0-9.-]+?)$/gi);
        if (match && match[0]) {
          return match[0];
        }
        return any;
    }


    function parseSocketIOEvent(prefix_length, event_data) {
        try {
            return JSON.parse(event_data.slice(prefix_length));
        } catch (error) {


            return null;
        }
    }


    const emits = _io.emits;
    const OriginalBotClient = QBit.findGlobal("BotClient");

    if (OriginalBotClient) {
        Object.assign(OriginalBotClient.prototype, {
            _onSocketOpenHandler(event) {
                const localThis = this;
                clearInterval(localThis.interval_id);
                localThis.interval_id = setInterval(function () {
                    if (!localThis.getReadyState()) {
                        clearInterval(localThis.interval_id);
                        localThis.notify("info", `Bot ${localThis.name} desconectado (ping fallido).`);
                        return;
                    }
                    localThis.send(2);
                }, localThis.interval_ms);

                if (localThis.room.id) {
                    localThis.send(emits.startplay(localThis.room, localThis.name, localThis.avatar));
                    localThis.notify("success", `Bot ${localThis.name} conectado y en sala ${localThis.room.id} (Tipo: ${localThis.room.type}).`);
                } else {
                    localThis.notify("warning", `Bot ${localThis.name} conectado, pero sin ID de sala para iniciar juego.`);
                }
            },

            _onSocketMessageHandler(message_event) {
                var prefix = String(message_event.data).match(/(^\d+)/gi)?.[0] || "";
                var data = parseSocketIOEvent(prefix.length, message_event.data) || [];

                if (data && data.length === 1) {
                    if (data[0].players) this.room.players = data[0].players;
                }
                if (data && data.length > 1) {
                    var event = data.shift();

                    if (event === "drawcmd") {
                        this.customObservers.forEach((listener) => {
                            if (listener.event === "bc_drawcommand") {
                                if (listener.callback) listener.callback(data);
                            }
                        });
                    }

                    this.customObservers.forEach((listener) => {
                        if (listener.event === event) if (listener.callback) listener.callback(data);
                    });
                }
            },

            _onSocketCloseHandler(event) {
                clearInterval(this.interval_id);
                this.socket = null;

            },

            _onSocketErrorHandler(event) {

                console.error(`WebSocket Error for ${this.name}:`, event);
                clearInterval(this.interval_id);
                this.socket = null;
            },


            connect(serverUrlSegment = "") {
                if (this.getReadyState()) {



                    return;
                }

                const fullServerUrl = parseServerUrl(serverUrlSegment);
                this.socket = new WebSocket(fullServerUrl);

                this.socket.addEventListener("open", this._onSocketOpenHandler.bind(this));
                this.socket.addEventListener("message", this._onSocketMessageHandler.bind(this));
                this.socket.addEventListener("close", this._onSocketCloseHandler.bind(this));
                this.socket.addEventListener("error", this._onSocketErrorHandler.bind(this));


            },


            enterRoom(roomid, roomTypeOverride = null) {

                if (this.getReadyState()) {

                    this.disconnect();
                }


                this.room.id = parseRoomId(roomid);
                if (roomTypeOverride !== null) {
                    this.room.type = roomTypeOverride;
                } else {


                    const parts = String(roomid).split('.');
                    if (parts.length === 1 && !isNaN(parseInt(parts[0], 16))) {
                        this.room.type = 0;

                    } else {
                        this.room.type = 2;

                    }
                }





                let serverIdSegment = "";
                const roomParts = String(roomid).split('.');
                if (roomParts.length > 1 && !isNaN(parseInt(roomParts[roomParts.length - 1]))) {
                    serverIdSegment = roomParts[roomParts.length - 1];
                }
                this.connect(serverIdSegment);


            },


            disconnect() {
                if (!this.getReadyState()) {

                    return;
                }
                clearInterval(this.interval_id);
                this.send(41);
                this.socket.close();
                this.socket = null;

            },


            reconnect() {
                if (this.getReadyState()) {


                    this.send(41);
                    this.send(40);
                } else {



                    this.enterRoom(this.room.id, this.room.type);
                }
            }
        });
    } else {
        console.error("BotClient class not found for modification. Join room functionality may not work.");
    }
})("QBit");

(function AutoManagerV3() {
    const QBit = window.QBit;


    const FONT_MAP = {
        'A': [[0,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0]],
        'B': [[1,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,0,0]],
        'C': [[0,1,1,1,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[0,1,1,1,0]],
        'D': [[1,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,0,0]],
        'E': [[1,1,1,1,0],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,0]],
        'F': [[1,1,1,1,0],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0]],
        'G': [[0,1,1,1,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,1,1,0],[1,0,0,1,0],[1,0,0,1,0],[0,1,1,1,0]],
        'H': [[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0]],
        'I': [[1,1,1,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,1,0,0,0],[1,1,1,0,0]],
        'J': [[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[1,0,1,0,0],[1,0,1,0,0],[0,1,0,0,0]],
        'K': [[1,0,0,1,0],[1,0,1,0,0],[1,1,0,0,0],[1,0,0,0,0],[1,1,0,0,0],[1,0,1,0,0],[1,0,0,1,0]],
        'L': [[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,0]],
        'M': [[1,0,0,1,0],[1,1,1,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0]],
        'N': [[1,0,0,1,0],[1,1,0,1,0],[1,0,1,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0]],
        'O': [[0,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[0,1,1,0,0]],
        'P': [[1,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0]],
        'Q': [[0,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,1,1,0],[1,0,1,0,0],[0,1,0,1,0]],
        'R': [[1,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,0,0],[1,0,1,0,0],[1,0,0,1,0],[1,0,0,1,0]],
        'S': [[0,1,1,1,0],[1,0,0,0,0],[1,0,0,0,0],[0,1,1,0,0],[0,0,0,1,0],[0,0,0,1,0],[1,1,1,0,0]],
        'T': [[1,1,1,1,1],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0]],
        'U': [[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[0,1,1,0,0]],
        'V': [[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[0,1,1,0,0],[0,0,1,0,0]],
        'W': [[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,1,0],[1,0,0,1,0]],
        'X': [[1,0,0,1,0],[1,0,0,1,0],[0,1,1,0,0],[0,0,1,0,0],[0,1,1,0,0],[1,0,0,1,0],[1,0,0,1,0]],
        'Y': [[1,0,0,1,0],[1,0,0,1,0],[1,0,0,1,0],[0,1,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0]],
        'Z': [[1,1,1,1,0],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,0]]
    };

    class AutoManagerV3 extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        #previewCanvas;
        #previewCtx;
        #originalCanvas;
        #originalCtx;

        #imageData = null;
        #executionLine = [];
        #drawingActive = false;
        #currentDrawingIndex = 0;

        #currentDrawingMode = 'rainMode';
        #rainColumns = [];
        #spiralAngle = 0;

        #imageSizeInput;
        #brushSizeInput;
        #pixelSizeInput;
        #offsetXInput;
        #offsetYInput;
        #drawingSpeedInput;
        #modeButtons = {};
        #imageFileInput;
        #statusMessageElement;

        #currentHue = 0;
        #rainbowMode = false;
        #rainbowSpeed = 50;
        #rainbowDirection = 'forward';
        #rainbowSpeedInput;
        #rainbowDirectionSelect;
        #lastRainbowUpdate = 0;

        #pantherTextInput;
        #pantherTextSizeInput;
        #pantherTextColorInput;

        #colorToleranceInput;


        #miniCanvas;
        #miniCtx;
        #isMiniDrawing = false;
        #miniLastX;
        #miniLastY;

        #rulerMode = false;
        #rulerHorizontal = false;
        #rulerVertical = false;
        #rulerV2 = false;
        #rulerAngle = 0;
        #rulerX = 100;
        #rulerY = 100;
        #rulerLineElement;
        #rulerAngleInput;
        #rulerXInput;
        #rulerYInput;
        #rulerHorizontalToggle;
        #rulerVerticalToggle;
        #rulerV2Toggle;

        #defaultSettings = {
            imageSize: 1,
            brushSize: 32,
            pixelSize: 1,
            offsetX: 10,
            offsetY: 0,
            drawingSpeed: 10,
            colorTolerance: 15,
            pantherTextSize: 4,
            pantherTextColor: "#000000"
        };

        constructor() {
            super("✏️Drawing Studio", '<i class="fas fa-pen"></i>');


            QBit.Styles.addRules([
                `#${QBit.identifier} .autodraw-controls {
                    display: flex;
                    flex-direction: column;
                    gap: 10px;
                }`,
                `#${QBit.identifier} .autodraw-controls .section-title {
                    font-weight: bold;
                    color: var(--dark-blue-title);
                    text-align: center;
                    margin-bottom: 5px;
                    border-bottom: 1px solid var(--CE-color);
                    padding-bottom: 5px;
                }`,
                `#${QBit.identifier} .autodraw-controls .control-group {
                    border: 1px solid var(--CE-color);
                    border-radius: .25rem;
                    padding: 5px;
                    background-color: var(--CE-bg_color);
                }`,
                `#${QBit.identifier} .autodraw-controls .cheat-row {
                    display: flex;
                    width: 100%;
                    flex-wrap: wrap;
                    gap: 5px;
                }`,
                `#${QBit.identifier} .autodraw-controls .cheat-row > div,
                 #${QBit.identifier} .autodraw-controls .cheat-row > input,
                 #${QBit.identifier} .autodraw-controls .cheat-row > select,
                 #${QBit.identifier} .autodraw-controls .cheat-row > button,
                 #${QBit.identifier} .autodraw-controls .cheat-row > label {
                    flex: 1 1 auto;
                    min-width: 80px;
                }`,
                `#${QBit.identifier} .autodraw-controls input[type="number"],
                 #${QBit.identifier} .autodraw-controls input[type="text"],
                 #${QBit.identifier} .autodraw-controls input[type="file"],
                 #${QBit.identifier} .autodraw-controls select,
                 #${QBit.identifier} .autodraw-controls textarea {
                    width: 100%;
                    padding: 5px;
                    box-sizing: border-box;
                    border: 1px solid var(--CE-color);
                    border-radius: .25rem;
                    background-color: var(--CE-bg_color);
                    color: var(--CE-color);
                }`,
                `#${QBit.identifier} .autodraw-controls label {
                    font-size: 0.85em;
                    margin-bottom: 3px;
                    display: block;
                }`,
                `#${QBit.identifier} .autodraw-controls .btn {
                    padding: 5px;
                    box-sizing: border-box;
                    background-color: var(--secondary);
                }`,
                `#${QBit.identifier} .autodraw-controls .btn.active {
                    background-color: var(--success);
                }`,
                `#${QBit.identifier} .autodraw-controls .effect-toggle.active {
                    background-color: var(--info);
                    color: white;
                }`,
                `#${QBit.identifier} .autodraw-controls .gradient-strip {
                    width: 100%;
                    height: 20px;
                    border: 1px solid var(--CE-color);
                    cursor: pointer;
                    margin-top: 5px;
                }`,
                `#${QBit.identifier} .autodraw-controls .status-message {
                    margin-top: 5px;
                    font-size: 0.9em;
                    color: var(--info);
                }`,
                `#${QBit.identifier} .autodraw-controls .loading-spinner {
                    border: 4px solid rgba(0, 0, 0, 0.1);
                    border-left-color: var(--info);
                    border-radius: 50%;
                    width: 20px;
                    height: 20px;
                    animation: spin 1s linear infinite;
                    display: inline-block;
                    vertical-align: middle;
                    margin-left: 5px;
                }`,
                `@keyframes spin {
                    0% { transform: rotate(0deg); }
                    100% { transform: rotate(360deg); }
                }`,

                `#${QBit.identifier} .mini-canvas-container {
                    border: 1px solid var(--CE-color);
                    background: #fff;
                    position: relative;
                    width: 100%; /* Make it responsive within its container */
                    max-width: 200px; /* Max size for visual appeal */
                    height: 200px; /* Fixed height for consistent aspect ratio */
                    margin: 5px auto; /* Center it */
                    display: block;
                }`,
                `#${QBit.identifier} .ruler-container {
                    display: flex;
                    flex-wrap: wrap;
                    gap: 5px;
                    align-items: center;
                    margin-top: 5px;
                    padding-top: 5px;
                    border-top: 1px dashed rgba(0,0,0,0.1);
                }`,
                `#${QBit.identifier} .ruler-line {
                    position: absolute;
                    border: 1px dashed var(--dark); /* Dashed line for ruler */
                    pointer-events: none;
                    width: 200px; /* Length of the ruler line */
                    transform-origin: center center; /* Rotate around its center */
                    z-index: 10; /* Ensure it's above canvas content */
                }`,
                `#${QBit.identifier} .ruler-container input[type="number"] {
                    width: 60px; /* Smaller inputs for ruler controls */
                }`,
                `#${QBit.identifier} .ruler-container label {
                    white-space: nowrap; /* Prevent labels from wrapping */
                }`
            ]);
            this.#onStartup();
        }

        #onStartup() {
            this.#loadInterface();
            this.#setupCanvas();
            this.#setInitialSettings();
            this.#initGradientStrip();
            this.#setupMiniCanvasDrawing();
            this.#setupRuler();
            requestAnimationFrame(this.#updateRainbowColor.bind(this));
        }

        #loadInterface() {
            const container = domMake.Tree("div", { class: "autodraw-controls" });


            const imageSettingsGroup = domMake.Tree("div", { class: "control-group" });
            imageSettingsGroup.appendChild(domMake.Tree("div", { class: "section-title" }, ["Imagen y Configuración de Dibujo"]));

            const imageLoadRow = domMake.Row({ class: "cheat-row" });
            this.#imageFileInput = domMake.Tree("input", { type: "file", id: "autodraw-image-input", title: "Cargar Imagen (PNG/JPG)" });
            imageLoadRow.appendChild(this.#imageFileInput);
            imageSettingsGroup.appendChild(imageLoadRow);

            const settingsRow1 = domMake.Row({ class: "cheat-row" });
            this.#imageSizeInput = domMake.Tree("input", { type: "number", min: "1", max: "20", value: this.#defaultSettings.imageSize, title: "Tamaño de Imagen (1=grande, 20=pequeña)" });
            this.#brushSizeInput = domMake.Tree("input", { type: "number", min: "1", max: "100", value: this.#defaultSettings.brushSize, title: "Tamaño del Pincel" });
            this.#pixelSizeInput = domMake.Tree("input", { type: "number", min: "1", max: "50", value: this.#defaultSettings.pixelSize, title: "Espacio entre Píxeles" });
            settingsRow1.appendAll(
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Tamaño Img:"]), this.#imageSizeInput]),
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Espacio Px:"]), this.#pixelSizeInput]),
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Tamaño Pincel:"]), this.#brushSizeInput])
            );
            imageSettingsGroup.appendChild(settingsRow1);

            const settingsRow2 = domMake.Row({ class: "cheat-row" });
            this.#offsetXInput = domMake.Tree("input", { type: "number", min: "-50", max: "150", value: this.#defaultSettings.offsetX, title: "Desplazamiento X (0-100)" });
            this.#offsetYInput = domMake.Tree("input", { type: "number", min: "-50", max: "150", value: this.#defaultSettings.offsetY, title: "Desplazamiento Y (0-100)" });
            this.#drawingSpeedInput = domMake.Tree("input", { type: "number", min: "1", max: "100", value: this.#defaultSettings.drawingSpeed, title: "Velocidad de Dibujo (ms/línea)" });
            this.#colorToleranceInput = domMake.Tree("input", { type: "number", min: "0", max: "255", value: this.#defaultSettings.colorTolerance, title: "Tolerancia de Color para Agrupación de Líneas" });
            settingsRow2.appendAll(
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Offset X:"]), this.#offsetXInput]),
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Offset Y:"]), this.#offsetYInput]),
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Vel. Dibujo (ms):"]), this.#drawingSpeedInput]),
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Tol. Color (0-255):"]), this.#colorToleranceInput])
            );
            imageSettingsGroup.appendChild(settingsRow2);
            container.appendChild(imageSettingsGroup);


            const modesGroup = domMake.Tree("div", { class: "control-group" });
            modesGroup.appendChild(domMake.Tree("div", { class: "section-title" }, ["Modos de Dibujo de Imagen"]));
            const modesRow = domMake.Row({ class: "cheat-row" });

            const addModeButton = (label, modeKey, iconClass) => {
                const button = domMake.Button(`<i class="${iconClass}"></i> ${label}`);
                button.classList.add("effect-toggle");
                button.addEventListener("click", () => this.#setDrawingMode(modeKey));
                this.#modeButtons[modeKey] = button;
                modesRow.appendChild(button);
            };

            addModeButton("Modo Lluvia", "rainMode", "fas fa-cloud-rain");
            addModeButton("Onda", "waveDraw", "fas fa-wave-square");
            addModeButton("Espiral", "spiralDraw", "fas fa-circle-notch");
            addModeButton("Píxel Aleatorio", "randomPixelDraw", "fas fa-dice");
            addModeButton("Modo Formas", "shapeDrawMode", "fas fa-bezier-curve");
            modesGroup.appendChild(modesRow);
            container.appendChild(modesGroup);



            const textDrawingGroup = domMake.Tree("div", { class: "control-group" });
            textDrawingGroup.appendChild(domMake.Tree("div", { class: "section-title" }, ["Dibujo de Texto"]));

            const textInputRow = domMake.Row({ class: "cheat-row" });
            this.#pantherTextInput = domMake.Tree("input", { type: "text", placeholder: "Texto para dibujar" });
            textInputRow.appendChild(this.#pantherTextInput);
            textDrawingGroup.appendChild(textInputRow);

            const textSettingsRow = domMake.Row({ class: "cheat-row" });
            this.#pantherTextSizeInput = domMake.Tree("input", { type: "number", min: "1", max: "10", value: this.#defaultSettings.pantherTextSize, title: "Tamaño del texto (1=pequeño, 10=grande)" });
            this.#pantherTextColorInput = domMake.Tree("input", { type: "color", value: this.#defaultSettings.pantherTextColor, title: "Color del texto" });
            textSettingsRow.appendAll(
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Tamaño Texto:"]), this.#pantherTextSizeInput]),
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Color Texto:"]), this.#pantherTextColorInput])
            );
            textDrawingGroup.appendChild(textSettingsRow);

            const drawTextButtonRow = domMake.Row({ class: "cheat-row" });
            const drawTextButton = domMake.Button('<i class="fas fa-font"></i> Dibujar Texto');
            drawTextButton.addEventListener("click", () => this.#startTextDrawing());
            drawTextButtonRow.appendChild(drawTextButton);
            textDrawingGroup.appendChild(drawTextButtonRow);
            container.appendChild(textDrawingGroup);


            const drawingTabletGroup = domMake.Tree("div", { class: "control-group" });
            drawingTabletGroup.appendChild(domMake.Tree("div", { class: "section-title" }, ["Tableta de Dibujo y Regla"]));

            this.#miniCanvas = domMake.Tree("canvas", { id: "miniCanvas", width: "200", height: "200", class: "mini-canvas-container" });
            this.#miniCtx = this.#miniCanvas.getContext('2d');
            drawingTabletGroup.appendChild(this.#miniCanvas);


            const rulerContainer = domMake.Tree("div", { class: "ruler-container" });
            rulerContainer.appendAll(
                domMake.Tree("label", {}, [
                    domMake.Tree("input", { type: "checkbox", id: "rulerModeToggle" }),
                    " Regla"
                ]),
                domMake.Tree("label", {}, [
                    domMake.Tree("input", { type: "checkbox", id: "rulerHorizontalToggle" }),
                    " Horizontal"
                ]),
                domMake.Tree("label", {}, [
                    domMake.Tree("input", { type: "checkbox", id: "rulerVerticalToggle" }),
                    " Vertical"
                ]),
                domMake.Tree("label", {}, [
                    domMake.Tree("input", { type: "checkbox", id: "rulerV2Toggle" }),
                    " Regla V2"
                ]),
                domMake.Tree("div", {}, [
                    domMake.Tree("label", {}, ["Ángulo (º):"]),
                    this.#rulerAngleInput = domMake.Tree("input", { type: "number", min: "0", max: "360", value: "0", title: "Ángulo de la regla" })
                ]),
                domMake.Tree("div", {}, [
                    domMake.Tree("label", {}, ["Pos X (px):"]),
                    this.#rulerXInput = domMake.Tree("input", { type: "number", min: "0", max: "200", value: "100", title: "Posición X de la regla" })
                ]),
                domMake.Tree("div", {}, [
                    domMake.Tree("label", {}, ["Pos Y (px):"]),
                    this.#rulerYInput = domMake.Tree("input", { type: "number", min: "0", max: "200", value: "100", title: "Posición Y de la regla" })
                ])
            );
            drawingTabletGroup.appendChild(rulerContainer);
            container.appendChild(drawingTabletGroup);


            const colorEffectsGroup = domMake.Tree("div", { class: "control-group" });
            colorEffectsGroup.appendChild(domMake.Tree("div", { class: "section-title" }, ["Efectos de Color"]));

            const rainbowControlRow = domMake.Row({ class: "cheat-row" });
            const rainbowModeToggle = domMake.Tree("label", {}, [
                domMake.Tree("input", { type: "checkbox", id: "rainbowModeToggle" }),
                " Modo Arcoíris"
            ]);
            this.#rainbowSpeedInput = domMake.Tree("input", { type: "number", min: "0", max: "500", value: "50", title: "Velocidad de cambio de color (ms)" });
            this.#rainbowDirectionSelect = domMake.Tree("select", { title: "Dirección del cambio de color" });
            ["forward", "reverse"].forEach(dir => {
                this.#rainbowDirectionSelect.appendChild(domMake.Tree("option", { value: dir }, [dir === "forward" ? "Hacia adelante" : "Hacia atrás"]));
            });
            rainbowControlRow.appendAll(
                rainbowModeToggle,
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Velocidad:"]), this.#rainbowSpeedInput]),
                domMake.Tree("div", {}, [domMake.Tree("label", {}, ["Dirección:"]), this.#rainbowDirectionSelect])
            );
            colorEffectsGroup.appendChild(rainbowControlRow);

            const gradientStripCanvas = domMake.Tree("canvas", { id: "gradientStrip", width: "200", height: "20", style: "width:100%;" });
            colorEffectsGroup.appendChild(gradientStripCanvas);
            container.appendChild(colorEffectsGroup);


            const controlButtonsRow = domMake.Row({ class: "cheat-row" });
            const startButton = domMake.Button('<i class="fas fa-play-circle"></i> Iniciar Tarea de Dibujo');
            startButton.addEventListener("click", () => this.#startDrawing());
            const stopButton = domMake.Button('<i class="fas fa-stop-circle"></i> Detener Tarea de Dibujo');
            stopButton.addEventListener("click", () => this.#stopDrawing());
            controlButtonsRow.appendAll(startButton, stopButton);
            container.appendChild(controlButtonsRow);


            this.#statusMessageElement = domMake.Tree("div", { class: "status-message" }, ["Estado: Listo."]);
            container.appendChild(this.#statusMessageElement);


            this.htmlElements.section.appendChild(container);


            rainbowModeToggle.querySelector('input').addEventListener('change', (e) => this.#toggleRainbowMode(e.target.checked));
            gradientStripCanvas.addEventListener('click', (e) => this.#handleGradientStripClick(e));
            this.#rainbowSpeedInput.addEventListener('input', (e) => this.#rainbowSpeed = parseInt(e.target.value));
            this.#rainbowDirectionSelect.addEventListener('change', (e) => this.#rainbowDirection = e.target.value);

            this.#imageFileInput.addEventListener("change", (e) => this.#readImage(e.target));
        }

        #setInitialSettings() {
            this.#imageSizeInput.value = this.#defaultSettings.imageSize;
            this.#brushSizeInput.value = this.#defaultSettings.brushSize;
            this.#pixelSizeInput.value = this.#defaultSettings.pixelSize;
            this.#offsetXInput.value = this.#defaultSettings.offsetX;
            this.#offsetYInput.value = this.#defaultSettings.offsetY;
            this.#drawingSpeedInput.value = this.#defaultSettings.drawingSpeed;
            this.#colorToleranceInput.value = this.#defaultSettings.colorTolerance;

            this.#pantherTextSizeInput.value = this.#defaultSettings.pantherTextSize;
            this.#pantherTextColorInput.value = this.#defaultSettings.pantherTextColor;

            this.#rainbowSpeedInput.value = this.#rainbowSpeed;
            this.#rainbowDirectionSelect.value = this.#rainbowDirection;


            this.#rulerAngleInput.value = this.#rulerAngle;
            this.#rulerXInput.value = this.#rulerX;
            this.#rulerYInput.value = this.#rulerY;

            this.#setDrawingMode('rainMode');
        }

        #setupCanvas() {
            this.#previewCanvas = document.createElement('canvas');
            this.#previewCtx = this.#previewCanvas.getContext('2d');

            this.#originalCanvas = document.getElementById('canvas');
            if (this.#originalCanvas) {
                this.#originalCtx = this.#originalCanvas.getContext('2d');
                this.#previewCanvas.width = this.#originalCanvas.width;
                this.#previewCanvas.height = this.#originalCanvas.height;
            } else {
                this.#previewCanvas.width = 1000;
                this.#previewCanvas.height = 1000;

            }
        }

        #readImage(fileInput) {
            if (!fileInput.files || !fileInput.files[0]) {
                this.#imageData = null;
                this.#updateStatus("No se seleccionó ninguna imagen.");
                return;
            }

            this.#updateStatus('Cargando imagen... <span class="loading-spinner"></span>');
            this.#imageData = null;

            const FR = new FileReader();
            FR.addEventListener('load', (evt) => {
                const img = new Image();
                img.addEventListener('load', async () => {

                    const ctx = this.#previewCtx;
                    ctx.clearRect(0, 0, this.#previewCanvas.width, this.#previewCanvas.height);

                    let imgWidth = img.width;
                    let imgHeight = img.height;
                    const canvasAspectRatio = this.#previewCanvas.width / this.#previewCanvas.height;
                    const imgAspectRatio = imgWidth / imgHeight;

                    if (imgWidth > this.#previewCanvas.width || imgHeight > this.#previewCanvas.height) {
                        if (imgAspectRatio > canvasAspectRatio) {
                            imgHeight = imgHeight * (this.#previewCanvas.width / imgWidth);
                            imgWidth = this.#previewCanvas.width;
                        } else {
                            imgWidth = imgWidth * (this.#previewCanvas.height / imgHeight);
                            imgHeight = this.#previewCanvas.height;
                        }
                    }

                    ctx.drawImage(img, 0, 0, imgWidth, imgHeight);

                    this.#imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
                    this.notify("success", "Imagen lista para dibujar.");


                    await this.#prepareDrawingCommands();
                    this.#updateStatus(`Imagen lista: ${this.#executionLine.length} líneas.`);

                });
                img.crossOrigin = 'Anonymous';
                img.src = evt.target.result;
            });
            FR.readAsDataURL(fileInput.files[0]);
        }


        #rgbaArrayToString(rgbaArray) {
            return `rgba(${rgbaArray[0]},${rgbaArray[1]},${rgbaArray[2]},${rgbaArray[3] / 255})`;
        }


        #areColorsSimilar(color1, color2, threshold = 15) {
            if (!color1 || !color2) return false;
            return (
                Math.abs(color1[0] - color2[0]) <= threshold &&
                Math.abs(color1[1] - color2[1]) <= threshold &&
                Math.abs(color1[2] - color2[2]) <= threshold &&
                Math.abs(color1[3] - color2[3]) <= threshold
            );
        }


        #toGameCoords(x_pixel, y_pixel, originalImgWidth, originalImgHeight) {
            const size = parseInt(this.#imageSizeInput.value);
            const offsetX = parseFloat(this.#offsetXInput.value);
            const offsetY = parseFloat(this.#offsetYInput.value);

            const finalDrawWidth = 100 / size;
            const finalDrawHeight = 100 / size;

            const gameX = (x_pixel / originalImgWidth) * finalDrawWidth + offsetX;
            const gameY = (y_pixel / originalImgHeight) * finalDrawHeight + offsetY;
            return [gameX, gameY];
        }


        #getPixelColor(x, y) {
            const width = this.#imageData.width;
            const height = this.#imageData.height;
            const pixels = this.#imageData.data;

            const originalPxX = Math.floor(x);
            const originalPxY = Math.floor(y);

            if (originalPxX < 0 || originalPxX >= width || originalPxY < 0 || originalPxY >= height) return null;
            const index = (originalPxY * width + originalPxX) * 4;
            const a = pixels[index + 3];
            if (a < 20) return null;
            const r = pixels[index + 0];
            const g = pixels[index + 1];
            const b = pixels[index + 2];
            return [r, g, b, a];
        }

        async #prepareDrawingCommands() {
            if (!this.#imageData) {

                return false;
            }

            this.#executionLine = [];
            this.#rainColumns = [];

            const { width, height } = this.#imageData;
            const pixelDensity = parseInt(this.#pixelSizeInput.value) || 1;
            const thickness = parseInt(this.#brushSizeInput.value) || 5;
            const colorTolerance = parseInt(this.#colorToleranceInput.value) || 15;



            switch (this.#currentDrawingMode) {
                case 'rainMode': {
                    for (let x = 0; x < width; x += pixelDensity) {
                        let columnPixels = [];
                        for (let y = 0; y < height; y += pixelDensity) {
                            const color = this.#getPixelColor(x, y);
                            if (color) {
                                columnPixels.push({ x, y, color });
                            }
                        }
                        if (columnPixels.length > 0) {
                            this.#rainColumns.push({ x, pixels: columnPixels });
                        }
                    }
                    this.#shuffleArray(this.#rainColumns);

                    for (let col of this.#rainColumns) {
                        let pixelsInColumn = col.pixels;
                        if (pixelsInColumn.length === 0) continue;

                        pixelsInColumn.sort((a, b) => a.y - b.y);

                        let startPixel = pixelsInColumn[0];
                        let prevPixel = pixelsInColumn[0];

                        for (let i = 1; i < pixelsInColumn.length; i++) {
                            let currentPixel = pixelsInColumn[i];

                            if (currentPixel.y !== prevPixel.y + pixelDensity || !this.#areColorsSimilar(startPixel.color, currentPixel.color, colorTolerance)) {
                                this.#executionLine.push({
                                    pos1: this.#toGameCoords(startPixel.x, startPixel.y, width, height),
                                    pos2: this.#toGameCoords(prevPixel.x, prevPixel.y, width, height),
                                    color: this.#rgbaArrayToString(startPixel.color),
                                    thickness
                                });
                                startPixel = currentPixel;
                            }
                            prevPixel = currentPixel;
                        }

                        this.#executionLine.push({
                            pos1: this.#toGameCoords(startPixel.x, startPixel.y, width, height),
                            pos2: this.#toGameCoords(prevPixel.x, prevPixel.y, width, height),
                            color: this.#rgbaArrayToString(startPixel.color),
                            thickness
                        });
                    }
                    break;
                }
                case 'waveDraw': {
                    const waveAmplitude = 15;
                    const waveFrequency = 0.05;

                    for (let y = 0; y < height; y += pixelDensity) {
                        let startPoint = null;
                        let lastColor = null;

                        for (let x = 0; x < width; x += pixelDensity) {
                            const currentXForWave = x;
                            const currentYForWave = y + waveAmplitude * Math.sin(x * waveFrequency);

                            const actualColor = this.#getPixelColor(currentXForWave, currentYForWave);

                            if (actualColor) {
                                if (!startPoint) {
                                    startPoint = { x: currentXForWave, y: currentYForWave, color: actualColor };
                                    lastColor = actualColor;
                                } else if (!this.#areColorsSimilar(actualColor, lastColor, colorTolerance)) {
                                    this.#executionLine.push({
                                        pos1: this.#toGameCoords(startPoint.x, startPoint.y, width, height),
                                        pos2: this.#toGameCoords(currentXForWave - pixelDensity, currentYForWave, width, height),
                                        color: this.#rgbaArrayToString(lastColor),
                                        thickness
                                    });
                                    startPoint = { x: currentXForWave, y: currentYForWave, color: actualColor };
                                    lastColor = actualColor;
                                }
                            } else if (startPoint) {

                                this.#executionLine.push({
                                    pos1: this.#toGameCoords(startPoint.x, startPoint.y, width, height),
                                    pos2: this.#toGameCoords(currentXForWave - pixelDensity, currentYForWave, width, height),
                                    color: this.#rgbaArrayToString(lastColor),
                                    thickness
                                });
                                startPoint = null;
                                lastColor = null;
                            }
                        }

                        if (startPoint) {
                            this.#executionLine.push({
                                pos1: this.#toGameCoords(startPoint.x, startPoint.y, width, height),
                                pos2: this.#toGameCoords(width - pixelDensity, y + waveAmplitude * Math.sin((width - pixelDensity) * waveFrequency), width, height),
                                color: this.#rgbaArrayToString(lastColor),
                                thickness
                            });
                        }
                    }
                    break;
                }
                case 'spiralDraw': {
                    const centerX = width / 2;
                    const centerY = height / 2;
                    const maxRadius = Math.min(width, height) / 2;
                    const density = 0.5;

                    for (let r = 0; r < maxRadius; r += pixelDensity * density) {
                        const numPoints = Math.floor(2 * Math.PI * r / pixelDensity); // Number of points on this spiral turn
                        if (numPoints === 0) continue;

                        let prevX = -1, prevY = -1;
                        let startPoint = null;
                        let lastColor = null;

                        for (let i = 0; i < numPoints; i++) {
                            const angle = (i / numPoints) * 2 * Math.PI + this.#spiralAngle; // Add offset angle for continuous spiral effect
                            const spiralX = centerX + r * Math.cos(angle);
                            const spiralY = centerY + r * Math.sin(angle);

                            const color = this.#getPixelColor(spiralX, spiralY);

                            if (color) {
                                if (startPoint === null) {
                                    startPoint = { x: spiralX, y: spiralY, color: color };
                                    lastColor = color;
                                } else if (!this.#areColorsSimilar(color, lastColor, colorTolerance)) {
                                    this.#executionLine.push({
                                        pos1: this.#toGameCoords(startPoint.x, startPoint.y, width, height),
                                        pos2: this.#toGameCoords(prevX, prevY, width, height),
                                        color: this.#rgbaArrayToString(lastColor),
                                        thickness
                                    });
                                    startPoint = { x: spiralX, y: spiralY, color: color };
                                    lastColor = color;
                                }
                                prevX = spiralX;
                                prevY = spiralY;
                            } else if (startPoint !== null) {
                                this.#executionLine.push({
                                    pos1: this.#toGameCoords(startPoint.x, startPoint.y, width, height),
                                    pos2: this.#toGameCoords(prevX, prevY, width, height),
                                    color: this.#rgbaArrayToString(lastColor),
                                    thickness
                                });
                                startPoint = null;
                            }
                        }
                        if (startPoint) {
                             this.#executionLine.push({
                                pos1: this.#toGameCoords(startPoint.x, startPoint.y, width, height),
                                pos2: this.#toGameCoords(prevX, prevY, width, height),
                                color: this.#rgbaArrayToString(lastColor),
                                thickness
                            });
                        }
                    }
                    this.#spiralAngle += 0.1;
                    break;
                }
                case 'randomPixelDraw': {
                    let allPixels = [];
                    for (let y = 0; y < height; y += pixelDensity) {
                        for (let x = 0; x < width; x += pixelDensity) {
                            const color = this.#getPixelColor(x, y);
                            if (color) {
                                allPixels.push({ x, y, color });
                            }
                        }
                    }
                    this.#shuffleArray(allPixels);

                    allPixels.forEach(p => {
                        this.#executionLine.push({
                            pos1: this.#toGameCoords(p.x, p.y, width, height),
                            pos2: this.#toGameCoords(p.x + pixelDensity / 2, p.y + pixelDensity / 2, width, height),
                            color: this.#rgbaArrayToString(p.color),
                            thickness
                        });
                    });
                    break;
                }
                case 'shapeDrawMode': {
                    const shapeDetectorClass = this.findGlobal("ShapeDetector");
                    if (!shapeDetectorClass || !shapeDetectorClass.siblings || shapeDetectorClass.siblings.length === 0) {

                        return false;
                    }
                    const shapeDetectorInstance = shapeDetectorClass.siblings[0];

                    if (!shapeDetectorInstance || typeof shapeDetectorInstance.analyzeImageDataForShapes !== 'function') {

                        return false;
                    }



                    const { drawingCommands: shapeCommands } = await shapeDetectorInstance.analyzeImageDataForShapes(
                        this.#imageData,
                        this.#imageData.width,
                        this.#imageData.height,
                        false
                    );

                    if (shapeCommands.length === 0) {

                        return false;
                    }

                    shapeCommands.forEach(cmd => {
                        this.#executionLine.push({
                            pos1: [cmd.x1, cmd.y1],
                            pos2: [cmd.x2, cmd.y2],
                            color: cmd.color || this.#pantherTextColorInput.value,
                            thickness: thickness
                        });
                    });
                    this.notify("success", `Modo Formas: Se prepararon ${this.#executionLine.length} líneas desde formas detectadas.`);
                    break;
                }
                default:


                    await this.#generateHorizontalLineCommands();
                    break;
            }


            return true;
        }


        async #generateHorizontalLineCommands() {
            if (!this.#imageData) {

                return;
            }

            const pixels = this.#imageData.data;
            const width = this.#imageData.width;
            const height = this.#imageData.height;

            const brushSize = parseInt(this.#brushSizeInput.value) || 2;
            const offsetX = parseFloat(this.#offsetXInput.value) || 0;
            const offsetY = parseFloat(this.#offsetYInput.value) || 0;
            const pixelDensity = parseInt(this.#pixelSizeInput.value) || 1;
            const colorTolerance = parseInt(this.#colorToleranceInput.value) || 15;

            const imageRenderSize = 100 / parseInt(this.#imageSizeInput.value);
            const scaleX = imageRenderSize / width;
            const scaleY = imageRenderSize / height;

            for (let y = 0; y < height; y += pixelDensity) {
                let currentLineColor = null;
                let lineStartX = -1;

                for (let x = 0; x < width; x += pixelDensity) {
                    const index = (y * width + x) * 4;
                    const r = pixels[index];
                    const g = pixels[index + 1];
                    const b = pixels[index + 2];
                    const a = pixels[index + 3];

                    const currentColor = [r, g, b, a];

                    if (a > 20) {
                        if (currentLineColor === null) {
                            currentLineColor = currentColor;
                            lineStartX = x;
                        } else if (!this.#areColorsSimilar(currentLineColor, currentColor, colorTolerance)) {
                            const gameX1 = lineStartX * scaleX + offsetX;
                            const gameY1 = y * scaleY + offsetY;
                            const gameX2 = (x - pixelDensity) * scaleX + offsetX;

                            this.#executionLine.push({
                                x1: gameX1,
                                y1: gameY1,
                                x2: gameX2 + (brushSize * scaleX * 0.5),
                                y2: gameY1,
                                color: this.#rgbaArrayToString(currentLineColor),
                                thickness: brushSize
                            });

                            currentLineColor = currentColor;
                            lineStartX = x;
                        }
                    } else {
                        if (currentLineColor !== null) {
                            const gameX1 = lineStartX * scaleX + offsetX;
                            const gameY1 = y * scaleY + offsetY;
                            const gameX2 = (x - pixelDensity) * scaleX + offsetX;
                            this.#executionLine.push({
                                x1: gameX1,
                                y1: gameY1,
                                x2: gameX2 + (brushSize * scaleX * 0.5),
                                y2: gameY1,
                                color: this.#rgbaArrayToString(currentLineColor),
                                thickness: brushSize
                            });
                            currentLineColor = null;
                            lineStartX = -1;
                        }
                    }
                }
                if (currentLineColor !== null && lineStartX !== -1) {
                    const gameX1 = lineStartX * scaleX + offsetX;
                    const gameY1 = y * scaleY + offsetY;
                    const gameX2 = (width - pixelDensity) * scaleX + offsetX;
                    this.#executionLine.push({
                        x1: gameX1,
                        y1: gameY1,
                        x2: gameX2 + (brushSize * scaleX * 0.5),
                        y2: gameY1,
                        color: this.#rgbaArrayToString(currentLineColor),
                        thickness: brushSize
                    });
                }
            }
        }


        #shuffleArray(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
        }

        async #startDrawing() {
            if (this.#imageData && this.#executionLine.length === 0) {
                 await this.#prepareDrawingCommands();
            }

            if (this.#executionLine.length === 0) {

                return;
            }

            const gameSocket = this.#getGameSocket();
            if (!gameSocket) {

                return;
            }

            this.#drawingActive = true;
            this.#currentDrawingIndex = 0;

            this.#updateStatus(`Dibujando... 0/${this.#executionLine.length} líneas.`);

            const delayMs = parseInt(this.#drawingSpeedInput.value);
            let currentLineIndex = 0;


            const localCtx = this.#originalCtx;
            const canvasWidth = this.#originalCanvas.width;
            const canvasHeight = this.#originalCanvas.height;

            while (this.#drawingActive && currentLineIndex < this.#executionLine.length) {
                const line = this.#executionLine[currentLineIndex];

                if (!gameSocket || gameSocket.readyState !== WebSocket.OPEN) {

                    this.#stopDrawing();
                    break;
                }

                let drawColor = line.color;
                if (this.#rainbowMode) {
                    drawColor = `hsl(${this.#currentHue}, 100%, 50%)`;
                }


                const localX1 = (line.pos1[0] / 100) * canvasWidth;
                const localY1 = (line.pos1[1] / 100) * canvasHeight;
                const localX2 = (line.pos2[0] / 100) * canvasWidth;
                const localY2 = (line.pos2[1] / 100) * canvasHeight;


                if (localCtx) {
                    localCtx.strokeStyle = drawColor;
                    localCtx.lineWidth = line.thickness;
                    localCtx.lineCap = 'round';
                    localCtx.beginPath();
                    localCtx.moveTo(localX1, localY1);
                    localCtx.lineTo(localX2, localY2);
                    localCtx.stroke();
                }


                const command = `42["drawcmd",0,[${line.pos1[0]/100},${line.pos1[1]/100},${line.pos2[0]/100},${line.pos2[1]/100},false,${0 - line.thickness},"${drawColor}",0,0,{}]]`;
                gameSocket.send(command);

                currentLineIndex++;
                this.#updateStatus(`Dibujando... ${currentLineIndex}/${this.#executionLine.length} líneas.`);
                await new Promise(resolve => setTimeout(resolve, delayMs));
            }

            if (!this.#drawingActive) {

            } else {
                this.notify("success", "Tarea de dibujo completada!");
            }
            this.#drawingActive = false;
            this.#updateStatus("Estado: Listo.");
        }

        #stopDrawing() {
            this.#drawingActive = false;

            this.#updateStatus("Estado: Detenido.");
        }

        #setDrawingMode(mode) {

            for (const key in this.#modeButtons) {
                if (this.#modeButtons.hasOwnProperty(key)) {
                    this.#modeButtons[key].classList.remove("active");
                }
            }

            if (this.#modeButtons[mode]) {
                this.#modeButtons[mode].classList.add("active");
            }

            this.#currentDrawingMode = mode;


            if (this.#imageData && this.#imageData.data && this.#imageData.data.length > 0) {
                this.#prepareDrawingCommands();
            }
        }

        async #startTextDrawing() {
            const text = this.#pantherTextInput.value.toUpperCase();
            if (!text) {

                return;
            }

            const gameSocket = this.#getGameSocket();
            if (!gameSocket) {

                return;
            }


            this.#updateStatus(`Dibujando texto...`);

            this.#drawingActive = true;
            this.#executionLine = [];

            const charWidth = 5;
            const charHeight = 7;
            const pixelSize = parseInt(this.#pixelSizeInput.value) || 1;
            const textSize = parseInt(this.#pantherTextSizeInput.value) || 4;
            const textColor = this.#pantherTextColorInput.value || "#000000";
            const thickness = parseInt(this.#brushSizeInput.value) || 5;

            const scaledPixelSize = pixelSize * (textSize / 2);
            let currentXOffset = parseFloat(this.#offsetXInput.value) || 0;
            const startYOffset = parseFloat(this.#offsetYInput.value) || 0;
            const delayMs = parseInt(this.#drawingSpeedInput.value) || 10;


            const localCtx = this.#originalCtx;
            const canvasWidth = this.#originalCanvas.width;
            const canvasHeight = this.#originalCanvas.height;

            while (this.#drawingActive) {
                let charDrawn = false;
                if (this.#executionLine.length < text.length * charWidth * charHeight) {
                    const charIndex = Math.floor(this.#executionLine.length / (charWidth * charHeight));
                    const char = text[charIndex];

                    if (char) {
                        if (!FONT_MAP[char]) {

                            currentXOffset += (charWidth + 1) * scaledPixelSize;
                            this.#executionLine.push({});
                        } else {
                            const bitmap = FONT_MAP[char];
                            const currentBitmapPixelIndex = this.#executionLine.length % (charWidth * charHeight);
                            const y = Math.floor(currentBitmapPixelIndex / charWidth);
                            const x = currentBitmapPixelIndex % charWidth;

                            if (y < charHeight && bitmap[y][x] === 1) {
                                const gameX = currentXOffset + x * scaledPixelSize;
                                const gameY = startYOffset + y * scaledPixelSize;

                                if (!gameSocket || gameSocket.readyState !== WebSocket.OPEN) {

                                    this.#stopDrawing();
                                    break;
                                }

                                let drawColor = textColor;
                                if (this.#rainbowMode) {
                                    drawColor = `hsl(${this.#currentHue}, 100%, 50%)`;
                                }


                                const localX = (gameX / 100) * canvasWidth;
                                const localY = (gameY / 100) * canvasHeight;


                                if (localCtx) {
                                    localCtx.strokeStyle = drawColor;
                                    localCtx.lineWidth = thickness;
                                    localCtx.lineCap = 'round';
                                    localCtx.beginPath();
                                    localCtx.moveTo(localX, localY);
                                    localCtx.lineTo(localX, localY);
                                    localCtx.stroke();
                                }


                                const command = `42["drawcmd",0,[${gameX/100},${gameY/100},${gameX/100},${gameY/100},false,${0 - thickness},"${drawColor}",0,0,{}]]`;
                                gameSocket.send(command);

                                this.#executionLine.push({});
                                charDrawn = true;
                            } else if (y >= charHeight) {
                                currentXOffset += (charWidth + 1) * scaledPixelSize;
                                this.#executionLine.length = (charIndex + 1) * (charWidth * charHeight);
                            }
                        }
                    } else {
                        break;
                    }
                } else {
                    break;
                }

                if (charDrawn) {
                    this.#updateStatus(`Dibujando texto... Línea: ${this.#executionLine.length}`);
                }
                await new Promise(resolve => setTimeout(resolve, delayMs));
            }

            if (!this.#drawingActive) {

            } else {
                this.notify("success", "Dibujo de texto completado!");
            }
            this.#drawingActive = false;
            this.#updateStatus("Estado: Listo.");
        }


        #initGradientStrip() {
            const strip = this.htmlElements.section.querySelector('#gradientStrip');
            if (!strip) return;
            const ctx = strip.getContext('2d');
            strip.width = strip.offsetWidth;
            strip.height = strip.offsetHeight;

            for (let x = 0; x < strip.width; x++) {
                const hue = (x / strip.width) * 360;
                ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
                ctx.fillRect(x, 0, 1, strip.height);
            }
        }

        #handleGradientStripClick(e) {
            const strip = e.target;
            const rect = strip.getBoundingClientRect();
            const x = e.clientX - rect.left;
            this.#currentHue = (x / rect.width) * 360;
            this.#rainbowMode = false;
            this.htmlElements.section.querySelector('#rainbowModeToggle').checked = false;

        }

        #toggleRainbowMode(checked) {
            this.#rainbowMode = checked;
            if (this.#rainbowMode) {

            } else {

            }
        }

        #updateRainbowColor(timestamp) {
            if (!this.#rainbowMode) {
                requestAnimationFrame(this.#updateRainbowColor.bind(this));
                return;
            }
            if (timestamp - this.#lastRainbowUpdate < this.#rainbowSpeed) {
                requestAnimationFrame(this.#updateRainbowColor.bind(this));
                return;
            }
            this.#lastRainbowUpdate = timestamp;
            const step = this.#rainbowDirection === 'forward' ? 1 : -1;
            this.#currentHue = (this.#currentHue + step) % 360;
            if (this.#currentHue < 0) this.#currentHue += 360;
            requestAnimationFrame(this.#updateRainbowColor.bind(this));
        }

        #updateStatus(message) {
            this.#statusMessageElement.innerHTML = `Estado: ${message}`;
        }



        #setupMiniCanvasDrawing() {
            this.#miniCtx.lineCap = 'round';
            this.#miniCtx.lineJoin = 'round';

            this.#miniCanvas.addEventListener('mousedown', (e) => this.#handleMiniCanvasMouseDown(e));
            this.#miniCanvas.addEventListener('mousemove', (e) => this.#handleMiniCanvasMouseMove(e));
            this.#miniCanvas.addEventListener('mouseup', () => this.#handleMiniCanvasMouseUp());
            this.#miniCanvas.addEventListener('mouseout', () => this.#handleMiniCanvasMouseUp());
        }

        #getMiniCanvasPos(e) {
            const rect = this.#miniCanvas.getBoundingClientRect();
            return {
                x: e.clientX - rect.left,
                y: e.clientY - rect.top
            };
        }

        #handleMiniCanvasMouseDown(e) {
            this.#isMiniDrawing = true;
            const pos = this.#getMiniCanvasPos(e);
            let x = pos.x;
            let y = pos.y;

            if (this.#rulerMode) {
                const effectiveAngle = this.#getRulerEffectiveAngle(x, y);
                const angleRad = (effectiveAngle * Math.PI) / 180;
                const dx = pos.x - this.#rulerX;
                const dy = pos.y - this.#rulerY;
                const proj = dx * Math.cos(angleRad) + dy * Math.sin(angleRad);
                x = this.#rulerX + proj * Math.cos(angleRad);
                y = this.#rulerY + proj * Math.sin(angleRad);
            }
            this.#miniLastX = x;
            this.#miniLastY = y;

            this.#drawOnMiniCanvas(pos.x, pos.y, this.#miniLastX, this.#miniLastY, this.#brushSizeInput.value);
        }

        #handleMiniCanvasMouseMove(e) {
            if (!this.#isMiniDrawing) return;
            const pos = this.#getMiniCanvasPos(e);
            let x = pos.x;
            let y = pos.y;

            if (this.#rulerMode) {
                const effectiveAngle = this.#getRulerEffectiveAngle(x, y, this.#miniLastX, this.#miniLastY);
                const angleRad = (effectiveAngle * Math.PI) / 180;
                const dx = pos.x - this.#rulerX;
                const dy = pos.y - this.#rulerY;
                const proj = dx * Math.cos(angleRad) + dy * Math.sin(angleRad);
                x = this.#rulerX + proj * Math.cos(angleRad);
                y = this.#rulerY + proj * Math.sin(angleRad);
            }
            this.#drawOnMiniCanvas(x, y, this.#miniLastX, this.#miniLastY, this.#brushSizeInput.value);
            this.#miniLastX = x;
            this.#miniLastY = y;
        }

        #handleMiniCanvasMouseUp() {
            this.#isMiniDrawing = false;
        }

        #drawOnMiniCanvas(x, y, lastX, lastY, thickness) {
            const gameSocket = this.#getGameSocket();
            if (!gameSocket) {

                return;
            }

            const miniCanvasWidth = this.#miniCanvas.width;
            const miniCanvasHeight = this.#miniCanvas.height;


            const gameX = (x / miniCanvasWidth) * 100;
            const gameY = (y / miniCanvasHeight) * 100;
            const gameLastX = (lastX / miniCanvasWidth) * 100;
            const gameLastY = (lastY / miniCanvasHeight) * 100;

            let drawColor = `hsl(${this.#currentHue}, 100%, 50%)`;


            if (this.#originalCtx) {
                const localX = (gameX / 100) * this.#originalCanvas.width;
                const localY = (gameY / 100) * this.#originalCanvas.height;
                const localLastX = (gameLastX / 100) * this.#originalCanvas.width;
                const localLastY = (gameLastY / 100) * this.#originalCanvas.height;

                this.#originalCtx.strokeStyle = drawColor;
                this.#originalCtx.lineWidth = thickness;
                this.#originalCtx.lineCap = 'round';
                this.#originalCtx.beginPath();
                this.#originalCtx.moveTo(localLastX, localLastY);
                this.#originalCtx.lineTo(localX, localY);
                this.#originalCtx.stroke();
            }


            const command = `42["drawcmd",0,[${gameLastX/100},${gameLastY/100},${gameX/100},${gameY/100},false,${0 - thickness},"${drawColor}",0,0,{}]]`;
            gameSocket.send(command);


            this.#miniCtx.beginPath();
            this.#miniCtx.moveTo(lastX, lastY);
            this.#miniCtx.lineTo(x, y);
            this.#miniCtx.strokeStyle = drawColor;
            this.#miniCtx.lineWidth = thickness / 2; // Scale down thickness for miniCanvas
            this.#miniCtx.stroke();
        }


        #setupRuler() {
            this.#rulerLineElement = domMake.Tree('div', { class: 'ruler-line' });
            this.#miniCanvas.parentNode.insertBefore(this.#rulerLineElement, this.#miniCanvas.nextSibling);


            this.htmlElements.section.querySelector('#rulerModeToggle').addEventListener('change', (e) => {
                this.#rulerMode = e.target.checked;
                this.#updateRulerDisplay();
            });
            this.htmlElements.section.querySelector('#rulerHorizontalToggle').addEventListener('change', (e) => {
                this.#rulerHorizontal = e.target.checked;
                if (this.#rulerHorizontal) {
                    this.htmlElements.section.querySelector('#rulerVerticalToggle').checked = false;
                    this.htmlElements.section.querySelector('#rulerV2Toggle').checked = false;
                    this.#rulerVertical = false;
                    this.#rulerV2 = false;
                }
                this.#updateRulerDisplay();
            });
            this.htmlElements.section.querySelector('#rulerVerticalToggle').addEventListener('change', (e) => {
                this.#rulerVertical = e.target.checked;
                if (this.#rulerVertical) {
                    this.htmlElements.section.querySelector('#rulerHorizontalToggle').checked = false;
                    this.htmlElements.section.querySelector('#rulerV2Toggle').checked = false;
                    this.#rulerHorizontal = false;
                    this.#rulerV2 = false;
                }
                this.#updateRulerDisplay();
            });
            this.htmlElements.section.querySelector('#rulerV2Toggle').addEventListener('change', (e) => {
                this.#rulerV2 = e.target.checked;
                if (this.#rulerV2) {
                    this.htmlElements.section.querySelector('#rulerHorizontalToggle').checked = false;
                    this.htmlElements.section.querySelector('#rulerVerticalToggle').checked = false;
                    this.#rulerHorizontal = false;
                    this.#rulerVertical = false;
                }
                this.#updateRulerDisplay();
            });

            this.#rulerAngleInput.addEventListener('input', (e) => {
                this.#rulerAngle = parseInt(e.target.value);
                this.#updateRulerDisplay();
            });
            this.#rulerXInput.addEventListener('input', (e) => {
                this.#rulerX = parseInt(e.target.value);
                this.#updateRulerDisplay();
            });
            this.#rulerYInput.addEventListener('input', (e) => {
                this.#rulerY = parseInt(e.target.value);
                this.#updateRulerDisplay();
            });

            this.#updateRulerDisplay();
        }

        #updateRulerDisplay() {
            if (!this.#rulerLineElement) return;

            this.#rulerLineElement.style.left = `${this.#rulerX}px`;
            this.#rulerLineElement.style.top = `${this.#rulerY}px`;

            let effectiveAngle = this.#rulerAngle;
            if (this.#rulerHorizontal) {
                effectiveAngle = 0;
            } else if (this.#rulerVertical) {
                effectiveAngle = 90;
            }


            if (this.#rulerV2) {


                this.#rulerLineElement.style.display = 'none';
                return;
            }

            this.#rulerLineElement.style.transform = `rotate(${effectiveAngle}deg)`;
            this.#rulerLineElement.style.display = this.#rulerMode ? 'block' : 'none';
        }

        #getRulerEffectiveAngle(currentX, currentY, lastX = 0, lastY = 0) {
            if (this.#rulerHorizontal) return 0;
            if (this.#rulerVertical) return 90;
            if (this.#rulerV2) {

                const dx = currentX - lastX;
                const dy = currentY - lastY;
                const angle = Math.atan2(dy, dx) * (180 / Math.PI); // Angle in degrees
                const snappedAngle = Math.round(angle / 45) * 45;
                return snappedAngle < 0 ? snappedAngle + 360 : snappedAngle;
            }
            return this.#rulerAngle;
        }


        #getGameSocket() {
            if (globalThis.sockets && globalThis.sockets.length > 0) {

                return globalThis.sockets.find(s => s.readyState === WebSocket.OPEN);
            }
            return null;
        }
    }
})();


(function ArtisanStudioModule() {
    const QBit = globalThis[arguments[0]];


    QBit.Styles.addRules([

        `#${QBit.identifier} .intelligent-artist-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .intelligent-artist-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .intelligent-artist-section input[type="text"],
         #${QBit.identifier} .intelligent-artist-section input[type="number"],
         #${QBit.identifier} .intelligent-artist-section input[type="color"],
         #${QBit.identifier} .drawing-assistant-controls input[type="number"],
         #${QBit.identifier} .drawing-assistant-controls input[type="color"] {
            width: 100%; padding: 5px; box-sizing: border-box;
            border: 1px solid var(--CE-color); border-radius: .25rem;
            background-color: var(--CE-bg_color); color: var(--CE-color);
         }`,
        `#${QBit.identifier} .intelligent-artist-section ._row > *,
         #${QBit.identifier} .drawing-assistant-controls ._row > * {
            margin: 0 2px;
        }`,

        `#${QBit.identifier} .intelligent-artist-sketch-list {
            display: flex;
            flex-wrap: nowrap; /* Forces horizontal layout */
            overflow-x: auto;  /* Adds the scrollbar when content overflows */
            gap: 5px;
            padding: 5px; /* Add padding inside the box */
            margin-top: 5px; /* Space from label above */
            border: 1px solid var(--CE-color); /* The "cuadrito" border */
            border-radius: .25rem;
            background-color: var(--CE-bg_color); /* Background for the box */
            line-height: normal; /* Ensure text within buttons is normal */
        }`,
        `#${QBit.identifier} .intelligent-artist-sketch-list .btn {
            flex: 0 0 auto; /* Prevents stretching and ensures they stay put */
            margin: 0; /* Remove default margin from btn class */
            padding: 2px 5px; /* Compact padding */
            font-size: 0.7em;
            white-space: nowrap; /* Prevents button text from wrapping */
        }`,


        `#drawing-assistant-overlay {
            position: absolute;
            top: 0;
            left: 0;
            z-index: 1000; /* Above game canvas, below main UI elements */
            pointer-events: none; /* Crucial to allow clicks to pass through to game canvas */
        }`,
        `#drawing-assistant-grid-toggle.active,
         #drawing-assistant-symmetry-toggle.active,
         #drawing-assistant-pixel-perfect-toggle.active {
             background-color: var(--info);
             color: white;
         }`,

        `#${QBit.identifier} .module-toggle-button {
            background-color: var(--secondary);
            color: var(--dark);
            width: 100%;
            padding: 8px 15px;
            box-sizing: border-box;
            font-size: 0.95em;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            margin-bottom: 10px;
        }`,
        `#${QBit.identifier} .module-toggle-button.active {
            background-color: var(--success);
            color: white;
        }`,

        `#${QBit.identifier} .transparent-brush-toggle-button {
            background: #492; /* Specific color for this button */
            color: #fff;
            width: 100%;
            padding: 7px 0;
            border: none;
            border-radius: 7px;
            font-weight: bold;
            cursor: pointer;
            margin-bottom: 8px;
        }`,
        `#${QBit.identifier} .transparent-brush-toggle-button.active {
            background: #2a5; /* Active color */
        }`,
        `#${QBit.identifier} .transparent-brush-opacity-slider {
            width: 70px;
            vertical-align: middle;
            -webkit-appearance: none; /* Remove default slider style */
            height: 6px; /* Adjust height */
            background: #ddd; /* Track color */
            outline: none;
            border-radius: 3px;
        }`,
        `#${QBit.identifier} .transparent-brush-opacity-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 16px; /* Thumb size */
            height: 16px;
            border-radius: 50%;
            background: #666; /* Thumb color */
            cursor: grab;
        }`,
        `#${QBit.identifier} .transparent-brush-opacity-slider::-moz-range-thumb {
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: #666;
            cursor: grab;
        }`,
        `#${QBit.identifier} .transparent-brush-percentage {
            display: inline-block;
            min-width: 35px;
            text-align: right;
        }`
    ]);



    function getGameSocket() {
        if (globalThis.sockets && globalThis.sockets.length > 0) {

            return globalThis.sockets.find(s => s.readyState === WebSocket.OPEN);
        }
        return null;
    }


    class ArtisanStudio extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");


        _isActive = false;


        _currentBrushSize = 5;
        _currentSketchColor = "#888888";
        _SKETCH_DATABASE = {};
        _sketchTextInput;
        _sketchListContainer;
        _sketchListCountLabel;
        _ui = {};


        _overlayCanvas;
        _overlayCtx;
        _gameCanvas;
        _gameCtx;

        _isSymmetryActive = false;
        _isPixelPerfectActive = false;
        _isGridVisible = false;

        _drawingColor = "#000000";
        _drawingThickness = 5;


        _isDrawingLocal = false;
        _lastX = 0;
        _lastY = 0;


        _isTransparentBrushActive = false;
        _customBrushAlpha = 0.3;


        _boundMouseDownHandler = this._handleMouseDown.bind(this);
        _boundMouseMoveHandler = this._handleMouseMove.bind(this);
        _boundMouseUpHandler = this._handleMouseUp.bind(this);


        _onClickShapeHandler = null;
        _onMoveShapeHandler = null;


        constructor() {
            super("🎨Artisan Sketch", '<i class="fas fa-palette"></i>');




            this._setModuleActive = (active) => {
                this._isActive = active;
                if (this._isActive) {
                    this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Desactivar Artisan Studio';
                    this._ui.moduleToggleButton.classList.add('active');

                    this._setUIEnabled(true);
                    this._hookGameDrawingEvents();
                } else {
                    this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Activar Artisan Studio';
                    this._ui.moduleToggleButton.classList.remove('active');

                    this._setUIEnabled(false);
                    this._unhookGameDrawingEvents();
                }
            };

            this._setUIEnabled = (enabled) => {
                const interactiveElements = [
                    this._sketchTextInput,
                    this._ui.generateSketchButton,
                    this._ui.clearCanvasButton,
                    this._ui.sketchColorInput,
                    this._ui.brushSizeInput,
                    this._ui.assistantColorInput,
                    this._ui.assistantThicknessInput,
                    this._ui.drawLineButton,
                    this._ui.drawRectButton,
                    this._ui.drawCircleButton,
                    this._ui.toggleGridButton,
                    this._ui.toggleSymmetryButton,
                    this._ui.togglePixelPerfectButton,
                    this._ui.startCollabDrawButton,
                    this._ui.transparentBrushToggleButton,
                    this._ui.transparentBrushSlider
                ];

                interactiveElements.forEach(el => {
                    if (el) {
                        el.disabled = !enabled;
                    }
                });

                if (this._ui.transparentBrushPercent) {
                    this._ui.transparentBrushPercent.style.color = enabled ? 'var(--CE-color)' : 'var(--dark-gray)';
                }

                if (this._sketchListContainer) {
                    Array.from(this._sketchListContainer.children).forEach(button => {
                        if (button.tagName === 'BUTTON') {
                            button.disabled = !enabled;
                        }
                    });
                }

                if (!enabled) {
                    this._isDrawingLocal = false;
                    this._clearOverlay();
                    if (this._gameCanvas) {
                        if (this._onClickShapeHandler) {
                            this._gameCanvas.removeEventListener('click', this._onClickShapeHandler);
                            this._onClickShapeHandler = null;
                        }
                        if (this._onMoveShapeHandler) {
                            this._gameCanvas.removeEventListener('mousemove', this._onMoveShapeHandler);
                            this._onMoveShapeHandler = null;
                        }
                    }
                }
            };

            this._onStartup();
        }

        async _onStartup() {
            this._findGameCanvas();
            this._setupOverlayCanvas();
            this._loadInterface();
            await this._loadSketchesFromGithub();


            this._setModuleActive(false);

        }

        _loadInterface() {
            const container = domMake.Tree("div");


            const moduleToggleRow = domMake.Row();
            this._ui.moduleToggleButton = domMake.Button('<i class="fas fa-power-off"></i> Activar Artisan Studio');
            this._ui.moduleToggleButton.classList.add('module-toggle-button');
            this._ui.moduleToggleButton.addEventListener('click', () => this._toggleModuleActive());
            moduleToggleRow.appendChild(this._ui.moduleToggleButton);
            container.appendChild(moduleToggleRow);



            const intelligentArtistSection = domMake.Tree("div", { class: "intelligent-artist-section" });
            intelligentArtistSection.appendChild(domMake.Tree("div", { class: "intelligent-artist-section-title" }, ["Bocetos Asistidos"]));


            const generateSketchRow = domMake.Row();
            this._sketchTextInput = domMake.Tree("input", { type: "text", placeholder: "Concepto de boceto (ej. 'árbol')" });
            this._ui.generateSketchButton = domMake.Button("Generar Boceto");
            this._ui.generateSketchButton.title = "Dibuja un boceto predefinido para la palabra ingresada.";
            this._ui.generateSketchButton.addEventListener("click", () => {
                this._simulateAISketch(this._sketchTextInput.value.toUpperCase());
            });
            generateSketchRow.appendAll(this._sketchTextInput, this._ui.generateSketchButton);
            intelligentArtistSection.appendChild(generateSketchRow);


            const clearCanvasRow = domMake.Row();
            this._ui.clearCanvasButton = domMake.Button("Limpiar Lienzo");
            this._ui.clearCanvasButton.title = "Limpia el lienzo con una línea blanca muy grande.";
            this._ui.clearCanvasButton.addEventListener("click", () => {
                const gameSocket = getGameSocket();
                if (gameSocket) {

                    if (this._gameCtx && this._gameCanvas) {
                        this._gameCtx.clearRect(0, 0, this._gameCanvas.width, this._gameCanvas.height);
                    }

                    gameSocket.send(`42["drawcmd",0,[0.5,0.5,0.5,0.5,true,-2000,"#FFFFFF",0,0,{}]]`);
                    this.notify("success", "El lienzo ha sido limpiado.");
                } else {

                }
            });
            clearCanvasRow.appendChild(this._ui.clearCanvasButton);
            intelligentArtistSection.appendChild(clearCanvasRow);


            const sketchConfigRow = domMake.Row();
            const sketchColorLabel = domMake.Tree("label", {}, ["Color Boceto:"]);
            this._ui.sketchColorInput = domMake.Tree("input", { type: "color", value: this._currentSketchColor });
            this._ui.sketchColorInput.title = "Define el color del boceto.";
            this._ui.sketchColorInput.addEventListener("change", (e) => {
                this._currentSketchColor = e.target.value;

            });
            const brushSizeLabel = domMake.Tree("label", {}, ["Tamaño Pincel:"]);
            this._ui.brushSizeInput = domMake.Tree("input", { type: "number", min: 1, max: 50, value: this._currentBrushSize });
            this._ui.brushSizeInput.title = "Define el tamaño del pincel para el boceto.";
            this._ui.brushSizeInput.addEventListener("change", (e) => {
                this._currentBrushSize = parseInt(e.target.value);

            });
            sketchConfigRow.appendAll(sketchColorLabel, this._ui.sketchColorInput, brushSizeLabel, this._ui.brushSizeInput);
            intelligentArtistSection.appendChild(sketchConfigRow);


            this._sketchListCountLabel = domMake.Tree("label", { id: "artisan-studio-sketch-count-label" }, ["Bocetos Rápidos (0):"]);

            intelligentArtistSection.appendChild(this._sketchListCountLabel);

            this._sketchListContainer = domMake.IconList({ class: 'intelligent-artist-sketch-list' });

            intelligentArtistSection.appendChild(this._sketchListContainer);


            container.appendChild(intelligentArtistSection);


            const drawingAssistantSection = domMake.Tree("div", { class: "intelligent-artist-section" });
            drawingAssistantSection.appendChild(domMake.Tree("div", { class: "intelligent-artist-section-title" }, ["Herramientas Asistidas"]));


            const assistantParamsRow = domMake.Row();
            assistantParamsRow.style.gap = "5px";

            this._ui.assistantColorInput = domMake.Tree("input", { type: "color", value: this._drawingColor, title: "Color de Dibujo" });
            this._ui.assistantColorInput.addEventListener("change", (e) => this._drawingColor = e.target.value);
            assistantParamsRow.appendAll(domMake.Tree("label", {}, ["Color:"]), this._ui.assistantColorInput);

            this._ui.assistantThicknessInput = domMake.Tree("input", { type: "number", min: "1", max: "100", value: this._drawingThickness, title: "Grosor de Dibujo" });
            this._ui.assistantThicknessInput.addEventListener("change", (e) => this._drawingThickness = parseInt(e.target.value));
            assistantParamsRow.appendAll(domMake.Tree("label", {}, ["Grosor:"]), this._ui.assistantThicknessInput);
            drawingAssistantSection.appendChild(assistantParamsRow);


            const assistantShapesRow = domMake.Row();
            assistantShapesRow.style.gap = "5px";

            this._ui.drawLineButton = domMake.Button('<i class="fas fa-grip-lines"></i> Línea');
            this._ui.drawLineButton.addEventListener("click", () => this._enableShapeDrawing('line'));
            assistantShapesRow.appendChild(this._ui.drawLineButton);

            this._ui.drawRectButton = domMake.Button('<i class="fas fa-vector-square"></i> Rect.');
            this._ui.drawRectButton.addEventListener("click", () => this._enableShapeDrawing('rect'));
            assistantShapesRow.appendChild(this._ui.drawRectButton);

            this._ui.drawCircleButton = domMake.Button('<i class="fas fa-circle"></i> Círculo');
            this._ui.drawCircleButton.addEventListener("click", () => this._enableShapeDrawing('circle'));
            assistantShapesRow.appendChild(this._ui.drawCircleButton);
            drawingAssistantSection.appendChild(assistantShapesRow);


            const assistantTogglesRow = domMake.Row();
            assistantTogglesRow.style.gap = "5px";

            this._ui.toggleGridButton = domMake.Button('<i class="fas fa-th"></i> Cuadrícula');
            this._ui.toggleGridButton.id = "drawing-assistant-grid-toggle";
            this._ui.toggleGridButton.addEventListener("click", () => this._toggleGrid(this._ui.toggleGridButton));
            assistantTogglesRow.appendChild(this._ui.toggleGridButton);

            this._ui.toggleSymmetryButton = domMake.Button('<i class="fas fa-arrows-alt-h"></i> Simetría');
            this._ui.toggleSymmetryButton.id = "drawing-assistant-symmetry-toggle";
            this._ui.toggleSymmetryButton.addEventListener("click", () => this._toggleSymmetry(this._ui.toggleSymmetryButton));
            assistantTogglesRow.appendChild(this._ui.toggleSymmetryButton);

            this._ui.togglePixelPerfectButton = domMake.Button('<i class="fas fa-compress-arrows-alt"></i> Píxel Perfect');
            this._ui.togglePixelPerfectButton.id = "drawing-assistant-pixel-perfect-toggle";
            this._ui.togglePixelPerfectButton.addEventListener("click", () => this._togglePixelPerfect(this._ui.togglePixelPerfectButton));
            assistantTogglesRow.appendChild(this._ui.togglePixelPerfectButton);
            drawingAssistantSection.appendChild(assistantTogglesRow);


            const assistantCollabRow = domMake.Row();
            this._ui.startCollabDrawButton = domMake.Button('<i class="fas fa-users-cog"></i> Iniciar Dibujo Colaborativo');
            this._ui.startCollabDrawButton.title = "Activa el módulo Autodraw V2 para que los bots colaboren en el dibujo (requiere imagen cargada en Autodraw V2).";
            this._ui.startCollabDrawButton.addEventListener("click", () => this._triggerAutodrawV2Collab());
            assistantCollabRow.appendChild(this._ui.startCollabDrawButton);
            drawingAssistantSection.appendChild(assistantCollabRow);

            container.appendChild(drawingAssistantSection);


            const transparentBrushSection = domMake.Tree("div", { class: "intelligent-artist-section" });
            transparentBrushSection.appendChild(domMake.Tree("div", { class: "intelligent-artist-section-title" }, ["Pincel Transparente"]));

            this._ui.transparentBrushToggleButton = domMake.Button('Pincel: OFF');
            this._ui.transparentBrushToggleButton.classList.add('transparent-brush-toggle-button');
            this._ui.transparentBrushToggleButton.addEventListener('click', this._toggleTransparentBrush.bind(this));
            transparentBrushSection.appendChild(this._ui.transparentBrushToggleButton);

            const opacityControlRow = domMake.Tree('div', { style: 'display:flex; align-items:center; justify-content:center; gap:8px; font-size:0.9em; color:var(--CE-color);' });
            const opacityLabel = domMake.Tree('label', {}, ['Opacidad:']);
            this._ui.transparentBrushSlider = domMake.Tree('input', {
                type: 'range', min: '0.05', max: '1', step: '0.01', value: '0.3',
                class: 'transparent-brush-opacity-slider'
            });
            this._ui.transparentBrushSlider.addEventListener('input', this._updateTransparentBrushAlpha.bind(this));
            this._ui.transparentBrushPercent = domMake.Tree('span', { class: 'transparent-brush-percentage' }, ['30%']);

            opacityControlRow.appendAll(opacityLabel, this._ui.transparentBrushSlider, this._ui.transparentBrushPercent);
            transparentBrushSection.appendChild(opacityControlRow);

            container.appendChild(transparentBrushSection);


            this.htmlElements.section.appendChild(container);


            this._ui.transparentBrushPercent.textContent = Math.round(parseFloat(this._ui.transparentBrushSlider.value) * 100) + "%";
        }


        _toggleModuleActive = () => {
            this._isActive = !this._isActive;
            if (this._isActive) {
                this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Desactivar Artisan Studio';
                this._ui.moduleToggleButton.classList.add('active');

                this._setUIEnabled(true);
                this._hookGameDrawingEvents();
            } else {
                this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Activar Artisan Studio';
                this._ui.moduleToggleButton.classList.remove('active');

                this._setUIEnabled(false);
                this._unhookGameDrawingEvents();
            }
        };

        _setUIEnabled = (enabled) => {

            const interactiveElements = [
                this._sketchTextInput,
                this._ui.generateSketchButton,
                this._ui.clearCanvasButton,
                this._ui.sketchColorInput,
                this._ui.brushSizeInput,
                this._ui.assistantColorInput,
                this._ui.assistantThicknessInput,
                this._ui.drawLineButton,
                this._ui.drawRectButton,
                this._ui.drawCircleButton,
                this._ui.toggleGridButton,
                this._ui.toggleSymmetryButton,
                this._ui.togglePixelPerfectButton,
                this._ui.startCollabDrawButton,

                this._ui.transparentBrushToggleButton,
                this._ui.transparentBrushSlider
            ];

            interactiveElements.forEach(el => {
                if (el) {
                    el.disabled = !enabled;
                }
            });


            if (this._ui.transparentBrushPercent) {
                this._ui.transparentBrushPercent.style.color = enabled ? 'var(--CE-color)' : 'var(--dark-gray)';
            }



            if (this._sketchListContainer) {
                Array.from(this._sketchListContainer.children).forEach(button => {
                    if (button.tagName === 'BUTTON') {
                        button.disabled = !enabled;
                    }
                });
            }


            if (!enabled) {
                this._isDrawingLocal = false;
                this._clearOverlay();


                if (this._gameCanvas) {
                    if (this._onClickShapeHandler) {
                        this._gameCanvas.removeEventListener('click', this._onClickShapeHandler);
                        this._onClickShapeHandler = null;
                    }
                    if (this._onMoveShapeHandler) {
                        this._gameCanvas.removeEventListener('mousemove', this._onMoveShapeHandler);
                        this._onMoveShapeHandler = null;
                    }
                }
            }
        };



        #sendDrawCommand(x1_norm, y1_norm, x2_norm, y2_norm, thickness, color) {
            if (!this._isActive) {

                return;
            }

            const gameSocket = getGameSocket();
            if (!gameSocket) {

                return;
            }


            let finalColor = color;
            if (this._isTransparentBrushActive) {
                finalColor = this._convertToRgba(color, this._customBrushAlpha);
            }



            const command = `42["drawcmd",0,[${x1_norm},${y1_norm},${x2_norm},${y2_norm},false,${0 - thickness},"${finalColor}",0,0,{}]]`;
            gameSocket.send(command);


            if (this._gameCtx && this._gameCanvas) {
                const localX1 = x1_norm * this._gameCanvas.width;
                const localY1 = y1_norm * this._gameCanvas.height;
                const localX2 = x2_norm * this._gameCanvas.width;
                const localY2 = y2_norm * this._gameCanvas.height;

                this._gameCtx.strokeStyle = finalColor;
                this._gameCtx.lineWidth = thickness;
                this._gameCtx.lineCap = 'round';
                this._gameCtx.lineJoin = 'round';
                this._gameCtx.beginPath();
                this._gameCtx.moveTo(localX1, localY1);
                this._gameCtx.lineTo(localX2, localY2);
                this._gameCtx.stroke();
            }
        }


        _convertToRgba(colorString, alpha) {

            if (colorString.startsWith('rgba')) {
                const parts = colorString.match(/rgba\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d*\.?\d+))?\)/);
                if (parts) {
                    return `rgba(${parts[1]},${parts[2]},${parts[3]},${alpha})`;
                }
            }

            else if (colorString.startsWith('rgb')) {
                const parts = colorString.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
                if (parts) {
                    return `rgba(${parts[1]},${parts[2]},${parts[3]},${alpha})`;
                }
            }

            else if (colorString.startsWith('#')) {
                let hex = colorString.slice(1);
                if (hex.length === 3) {
                    hex = hex.split('').map(char => char + char).join('');
                }
                const r = parseInt(hex.substring(0, 2), 16);
                const g = parseInt(hex.substring(2, 4), 16);
                const b = parseInt(hex.substring(4, 6), 16);
                return `rgba(${r},${g},${b},${alpha})`;
            }

            console.warn("ArtisanStudio: Unexpected color format for transparency conversion:", colorString);
            return `rgba(0,0,0,${alpha})`;
        }



async _loadSketchesFromGithub() {

    try {

        if (typeof drawingData !== 'undefined') {
            this._SKETCH_DATABASE = drawingData;
        } else if (typeof window.drawingData !== 'undefined') {
            this._SKETCH_DATABASE = window.drawingData;
        } else {
            throw new Error("La variable drawingData no está disponible. Asegúrate de que el script requerido se haya cargado correctamente.");
        }


        this._populateSketchList();
    } catch (error) {

        console.error("Error loading SKETCH_DATABASE from required script:", error);
    }
}


        _populateSketchList() {
            if (!this._sketchListContainer || !this._sketchListCountLabel) return;

            this._sketchListContainer.innerHTML = '';
            const words = Object.keys(this._SKETCH_DATABASE);


            this._sketchListCountLabel.textContent = `Bocetos Rápidos (${words.length}):`;

            words.forEach(word => {
                const sketchButton = domMake.Button(word);
                sketchButton.title = `Generar boceto para: ${word}`;
                sketchButton.style.flex = '0 0 auto';
                sketchButton.style.margin = '0';
                sketchButton.style.padding = '2px 5px';
                sketchButton.style.fontSize = '0.7em';

                sketchButton.addEventListener("click", () => {
                    this._simulateAISketch(word);
                });
                this._sketchListContainer.appendChild(sketchButton);
            });


            this._setUIEnabled(this._isActive);
        }

        _simulateAISketch(concept) {
            if (!this._isActive) {

                return;
            }

            const gameSocket = getGameSocket();
            if (!gameSocket) {

                return;
            }

            const sketchData = this._SKETCH_DATABASE[concept];

            if (!sketchData) {

                return;
            }




            sketchData.forEach(line => {
                const x1_norm = line.x1 / 100;
                const y1_norm = line.y1 / 100;
                const x2_norm = line.x2 / 100;
                const y2_norm = line.y2 / 100;

                if (line.type === "circle") {

                    const centerX_norm = line.x1 / 100;
                    const centerY_norm = line.y1 / 100; // Corrected from centerY_game
                    const radius_norm = line.radius / 100;
                    const segments = 24;
                    for (let i = 0; i < segments; i++) {
                        const angle1 = (i / segments) * Math.PI * 2;
                        const angle2 = ((i + 1) / segments) * Math.PI * 2;

                        const cx1_norm = centerX_norm + radius_norm * Math.cos(angle1);
                        const cy1_norm = centerY_norm + radius_norm * Math.sin(angle1);
                        const cx2_norm = centerX_norm + radius_norm * Math.cos(angle2);
                        const cy2_norm = centerY_norm + radius_norm * Math.sin(angle2);

                        this.#sendDrawCommand(cx1_norm, cy1_norm, cx2_norm, cy2_norm, this._currentBrushSize, this._currentSketchColor);
                    }
                } else {
                    this.#sendDrawCommand(x1_norm, y1_norm, x2_norm, y2_norm, this._currentBrushSize, this._currentSketchColor);
                }
            });

        }


        _findGameCanvas() {
            this._gameCanvas = document.getElementById('canvas');
            if (this._gameCanvas) {
                this._gameCtx = this._gameCanvas.getContext('2d');
            } else {

            }
        }

        _setupOverlayCanvas() {
            this._overlayCanvas = domMake.Tree('canvas', { id: 'drawing-assistant-overlay' });
            if (this._gameCanvas) {
                this._overlayCanvas.width = this._gameCanvas.width;
                this._overlayCanvas.height = this._gameCanvas.height;
                this._overlayCanvas.style.width = this._gameCanvas.style.width;
                this._overlayCanvas.style.height = this._gameCanvas.style.height;
                this._gameCanvas.parentNode.insertBefore(this._overlayCanvas, this._gameCanvas.nextSibling);
            } else {
                this._overlayCanvas.width = 1000;
                this._overlayCanvas.height = 1000;
                this._overlayCanvas.style.width = '700px';
                this._overlayCanvas.style.height = '700px';
                document.body.appendChild(this._overlayCanvas);
            }
            this._overlayCtx = this._overlayCanvas.getContext('2d');
            this._updateOverlaySizeAndPosition();

            window.addEventListener('resize', this._updateOverlaySizeAndPosition.bind(this));
        }

        _updateOverlaySizeAndPosition() {
            if (!this._gameCanvas || !this._overlayCanvas) return;

            const gameCanvasRect = this._gameCanvas.getBoundingClientRect();

            this._overlayCanvas.style.top = `${gameCanvasRect.top}px`;
            this._overlayCanvas.style.left = `${gameCanvasRect.left}px`;
            this._overlayCanvas.style.width = `${gameCanvasRect.width}px`;
            this._overlayCanvas.style.height = `${gameCanvasRect.height}px`;

            if (this._overlayCanvas.width !== this._gameCanvas.width) {
                 this._overlayCanvas.width = this._gameCanvas.width;
            }
            if (this._overlayCanvas.height !== this._gameCanvas.height) {
                 this._overlayCanvas.height = this._gameCanvas.height;
            }

            this._clearOverlay();
            if (this._isGridVisible) {
                this._drawGrid();
            }
            if (this._isSymmetryActive) {
                this._drawSymmetryLines();
            }
        }

        _clearOverlay() {
            this._overlayCtx.clearRect(0, 0, this._overlayCtx.canvas.width, this._overlayCtx.canvas.height);
        }

        _hookGameDrawingEvents() {
            if (!this._gameCanvas) return;
            this._gameCanvas.addEventListener('mousedown', this._boundMouseDownHandler);
            this._gameCanvas.addEventListener('mousemove', this._boundMouseMoveHandler);
            this._gameCanvas.addEventListener('mouseup', this._boundMouseUpHandler);
            this._gameCanvas.addEventListener('mouseout', this._boundMouseUpHandler);
        }

        _unhookGameDrawingEvents() {
            if (!this._gameCanvas) return;
            this._gameCanvas.removeEventListener('mousedown', this._boundMouseDownHandler);
            this._gameCanvas.removeEventListener('mousemove', this._boundMouseMoveHandler);
            this._gameCanvas.removeEventListener('mouseup', this._boundMouseUpHandler);
            this._gameCanvas.removeEventListener('mouseout', this._boundMouseUpHandler);


            if (this._onClickShapeHandler) {
                this._gameCanvas.removeEventListener('click', this._onClickShapeHandler);
                this._onClickShapeHandler = null;
            }
            if (this._onMoveShapeHandler) {
                this._gameCanvas.removeEventListener('mousemove', this._onMoveShapeHandler);
                this._onMoveShapeHandler = null;
            }
            this._clearOverlay();
        }

        _handleMouseDown(e) {
            if (!this._isActive) return;
            this._isDrawingLocal = true;
            const rect = this._gameCanvas.getBoundingClientRect();
            this._lastX = e.clientX - rect.left;
            this._lastY = e.clientY - rect.top;
            this._overlayCtx.beginPath();
            this._overlayCtx.moveTo(this._lastX, this._lastY);
        }

        _handleMouseMove(e) {
            if (!this._isActive || !this._isDrawingLocal) return;
            const rect = this._gameCanvas.getBoundingClientRect();
            const currentX = e.clientX - rect.left;
            const currentY = e.clientY - rect.top;

            this._sendLineCommandFromPixels(this._lastX, this._lastY, currentX, currentY);

            this._overlayCtx.lineTo(currentX, currentY);
            this._overlayCtx.strokeStyle = this._drawingColor;
            this._overlayCtx.lineWidth = this._drawingThickness;
            this._overlayCtx.lineCap = 'round';
            this._overlayCtx.lineJoin = 'round';
            this._overlayCtx.stroke();
            this._overlayCtx.beginPath();
            this._overlayCtx.moveTo(currentX, currentY);

            this._lastX = currentX;
            this._lastY = currentY;
        }

        _handleMouseUp() {
            if (!this._isActive) return;
            this._isDrawingLocal = false;
            this._clearOverlay();
            if (this._isGridVisible) this._drawGrid();
            if (this._isSymmetryActive) this._drawSymmetryLines();
        }


        _sendLineCommandFromPixels(startX_px, startY_px, endX_px, endY_px) {
            if (!this._isActive) return;

            const rect = this._gameCanvas.getBoundingClientRect();
            const scaleFactorX = 100 / rect.width;
            const scaleFactorY = 100 / rect.height;

            let gameX1_norm = (startX_px * scaleFactorX) / 100;
            let gameY1_norm = (startY_px * scaleFactorY) / 100;
            let gameX2_norm = (endX_px * scaleFactorX) / 100;
            let gameY2_norm = (endY_px * scaleFactorY) / 100;

            if (this._isPixelPerfectActive) {
                const snapResolution = 2 / 100; // 2 units in game coords, converted to 0-1 range
                gameX1_norm = Math.round(gameX1_norm / snapResolution) * snapResolution;
                gameY1_norm = Math.round(gameY1_norm / snapResolution) * snapResolution;
                gameX2_norm = Math.round(gameX2_norm / snapResolution) * snapResolution;
                gameY2_norm = Math.round(gameY2_norm / snapResolution) * snapResolution;
            }


            gameX1_norm = Math.max(0, Math.min(1, gameX1_norm));
            gameY1_norm = Math.max(0, Math.min(1, gameY1_norm));
            gameX2_norm = Math.max(0, Math.min(1, gameX2_norm));
            gameY2_norm = Math.max(0, Math.min(1, gameY2_norm));

            this.#sendDrawCommand(gameX1_norm, gameY1_norm, gameX2_norm, gameY2_norm, this._drawingThickness, this._drawingColor);

            if (this._isSymmetryActive) {
                const midX_norm = 0.5;
                const mirroredGameX1_norm = midX_norm + (midX_norm - gameX1_norm);
                const mirroredGameX2_norm = midX_norm + (midX_norm - gameX2_norm);
                this.#sendDrawCommand(mirroredGameX1_norm, gameY1_norm, mirroredGameX2_norm, gameY2_norm, this._drawingThickness, this._drawingColor);
            }
        }

        _enableShapeDrawing(shapeType) {
            if (!this._isActive) {

                return;
            }


            const gameSocket = getGameSocket();
            if (!gameSocket) {

                return;
            }

            let startCoords = null;


            if (this._onClickShapeHandler) {
                this._gameCanvas.removeEventListener('click', this._onClickShapeHandler);
                this._onClickShapeHandler = null;
            }
            if (this._onMoveShapeHandler) {
                this._gameCanvas.removeEventListener('mousemove', this._onMoveShapeHandler);
                this._onMoveShapeHandler = null;
            }


            const drawShapePreview = (x1_px, y1_px, x2_px, y2_px) => {
                this._clearOverlay();
                if (this._isGridVisible) this._drawGrid();
                if (this._isSymmetryActive) this._drawSymmetryLines();

                const ctx = this._overlayCtx;
                ctx.strokeStyle = this._drawingColor;
                ctx.lineWidth = 1;
                ctx.setLineDash([5, 5]);
                ctx.beginPath();

                const width_px = x2_px - x1_px;
                const height_px = y2_px - y1_px;

                if (shapeType === 'line') {
                    ctx.moveTo(x1_px, y1_px);
                    ctx.lineTo(x2_px, y2_px);
                } else if (shapeType === 'rect') {
                    ctx.rect(x1_px, y1_px, width_px, height_px);
                } else if (shapeType === 'circle') {
                    const dx_px = x2_px - x1_px;
                    const dy_px = y2_px - y1_px;
                    const radius_px = Math.sqrt(dx_px * dx_px + dy_px * dy_px);
                    ctx.arc(x1_px, y1_px, radius_px, 0, 2 * Math.PI);
                }
                ctx.stroke();
                ctx.setLineDash([]);
            };

            this._onClickShapeHandler = (e) => {
                if (!this._isActive) return;

                const rect = this._gameCanvas.getBoundingClientRect();
                const currentX_px = e.clientX - rect.left;
                const currentY_px = e.clientY - rect.top;

                if (!startCoords) {
                    startCoords = { x: currentX_px, y: currentY_px };


                    this._onMoveShapeHandler = (moveEvent) => {
                        if (!this._isActive) return;
                        const moveRect = this._gameCanvas.getBoundingClientRect();
                        const moveX_px = moveEvent.clientX - moveRect.left;
                        const moveY_px = moveEvent.clientY - moveRect.top;
                        drawShapePreview(startCoords.x, startCoords.y, moveX_px, moveY_px);
                    };
                    this._gameCanvas.addEventListener('mousemove', this._onMoveShapeHandler);

                } else {
                    const gameSocket = getGameSocket();
                    if (!gameSocket) {

                        this._gameCanvas.removeEventListener('click', this._onClickShapeHandler);
                        this._gameCanvas.removeEventListener('mousemove', this._onMoveShapeHandler);
                        this._onClickShapeHandler = null; this._onMoveShapeHandler = null;
                        this._clearOverlay();
                        return;
                    }

                    const rect = this._gameCanvas.getBoundingClientRect();
                    const scaleFactorX = 100 / rect.width;
                    const scaleFactorY = 100 / rect.height;

                    const x1_game = startCoords.x * scaleFactorX;
                    const y1_game = startCoords.y * scaleFactorY;
                    const x2_game = currentX_px * scaleFactorX;
                    const y2_game = currentY_px * scaleFactorY;

                    const thickness = this._drawingThickness;
                    const color = this._drawingColor;


                    const x1_norm = x1_game / 100;
                    const y1_norm = y1_game / 100;
                    const x2_norm = x2_game / 100;
                    const y2_norm = y2_game / 100;


                    if (shapeType === 'line') {
                        this.#sendDrawCommand(x1_norm, y1_norm, x2_norm, y2_norm, thickness, color);
                    } else if (shapeType === 'rect') {
                        this.#sendDrawCommand(x1_norm, y1_norm, x2_norm, y1_norm, thickness, color);
                        this.#sendDrawCommand(x2_norm, y1_norm, x2_norm, y2_norm, thickness, color);
                        this.#sendDrawCommand(x2_norm, y2_norm, x1_norm, y2_norm, thickness, color);
                        this.#sendDrawCommand(x1_norm, y2_norm, x1_norm, y1_norm, thickness, color);
                    } else if (shapeType === 'circle') {
                        const centerX_game = x1_game;
                        const centerY_game = y1_game;
                        const dx_game = x2_game - x1_game;
                        const dy_game = y2_game - y1_game;
                        const radius_game = Math.sqrt(dx_game * dx_game + dy_game * dy_game);

                        const segments = 48;
                        for (let i = 0; i < segments; i++) {
                            const angle1 = (i / segments) * Math.PI * 2;
                            const angle2 = ((i + 1) / segments) * Math.PI * 2;

                            const cx1_game = centerX_game + radius_game * Math.cos(angle1);
                            const cy1_game = centerY_game + radius_game * Math.sin(angle1);
                            const cx2_game = centerX_game + radius_game * Math.cos(angle2);
                            const cy2_game = centerY_game + radius_game * Math.sin(angle2);

                            this.#sendDrawCommand(cx1_game / 100, cy1_game / 100, cx2_game / 100, cy2_game / 100, thickness, color);
                        }
                    }

                    this._gameCanvas.removeEventListener('click', this._onClickShapeHandler);
                    this._gameCanvas.removeEventListener('mousemove', this._onMoveShapeHandler);
                    this._onClickShapeHandler = null; this._onMoveShapeHandler = null;
                    this._clearOverlay();
                    this.notify("success", `${shapeType} dibujado.`);
                    startCoords = null;
                }
            };
            this._gameCanvas.addEventListener('click', this._onClickShapeHandler);
        }

        _toggleGrid(button) {
            if (!this._isActive) {

                return;
            }
            this._isGridVisible = !this._isGridVisible;
            button.classList.toggle("active", this._isGridVisible);
            this._clearOverlay();
            if (this._isGridVisible) {
                this._drawGrid();

            } else {

            }
            if (this._isSymmetryActive) this._drawSymmetryLines();
        }

        _drawGrid() {
            if (!this._gameCanvas) return;
            const ctx = this._overlayCtx;
            const rect = this._gameCanvas.getBoundingClientRect();
            const cellSize = 50;

            ctx.strokeStyle = "rgba(100, 100, 100, 0.5)";
            ctx.lineWidth = 1;
            ctx.setLineDash([2, 2]);

            for (let x = 0; x <= rect.width; x += cellSize) {
                ctx.beginPath();
                ctx.moveTo(x, 0);
                ctx.lineTo(x, rect.height);
                ctx.stroke();
            }

            for (let y = 0; y <= rect.height; y += cellSize) {
                ctx.beginPath();
                ctx.moveTo(0, y);
                ctx.lineTo(rect.width, y);
                ctx.stroke();
            }
            ctx.setLineDash([]);
        }

        _toggleSymmetry(button) {
            if (!this._isActive) {

                return;
            }
            this._isSymmetryActive = !this._isSymmetryActive;
            button.classList.toggle("active", this._isSymmetryActive);
            this._clearOverlay();
            if (this._isGridVisible) this._drawGrid();
            if (this._isSymmetryActive) {
                this._drawSymmetryLines();

            } else {

            }
        }

        _drawSymmetryLines() {
            if (!this._gameCanvas) return;
            const ctx = this._overlayCtx;
            const rect = this._gameCanvas.getBoundingClientRect();

            ctx.strokeStyle = "rgba(255, 0, 0, 0.7)";
            ctx.lineWidth = 2;
            ctx.setLineDash([5, 5]);
            ctx.beginPath();
            ctx.moveTo(rect.width / 2, 0);
            ctx.lineTo(rect.width / 2, rect.height);
            ctx.stroke();
            ctx.setLineDash([]);
        }

        _togglePixelPerfect(button) {
            if (!this._isActive) {

                return;
            }
            this._isPixelPerfectActive = !this._isPixelPerfectActive;
            button.classList.toggle("active", this._isPixelPerfectActive);

        }

        _triggerAutodrawV2Collab() {
            if (!this._isActive) {

                return;
            }

            const autodrawV2Class = this.findGlobal("AutodrawV2");
            if (!autodrawV2Class || !autodrawV2Class.siblings || autodrawV2Class.siblings.length === 0) {

                 return;
            }
            const autodrawV2Instance = autodrawV2Class.siblings[0];

            if (autodrawV2Instance && typeof autodrawV2Instance.startDrawing === 'function') {
                autodrawV2Instance.startDrawing();

            } else {

            }
        }


        _toggleTransparentBrush() {
            if (!this._isActive) {


                this._ui.transparentBrushToggleButton.classList.remove('active');
                this._ui.transparentBrushToggleButton.textContent = 'Pincel: OFF';
                this._ui.transparentBrushToggleButton.style.background = '#492';
                this._isTransparentBrushActive = false;
                return;
            }

            this._isTransparentBrushActive = !this._isTransparentBrushActive;
            this._ui.transparentBrushToggleButton.textContent = this._isTransparentBrushActive ? 'Pincel: ON' : 'Pincel: OFF';
            this._ui.transparentBrushToggleButton.style.background = this._isTransparentBrushActive ? '#2a5' : '#492';

        }

        _updateTransparentBrushAlpha() {
            if (!this._isActive) return;

            this._customBrushAlpha = parseFloat(this._ui.transparentBrushSlider.value);
            this._ui.transparentBrushPercent.textContent = Math.round(this._customBrushAlpha * 100) + "%";

        }
    }
})("QBit");


(function PaletteMasterModule() {
    const QBit = globalThis[arguments[0]];

    const LOCAL_STORAGE_KEY = 'cubicEngineCustomColors';
    const DEFAULT_CUSTOM_COLORS = [
        { name: "Teal", hex: "#008080" }, { name: "Lime", hex: "#AAFF00" },
        { name: "Cyan", hex: "#00FFFF" }, { name: "Magenta", hex: "#FF00FF" },
        { name: "Olive", hex: "#808000" }, { name: "Maroon", hex: "#800000" }
    ];

    QBit.Styles.addRules([

        `#${QBit.identifier} .palette-master-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .palette-master-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,

        `#${QBit.identifier} .custom-color-button {
            box-shadow: 0 0 2px rgba(0,0,0,0.3);
            cursor: pointer;
            border: 1px solid transparent;
        }`,
        `#${QBit.identifier} .custom-color-button.custom-active-color {
            box-shadow: 0 0 5px 2px var(--info);
            border: 1px solid var(--info);
        }`,

        `#${QBit.identifier} .stroke-master-toggle-button.active {
            background-color: var(--info);
            color: white;
        }`,
        `#${QBit.identifier} .stroke-master-control-group {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            margin-top: 5px;
            padding-top: 5px;
            border-top: 1px solid rgba(0,0,0,0.1);
        }`,
        `#${QBit.identifier} .stroke-master-control-group > div {
            flex: 1 1 48%; /* For responsiveness */
            display: flex;
            flex-direction: column;
            align-items: flex-start;
        }`,
        `#${QBit.identifier} .stroke-master-control-group input[type="number"],
         #${QBit.identifier} .stroke-master-control-group input[type="range"] {
            width: 100%;
        }`
    ]);

    class PaletteMaster extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        _customColors = [];
        _colorButtonsContainer;
        _colorInput;
        _colorPaletteObserver;
        _gameTriangleElement = null;
        _proxyGameButton = null;
        _gameCanvas = null;
        _gameCtx = null;

        _isPressureActive = false;
        _isTextureActive = false;
        _lastMousePos = { x: 0, y: 0 };
        _lastTimestamp = 0;
        _lastDrawThickness = 5;


        _handleMouseDown = this._handleMouseDown.bind(this);
        _handleMouseMove = this._handleMouseMove.bind(this);
        _handleMouseUp = this._handleMouseUp.bind(this);
        _addNewCustomColor = this._addNewCustomColor.bind(this);
        _clearAllCustomColors = this._clearAllCustomColors.bind(this);
        _handleCustomColorClick = this._handleCustomColorClick.bind(this);
        _handleGameColorClick = this._handleGameColorClick.bind(this);
        _togglePressureControl = this._togglePressureControl.bind(this);
        _toggleTextureBrush = this._toggleTextureBrush.bind(this);
        _simulateGradientFill = this._simulateGradientFill.bind(this);

        constructor() {
            super("🖌️Palette Designer", '<i class="fas fa-brush"></i>');
            this._onStartup();
        }

        _onStartup() {
            this._findGameCanvas();
            this._loadInterface();
            this._loadCustomColors();
            this._setupColorPaletteObserver();
            this._hookDrawingEvents();
        }

        _findGameCanvas() {
            this._gameCanvas = document.getElementById('canvas');
            if (this._gameCanvas) {
                this._gameCtx = this._gameCanvas.getContext('2d');
            } else {

            }
        }

        _loadInterface() {
            const container = domMake.Tree("div");


            const paletteSection = domMake.Tree("div", { class: "palette-master-section" });
            paletteSection.appendChild(domMake.Tree("div", { style: "width: 100%; text-align: center; font-weight: bold; margin-bottom: 5px;" },  ["Gestión de Paletas"]));

            const addColorRow = domMake.Row();
            const addColorLabel = domMake.Tree("label", {}, ["Añadir Color:"]);
            this._colorInput = domMake.Tree("input", { type: "color", value: "#FF0000" });
            const addColorButton = domMake.Button("Añadir");
            addColorButton.addEventListener("click", () => this._addNewCustomColor(this._colorInput.value));
            addColorRow.appendAll(addColorLabel, this._colorInput, addColorButton);
            paletteSection.appendChild(addColorRow);

            const clearColorsRow = domMake.Row();
            const clearAllColorsButton = domMake.Button("Limpiar Todos");
            clearAllColorsButton.addEventListener("click", this._clearAllCustomColors);
            clearColorsRow.appendChild(clearAllColorsButton);
            paletteSection.appendChild(clearColorsRow);

            const customColorsDisplayRow = domMake.Row();
            this._colorButtonsContainer = domMake.IconList();
            customColorsDisplayRow.appendChild(this._colorButtonsContainer);
            paletteSection.appendChild(customColorsDisplayRow);
            container.appendChild(paletteSection);


            const strokeSection = domMake.Tree("div", { class: "palette-master-section" });



            const pressureRow = domMake.Row();
            const pressureButton = domMake.Button("Control de Presión");
            pressureButton.classList.add("stroke-master-toggle-button");
            pressureButton.addEventListener("click", () => this._togglePressureControl(pressureButton));
            pressureRow.appendChild(pressureButton);



            const textureRow = domMake.Row();
            const textureButton = domMake.Button("Pincel Texturizado");
            textureButton.classList.add("stroke-master-toggle-button");
            textureButton.addEventListener("click", () => this._toggleTextureBrush(textureButton));
            textureRow.appendChild(textureButton);




            const gradientGroup = domMake.Tree("div", { class: "stroke-master-control-group" });
            gradientGroup.appendChild(domMake.Tree("label", { style: "width: 100%; text-align: center; font-weight: bold; margin-bottom: 5px;" }, ["Rellenos Degradados"]));

            const diamondGradientButton = domMake.Button('<i class="fas fa-gem"></i>Efecto de Degradado<br> Diamante');
            diamondGradientButton.addEventListener("click", () => this._simulateGradientFill("diamond"));
            gradientGroup.appendChild(domMake.Tree("div", {}, [diamondGradientButton]));

            const radialGradientButton = domMake.Button('<i class="fas fa-bullseye"></i>Efecto de Degradado<br> Radial');
            radialGradientButton.addEventListener("click", () => this._simulateGradientFill("radial"));
            gradientGroup.appendChild(domMake.Tree("div", {}, [radialGradientButton]));

            const linearGradientButton = domMake.Button('<i class="fas fa-grip-lines"></i>Efecto de Degradado<br> Lineal');
            linearGradientButton.addEventListener("click", () => this._simulateGradientFill("linear"));
            gradientGroup.appendChild(domMake.Tree("div", {}, [linearGradientButton]));

            const verticalGradientButton = domMake.Button('<i class="fas fa-arrows-alt-v"></i>Efecto de Degradado<br> Vertical');
            verticalGradientButton.addEventListener("click", () => this._simulateGradientFill("vertical"));
            gradientGroup.appendChild(domMake.Tree("div", {}, [verticalGradientButton]));

            const conicalGradientButton = domMake.Button('<i class="fas fa-circle-notch"></i>Efecto de Degradado<br> Cónico');
            conicalGradientButton.addEventListener("click", () => this._simulateGradientFill("conical"));
            gradientGroup.appendChild(domMake.Tree("div", {}, [conicalGradientButton]));

            const waveGradientButton = domMake.Button('<i class="fas fa-water"></i>Efecto de Degradado<br> Ondulado');
            waveGradientButton.addEventListener("click", () => this._simulateGradientFill("wave"));
            gradientGroup.appendChild(domMake.Tree("div", {}, [waveGradientButton]));


            strokeSection.appendChild(gradientGroup);
            container.appendChild(strokeSection);

            this.htmlElements.section.appendChild(container);
        }


        #getGameSocket() {
            if (globalThis.sockets && globalThis.sockets.length > 0) {

                return globalThis.sockets.find(s => s.readyState === WebSocket.OPEN);
            }
            return null;
        }



        _sendDrawCommand(x1_game, y1_game, x2_game, y2_game, thickness, color) {
            const gameSocket = this.#getGameSocket();
            if (!gameSocket) {

                return;
            }


            const command = `42["drawcmd",0,[${x1_game/100},${y1_game/100},${x2_game/100},${y2_game/100},false,${0 - thickness},"${color}",0,0,{}]]`;
            gameSocket.send(command);


            if (this._gameCtx && this._gameCanvas) {
                const localX1 = (x1_game / 100) * this._gameCanvas.width;
                const localY1 = (y1_game / 100) * this._gameCanvas.height;
                const localX2 = (x2_game / 100) * this._gameCanvas.width;
                const localY2 = (y2_game / 100) * this._gameCanvas.height;

                this._gameCtx.strokeStyle = color;
                this._gameCtx.lineWidth = thickness;
                this._gameCtx.lineCap = 'round';
                this._gameCtx.lineJoin = 'round';
                this._gameCtx.beginPath();
                this._gameCtx.moveTo(localX1, localY1);
                this._gameCtx.lineTo(localX2, localY2);
                this._gameCtx.stroke();
            }
        }



        _loadCustomColors() {
            try {
                const storedColors = localStorage.getItem(LOCAL_STORAGE_KEY);
                this._customColors = storedColors ? JSON.parse(storedColors) : [...DEFAULT_CUSTOM_COLORS];
            } catch (e) {

                this._customColors = [...DEFAULT_CUSTOM_COLORS];
            }
            this._renderCustomColorButtons();
        }

        _saveCustomColors() {
            try {
                localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this._customColors));
                this.notify("success", "Colores personalizados guardados.");
            } catch (e) {

            }
        }

        _renderCustomColorButtons() {
            this._colorButtonsContainer.innerHTML = '';
            this._customColors.forEach(color => {
                this._createColorButton(color.hex, color.name);
            });
        }

        _addNewCustomColor(hexColor) {
            if (this._customColors.some(color => color.hex.toLowerCase() === hexColor.toLowerCase())) {

                return;
            }
            const newColor = { name: `Custom-${hexColor}`, hex: hexColor };
            this._customColors.push(newColor);
            this._saveCustomColors();
            this._createColorButton(newColor.hex, newColor.name);

        }


        addCustomColorFromExternal(hexColor) {
            this._addNewCustomColor(hexColor);
        }

        _clearAllCustomColors() {
            if (confirm("¿Estás seguro de que quieres eliminar todos los colores personalizados?")) {
                this._customColors = [...DEFAULT_CUSTOM_COLORS];
                this._saveCustomColors();
                this._renderCustomColorButtons();

            }
        }

        _createColorButton(hexColor, name) {
            const newButton = domMake.Tree("div", {
                class: "drawcontrols-button drawcontrols-color custom-color-button",
                style: `background-color: ${hexColor};`,
                title: name,
                "data-hex": hexColor
            });

            newButton.addEventListener('click', this._handleCustomColorClick);
            this._colorButtonsContainer.appendChild(newButton);
        }

        _findGameElementsForColorPalette() {
            if (!this._gameTriangleElement || !document.body.contains(this._gameTriangleElement)) {
                this._gameTriangleElement = document.getElementById('colorpicker-cursor');
            }
            if (!this._proxyGameButton || !document.body.contains(this._proxyGameButton)) {
                const drawControls = document.getElementById('drawcontrols-colors') || document.getElementById('drawcontrols');
                if (drawControls) {
                    this._proxyGameButton = drawControls.querySelector('.drawcontrols-button.drawcontrols-color:not(.drawcontrols-colorpicker):not(.custom-color-button)');
                }
            }
        }

        _handleCustomColorClick(event) {
            const clickedButton = event.currentTarget;
            this._findGameElementsForColorPalette();

            if (!this._proxyGameButton) {

                return;
            }

            const customColor = clickedButton.dataset.hex;
            const originalProxyColor = this._proxyGameButton.style.backgroundColor;

            this._proxyGameButton.style.backgroundColor = customColor;
            this._proxyGameButton.click();

            requestAnimationFrame(() => {
                this._proxyGameButton.style.backgroundColor = originalProxyColor;
                this._updateTrianglePosition(clickedButton);

                document.querySelectorAll('.custom-color-button.custom-active-color').forEach(btn => {
                    btn.classList.remove('custom-active-color');
                });
                clickedButton.classList.add('custom-active-color');
            });
        }

        _updateTrianglePosition(targetButton) {
            const triangle = this._gameTriangleElement;
            if (!triangle || !targetButton) return;
            const buttonContainer = document.getElementById('drawcontrols-colors') || document.getElementById('drawcontrols');
            if (!buttonContainer) return;

            const buttonRect = targetButton.getBoundingClientRect();
            const containerRect = buttonContainer.getBoundingClientRect();

            const buttonCenterRelativeToContainer = (buttonRect.left - containerRect.left) + (buttonRect.width / 2);
            const triangleWidth = triangle.offsetWidth || 8;
            const newLeft = buttonCenterRelativeToContainer - (triangleWidth / 2);

            triangle.style.left = `${newLeft}px`;
        }

        _setupColorPaletteObserver() {
            const observerTarget = document.getElementById('drawcontrols-colors') || document.getElementById('drawcontrols');
            if (!observerTarget) {

                setTimeout(() => this._setupColorPaletteObserver(), 1000);
                return;
            }

            this._colorPaletteObserver = new MutationObserver((mutations) => {
                mutations.forEach(mutation => {
                    if (mutation.type === 'childList' || mutation.type === 'attributes') {
                        this._addListenersToGameColorButtons();
                    }
                });
            });
            this._colorPaletteObserver.observe(observerTarget, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style'] });
            this._addListenersToGameColorButtons();
        }

        _addListenersToGameColorButtons() {
            this._findGameElementsForColorPalette();
            const gameColorButtons = document.querySelectorAll('.drawcontrols-button.drawcontrols-color:not(.custom-color-button)');
            gameColorButtons.forEach(gameBtn => {
                gameBtn.removeEventListener('click', this._handleGameColorClick);
                gameBtn.addEventListener('click', this._handleGameColorClick);
            });
        }

        _handleGameColorClick() {
            document.querySelectorAll('.custom-color-button.custom-active-color').forEach(customBtn => {
                customBtn.classList.remove('custom-active-color');
            });
        }



        _hookDrawingEvents() {
            if (!this._gameCanvas) return;


            this._gameCanvas.removeEventListener("mousedown", this._boundMouseDownHandler);
            this._gameCanvas.removeEventListener("mousemove", this._boundMouseMoveHandler);
            this._gameCanvas.removeEventListener("mouseup", this._boundMouseUpHandler);
            this._gameCanvas.removeEventListener("mouseout", this._boundMouseUpHandler);


            this._gameCanvas.addEventListener("mousedown", this._boundMouseDownHandler);
            this._gameCanvas.addEventListener("mousemove", this._boundMouseMoveHandler);
            this._gameCanvas.addEventListener("mouseup", this._boundMouseUpHandler);
            this._gameCanvas.addEventListener("mouseout", this._boundMouseUpHandler);
        }

        _handleMouseDown(e) {
            this.isDrawingLocal = true;
            const rect = e.target.getBoundingClientRect();

            this._lastMousePos.x = ((e.clientX - rect.left) / rect.width) * 100;
            this._lastMousePos.y = ((e.clientY - rect.top) / rect.height) * 100;
            this._lastTimestamp = performance.now();
        }

        _handleMouseMove(e) {
            if (!this.isDrawingLocal) return;

            const rect = e.target.getBoundingClientRect();
            const currentX_game = ((e.clientX - rect.left) / rect.width) * 100;
            const currentY_game = ((e.clientY - rect.top) / rect.height) * 100;

            let currentThickness = this._lastDrawThickness;
            let currentColor = this._getCurrentBrushColor();

            if (this._isPressureActive) {
                const currentTimestamp = performance.now();

                const originalRect = this._gameCanvas.getBoundingClientRect();
                const dx_px = e.clientX - ((this._lastMousePos.x / 100) * originalRect.width + originalRect.left);
                const dy_px = e.clientY - ((this._lastMousePos.y / 100) * originalRect.height + originalRect.top);
                const distance = Math.sqrt(dx_px * dx_px + dy_px * dy_px);
                const timeDelta = currentTimestamp - this._lastTimestamp;
                const speed = distance / timeDelta;

                const minThickness = 2;
                const maxThickness = 20;
                const speedFactor = 0.5;

                currentThickness = maxThickness - (speed * speedFactor);
                currentThickness = Math.max(minThickness, Math.min(maxThickness, currentThickness));
                this._lastDrawThickness = currentThickness;
            }

            if (this._isTextureActive) {
                currentColor = this._applyColorNoise(currentColor, 10);
            }

            this._sendDrawCommand(this._lastMousePos.x, this._lastMousePos.y, currentX_game, currentY_game, currentThickness, currentColor);

            this._lastMousePos.x = currentX_game;
            this._lastMousePos.y = currentY_game;
            this._lastTimestamp = performance.now();
        }

        _handleMouseUp() {
            this.isDrawingLocal = false;
            this._lastDrawThickness = 5;
        }

        _getCurrentBrushColor() {
            const colorPicker = document.querySelector('.drawcontrols-color.active');
            if (colorPicker) {
                const rgb = colorPicker.style.backgroundColor;
                if (rgb) return rgb;
            }
            return "#000000";
        }

        _applyColorNoise(color, noiseAmount) {
            let r, g, b;
            if (color.startsWith("rgb")) {
                const parts = color.match(/\d+/g).map(Number);
                r = parts[0]; g = parts[1]; b = parts[2];
            } else if (color.startsWith("#")) {
                const hex = color.slice(1);
                r = parseInt(hex.substring(0, 2), 16);
                g = parseInt(hex.substring(2, 4), 16);
                b = parseInt(hex.substring(4, 6), 16);
            } else {
                return color;
            }

            const addNoise = (value) => {
                const noise = (Math.random() - 0.5) * 2 * noiseAmount;
                return Math.max(0, Math.min(255, Math.floor(value + noise)));
            };
            return `rgb(${addNoise(r)},${addNoise(g)},${addNoise(b)})`;
        }

        _togglePressureControl(button) {
            this._isPressureActive = !this._isPressureActive;
            button.classList.toggle("active", this._isPressureActive);
            button.textContent = this._isPressureActive ? "Control de Presión Activo" : "Control de Presión";

        }

        _toggleTextureBrush(button) {
            this._isTextureActive = !this._isTextureActive;
            button.classList.toggle("active", this._isTextureActive);
            button.textContent = this._isTextureActive ? "Pincel Texturizado Activo" : "Pincel Texturizado";

        }

        async _simulateGradientFill(type) {


            const startX = 20, endX = 80;
            const startY = 20, endY = 80;
            const steps = 20;
            const thickness = 25;
            const delayMs = 50;

            for (let i = 0; i <= steps; i++) {
                const ratio = i / steps;
                let r, g, b;
                let currentColor;

                switch (type) {
                    case "diamond": {
                        r = Math.floor(0 + (255 - 0) * (1 - ratio));
                        g = Math.floor(0 + (215 - 0) * (1 - ratio));
                        b = Math.floor(139 + (0 - 139) * (1 - ratio));
                        currentColor = `rgb(${r},${g},${b})`;
                        const currentDistance = 40 * ratio;
                        const centerX = 50, centerY = 50;
                        const p1x = centerX, p1y = centerY - currentDistance;
                        const p2x = centerX + currentDistance, p2y = centerY;
                        const p3x = centerX, p3y = centerY + currentDistance;
                        const p4x = centerX - currentDistance, p4y = centerY;
                        this._sendDrawCommand(p1x, p1y, p2x, p2y, thickness, currentColor);
                        this._sendDrawCommand(p2x, p2y, p3x, p3y, thickness, currentColor);
                        this._sendDrawCommand(p3x, p3y, p4x, p4y, thickness, currentColor);
                        this._sendDrawCommand(p4x, p4y, p1x, p1y, thickness, currentColor);
                        break;
                    }
                    case "radial": {
                        r = 255;
                        g = Math.floor(165 + (255 - 165) * (1 - ratio));
                        b = 0;
                        currentColor = `rgb(${r},${g},${b})`;
                        const currentRadius = 30 * ratio;
                        const numSegments = 36;
                        for (let j = 0; j < numSegments; j++) {
                            const angle = (j / numSegments) * 2 * Math.PI;
                            const x = 50 + currentRadius * Math.cos(angle);
                            const y = 50 + currentRadius * Math.sin(angle);
                            this._sendDrawCommand(x, y, x + 0.1, y + 0.1, thickness, currentColor);
                        }
                        break;
                    }
                    case "linear": {
                        r = Math.floor(255 * (1 - ratio));
                        g = 0;
                        b = Math.floor(255 * ratio);
                        currentColor = `rgb(${r},${g},${b})`;
                        const currentY = startY + (endY - startY) * ratio;
                        this._sendDrawCommand(startX, currentY, endX, currentY, thickness, currentColor);
                        break;
                    }
                    case "vertical": {
                        r = Math.floor(128 + (255 - 128) * ratio);
                        g = Math.floor(0 + (192 - 0) * ratio);
                        b = Math.floor(128 + (203 - 128) * ratio);
                        currentColor = `rgb(${r},${g},${b})`;
                        const vertY = startY + (endY - startY) * ratio;
                        this._sendDrawCommand(startX, vertY, endX, vertY, thickness, currentColor);
                        break;
                    }
                    case "conical": {
                        r = Math.floor(0 + (255 - 0) * ratio);
                        g = 0;
                        b = 255;
                        currentColor = `rgb(${r},${g},${b})`;
                        const angle = (ratio * 2 * Math.PI);
                        const radius = 40;
                        const cx = 50, cy = 50;
                        const x2 = cx + radius * Math.cos(angle);
                        const y2 = cy + radius * Math.sin(angle);
                        this._sendDrawCommand(cx, cy, x2, y2, thickness, currentColor);
                        break;
                    }
                    case "wave": {
                        r = Math.floor(0 + (255 - 0) * ratio);
                        g = Math.floor(255 + (127 - 255) * ratio);
                        b = Math.floor(255 + (80 - 255) * ratio);
                        currentColor = `rgb(${r},${g},${b})`;
                        const waveAmplitude = 10;
                        const waveFrequency = 0.1;
                        const wavyY = startY + (endY - startY) * ratio + waveAmplitude * Math.sin(ratio * Math.PI * 2 * waveFrequency);
                        this._sendDrawCommand(startX, wavyY, endX, wavyY, thickness, currentColor);
                        break;
                    }
                }
                await new Promise(resolve => setTimeout(resolve, delayMs));
            }
            this.notify("success", `Degradado tipo '${type}' dibujado.`);
        }
    }
})("QBit");



(function PlayerProfileExtractorModule() {
    const QBit = globalThis[arguments[0]];


    const TOKEN_NAMES = {
        0: "Thumbs Up",
        1: "Heart",
        2: "Paint Brush",
        3: "Cocktail",
        4: "Peace Sign",
        5: "Feather",
        6: "Trophy",
        7: "Mug",
        8: "Gift"
    };

    class PlayerProfileExtractor extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        #profileUrlInput;
        #extractedDataDisplay;
        #downloadButton;
        #lastExtractedData = null;

        constructor() {
            super("📊Profile Data Extractor", '<i class="fas fa-id-card"></i>');
            this.#onStartup();
        }

        #onStartup() {
            this.#loadInterface();

        }

        #loadInterface() {
            const container = domMake.Tree("div");


            const urlRow = domMake.Row();

            this.#profileUrlInput = domMake.Tree("input", {
                type: "text",
                value: "https://drawaria.online/profile/?uid=86e33830-86ea-11ec-8553-bff27824cf71",
                style: "width: 100%; padding: 5px; box-sizing: border-box;"
            });
            urlRow.appendChild(this.#profileUrlInput);
            container.appendChild(urlRow);



            const searchRow = domMake.Row();
            const searchButton = domMake.Button('<i class="fas fa-search"></i> Buscar Perfil');
            searchButton.addEventListener("click", () => this.#fetchAndExtractProfile());
            searchRow.appendChild(searchButton);
            container.appendChild(searchRow);


            const displayRow = domMake.Row();
            this.#extractedDataDisplay = domMake.Tree("div", {
                style: `
                    max-height: 400px;
                    overflow-y: auto;
                    border: 1px solid var(--CE-color);
                    padding: 8px;
                    font-size: 0.85em;
                    background-color: var(--CE-bg_color);
                    color: var(--CE-color);
                    margin-top: 5px;
                    white-space: pre-wrap; /* Preserve whitespace and wrap lines */
                    font-family: monospace; /* For better readability of structured data */
                `
            }, ["Datos del perfil aparecerán aquí."]);
            displayRow.appendChild(this.#extractedDataDisplay);
            container.appendChild(displayRow);


            const downloadRow = domMake.Row();
            this.#downloadButton = domMake.Button('<i class="fas fa-download"></i> Descargar Datos (JSON)');
            this.#downloadButton.disabled = true;
            this.#downloadButton.addEventListener("click", () => this.#downloadExtractedData());
            downloadRow.appendChild(this.#downloadButton);
            container.appendChild(downloadRow);

            this.htmlElements.section.appendChild(container);
        }


        async #fetchAndExtractProfile() {
            const profileUrl = this.#profileUrlInput.value.trim();
            if (!profileUrl) {

                return;
            }


            const uidMatch = profileUrl.match(/uid=([a-f0-9-]+)/i);
            if (!uidMatch || !uidMatch[1]) {

                return;
            }
            const uid = uidMatch[1];



            this.#extractedDataDisplay.textContent = "Cargando...";
            this.#downloadButton.disabled = true;


            const parser = new DOMParser();

            try {

                const profileResponse = await fetch(profileUrl);
                if (!profileResponse.ok) {
                    throw new Error(`Error HTTP (${profileUrl}): ${profileResponse.status} ${profileResponse.statusText}`);
                }
                const profileHtmlContent = await profileResponse.text();

                const extracted = this._parseMainProfileHTML(profileHtmlContent, parser);
                extracted.uid = uid;


                const galleryPageUrl = `https://drawaria.online/gallery/?uid=${uid}`;
                try {
                    const galleryResponse = await fetch(galleryPageUrl);
                    if (galleryResponse.ok) {
                        const galleryHtmlContent = await galleryResponse.text();

                        const galleryDoc = parser.parseFromString(galleryHtmlContent, 'text/html');

                        extracted.galleryImagesCount = galleryDoc.querySelectorAll('.grid .grid-item.galleryimage').length;
                    } else {
                        extracted.galleryImagesCount = `Error (${galleryResponse.status})`;

                    }
                } catch (e) {
                    extracted.galleryImagesCount = `Error al parsear galería (${e.message.substring(0, 50)})`;

                }


                const friendsPageUrl = `https://drawaria.online/friends/?uid=${uid}`;
                try {
                    const friendsResponse = await fetch(friendsPageUrl);
                    if (friendsResponse.ok) {
                        const friendsHtmlContent = await friendsResponse.text();

                        const friendsDoc = parser.parseFromString(friendsHtmlContent, 'text/html');
                        extracted.friendsCount = friendsDoc.querySelectorAll('#friendscontainer .friendcard').length || 0;
                    } else {
                        extracted.friendsCount = `Error (${friendsResponse.status})`;

                    }
                } catch (e) {
                    extracted.friendsCount = `Error al parsear amigos (${e.message.substring(0, 50)})`;

                }


                const palettesPageUrl = `https://drawaria.online/palettes/?uid=${uid}`;
                try {
                    const palettesResponse = await fetch(palettesPageUrl);
                    if (palettesResponse.ok) {
                        const palettesHtmlContent = await palettesResponse.text();

                        const palettesDoc = parser.parseFromString(palettesHtmlContent, 'text/html');
                        extracted.palettesCount = palettesDoc.querySelectorAll('.palettelist .rowitem').length || 0;
                    } else {
                        extracted.palettesCount = `Error (${palettesResponse.status})`;

                    }
                } catch (e) {
                    extracted.palettesCount = `Error al parsear paletas (${e.message.substring(0, 50)})`;

                }




                const doc = parser.parseFromString(profileHtmlContent, 'text/html');
                if (extracted.playerName === 'N/A' && !doc.querySelector('h1')) {
                     this.#extractedDataDisplay.textContent = "No se pudo encontrar el perfil o extraer datos. Asegúrate de que la URL es correcta y el perfil existe.";
                     this.#lastExtractedData = null;
                     this.#downloadButton.disabled = true;

                     return;
                 }



                this.#lastExtractedData = extracted;
                this.#displayExtractedData();
                this.#downloadButton.disabled = false;
                this.notify("success", "Datos del perfil extraídos exitosamente.");

            } catch (error) {

                this.#extractedDataDisplay.textContent = `Error: ${error.message}`;
                this.#lastExtractedData = null;
            }
        }


        _parseMainProfileHTML(htmlContent, parser) {
            const doc = parser.parseFromString(htmlContent, 'text/html');
            const extracted = {};


            const playerInfoAnchor = doc.querySelector('h1 a[href*="profile/?uid="]');
            if (playerInfoAnchor) {
                extracted.avatarUrl = playerInfoAnchor.querySelector('img.turnresults-avatar')?.src || 'N/A';

                const playerNameNode = Array.from(playerInfoAnchor.childNodes).find(node => node.nodeType === Node.TEXT_NODE && node.textContent.trim().length > 0);
                extracted.playerName = playerNameNode?.textContent.trim() || 'N/A';
            } else {
                extracted.playerName = doc.querySelector('h1')?.textContent.trim() || 'N/A';
                extracted.avatarUrl = 'N/A';
            }


            extracted.level = doc.getElementById('levelval')?.textContent.trim() || 'N/A';
            extracted.experience = doc.getElementById('exp-val')?.textContent.trim() || 'N/A';


            extracted.pictionaryStats = {};
            const playStatsTableBody = doc.querySelector('#playstats table tbody');
            if (playStatsTableBody) {
                playStatsTableBody.querySelectorAll('tr').forEach(row => {
                    const label = row.querySelector('td:first-child')?.textContent.trim();
                    const value = row.querySelector('td:last-child')?.textContent.trim();
                    if (label && value) {
                        const cleanLabel = label.replace(/[:\s]/g, '').toLowerCase();
                        extracted.pictionaryStats[cleanLabel] = value;
                    }
                });
            }


            extracted.accusedTokens = {};
            const tokensTableBody = doc.querySelector('#tokens table tbody');
            if (tokensTableBody) {
                tokensTableBody.querySelectorAll('i[data-tokenid]').forEach(iconElement => {
                    const tokenId = parseInt(iconElement.dataset.tokenid);
                    const tokenName = TOKEN_NAMES[tokenId] || `Unknown Token ${tokenId}`;
                    const count = parseInt(iconElement.textContent.trim()) || 0;
                    extracted.accusedTokens[tokenName] = count;
                });
            }

            return extracted;
        }


        #displayExtractedData() {
            if (!this.#lastExtractedData) {
                this.#extractedDataDisplay.textContent = "No hay datos para mostrar.";
                return;
            }

            let displayTxt = `--- Datos del Perfil ---\n`;
            displayTxt += `  UID: ${this.#lastExtractedData.uid}\n`;
            displayTxt += `  Nombre del Jugador: ${this.#lastExtractedData.playerName}\n`;

            displayTxt += `  Experiencia: ${this.#lastExtractedData.experience}\n`;
            displayTxt += `  URL del Avatar: ${this.#lastExtractedData.avatarUrl}\n`;

            displayTxt += `  Cantidad de Amigos: ${this.#lastExtractedData.friendsCount}\n`;
            displayTxt += `  Cantidad de Paletas: ${this.#lastExtractedData.palettesCount}\n\n`;

            displayTxt += `--- Estadísticas de Pictionary / Adivinanza ---\n`;
            if (Object.keys(this.#lastExtractedData.pictionaryStats).length > 0) {
                for (const key in this.#lastExtractedData.pictionaryStats) {
                    const displayKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
                    displayTxt += `  ${displayKey}: ${this.#lastExtractedData.pictionaryStats[key]}\n`;
                }
            } else {
                displayTxt += `  No se encontraron estadísticas detalladas de Pictionary.\n`;
            }
            displayTxt += `\n`;

            displayTxt += `--- Menciones de Homenaje (Tokens) ---\n`;
            if (Object.keys(this.#lastExtractedData.accusedTokens).length > 0) {
                for (const tokenName in this.#lastExtractedData.accusedTokens) {
                    displayTxt += `  ${tokenName}: ${this.#lastExtractedData.accusedTokens[tokenName]}\n`;
                }
            } else {
                displayTxt += `  No se encontraron Menciones de Homenaje.\n`;
            }

            this.#extractedDataDisplay.textContent = displayTxt;
        }


        #downloadExtractedData() {
            if (!this.#lastExtractedData) {

                return;
            }

            const dataStr = JSON.stringify(this.#lastExtractedData, null, 2);
            const blob = new Blob([dataStr], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const playerNameForFilename = this.#lastExtractedData.playerName.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 30);
            const filename = `drawaria_profile_${playerNameForFilename}_${this.#lastExtractedData.uid ? this.#lastExtractedData.uid.substring(0, 8) : 'data'}.json`;

            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
            this.notify("success", `Datos del perfil descargados como ${filename}.`);
        }
    }
})("QBit");






(function ImageToolsModule() {
    const QBit = globalThis["QBit"];


    function rgbToHex(r, g, b) {
        return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
    }

    class ImageTools extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        constructor() {
            super("🏞️Smart Gallery Editor", '<i class="fas fa-image"></i>');//id-badge
            this.imageUrlInput = null;


            this.scaleInput = null;
            this.widthInput = null;
            this.heightInput = null;
            this.resizeProcessButton = null;
            this.resizePreviewContainer = null;


            this.pickedColorBox = null;
            this.pickedColorHex = null;
            this.pickedColorRgb = null;
            this.copyColorButton = null;


            this.flipRotateSection = null;
            this.flipHButton = null;
            this.flipVButton = null;
            this.rotateCWButton = null;
            this.rotateCCWButton = null;


            this.grayscaleSection = null;
            this.grayscaleTypeSelect = null;
            this.applyGrayscaleButton = null;
            this.resetGrayscaleButton = null;


            this.brightnessContrastSection = null;
            this.brightnessInput = null;
            this.contrastInput = null;
            this.applyBrightnessContrastButton = null;
            this.resetBrightnessContrastButton = null;


            this.invertColorsSection = null;
            this.invertColorsButton = null;


            this.blurSection = null;
            this.blurRadiusInput = null;
            this.applyBlurButton = null;
            this.resetBlurButton = null;


            this.sharpenSection = null;
            this.sharpenAmountInput = null;
            this.applySharpenButton = null;
            this.resetSharpenButton = null;


            this.saturationSection = null;
            this.saturationInput = null;
            this.applySaturationButton = null;
            this.resetSaturationButton = null;


            this.sepiaSection = null;
            this.sepiaAmountInput = null;
            this.applySepiaButton = null;
            this.resetSepiaButton = null;


            this.colorFilterSection = null;
            this.colorFilterRInput = null;
            this.colorFilterGInput = null;
            this.colorFilterBInput = null;
            this.colorFilterMixInput = null;
            this.colorFilterColorBox = null;
            this.applyColorFilterButton = null;
            this.resetColorFilterButton = null;


            this.thresholdSection = null;
            this.thresholdInput = null;
            this.applyThresholdButton = null;
            this.resetThresholdButton = null;


            this.edgeDetectionSection = null;
            this.edgeDetectionButton = null;


            this.interactiveCanvas = null;
            this.loadingIndicator = null;

            this.currentImageBase64 = null;
            this.originalImageBase64OnLoad = null;
            this.originalWidth = 0;
            this.originalHeight = 0;
            this.isColorPickingMode = false;

            this._onStartup();
        }

        _onStartup() {
            this._loadInterface();
        }

        _loadInterface() {
            const section = this.htmlElements.section;


            const loadControlsSection = domMake.Tree("div", { style: "margin-bottom:15px;" });
            loadControlsSection.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Cargar Imagen por URL"));

            const urlRow = domMake.Row();
            this.imageUrlInput = domMake.Tree("input", {
                type: "text",
                value: "https://galleryhost2-cf.drawaria.online/images/1838s.jpg",
                style: "min-width:170px;max-width:260px;",
                id: "image-tools-url-input"
            });
            urlRow.appendChild(this.imageUrlInput);
            loadControlsSection.appendChild(urlRow);

            const loadImageButton = domMake.Button("Cargar Imagen");
            loadImageButton.addEventListener('click', () => this._loadImageFromURL(this.imageUrlInput.value));
            loadControlsSection.appendChild(domMake.Tree("div", { style: "text-align:center; margin-top: 10px;" }, loadImageButton));

            section.appendChild(loadControlsSection);
            section.appendChild(domMake.Tree("hr"));


            const modeTabs = domMake.Tree("div", { class: "icon-list", style: "justify-content:center; margin-bottom:10px;" });


            const resizerTabInputId = generate.uuidv4();
            const resizerTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: resizerTabInputId, hidden: true, checked: true });
            const resizerTabLabel = domMake.Tree("label", { for: resizerTabInputId, class: "icon", title: "Redimensionar Imagen" }, domMake.Tree("i", { class: "fas fa-arrows-alt-h" }));
            resizerTabInput.addEventListener("change", () => this._toggleToolMode('resizer'));


            const colorPickerTabInputId = generate.uuidv4();
            const colorPickerTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: colorPickerTabInputId, hidden: true });
            const colorPickerTabLabel = domMake.Tree("label", { for: colorPickerTabInputId, class: "icon", title: "Selector de Color" }, domMake.Tree("i", { class: "fas fa-palette" }));
            colorPickerTabInput.addEventListener("change", () => this._toggleToolMode('colorPicker'));


            const flipRotateTabInputId = generate.uuidv4();
            const flipRotateTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: flipRotateTabInputId, hidden: true });
            const flipRotateTabLabel = domMake.Tree("label", { for: flipRotateTabInputId, class: "icon", title: "Voltear/Rotar Imagen" }, domMake.Tree("i", { class: "fas fa-sync-alt" }));
            flipRotateTabInput.addEventListener("change", () => this._toggleToolMode('flipRotate'));


            const grayscaleTabInputId = generate.uuidv4();
            const grayscaleTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: grayscaleTabInputId, hidden: true });
            const grayscaleTabLabel = domMake.Tree("label", { for: grayscaleTabInputId, class: "icon", title: "Escala de Grises" }, domMake.Tree("i", { class: "fas fa-tint-slash" }));
            grayscaleTabInput.addEventListener("change", () => this._toggleToolMode('grayscale'));


            const brightnessContrastTabInputId = generate.uuidv4();
            const brightnessContrastTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: brightnessContrastTabInputId, hidden: true });
            const brightnessContrastTabLabel = domMake.Tree("label", { for: brightnessContrastTabInputId, class: "icon", title: "Brillo/Contraste" }, domMake.Tree("i", { class: "fas fa-sun" }));
            brightnessContrastTabInput.addEventListener("change", () => this._toggleToolMode('brightnessContrast'));


            const invertColorsTabInputId = generate.uuidv4();
            const invertColorsTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: invertColorsTabInputId, hidden: true });
            const invertColorsTabLabel = domMake.Tree("label", { for: invertColorsTabInputId, class: "icon", title: "Invertir Colores" }, domMake.Tree("i", { class: "fas fa-exchange-alt" }));
            invertColorsTabInput.addEventListener("change", () => this._toggleToolMode('invertColors'));


            const blurTabInputId = generate.uuidv4();
            const blurTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: blurTabInputId, hidden: true });
            const blurTabLabel = domMake.Tree("label", { for: blurTabInputId, class: "icon", title: "Desenfoque" }, domMake.Tree("i", { class: "fas fa-water" }));
            blurTabInput.addEventListener("change", () => this._toggleToolMode('blur'));


            const sharpenTabInputId = generate.uuidv4();
            const sharpenTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: sharpenTabInputId, hidden: true });
            const sharpenTabLabel = domMake.Tree("label", { for: sharpenTabInputId, class: "icon", title: "Nitidez" }, domMake.Tree("i", { class: "fas fa-plus-circle" }));
            sharpenTabInput.addEventListener("change", () => this._toggleToolMode('sharpen'));


            const saturationTabInputId = generate.uuidv4();
            const saturationTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: saturationTabInputId, hidden: true });
            const saturationTabLabel = domMake.Tree("label", { for: saturationTabInputId, class: "icon", title: "Saturación" }, domMake.Tree("i", { class: "fas fa-fill-drip" }));
            saturationTabInput.addEventListener("change", () => this._toggleToolMode('saturation'));


            const sepiaTabInputId = generate.uuidv4();
            const sepiaTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: sepiaTabInputId, hidden: true });
            const sepiaTabLabel = domMake.Tree("label", { for: sepiaTabInputId, class: "icon", title: "Sepia" }, domMake.Tree("i", { class: "fas fa-tint" }));
            sepiaTabInput.addEventListener("change", () => this._toggleToolMode('sepia'));


            const colorFilterTabInputId = generate.uuidv4();
            const colorFilterTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: colorFilterTabInputId, hidden: true });
            const colorFilterTabLabel = domMake.Tree("label", { for: colorFilterTabInputId, class: "icon", title: "Filtro de Color" }, domMake.Tree("i", { class: "fas fa-fill" }));
            colorFilterTabInput.addEventListener("change", () => this._toggleToolMode('colorFilter'));


            const thresholdTabInputId = generate.uuidv4();
            const thresholdTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: thresholdTabInputId, hidden: true });
            const thresholdTabLabel = domMake.Tree("label", { for: thresholdTabInputId, class: "icon", title: "Umbral" }, domMake.Tree("i", { class: "fas fa-adjust" }));
            thresholdTabInput.addEventListener("change", () => this._toggleToolMode('threshold'));


            const edgeDetectionTabInputId = generate.uuidv4();
            const edgeDetectionTabInput = domMake.Tree("input", { type: "radio", name: "imageToolMode", id: edgeDetectionTabInputId, hidden: true });
            const edgeDetectionTabLabel = domMake.Tree("label", { for: edgeDetectionTabInputId, class: "icon", title: "Detección de Bordes" }, domMake.Tree("i", { class: "fas fa-ruler-combined" }));
            edgeDetectionTabInput.addEventListener("change", () => this._toggleToolMode('edgeDetection'));


            modeTabs.appendAll(
                resizerTabInput, resizerTabLabel,
                colorPickerTabInput, colorPickerTabLabel,
                flipRotateTabInput, flipRotateTabLabel,
                grayscaleTabInput, grayscaleTabLabel,
                brightnessContrastTabInput, brightnessContrastTabLabel,
                invertColorsTabInput, invertColorsTabLabel,
                blurTabInput, blurTabLabel,
                sharpenTabInput, sharpenTabLabel,
                saturationTabInput, saturationTabLabel,
                sepiaTabInput, sepiaTabLabel,
                colorFilterTabInput, colorFilterTabLabel,
                thresholdTabInput, thresholdTabLabel,
                edgeDetectionTabInput, edgeDetectionTabLabel
            );
            section.appendChild(modeTabs);


            this.resizerSection = domMake.Tree("section", { id: "image-resizer-section" });
            this.colorPickerSection = domMake.Tree("section", { id: "image-color-picker-section", hidden: true });
            this.flipRotateSection = domMake.Tree("section", { id: "image-flip-rotate-section", hidden: true });
            this.grayscaleSection = domMake.Tree("section", { id: "image-grayscale-section", hidden: true });
            this.brightnessContrastSection = domMake.Tree("section", { id: "image-brightness-contrast-section", hidden: true });
            this.invertColorsSection = domMake.Tree("section", { id: "image-invert-colors-section", hidden: true });
            this.blurSection = domMake.Tree("section", { id: "image-blur-section", hidden: true });
            this.sharpenSection = domMake.Tree("section", { id: "image-sharpen-section", hidden: true });
            this.saturationSection = domMake.Tree("section", { id: "image-saturation-section", hidden: true });
            this.sepiaSection = domMake.Tree("section", { id: "image-sepia-section", hidden: true });
            this.colorFilterSection = domMake.Tree("section", { id: "image-color-filter-section", hidden: true });
            this.thresholdSection = domMake.Tree("section", { id: "image-threshold-section", hidden: true });
            this.edgeDetectionSection = domMake.Tree("section", { id: "image-edge-detection-section", hidden: true });


            section.appendAll(
                this.resizerSection,
                this.colorPickerSection,
                this.flipRotateSection,
                this.grayscaleSection,
                this.brightnessContrastSection,
                this.invertColorsSection,
                this.blurSection,
                this.sharpenSection,
                this.saturationSection,
                this.sepiaSection,
                this.colorFilterSection,
                this.thresholdSection,
                this.edgeDetectionSection
            );



            this.interactiveCanvas = domMake.Tree("canvas", {
                style: "max-width:100%; height:auto; display:block; margin: 15px auto 10px auto; border: 1px dashed var(--CE-color); cursor:crosshair;",
            });
            this.interactiveCanvas.width = 1;
            this.interactiveCanvas.height = 1;
            this.interactiveCanvas.addEventListener('click', (e) => this._pickColor(e));
            this.interactiveCanvas.addEventListener('mousemove', (e) => this._previewColor(e));
            this.interactiveCanvas.addEventListener('mouseout', () => this._clearPreviewColor());

            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin:15px 0 5px 0;" }, "Imagen Cargada:"));
            section.appendChild(this.interactiveCanvas);



            this.loadingIndicator = domMake.Tree("div", {
                style: "font-size:0.9em;color:#888;margin-top:5px;display:none; text-align:center;",
            }, "Procesando...");
            section.appendChild(this.loadingIndicator);


            section.appendChild(
                domMake.Tree("div", {
                    style: "font-size:0.8em;opacity:.7;margin:10px 0;line-height:1.4",
                }, [
                    domMake.Tree("strong", {}, "Funcionamiento:"),
                    " Esta herramienta carga y manipula imágenes localmente en tu navegador. Pega un link directo de imagen de galería de Drawaria. Se usará GM_xmlhttpRequest para superar CORS."
                ])
            );


            this._loadResizerUI();
            this._loadColorPickerUI();
            this._loadFlipRotateUI();
            this._loadGrayscaleUI();
            this._loadBrightnessContrastUI();
            this._loadInvertColorsUI();
            this._loadBlurUI();
            this._loadSharpenUI();
            this._loadSaturationUI();
            this._loadSepiaUI();
            this._loadColorFilterUI();
            this._loadThresholdUI();
            this._loadEdgeDetectionUI();

            this._updateToolButtonsState();
        }

        _loadResizerUI() {
            const section = this.resizerSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta de Redimensionar"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "5px";

            this.scaleInput = domMake.Tree("input", {
                type: "number", min: 1, max: 500, value: 100, step: 5,
                title: "Escala (%)", style: "width: calc(33% - 5px);", id: "resizer-scale-input"
            });
            this.widthInput = domMake.Tree("input", {
                type: "number", min: 1, placeholder: "Ancho (px)", title: "Ancho Fijo (px)",
                style: "width: calc(33% - 5px);", id: "resizer-width-input"
            });
            this.heightInput = domMake.Tree("input", {
                type: "number", min: 1, placeholder: "Alto (px)", title: "Alto Fijo (px)",
                style: "width: calc(33% - 5px);", id: "resizer-height-input"
            });

            controlsRow.appendAll(
                domMake.Tree("span", { style: "width:100%; text-align:center; font-size:0.9em; opacity:0.8;" }, "Ajustar Tamaño:"),
                this.scaleInput,
                domMake.Tree("span", { style: "font-size:0.8em; opacity:0.7; display:flex; align-items:center;" }, "%"),
                this.widthInput,
                this.heightInput
            );

            [this.scaleInput, this.widthInput, this.heightInput].forEach(inputElement => {
                inputElement.addEventListener("input", () => {
                    if (inputElement.value !== "") {
                        if (inputElement !== this.scaleInput) this.scaleInput.value = "";
                        if (inputElement !== this.widthInput) this.widthInput.value = "";
                        if (inputElement !== this.heightInput) this.heightInput.value = "";
                    }
                });
            });
            section.appendChild(controlsRow);

            this.resizeProcessButton = domMake.Button("Aplicar Redimensionar");
            this.resizeProcessButton.disabled = true;
            this.resizeProcessButton.addEventListener("click", () => this.processResize());
            section.appendChild(domMake.Tree("div", { style: "text-align:center; margin-top: 10px;" }, this.resizeProcessButton));

            this.resizePreviewContainer = domMake.Tree("div", {
                style: "margin-top:15px; border-top: 1px solid var(--CE-color); padding-top:10px; text-align: center;"
            });
            section.appendChild(this.resizePreviewContainer);
        }

        _loadColorPickerUI() {
            const section = this.colorPickerSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta de Selector de Color"));

            const colorDisplayRow = domMake.Row();
            colorDisplayRow.style.alignItems = "center";
            colorDisplayRow.style.gap = "10px";

            this.pickedColorBox = domMake.Tree("div", {
                style: "width:40px; height:40px; border:1px solid var(--CE-color); border-radius:4px; background-color:#CCC; flex-shrink:0;",
            });
            this.pickedColorHex = domMake.Tree("span", { style: "font-weight:bold; width: 100%;" }, "HEX: -");
            this.pickedColorRgb = domMake.Tree("span", { style: "font-size:0.9em; opacity:0.8; width: 100%;" }, "RGB: -");
            this.copyColorButton = domMake.Button('<i class="fas fa-copy"></i> Copiar');
            this.copyColorButton.disabled = true;
            this.copyColorButton.addEventListener("click", () => this._copyPickedColor());

            const textContainer = domMake.Tree("div", { style: "display:flex; flex-direction:column; flex-grow:1;" });
            textContainer.appendAll(this.pickedColorHex, this.pickedColorRgb);

            colorDisplayRow.appendAll(this.pickedColorBox, textContainer, this.copyColorButton);
            section.appendChild(colorDisplayRow);

            section.appendChild(domMake.Tree("p", { style: "text-align:center; font-size:0.85em; opacity:0.7; margin-top:10px;" }, "Haz click en la imagen de arriba para seleccionar un color."));
        }


        _loadFlipRotateUI() {
            const section = this.flipRotateSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Voltear/Rotar"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.justifyContent = "center";
            controlsRow.style.gap = "10px";

            this.flipHButton = domMake.Button('<i class="fas fa-arrows-alt-h"></i> Voltear Horizontal');
            this.flipVButton = domMake.Button('<i class="fas fa-arrows-alt-v"></i> Voltear Vertical');
            this.rotateCWButton = domMake.Button('<i class="fas fa-redo"></i> Rotar 90° CW');
            this.rotateCCWButton = domMake.Button('<i class="fas fa-undo"></i> Rotar 90° CCW');

            this.flipHButton.addEventListener('click', () => this.processFlipRotate('horizontal'));
            this.flipVButton.addEventListener('click', () => this.processFlipRotate('vertical'));
            this.rotateCWButton.addEventListener('click', () => this.processFlipRotate('rotateCW'));
            this.rotateCCWButton.addEventListener('click', () => this.processFlipRotate('rotateCCW'));

            controlsRow.appendAll(this.flipHButton, this.flipVButton, this.rotateCWButton, this.rotateCCWButton);
            section.appendChild(controlsRow);
        }


        _loadGrayscaleUI() {
            const section = this.grayscaleSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Escala de Grises"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.justifyContent = "center";
            controlsRow.style.gap = "10px";

            const typeLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Tipo de Grises: ");
            this.grayscaleTypeSelect = domMake.Tree("select", { style: "width:auto;" });
            this.grayscaleTypeSelect.append(
                domMake.Tree("option", { value: "luminance" }, "Luminancia (Perceptual)"),
                domMake.Tree("option", { value: "average" }, "Promedio Simple")
            );

            controlsRow.appendAll(
                typeLabel, this.grayscaleTypeSelect
            );
            section.appendChild(controlsRow);

            this.applyGrayscaleButton = domMake.Button('Aplicar Escala de Grises');
            this.applyGrayscaleButton.addEventListener('click', () => this.processGrayscale());

            this.resetGrayscaleButton = domMake.Button('Reset');
            this.resetGrayscaleButton.addEventListener('click', () => {
                this.grayscaleTypeSelect.value = "luminance";
                this.processGrayscale(true);
            });

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";
            buttonsRow.appendAll(this.applyGrayscaleButton, this.resetGrayscaleButton);
            section.appendChild(buttonsRow);
        }


        _loadBrightnessContrastUI() {
            const section = this.brightnessContrastSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Brillo/Contraste"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "10px";
            controlsRow.style.justifyContent = "center";



            const brightnessLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Brillo: ");
            this.brightnessInput = domMake.Tree("input", {
                type: "range", min: -100, max: 100, value: 0, step: 1,
                style: "width:80%;", title: "Ajuste de Brillo"
            });
            const brightnessValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "0");
            this.brightnessInput.addEventListener('input', () => brightnessValueSpan.textContent = this.brightnessInput.value);


            const contrastLabel = domMake.Tree("label", { style: "width:100%; text-align:center; margin-top:10px;" }, "Contraste: ");
            this.contrastInput = domMake.Tree("input", {
                type: "range", min: -100, max: 100, value: 0, step: 1,
                style: "width:80%;", title: "Ajuste de Contraste"
            });
            const contrastValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "0");
            this.contrastInput.addEventListener('input', () => contrastValueSpan.textContent = this.contrastInput.value);


            controlsRow.appendAll(
                brightnessLabel, this.brightnessInput, brightnessValueSpan,
                contrastLabel, this.contrastInput, contrastValueSpan
            );
            section.appendChild(controlsRow);

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";

            this.applyBrightnessContrastButton = domMake.Button('Aplicar');
            this.applyBrightnessContrastButton.addEventListener('click', () => this.processBrightnessContrast());

            this.resetBrightnessContrastButton = domMake.Button('Reset');
            this.resetBrightnessContrastButton.addEventListener('click', () => {
                this.brightnessInput.value = 0;
                this.contrastInput.value = 0;
                brightnessValueSpan.textContent = "0";
                contrastValueSpan.textContent = "0";
                this.processBrightnessContrast(true);
            });

            buttonsRow.appendAll(this.applyBrightnessContrastButton, this.resetBrightnessContrastButton);
            section.appendChild(buttonsRow);
        }

        _loadInvertColorsUI() {
            const section = this.invertColorsSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Invertir Colores"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.justifyContent = "center";
            controlsRow.style.gap = "10px";

            this.invertColorsButton = domMake.Button('<i class="fas fa-adjust"></i> Invertir Colores');
            this.invertColorsButton.addEventListener('click', () => this.processInvertColors());

            controlsRow.appendAll(this.invertColorsButton);
            section.appendChild(controlsRow);
        }

        _loadBlurUI() {
            const section = this.blurSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Desenfoque"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "10px";
            controlsRow.style.justifyContent = "center";

            const blurLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Radio de Desenfoque (píxeles): ");
            this.blurRadiusInput = domMake.Tree("input", {
                type: "range", min: 0, max: 10, value: 0, step: 1,
                style: "width:80%;", title: "Radio de Desenfoque"
            });
            const blurValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "0");
            this.blurRadiusInput.addEventListener('input', () => blurValueSpan.textContent = this.blurRadiusInput.value);

            controlsRow.appendAll(
                blurLabel, this.blurRadiusInput, blurValueSpan
            );
            section.appendChild(controlsRow);

            this.applyBlurButton = domMake.Button('Aplicar Desenfoque');
            this.applyBlurButton.addEventListener('click', () => this.processBlur());

            this.resetBlurButton = domMake.Button('Reset Desenfoque');
            this.resetBlurButton.addEventListener('click', () => {
                this.blurRadiusInput.value = 0;
                blurValueSpan.textContent = "0";
                this.processBlur(true);
            });

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";
            buttonsRow.appendAll(this.applyBlurButton, this.resetBlurButton);
            section.appendChild(buttonsRow);
        }

        _loadSharpenUI() {
            const section = this.sharpenSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Nitidez"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "10px";
            controlsRow.style.justifyContent = "center";

            const sharpenLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Cantidad de Nitidez: ");
            this.sharpenAmountInput = domMake.Tree("input", {
                type: "range", min: 0, max: 10, value: 0, step: 1,
                style: "width:80%;", title: "Cantidad de Nitidez"
            });
            const sharpenValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "0");
            this.sharpenAmountInput.addEventListener('input', () => sharpenValueSpan.textContent = this.sharpenAmountInput.value);

            controlsRow.appendAll(
                sharpenLabel, this.sharpenAmountInput, sharpenValueSpan
            );
            section.appendChild(controlsRow);

            this.applySharpenButton = domMake.Button('Aplicar Nitidez');
            this.applySharpenButton.addEventListener('click', () => this.processSharpen());

            this.resetSharpenButton = domMake.Button('Reset Nitidez');
            this.resetSharpenButton.addEventListener('click', () => {
                this.sharpenAmountInput.value = 0;
                sharpenValueSpan.textContent = "0";
                this.processSharpen(true);
            });

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";
            buttonsRow.appendAll(this.applySharpenButton, this.resetSharpenButton);
            section.appendChild(buttonsRow);
        }

        _loadSaturationUI() {
            const section = this.saturationSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Saturación"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "10px";
            controlsRow.style.justifyContent = "center";

            const saturationLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Nivel de Saturación: ");
            this.saturationInput = domMake.Tree("input", {
                type: "range", min: 0, max: 200, value: 100, step: 1,
                style: "width:80%;", title: "Ajuste de Saturación"
            });
            const saturationValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "100%");
            this.saturationInput.addEventListener('input', () => saturationValueSpan.textContent = this.saturationInput.value + "%");

            controlsRow.appendAll(
                saturationLabel, this.saturationInput, saturationValueSpan
            );
            section.appendChild(controlsRow);

            this.applySaturationButton = domMake.Button('Aplicar Saturación');
            this.applySaturationButton.addEventListener('click', () => this.processSaturation());

            this.resetSaturationButton = domMake.Button('Reset Saturación');
            this.resetSaturationButton.addEventListener('click', () => {
                this.saturationInput.value = 100;
                saturationValueSpan.textContent = "100%";
                this.processSaturation(true);
            });

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";
            buttonsRow.appendAll(this.applySaturationButton, this.resetSaturationButton);
            section.appendChild(buttonsRow);
        }

        _loadSepiaUI() {
            const section = this.sepiaSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Sepia"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "10px";
            controlsRow.style.justifyContent = "center";

            const sepiaLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Intensidad Sepia (%): ");
            this.sepiaAmountInput = domMake.Tree("input", {
                type: "range", min: 0, max: 100, value: 100, step: 1,
                style: "width:80%;", title: "Intensidad Sepia"
            });
            const sepiaValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "100%");
            this.sepiaAmountInput.addEventListener('input', () => sepiaValueSpan.textContent = this.sepiaAmountInput.value + "%");

            controlsRow.appendAll(
                sepiaLabel, this.sepiaAmountInput, sepiaValueSpan
            );
            section.appendChild(controlsRow);

            this.applySepiaButton = domMake.Button('Aplicar Sepia');
            this.applySepiaButton.addEventListener('click', () => this.processSepia());

            this.resetSepiaButton = domMake.Button('Reset Sepia');
            this.resetSepiaButton.addEventListener('click', () => {
                this.sepiaAmountInput.value = 100;
                sepiaValueSpan.textContent = "100%";
                this.processSepia(true);
            });

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";
            buttonsRow.appendAll(this.applySepiaButton, this.resetSepiaButton);
            section.appendChild(buttonsRow);
        }

        _loadColorFilterUI() {
            const section = this.colorFilterSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Filtro de Color"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "10px";
            controlsRow.style.justifyContent = "center";


            const rLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Rojo: ");
            this.colorFilterRInput = domMake.Tree("input", { type: "range", min: 0, max: 255, value: 0, step: 1, style: "width:80%;" });
            const rValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "0");
            this.colorFilterRInput.addEventListener('input', () => { rValueSpan.textContent = this.colorFilterRInput.value; this._updateColorFilterPreview(); });


            const gLabel = domMake.Tree("label", { style: "width:100%; text-align:center; margin-top:10px;" }, "Verde: ");
            this.colorFilterGInput = domMake.Tree("input", { type: "range", min: 0, max: 255, value: 0, step: 1, style: "width:80%;" });
            const gValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "0");
            this.colorFilterGInput.addEventListener('input', () => { gValueSpan.textContent = this.colorFilterGInput.value; this._updateColorFilterPreview(); });


            const bLabel = domMake.Tree("label", { style: "width:100%; text-align:center; margin-top:10px;" }, "Azul: ");
            this.colorFilterBInput = domMake.Tree("input", { type: "range", min: 0, max: 255, value: 0, step: 1, style: "width:80%;" });
            const bValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "0");
            this.colorFilterBInput.addEventListener('input', () => { bValueSpan.textContent = this.colorFilterBInput.value; this._updateColorFilterPreview(); });


            const mixLabel = domMake.Tree("label", { style: "width:100%; text-align:center; margin-top:10px;" }, "Intensidad Mezcla (%): ");
            this.colorFilterMixInput = domMake.Tree("input", { type: "range", min: 0, max: 100, value: 50, step: 1, style: "width:80%;" });
            const mixValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "50%");
            this.colorFilterMixInput.addEventListener('input', () => mixValueSpan.textContent = this.colorFilterMixInput.value + "%");

            this.colorFilterColorBox = domMake.Tree("div", {
                style: "width:40px; height:40px; border:1px solid var(--CE-color); border-radius:4px; background-color:#CCC; flex-shrink:0; margin: 10px auto;"
            });

            controlsRow.appendAll(
                rLabel, this.colorFilterRInput, rValueSpan,
                gLabel, this.colorFilterGInput, gValueSpan,
                bLabel, this.colorFilterBInput, bValueSpan,
                mixLabel, this.colorFilterMixInput, mixValueSpan,
                this.colorFilterColorBox
            );
            section.appendChild(controlsRow);

            this.applyColorFilterButton = domMake.Button('Aplicar Filtro');
            this.applyColorFilterButton.addEventListener('click', () => this.processColorFilter());

            this.resetColorFilterButton = domMake.Button('Reset Filtro');
            this.resetColorFilterButton.addEventListener('click', () => {
                this.colorFilterRInput.value = 0; rValueSpan.textContent = "0";
                this.colorFilterGInput.value = 0; gValueSpan.textContent = "0";
                this.colorFilterBInput.value = 0; bValueSpan.textContent = "0";
                this.colorFilterMixInput.value = 50; mixValueSpan.textContent = "50%";
                this._updateColorFilterPreview();
                this.processColorFilter(true);
            });

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";
            buttonsRow.appendAll(this.applyColorFilterButton, this.resetColorFilterButton);
            section.appendChild(buttonsRow);
            this._updateColorFilterPreview();
        }

        _loadThresholdUI() {
            const section = this.thresholdSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Umbral (Binarizar)"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.gap = "10px";
            controlsRow.style.justifyContent = "center";

            const thresholdLabel = domMake.Tree("label", { style: "width:100%; text-align:center;" }, "Valor del Umbral (0-255): ");
            this.thresholdInput = domMake.Tree("input", {
                type: "range", min: 0, max: 255, value: 128, step: 1,
                style: "width:80%;", title: "Valor del Umbral"
            });
            const thresholdValueSpan = domMake.Tree("span", { style: "width:10%; text-align:right;" }, "128");
            this.thresholdInput.addEventListener('input', () => thresholdValueSpan.textContent = this.thresholdInput.value);

            controlsRow.appendAll(
                thresholdLabel, this.thresholdInput, thresholdValueSpan
            );
            section.appendChild(controlsRow);

            this.applyThresholdButton = domMake.Button('Aplicar Umbral');
            this.applyThresholdButton.addEventListener('click', () => this.processThreshold());

            this.resetThresholdButton = domMake.Button('Reset Umbral');
            this.resetThresholdButton.addEventListener('click', () => {
                this.thresholdInput.value = 128;
                thresholdValueSpan.textContent = "128";
                this.processThreshold(true);
            });

            const buttonsRow = domMake.Row();
            buttonsRow.style.justifyContent = "center";
            buttonsRow.style.gap = "10px";
            buttonsRow.style.marginTop = "15px";
            buttonsRow.appendAll(this.applyThresholdButton, this.resetThresholdButton);
            section.appendChild(buttonsRow);
        }

        _loadEdgeDetectionUI() {
            const section = this.edgeDetectionSection;
            section.appendChild(domMake.Tree("h5", { style: "text-align:center; margin-bottom:10px;" }, "Herramienta Detección de Bordes"));

            const controlsRow = domMake.Row();
            controlsRow.style.flexFlow = "wrap";
            controlsRow.style.justifyContent = "center";
            controlsRow.style.gap = "10px";

            this.edgeDetectionButton = domMake.Button('<i class="fas fa-ruler-combined"></i> Aplicar Detección de Bordes');
            this.edgeDetectionButton.addEventListener('click', () => this.processEdgeDetection());

            controlsRow.appendAll(this.edgeDetectionButton);
            section.appendChild(controlsRow);
        }


        _toggleToolMode(mode) {

            this.resizerSection.hidden = true;
            this.colorPickerSection.hidden = true;
            this.flipRotateSection.hidden = true;
            this.grayscaleSection.hidden = true;
            this.brightnessContrastSection.hidden = true;
            this.invertColorsSection.hidden = true;
            this.blurSection.hidden = true;
            this.sharpenSection.hidden = true;
            this.saturationSection.hidden = true;
            this.sepiaSection.hidden = true;
            this.colorFilterSection.hidden = true;
            this.thresholdSection.hidden = true;
            this.edgeDetectionSection.hidden = true;


            if (mode === 'resizer') {
                this.resizerSection.hidden = false;
                this.isColorPickingMode = false;
            } else if (mode === 'colorPicker') {
                this.colorPickerSection.hidden = false;
                this.isColorPickingMode = true;
                this._clearPickedColor();
            } else if (mode === 'flipRotate') {
                this.flipRotateSection.hidden = false;
                this.isColorPickingMode = false;
            } else if (mode === 'grayscale') {
                this.grayscaleSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.grayscaleTypeSelect) this.grayscaleTypeSelect.value = "luminance";
            } else if (mode === 'brightnessContrast') {
                this.brightnessContrastSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.brightnessInput) this.brightnessInput.value = 0;
                if (this.contrastInput) this.contrastInput.value = 0;
                const brightnessValueSpan = this.brightnessInput?.nextElementSibling;
                if(brightnessValueSpan) brightnessValueSpan.textContent = "0";
                const contrastValueSpan = this.contrastInput?.nextElementSibling;
                if(contrastValueSpan) contrastValueSpan.textContent = "0";
            } else if (mode === 'invertColors') {
                this.invertColorsSection.hidden = false;
                this.isColorPickingMode = false;
            } else if (mode === 'blur') {
                this.blurSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.blurRadiusInput) this.blurRadiusInput.value = 0;
                const blurValueSpan = this.blurRadiusInput?.nextElementSibling;
                if(blurValueSpan) blurValueSpan.textContent = "0";
            } else if (mode === 'sharpen') {
                this.sharpenSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.sharpenAmountInput) this.sharpenAmountInput.value = 0;
                const sharpenValueSpan = this.sharpenAmountInput?.nextElementSibling;
                if(sharpenValueSpan) sharpenValueSpan.textContent = "0";
            } else if (mode === 'saturation') {
                this.saturationSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.saturationInput) this.saturationInput.value = 100;
                const saturationValueSpan = this.saturationInput?.nextElementSibling;
                if(saturationValueSpan) saturationValueSpan.textContent = "100%";
            } else if (mode === 'sepia') {
                this.sepiaSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.sepiaAmountInput) this.sepiaAmountInput.value = 100;
                const sepiaValueSpan = this.sepiaAmountInput?.nextElementSibling;
                if(sepiaValueSpan) sepiaValueSpan.textContent = "100%";
            } else if (mode === 'colorFilter') {
                this.colorFilterSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.colorFilterRInput) this.colorFilterRInput.value = 0;
                if (this.colorFilterGInput) this.colorFilterGInput.value = 0;
                if (this.colorFilterBInput) this.colorFilterBInput.value = 0;
                if (this.colorFilterMixInput) this.colorFilterMixInput.value = 50;
                const rSpan = this.colorFilterRInput?.nextElementSibling; if(rSpan) rSpan.textContent = "0";
                const gSpan = this.colorFilterGInput?.nextElementSibling; if(gSpan) gSpan.textContent = "0";
                const bSpan = this.colorFilterBInput?.nextElementSibling; if(bSpan) bSpan.textContent = "0";
                const mixSpan = this.colorFilterMixInput?.nextElementSibling; if(mixSpan) mixSpan.textContent = "50%";
                this._updateColorFilterPreview();
            } else if (mode === 'threshold') {
                this.thresholdSection.hidden = false;
                this.isColorPickingMode = false;

                if (this.thresholdInput) this.thresholdInput.value = 128;
                const thresholdValueSpan = this.thresholdInput?.nextElementSibling;
                if(thresholdValueSpan) thresholdValueSpan.textContent = "128";
            } else if (mode === 'edgeDetection') {
                this.edgeDetectionSection.hidden = false;
                this.isColorPickingMode = false;
            }

            this._updateToolButtonsState();
        }

        _updateToolButtonsState() {
            const hasLoadedImage = this.currentImageBase64 !== null;


            if (this.resizeProcessButton) {
                this.resizeProcessButton.disabled = !hasLoadedImage;
            }

            if (this.flipHButton) this.flipHButton.disabled = !hasLoadedImage;
            if (this.flipVButton) this.flipVButton.disabled = !hasLoadedImage;
            if (this.rotateCWButton) this.rotateCWButton.disabled = !hasLoadedImage;
            if (this.rotateCCWButton) this.rotateCCWButton.disabled = !hasLoadedImage;

            if (this.applyGrayscaleButton) this.applyGrayscaleButton.disabled = !hasLoadedImage;
            if (this.resetGrayscaleButton) this.resetGrayscaleButton.disabled = !hasLoadedImage;

            if (this.applyBrightnessContrastButton) this.applyBrightnessContrastButton.disabled = !hasLoadedImage;
            if (this.resetBrightnessContrastButton) this.resetBrightnessContrastButton.disabled = !hasLoadedImage;

            if (this.invertColorsButton) this.invertColorsButton.disabled = !hasLoadedImage;

            if (this.applyBlurButton) this.applyBlurButton.disabled = !hasLoadedImage;
            if (this.resetBlurButton) this.resetBlurButton.disabled = !hasLoadedImage;

            if (this.applySharpenButton) this.applySharpenButton.disabled = !hasLoadedImage;
            if (this.resetSharpenButton) this.resetSharpenButton.disabled = !hasLoadedImage;

            if (this.applySaturationButton) this.applySaturationButton.disabled = !hasLoadedImage;
            if (this.resetSaturationButton) this.resetSaturationButton.disabled = !hasLoadedImage;

            if (this.applySepiaButton) this.applySepiaButton.disabled = !hasLoadedImage;
            if (this.resetSepiaButton) this.resetSepiaButton.disabled = !hasLoadedImage;

            if (this.applyColorFilterButton) this.applyColorFilterButton.disabled = !hasLoadedImage;
            if (this.resetColorFilterButton) this.resetColorFilterButton.disabled = !hasLoadedImage;

            if (this.applyThresholdButton) this.applyThresholdButton.disabled = !hasLoadedImage;
            if (this.resetThresholdButton) this.resetThresholdButton.disabled = !hasLoadedImage;

            if (this.edgeDetectionButton) this.edgeDetectionButton.disabled = !hasLoadedImage;


        }

        async _loadImageFromURL(imageUrl) {
            if (!imageUrl) {

                return;
            }

            this.loadingIndicator.style.display = 'block';

            this._updateToolButtonsState();
            this.currentImageBase64 = null;
            this.originalImageBase64OnLoad = null;
            if (this.resizePreviewContainer) {
                this.resizePreviewContainer.innerHTML = "";
            }


            const img = new Image();
            img.crossOrigin = "anonymous";

            const handleImageLoaded = () => {
                this.originalWidth = img.naturalWidth;
                this.originalHeight = img.naturalHeight;

                this.interactiveCanvas.width = img.naturalWidth;
                this.interactiveCanvas.height = img.naturalHeight;
                const ctx = this.interactiveCanvas.getContext('2d');
                ctx.clearRect(0, 0, this.interactiveCanvas.width, this.interactiveCanvas.height);
                ctx.drawImage(img, 0, 0);

                try {
                    this.currentImageBase64 = this.interactiveCanvas.toDataURL('image/png');
                    this.originalImageBase64OnLoad = this.currentImageBase64;
                    this.notify("success", "Imagen cargada y lista.");
                } catch (e) {
                    console.error("Error al obtener Base64 desde el canvas (Tainted Canvas inesperado):", e);

                    this.currentImageBase64 = null;
                    this.originalImageBase64OnLoad = null;
                    this.interactiveCanvas.getContext('2d').clearRect(0,0,this.interactiveCanvas.width,this.interactiveCanvas.height);
                }

                this.loadingIndicator.style.display = 'none';
                this._updateToolButtonsState();
            };

            const handleImageError = (msg, errorObj = null) => {
                this.loadingIndicator.style.display = 'none';

                if (errorObj) console.error("Image loading error:", errorObj);
                this.currentImageBase64 = null;
                this.originalImageBase64OnLoad = null;
                this._updateToolButtonsState();
                this.interactiveCanvas.getContext('2d').clearRect(0,0,this.interactiveCanvas.width,this.interactiveCanvas.height);
            };

            if (typeof GM_xmlhttpRequest !== 'undefined') {
                GM_xmlhttpRequest({
                    method: "GET",
                    url: imageUrl,
                    responseType: "blob",
                    onload: (res) => {
                        if (res.status === 200) {
                            const reader = new FileReader();
                            reader.onloadend = (e) => {
                                img.onload = handleImageLoaded;
                                img.onerror = (imgErr) => handleImageError("No se pudo cargar la imagen desde el blob (archivo corrupto o formato no soportado).", imgErr);
                                img.src = e.target.result;
                            };
                            reader.onerror = (readerErr) => handleImageError("Error al leer los datos de la imagen (blob).", readerErr);
                            reader.readAsDataURL(res.response);
                        } else {
                            handleImageError(`Fallo al cargar URL via GM_xmlhttpRequest: Status ${res.status} - ${res.statusText || 'Error desconocido'}.`);
                        }
                    },
                    onerror: (err) => {
                        handleImageError(`Error de red con GM_xmlhttpRequest. No se pudo cargar la imagen: ${err.statusText || JSON.stringify(err)}`);
                    }
                });
            } else {
                handleImageError("GM_xmlhttpRequest no está disponible. Asegúrate que `@grant GM_xmlhttpRequest` está en el encabezado de tu userscript. La carga de imágenes de galería puede fallar.");
            }
        }


        async _processImageTransform(transformFunction, notificationMsg, isReset = false) {
            if (!this.originalImageBase64OnLoad) {

                return;
            }

            this.loadingIndicator.style.display = 'block';

            this._updateToolButtonsState();

            const img = new Image();
            img.src = isReset ? this.originalImageBase64OnLoad : this.currentImageBase64;
            img.onload = () => {
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = this.originalWidth;
                canvas.height = this.originalHeight;

                ctx.drawImage(img, 0, 0);

                transformFunction(ctx, canvas.width, canvas.height, img);

                this.currentImageBase64 = canvas.toDataURL('image/png');
                this.interactiveCanvas.width = this.originalWidth;
                this.interactiveCanvas.height = this.originalHeight;
                this.interactiveCanvas.getContext('2d').drawImage(canvas, 0, 0);

                this.notify("success", notificationMsg + " completado.");
                this.loadingIndicator.style.display = 'none';
                this._updateToolButtonsState();
            };
            img.onerror = (e) => {
                console.error(`Error al cargar imagen para ${notificationMsg}:`, e);

                this.loadingIndicator.style.display = 'none';
                this._updateToolButtonsState();
            };
        }


        processResize() {
            if (!this.currentImageBase64) {

                return;
            }

            this.loadingIndicator.style.display = 'block';
            this.resizeProcessButton.disabled = true;
            if (this.resizePreviewContainer) {
                this.resizePreviewContainer.innerHTML = "";
            }


            const img = new Image();
            img.src = this.currentImageBase64;
            img.onload = () => {
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');

                let targetWidth = this.originalWidth;
                let targetHeight = this.originalHeight;

                const scale = parseFloat(this.scaleInput.value);
                const fixedWidth = parseInt(this.widthInput.value, 10);
                const fixedHeight = parseInt(this.heightInput.value, 10);

                if (!isNaN(scale) && scale > 0) {
                    targetWidth = this.originalWidth * (scale / 100);
                    targetHeight = this.originalHeight * (scale / 100);
                } else if (!isNaN(fixedWidth) && fixedWidth > 0) {
                    targetWidth = fixedWidth;
                    targetHeight = this.originalHeight * (fixedWidth / this.originalWidth);
                } else if (!isNaN(fixedHeight) && fixedHeight > 0) {
                    targetHeight = fixedHeight;
                    targetWidth = this.originalWidth * (fixedHeight / this.originalHeight);
                }

                targetWidth = Math.max(1, Math.round(targetWidth));
                targetHeight = Math.max(1, Math.round(targetHeight));

                canvas.width = targetWidth;
                canvas.height = targetHeight;

                ctx.drawImage(img, 0, 0, targetWidth, targetHeight);

                const previewImg = domMake.Tree("img", {
                    src: canvas.toDataURL('image/png'),
                    alt: "Imagen Redimensionada",
                    style: "max-width:100%; height:auto; display:block; margin: 0 auto; border: 1px dashed var(--CE-color);",
                });
                if (this.resizePreviewContainer) {
                    this.resizePreviewContainer.appendChild(previewImg);
                }

                const downloadLink = domMake.Tree("a", {
                    href: canvas.toDataURL('image/png'),
                    download: `resized_image_${targetWidth}x${targetHeight}.png`,
                    class: "btn btn-outline-secondary",
                    style: "margin-top: 10px;",
                }, "Descargar Imagen Redimensionada");
                if (this.resizePreviewContainer) {
                    this.resizePreviewContainer.appendChild(downloadLink);
                }

                this.notify("success", `Imagen redimensionada a ${targetWidth}x${targetHeight}.`);
                this.loadingIndicator.style.display = 'none';
                this._updateToolButtonsState();
            };
            img.onerror = (e) => {
                console.error("Error al cargar la imagen para redimensionar (executeResizeProcess):", e);

                this.loadingIndicator.style.display = 'none';
                this._updateToolButtonsState();
            };
        }


        async processFlipRotate(transformType) {
            this._processImageTransform((ctx, width, height, imgRef) => {
                let newWidth = this.originalWidth;
                let newHeight = this.originalHeight;

                ctx.setTransform(1, 0, 0, 1, 0, 0);
                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);


                if (transformType === 'rotateCW' || transformType === 'rotateCCW') {

                    newWidth = this.originalHeight;
                    newHeight = this.originalWidth;
                    ctx.canvas.width = newWidth;
                    ctx.canvas.height = newHeight;
                    ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
                    ctx.rotate(transformType === 'rotateCW' ? Math.PI / 2 : -Math.PI / 2);
                    ctx.drawImage(imgRef, -this.originalWidth / 2, -this.originalHeight / 2);
                } else if (transformType === 'horizontal') {
                    ctx.canvas.width = newWidth;
                    ctx.canvas.height = newHeight;
                    ctx.translate(ctx.canvas.width, 0);
                    ctx.scale(-1, 1);
                    ctx.drawImage(imgRef, 0, 0);
                } else if (transformType === 'vertical') {
                    ctx.canvas.width = newWidth;
                    ctx.canvas.height = newHeight;
                    ctx.translate(0, ctx.canvas.height);
                    ctx.scale(1, -1);
                    ctx.drawImage(imgRef, 0, 0);
                } else {
                    ctx.canvas.width = newWidth;
                    ctx.canvas.height = newHeight;
                    ctx.drawImage(imgRef, 0, 0);
                }

                this.originalWidth = newWidth;
                this.originalHeight = newHeight;

            }, `Aplicando transformación de volteo/rotación.`, false);
        }



        async processGrayscale(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                if (isReset) return;

                const imageData = ctx.getImageData(0, 0, width, height);
                const data = imageData.data;
                const type = this.grayscaleTypeSelect.value;

                for (let i = 0; i < data.length; i += 4) {
                    const r = data[i];
                    const g = data[i + 1];
                    const b = data[i + 2];
                    let avg;

                    if (type === 'luminance') {
                        avg = (r * 0.299 + g * 0.587 + b * 0.114);
                    } else {
                        avg = (r + g + b) / 3;
                    }
                    data[i] = avg;
                    data[i + 1] = avg;
                    data[i + 2] = avg;
                }
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando escala de grises (${this.grayscaleTypeSelect?.value || 'luminancia'}).`, isReset);
        }


        async processBrightnessContrast(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                if (isReset) return;

                const brightness = parseFloat(this.brightnessInput.value);
                const contrast = parseFloat(this.contrastInput.value);

                const imageData = ctx.getImageData(0, 0, width, height);
                const data = imageData.data;

                const brightnessFactor = brightness / 100;
                const contrastFactor = (contrast + 100) / 100;
                const intercept = 128 * (1 - contrastFactor);

                for (let i = 0; i < data.length; i += 4) {
                    data[i] = data[i] + (brightnessFactor * 255);
                    data[i + 1] = data[i + 1] + (brightnessFactor * 255);
                    data[i + 2] = data[i + 2] + (brightnessFactor * 255);

                    data[i] = (data[i] * contrastFactor) + intercept;
                    data[i + 1] = (data[i + 1] * contrastFactor) + intercept;
                    data[i + 2] = (data[i + 2] * contrastFactor) + intercept;

                    data[i] = Math.max(0, Math.min(255, data[i]));
                    data[i + 1] = Math.max(0, Math.min(255, data[i + 1]));
                    data[i + 2] = Math.max(0, Math.min(255, data[i + 2]));
                }
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando brillo (${this.brightnessInput?.value}) y contraste (${this.contrastInput?.value}).`, isReset);
        }


        async processInvertColors() {
             this._processImageTransform((ctx, width, height) => {
                const imageData = ctx.getImageData(0, 0, width, height);
                const data = imageData.data;

                for (let i = 0; i < data.length; i += 4) {
                    data[i] = 255 - data[i];
                    data[i + 1] = 255 - data[i + 1];
                    data[i + 2] = 255 - data[i + 2];
                }
                ctx.putImageData(imageData, 0, 0);
            }, "Invirtiendo colores.");
        }


        async processBlur(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                const radius = isReset ? 0 : parseInt(this.blurRadiusInput.value, 10);
                if (radius === 0) return;

                const imageData = ctx.getImageData(0, 0, width, height);
                const pixels = imageData.data;
                const blurredPixels = new Uint8ClampedArray(pixels.length);

                for (let y = 0; y < height; y++) {
                    for (let x = 0; x < width; x++) {
                        let rSum = 0, gSum = 0, bSum = 0, count = 0;
                        for (let dy = -radius; dy <= radius; dy++) {
                            for (let dx = -radius; dx <= radius; dx++) {
                                const nx = x + dx;
                                const ny = y + dy;
                                if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
                                    const index = (ny * width + nx) * 4;
                                    rSum += pixels[index]; gSum += pixels[index + 1]; bSum += pixels[index + 2];
                                    count++;
                                }
                            }
                        }
                        const outputIndex = (y * width + x) * 4;
                        blurredPixels[outputIndex] = rSum / count;
                        blurredPixels[outputIndex + 1] = gSum / count;
                        blurredPixels[outputIndex + 2] = bSum / count;
                        blurredPixels[outputIndex + 3] = pixels[outputIndex + 3];
                    }
                }
                imageData.data.set(blurredPixels);
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando desenfoque (Radio: ${this.blurRadiusInput?.value || 0}).`, isReset);
        }


        async processSharpen(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                const amount = isReset ? 0 : parseInt(this.sharpenAmountInput.value, 10);
                if (amount === 0) return;

                const imageData = ctx.getImageData(0, 0, width, height);
                const pixels = imageData.data;
                const sharpenedPixels = new Uint8ClampedArray(pixels.length);

                const kernelFactor = amount;
                const kernel = [
                    0, -kernelFactor, 0,
                    -kernelFactor, 1 + 4 * kernelFactor, -kernelFactor,
                    0, -kernelFactor, 0
                ];

                const kernelSize = Math.sqrt(kernel.length);
                const halfKernel = Math.floor(kernelSize / 2);

                for (let y = 0; y < height; y++) {
                    for (let x = 0; x < width; x++) {
                        let rSum = 0, gSum = 0, bSum = 0;

                        for (let ky = 0; ky < kernelSize; ky++) {
                            for (let kx = 0; kx < kernelSize; kx++) {
                                const px = x + kx - halfKernel;
                                const py = y + ky - halfKernel;

                                if (px >= 0 && px < width && py >= 0 && py < height) {
                                    const index = (py * width + px) * 4;
                                    const kernelValue = kernel[ky * kernelSize + kx];

                                    rSum += pixels[index] * kernelValue;
                                    gSum += pixels[index + 1] * kernelValue;
                                    bSum += pixels[index + 2] * kernelValue;
                                }
                            }
                        }

                        const outputIndex = (y * width + x) * 4;
                        sharpenedPixels[outputIndex] = Math.max(0, Math.min(255, rSum));
                        sharpenedPixels[outputIndex + 1] = Math.max(0, Math.min(255, gSum));
                        sharpenedPixels[outputIndex + 2] = Math.max(0, Math.min(255, bSum));
                        sharpenedPixels[outputIndex + 3] = pixels[outputIndex + 3];
                    }
                }
                imageData.data.set(sharpenedPixels);
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando nitidez (Cantidad: ${this.sharpenAmountInput?.value || 0}).`, isReset);
        }


        async processSaturation(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                const saturation = isReset ? 100 : parseFloat(this.saturationInput.value);
                if (saturation === 100 && !isReset) return;

                const imageData = ctx.getImageData(0, 0, width, height);
                const data = imageData.data;

                const R = 0.299;
                const G = 0.587;
                const B = 0.114;
                const S = saturation / 100;

                for (let i = 0; i < data.length; i += 4) {
                    const r = data[i];
                    const g = data[i + 1];
                    const b = data[i + 2];

                    const gray = R * r + G * g + B * b;

                    data[i] = Math.max(0, Math.min(255, gray + (r - gray) * S));
                    data[i + 1] = Math.max(0, Math.min(255, gray + (g - gray) * S));
                    data[i + 2] = Math.max(0, Math.min(255, gray + (b - gray) * S));
                }
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando saturación (${this.saturationInput?.value || 100}%).`, isReset);
        }


        async processSepia(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                const sepiaAmount = isReset ? 100 : parseFloat(this.sepiaAmountInput.value);
                if (sepiaAmount === 0 && !isReset) return;

                const imageData = ctx.getImageData(0, 0, width, height);
                const data = imageData.data;

                const amountFactor = sepiaAmount / 100;

                for (let i = 0; i < data.length; i += 4) {
                    const r = data[i];
                    const g = data[i + 1];
                    const b = data[i + 2];

                    const gray = (r * 0.299 + g * 0.587 + b * 0.114);

                    let sr = Math.min(255, (gray * 0.393) + (gray * 0.769) + (gray * 0.189));
                    let sg = Math.min(255, (gray * 0.349) + (gray * 0.686) + (gray * 0.168));
                    let sb = Math.min(255, (gray * 0.272) + (gray * 0.534) + (gray * 0.131));


                    data[i] = r + (sr - r) * amountFactor;
                    data[i + 1] = g + (sg - g) * amountFactor;
                    data[i + 2] = b + (sb - b) * amountFactor;

                    data[i] = Math.max(0, Math.min(255, data[i]));
                    data[i + 1] = Math.max(0, Math.min(255, data[i + 1]));
                    data[i + 2] = Math.max(0, Math.min(255, data[i + 2]));
                }
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando efecto sepia (${this.sepiaAmountInput?.value || 100}%).`, isReset);
        }


        async processColorFilter(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                const targetR = isReset ? 0 : parseInt(this.colorFilterRInput.value, 10);
                const targetG = isReset ? 0 : parseInt(this.colorFilterGInput.value, 10);
                const targetB = isReset ? 0 : parseInt(this.colorFilterBInput.value, 10);
                const mixAmount = isReset ? 50 : parseInt(this.colorFilterMixInput.value, 10);

                const imageData = ctx.getImageData(0, 0, width, height);
                const data = imageData.data;

                const mixFactor = mixAmount / 100;

                for (let i = 0; i < data.length; i += 4) {
                    const r = data[i];
                    const g = data[i + 1];
                    const b = data[i + 2];

                    data[i] = r + (targetR - r) * mixFactor;
                    data[i + 1] = g + (targetG - g) * mixFactor;
                    data[i + 2] = b + (targetB - b) * mixFactor;

                    data[i] = Math.max(0, Math.min(255, data[i]));
                    data[i + 1] = Math.max(0, Math.min(255, data[i + 1]));
                    data[i + 2] = Math.max(0, Math.min(255, data[i + 2]));
                }
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando filtro de color (R:${this.colorFilterRInput?.value}, G:${this.colorFilterGInput?.value}, B:${this.colorFilterBInput?.value}, Mezcla:${this.colorFilterMixInput?.value}%).`, isReset);
        }


        _updateColorFilterPreview() {
            if (!this.colorFilterColorBox || !this.colorFilterRInput) return;

            const r = parseInt(this.colorFilterRInput.value, 10);
            const g = parseInt(this.colorFilterGInput.value, 10);
            const b = parseInt(this.colorFilterBInput.value, 10);
            const hex = rgbToHex(r, g, b);
            this.colorFilterColorBox.style.backgroundColor = hex;
        }


        async processThreshold(isReset = false) {
            this._processImageTransform((ctx, width, height) => {
                const thresholdValue = isReset ? 128 : parseInt(this.thresholdInput.value, 10);

                const imageData = ctx.getImageData(0, 0, width, height);
                const data = imageData.data;

                for (let i = 0; i < data.length; i += 4) {
                    const r = data[i];
                    const g = data[i + 1];
                    const b = data[i + 2];
                    const avg = (r * 0.299 + g * 0.587 + b * 0.114);

                    const color = avg >= thresholdValue ? 255 : 0;
                    data[i] = color;
                    data[i + 1] = color;
                    data[i + 2] = color;
                }
                ctx.putImageData(imageData, 0, 0);
            }, `Aplicando umbral (Valor: ${this.thresholdInput?.value || 128}).`, isReset);
        }


        async processEdgeDetection() {
            this._processImageTransform((ctx, width, height) => {
                const imageData = ctx.getImageData(0, 0, width, height);
                const pixels = imageData.data;
                const outputPixels = new Uint8ClampedArray(pixels.length);

                const grayPixels = new Uint8ClampedArray(pixels.length / 4);
                for (let i = 0; i < pixels.length; i += 4) {
                    grayPixels[i / 4] = (pixels[i] * 0.299 + pixels[i + 1] * 0.587 + pixels[i + 2] * 0.114);
                }

                const sobelX = [
                    -1, 0, 1,
                    -2, 0, 2,
                    -1, 0, 1
                ];
                const sobelY = [
                    -1, -2, -1,
                    0, 0, 0,
                    1, 2, 1
                ];

                for (let y = 1; y < height - 1; y++) {
                    for (let x = 1; x < width - 1; x++) {
                        let Gx = 0;
                        let Gy = 0;

                        for (let ky = 0; ky < 3; ky++) {
                            for (let kx = 0; kx < 3; kx++) {
                                const px = x + kx - 1;
                                const py = y + ky - 1;
                                const index = (py * width + px);
                                const kernelIndex = ky * 3 + kx;

                                Gx += grayPixels[index] * sobelX[kernelIndex];
                                Gy += grayPixels[index] * sobelY[kernelIndex];
                            }
                        }

                        const magnitude = Math.sqrt(Gx * Gx + Gy * Gy);
                        const edgeValue = 255 - Math.min(255, Math.max(0, magnitude));

                        const outputIndex = (y * width + x) * 4;
                        outputPixels[outputIndex] = edgeValue;
                        outputPixels[outputIndex + 1] = edgeValue;
                        outputPixels[outputIndex + 2] = edgeValue;
                        outputPixels[outputIndex + 3] = pixels[outputIndex + 3];
                    }
                }

                for (let i = 0; i < pixels.length; i += 4) {
                    const x = (i / 4) % width;
                    const y = Math.floor((i / 4) / width);
                    if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {
                        outputPixels[i] = 0;
                        outputPixels[i + 1] = 0;
                        outputPixels[i + 2] = 0;
                        outputPixels[i + 3] = pixels[i + 3];
                    }
                }

                imageData.data.set(outputPixels);
                ctx.putImageData(imageData, 0, 0);
            }, "Aplicando detección de bordes.");
        }



        _pickColor(event) {
            if (!this.currentImageBase64) {

                return;
            }
            if (!this.isColorPickingMode) return;

            const canvas = this.interactiveCanvas;
            const ctx = canvas.getContext('2d');

            const rect = canvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;

            const scaleX = canvas.width / rect.width;
            const scaleY = canvas.height / rect.height;
            const pixelX = Math.floor(x * scaleX);
            const pixelY = Math.floor(y * scaleY);

            if (pixelX < 0 || pixelX >= canvas.width || pixelY < 0 || pixelY >= canvas.height) {
                this._clearPickedColor();
                return;
            }

            const pixel = ctx.getImageData(pixelX, pixelY, 1, 1).data;
            const r = pixel[0];
            const g = pixel[1];
            const b = pixel[2];
            const a = pixel[3];

            if (a === 0) {

                this._clearPickedColor();
                return;
            }

            const hex = rgbToHex(r, g, b);
            if (this.pickedColorBox) this.pickedColorBox.style.backgroundColor = hex;
            if (this.pickedColorHex) this.pickedColorHex.textContent = `HEX: ${hex}`;
            if (this.pickedColorRgb) this.pickedColorRgb.textContent = `RGB: ${r}, ${g}, ${b}`;
            if (this.copyColorButton) this.copyColorButton.disabled = false;

            this.notify("success", `Color seleccionado: ${hex}`);
        }

        _previewColor(event) {
            if (!this.currentImageBase64 || !this.isColorPickingMode) {
                this._clearPreviewColor();
                return;
            }

            const canvas = this.interactiveCanvas;
            const ctx = canvas.getContext('2d');

            const rect = canvas.getBoundingClientRect();
            const x = event.clientX - rect.left;
            const y = event.clientY - rect.top;

            const scaleX = canvas.width / rect.width;
            const scaleY = canvas.height / rect.height;
            const pixelX = Math.floor(x * scaleX);
            const pixelY = Math.floor(y * scaleY);

            if (pixelX < 0 || pixelX >= canvas.width || pixelY < 0 || pixelY >= canvas.height) {
                this._clearPreviewColor();
                return;
            }

            const pixel = ctx.getImageData(pixelX, pixelY, 1, 1).data;
            const r = pixel[0];
            const g = pixel[1];
            const b = pixel[2];
            const a = pixel[3];

            if (a === 0) {
                this._clearPreviewColor();
                return;
            }

            const hex = rgbToHex(r, g, b);
            if (this.pickedColorBox) this.pickedColorBox.style.backgroundColor = hex;
            if (this.pickedColorHex) this.pickedColorHex.textContent = `HEX: ${hex}`;
            if (this.pickedColorRgb) this.pickedColorRgb.textContent = `RGB: ${r}, ${g}, ${b}`;
            if (this.copyColorButton) this.copyColorButton.disabled = false;
        }

        _clearPreviewColor() {
            if (!this.isColorPickingMode || !this.currentImageBase64) {
                this._clearPickedColor();
            }
        }

        _clearPickedColor() {
            if (this.pickedColorBox) this.pickedColorBox.style.backgroundColor = '#CCC';
            if (this.pickedColorHex) this.pickedColorHex.textContent = 'HEX: -';
            if (this.pickedColorRgb) this.pickedColorRgb.textContent = 'RGB: -';
            if (this.copyColorButton) this.copyColorButton.disabled = true;
        }

        _copyPickedColor() {
            const hex = this.pickedColorHex.textContent.replace('HEX: ', '');
            if (hex && hex !== '-') {
                navigator.clipboard.writeText(hex)
                    .then(() => this.notify("success", `Color ${hex} copiado al portapapeles!`))
                    .catch(err => {

                        console.error("Copy color error:", err);
                    });
            } else {

            }
        }
    }
})();





(function ImageAnalyzerModule() {
    const QBit = globalThis[arguments[0]];

    QBit.Styles.addRules([
        `#image-analyzer-container {
            display: flex;
            flex-direction: column;
            gap: 10px;
            padding: 5px;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
        }`,
        `#image-analyzer-container input[type="text"],
         #image-analyzer-container button {
            width: 100%;
            padding: 5px;
            box-sizing: border-box;
         }`,
        `#image-preview-canvas {
            border: 1px dashed var(--CE-color);
            max-width: 100%;
            height: auto;
            display: block;
            margin: 5px auto;
        }`,
        `#dominant-colors-display {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            margin-top: 5px;
        }`,
        `#dominant-colors-display .color-swatch {
            width: 30px;
            height: 30px;
            border: 1px solid #ccc;
            border-radius: 3px;
            cursor: pointer;
            box-shadow: 0 0 3px rgba(0,0,0,0.2);
        }`,
        `#analysis-results {
            background-color: var(--CE-bg_color);
            border: 1px solid var(--CE-color);
            padding: 8px;
            margin-top: 10px;
            font-size: 0.8em;
            max-height: 150px;
            overflow-y: auto;
            white-space: pre-wrap;
            font-family: monospace;
        }`,
        `#image-analyzer-container .action-buttons {
            display: flex;
            gap: 5px;
            margin-top: 5px;
        }`,
        `#image-analyzer-container .action-buttons button {
            flex: 1;
        }`
    ]);

    class ImageAnalyzer extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        #imageUrlInput;
        #loadAndAnalyzeButton;
        #imagePreviewCanvas;
        #imagePreviewCtx;
        #dominantColorsDisplay;
        #analysisResultsDisplay;
        #copyResultsButton;
        #downloadResultsButton;

        constructor() {
            super("📸Image Analytics", '<i class="fas fa-camera-retro"></i>');
            this.#onStartup();
        }

        #onStartup() {
            this.#loadInterface();

            this.#loadAndAnalyzeButton.disabled = false;
            this.#loadAndAnalyzeButton.textContent = 'Cargar y Analizar';
            this.#copyResultsButton.disabled = true;
            this.#downloadResultsButton.disabled = true;
        }

        #loadInterface() {
            const container = domMake.Tree("div", { id: "image-analyzer-container" });

            this.#imageUrlInput = domMake.Tree("input", {
                type: "text",
                placeholder: "Pegar URL de imagen (ej. avatar)",
                value: "https://drawaria.online/avatar/cache/1a5f4450-7153-11ef-acaf-250da20bac69.jpg"
            });
            container.appendChild(this.#imageUrlInput);

            this.#loadAndAnalyzeButton = domMake.Button('<i class="fas fa-chart-pie"></i> Cargar y Analizar');
            this.#loadAndAnalyzeButton.addEventListener("click", () => this.#loadImageAndAnalyze());
            container.appendChild(this.#loadAndAnalyzeButton);

            this.#imagePreviewCanvas = domMake.Tree("canvas", { id: "image-preview-canvas" });
            container.appendChild(this.#imagePreviewCanvas);
            this.#imagePreviewCtx = this.#imagePreviewCanvas.getContext('2d');

            container.appendChild(domMake.Tree("div", {}, ["Colores Dominantes (clic para añadir a Paletas):"]));
            this.#dominantColorsDisplay = domMake.Tree("div", { id: "dominant-colors-display" });
            container.appendChild(this.#dominantColorsDisplay);

            this.#analysisResultsDisplay = domMake.Tree("pre", { id: "analysis-results" }, ["Resultados del análisis aparecerán aquí."]);
            container.appendChild(this.#analysisResultsDisplay);


            const actionButtonsRow = domMake.Row({ class: "action-buttons" });
            this.#copyResultsButton = domMake.Button('<i class="fas fa-copy"></i> Copiar');
            this.#copyResultsButton.addEventListener("click", () => this.#copyResultsToClipboard());
            actionButtonsRow.appendChild(this.#copyResultsButton);

            this.#downloadResultsButton = domMake.Button('<i class="fas fa-download"></i> Descargar');
            this.#downloadResultsButton.addEventListener("click", () => this.#downloadResults());
            actionButtonsRow.appendChild(this.#downloadResultsButton);
            container.appendChild(actionButtonsRow);


            this.htmlElements.section.appendChild(container);
        }

        async #loadImageAndAnalyze() {
            const imageUrl = this.#imageUrlInput.value.trim();
            if (!imageUrl) {

                return;
            }


            this.#analysisResultsDisplay.textContent = "Cargando...";
            this.#dominantColorsDisplay.innerHTML = '';
            this.#imagePreviewCtx.clearRect(0, 0, this.#imagePreviewCanvas.width, this.#imagePreviewCanvas.height);
            this.#copyResultsButton.disabled = true;
            this.#downloadResultsButton.disabled = true;

            const img = new window.Image();
            img.crossOrigin = "Anonymous";
            img.src = imageUrl;

            img.onload = async () => {
                try {
                    const maxWidth = 300;
                    const maxHeight = 300;
                    let width = img.width;
                    let height = img.height;

                    if (width > maxWidth || height > maxHeight) {
                        if (width / maxWidth > height / maxHeight) {
                            height = height * (maxWidth / width);
                            width = maxWidth;
                        } else {
                            width = width * (maxHeight / height);
                            height = maxHeight;
                        }
                    }

                    this.#imagePreviewCanvas.width = width;
                    this.#imagePreviewCanvas.height = height;
                    this.#imagePreviewCtx.drawImage(img, 0, 0, width, height);

                    const imageData = this.#imagePreviewCtx.getImageData(0, 0, width, height);
                    const pixels = imageData.data;

                    const results = {};

                    const colorMap = new Map();
                    const sampleStep = Math.max(1, Math.floor(pixels.length / 4 / 5000));

                    for (let i = 0; i < pixels.length; i += 4 * sampleStep) {
                        const r = pixels[i];
                        const g = pixels[i + 1];
                        const b = pixels[i + 2];
                        const a = pixels[i + 3];

                        if (a > 20) {
                            const colorKey = `${r},${g},${b}`;
                            colorMap.set(colorKey, (colorMap.get(colorKey) || 0) + 1);
                        }
                    }

                    const sortedColors = Array.from(colorMap.entries())
                        .sort((a, b) => b[1] - a[1])
                        .slice(0, 5);

                    results.dominantColors = sortedColors.map(([rgbStr, count]) => {
                        const [r, g, b] = rgbStr.split(',').map(Number);
                        return { r, g, b, hex: `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}` };
                    });

                    this.#dominantColorsDisplay.innerHTML = '';
                    results.dominantColors.forEach(color => {
                        const swatch = domMake.Tree("div", {
                            class: "color-swatch",
                            style: `background-color: ${color.hex};`,
                            title: `Clic para añadir ${color.hex} a Paletas de Color.`
                        });
                        swatch.addEventListener('click', () => this.#addDominantColorToPalette(color.hex));
                        this.#dominantColorsDisplay.appendChild(swatch);
                    });

                    let totalBrightness = 0;
                    let nonTransparentPixelCount = 0;
                    for (let i = 0; i < pixels.length; i += 4) {
                        const r = pixels[i];
                        const g = pixels[i + 1];
                        const b = pixels[i + 2];
                        const a = pixels[i + 3];
                        if (a > 0) {
                            totalBrightness += (0.299 * r + 0.587 * g + 0.114 * b);
                            nonTransparentPixelCount++;
                        }
                    }
                    results.averageBrightness = nonTransparentPixelCount > 0 ? (totalBrightness / nonTransparentPixelCount).toFixed(2) : 'N/A';
                    results.imageDimensions = `${width}x${height}`;
                    results.totalPixels = width * height;
                    results.nonTransparentPixels = nonTransparentPixelCount;

                    let resultsText = "--- Resultados del Análisis de Imagen ---\n";
                    resultsText += `Dimensiones: ${results.imageDimensions} píxeles\n`;
                    resultsText += `Píxeles no transparentes: ${results.nonTransparentPixels}\n`;
                    resultsText += `Brillo promedio: ${results.averageBrightness}\n`;
                    resultsText += `Colores Dominantes (HEX): ${results.dominantColors.map(c => c.hex).join(', ')}\n`;
                    resultsText += "\n¡Análisis completado!";
                    this.#analysisResultsDisplay.textContent = resultsText;

                    this.notify("success", "Análisis de imagen completado.");
                    this.#copyResultsButton.disabled = false;
                    this.#downloadResultsButton.disabled = false;

                } catch (e) {
                    if (e.name === "SecurityError" || (e.message && e.message.includes("tainted"))) {
                        // this.notify("error", "Error de CORS: No se pudo acceder a los píxeles de la imagen. La imagen debe estar en el mismo origen o permitir CORS.");
                        this.#analysisResultsDisplay.textContent = "Error: No se pudo leer la imagen debido a restricciones de seguridad (CORS).";
                    } else {
                        // this.notify("error", `Error al analizar la imagen: ${e.message}`);
                        this.#analysisResultsDisplay.textContent = `Error: ${e.message}`;
                        console.error("Image analysis error:", e);
                    }
                }
            };

            img.onerror = () => {
                // this.notify("error", "Fallo al cargar la imagen. ¿URL correcta o problema de red?");
                this.#analysisResultsDisplay.textContent = "Error: Fallo al cargar la imagen.";
            };
        }

        // --- Nuevas funciones de Utilidad ---
        async #copyResultsToClipboard() {
            const resultsText = this.#analysisResultsDisplay.textContent;
            if (!resultsText.trim()) {
                // this.notify("warning", "No hay resultados para copiar.");
                return;
            }
            try {
                await navigator.clipboard.writeText(resultsText);
                this.notify("success", "Resultados copiados al portapapeles.");
            } catch (err) {
                // this.notify("error", `Error al copiar: ${err.message}`);
                console.error("Copy to clipboard failed:", err);
            }
        }

        #downloadResults() {
            const resultsText = this.#analysisResultsDisplay.textContent;
            if (!resultsText.trim()) {
                // this.notify("warning", "No hay resultados para descargar.");
                return;
            }

            const blob = new Blob([resultsText], { type: 'text/plain;charset=utf-8' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `drawaria_image_analysis_${Date.now()}.txt`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
            this.notify("success", "Resultados descargados como TXT.");
        }

        #addDominantColorToPalette(hexColor) {
            const moreColorPalettesModule = this.findGlobal("MoreColorPalettes");
            if (moreColorPalettesModule && moreColorPalettesModule.siblings && moreColorPalettesModule.siblings.length > 0) {
                const paletteInstance = moreColorPalettesModule.siblings[0]; // Assuming first instance
                if (paletteInstance && typeof paletteInstance.addCustomColorFromExternal === 'function') {
                    paletteInstance.addCustomColorFromExternal(hexColor);
                    // this.notify("info", `Color ${hexColor} enviado a 'Paletas de Color'.`);
                } else {
                    // this.notify("warning", "El módulo 'Paletas de Color' no está listo o le falta la función para añadir colores.");
                }
            } else {
                // this.notify("warning", "El módulo 'Paletas de Color' no se encontró o no está activo.");
            }
        }
    }
})("QBit");
// --- END Analyzer

// --- START RANDOM PROFILE
(function RandomProfileSelectorModule() {
    const QBit = globalThis[arguments[0]];

    QBit.Styles.addRules([
        `#${QBit.identifier} .random-profile-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .random-profile-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .profile-display-area {
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            padding: 10px;
            border: 1px dashed var(--CE-color);
            border-radius: .25rem;
            margin-bottom: 10px;
            flex-wrap: wrap;
        }`,
        // Estilos para el canvas de avatar
        `#${QBit.identifier} #avatar-canvas {
            width: 150px;
            height: 150px;
            border-radius: 50%;
            border: 2px solid var(--info);
            object-fit: cover; /* Aunque es canvas, mantiene la intención visual */
            flex-shrink: 0;
            background-color: #f0f0f0; /* Fondo para cuando no hay imagen */
        }`,
        `#${QBit.identifier} .profile-display-info {
            flex-grow: 1;
            text-align: center;
            min-width: 150px;
            margin-top: 5px;
        }`,
        `#${QBit.identifier} .profile-display-name {
            font-weight: bold;
            font-size: 1.2em;
            color: var(--dark-blue-title);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }`,
        `#${QBit.identifier} .profile-display-stats {
            font-size: 0.9em;
            color: var(--CE-color);
        }`,
        /* Geometry Dash style navigation buttons */
        `#${QBit.identifier} .gd-nav-button {
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.8em;
            background-color: var(--secondary);
            color: var(--dark);
            cursor: pointer;
            border: 2px solid var(--CE-color);
            box-shadow: inset 0 2px 4px rgba(255,255,255,0.2), 0 2px 5px rgba(0,0,0,0.3);
            transition: transform 0.2s ease-in-out, background-color 0.2s ease, box-shadow 0.2s ease;
            flex-shrink: 0;
        }`,
        `#${QBit.identifier} .gd-nav-button:hover {
            background-color: var(--info);
            color: white;
            transform: translateY(-2px) scale(1.05);
            box-shadow: inset 0 2px 4px rgba(255,255,255,0.3), 0 5px 10px rgba(0,0,0,0.4);
        }`,
        `#${QBit.identifier} .gd-nav-button:active {
            transform: translateY(0) scale(1);
            box-shadow: inset 0 1px 2px rgba(0,0,0,0.3);
        }`,
        `#${QBit.identifier} .button-row .gd-nav-button {
            flex: none;
            margin: 0 5px;
        }`,
        `#${QBit.identifier} .button-row {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            margin-bottom: 10px;
            justify-content: center;
        }`,
        `#${QBit.identifier} .button-row .btn {
            flex: 1 1 48%;
            min-width: 120px;
        }`,
        `#${QBit.identifier} .profile-details-display {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 10px;
            margin-top: 10px;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
            font-family: monospace;
            font-size: 0.9em;
            white-space: pre-wrap;
            max-height: 400px;
            overflow-y: auto;
            display: none; /* Controlled by #displayProfileDetails */
        }`,
        `#${QBit.identifier} .profile-details-loader {
            text-align: center;
            padding: 20px;
            color: var(--info);
        }`
    ]);

    class RandomProfileSelector extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        #playersData = [];
        #currentIndex = -1;
        #currentPage = 1;
        #totalPages = 1;

        // UI elements
        #avatarCanvas;
        #avatarCtx;
        #playerNameDisplay;
        #playerStatsDisplay;
        #prevButton;
        #nextButton;
        #downloadProfileButton;
        #openPlayerProfileButton;
        #openProfileDetailsButton;
        #randomPlayerButton;
        #loadingIndicator;
        #profileDetailsContent;
        #modalLoader;
        #analyzeAvatarButton;

        // Dependencies
        #playerProfileExtractorModule = null;
        #imageAnalyzerModule = null;

        constructor() {
            super("🕵️Profile Accounts Detective", '<i class="fas fa-user-circle"></i>');
            this.#onStartup();
        }

        async #onStartup() {
            this.#loadInterface();
            await this.#fetchScoreboardData();
            // Ensure modules are found after CubeEngine might have loaded them
            this.#playerProfileExtractorModule = this.findGlobal("PlayerProfileExtractor");
            this.#imageAnalyzerModule = this.findGlobal("ImageAnalyzer");
        }

        #loadInterface() {
            const container = domMake.Tree("div", { class: "random-profile-section" });

            container.appendChild(domMake.Tree("div", { class: "random-profile-section-title" }, ["Explorador de Perfiles"]));

            this.#loadingIndicator = domMake.Tree("div", { class: "profile-details-loader" }, ["Cargando datos del marcador..."]);
            container.appendChild(this.#loadingIndicator); // Append loading indicator directly

            const profileDisplayArea = domMake.Tree("div", { class: "profile-display-area" });

            this.#avatarCanvas = domMake.Tree("canvas", { id: "avatar-canvas", width: "150", height: "150" });
            this.#avatarCtx = this.#avatarCanvas.getContext('2d');

            this.#playerNameDisplay = domMake.Tree("div", { class: "profile-display-name" }, ["Nombre del Jugador"]);
            this.#playerStatsDisplay = domMake.Tree("div", { class: "profile-display-stats" }, ["Puntuación: N/A | Victorias: N/A"]);

            this.#prevButton = domMake.Button('<i class="fas fa-chevron-left"></i>', { class: "gd-nav-button" });
            this.#prevButton.addEventListener("click", () => this.#navigateProfile(-1));

            this.#nextButton = domMake.Button('<i class="fas fa-chevron-right"></i>', { class: "gd-nav-button" });
            this.#nextButton.addEventListener("click", () => this.#navigateProfile(1));

            profileDisplayArea.appendAll(this.#prevButton, domMake.Tree("div", { style: "display: flex; flex-direction: column; align-items: center;" }, [
                this.#avatarCanvas,
                this.#playerNameDisplay,
                this.#playerStatsDisplay
            ]), this.#nextButton);
            container.appendChild(profileDisplayArea);

            const actionButtonsRow1 = domMake.Row({ class: "button-row" });
            this.#downloadProfileButton = domMake.Button('<i class="fas fa-file-download"></i> Descargar Perfil (JSON)');
            this.#downloadProfileButton.addEventListener("click", () => this.#downloadProfileData());

            this.#openPlayerProfileButton = domMake.Button('<i class="fas fa-user"></i> Abrir Perfil de Jugador');
            this.#openPlayerProfileButton.addEventListener("click", () => this.#openPlayerProfilePage());
            actionButtonsRow1.appendAll(this.#downloadProfileButton, this.#openPlayerProfileButton);
            container.appendChild(actionButtonsRow1);

            const actionButtonsRow2 = domMake.Row({ class: "button-row" });
            this.#openProfileDetailsButton = domMake.Button('<i class="fas fa-info-circle"></i> Mostrar Detalles Completos');
            this.#openProfileDetailsButton.addEventListener("click", () => this.#displayProfileDetails());
            this.#randomPlayerButton = domMake.Button('<i class="fas fa-random"></i> Jugador al Azar');
            this.#randomPlayerButton.addEventListener("click", () => this.#selectRandomProfile());
            actionButtonsRow2.appendAll(this.#openProfileDetailsButton, this.#randomPlayerButton);
            container.appendChild(actionButtonsRow2);

            this.#modalLoader = domMake.Tree("div", { class: "profile-details-loader" }, ["Cargando datos del perfil extendidos..."]);
            this.#profileDetailsContent = domMake.Tree("pre", { class: "profile-details-display", style: "margin: 0;" });
            container.appendAll(this.#modalLoader, this.#profileDetailsContent);

            this.htmlElements.section.appendChild(container);

            this.#setInterfaceEnabled(false);
        }

        #setInterfaceEnabled(enabled) {
            this.#loadingIndicator.style.display = enabled ? 'none' : 'block';

            const mainContent = this.htmlElements.section.querySelector('.profile-display-area');
            const buttonRows = this.htmlElements.section.querySelectorAll('.button-row');

            if (mainContent) {
                mainContent.style.display = enabled ? 'flex' : 'none';
            }

            buttonRows.forEach(row => {
                row.style.display = enabled ? 'flex' : 'none';
            });

            if (this.#downloadProfileButton) this.#downloadProfileButton.disabled = !enabled;
            if (this.#openPlayerProfileButton) this.#openPlayerProfileButton.disabled = !enabled;
            if (this.#openProfileDetailsButton) this.#openProfileDetailsButton.disabled = !enabled;
            if (this.#randomPlayerButton) this.#randomPlayerButton.disabled = !enabled;
            if (this.#analyzeAvatarButton) this.#analyzeAvatarButton.disabled = !enabled;

            if (this.#prevButton) this.#prevButton.disabled = !enabled;
            if (this.#nextButton) this.#nextButton.disabled = !enabled;
        }

        async #fetchScoreboardData() {
            console.log("RandomProfileSelector: Obteniendo datos del marcador...");
            this.#setInterfaceEnabled(false);

            try {
                let allPlayers = [];
                // Fetch up to 5 pages of the scoreboard
                for (let page = 1; page <= 5; page++) {
                    this.#loadingIndicator.textContent = `Cargando datos del marcador (Página ${page})...`;
                    const url = `https://drawaria.online/scoreboards/?page=${page}`;
                    const response = await fetch(url);
                    if (!response.ok) {
                        if (response.status === 404) {
                            console.log(`RandomProfileSelector: Página de marcador ${page} no encontrada, deteniendo la carga.`);
                            break;
                        }
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    const html = await response.text();
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(html, 'text/html');

                    // Select all rows with IDs r1 to r100, assuming a pattern
                    const rows = doc.querySelectorAll('table.table tbody tr[id^="r"]');

                    if (rows.length === 0) {
                        console.log(`RandomProfileSelector: No más jugadores encontrados en la página ${page}.`);
                        break; // Stop if a page returns no rows
                    }

                    rows.forEach(row => {
                        const rank = row.querySelector('th[scope="row"]')?.textContent.trim();
                        const playerId = row.dataset.avatarid; // Get data-avatarid from the row
                        const playerLink = row.querySelector('td:nth-child(2) a');
                        const playerName = playerLink?.textContent.trim();
                        const avatarUrl = playerLink?.querySelector('img')?.src;

                        const score = row.querySelector('td:nth-child(3)')?.textContent.trim();
                        const stars = row.querySelector('td:nth-child(4)')?.textContent.trim();
                        const wins = row.querySelector('td:nth-child(5)')?.textContent.trim();
                        const matches = row.querySelector('td:nth-child(6)')?.textContent.trim();
                        const guesses = row.querySelector('td:nth-child(7)')?.textContent.trim();
                        const avgGuessTime = row.querySelector('td:nth-child(8)')?.textContent.trim();

                        // Only add if essential data is present
                        if (playerId && playerName && avatarUrl) {
                            allPlayers.push({
                                rank: rank,
                                uid: playerId,
                                name: playerName,
                                avatarUrl: avatarUrl,
                                score: score,
                                stars: stars,
                                wins: wins,
                                matches: matches,
                                guesses: guesses,
                                avgGuessTime: avgGuessTime,
                                profileUrl: `https://drawaria.online/profile/?uid=${playerId}`
                            });
                        } else {
                            console.warn(`RandomProfileSelector: Faltan datos esenciales para la fila del jugador: ID=${playerId}, Nombre=${playerName}, Avatar=${avatarUrl}`);
                        }
                    });
                }

                if (allPlayers.length === 0) {
                    console.warn("RandomProfileSelector: No se encontraron jugadores en el marcador.");
                    this.#loadingIndicator.textContent = "No se encontraron jugadores.";
                    return;
                }

                this.#playersData = allPlayers;
                this.#setInterfaceEnabled(true);
                this.#currentIndex = 0;
                this.#updateProfileDisplay();

            } catch (error) {
                console.error(`RandomProfileSelector: Error al cargar datos del marcador: ${error.message}`);
                this.#loadingIndicator.textContent = `Error: ${error.message}`;
            }
        }

        async #updateProfileDisplay() {
            if (this.#playersData.length === 0) {
                this.#avatarCtx.clearRect(0, 0, this.#avatarCanvas.width, this.#avatarCanvas.height);
                this.#playerNameDisplay.textContent = "No hay datos de jugadores.";
                this.#playerStatsDisplay.textContent = "";
                this.#setInterfaceEnabled(false);
                this.#profileDetailsContent.style.display = 'none';
                return;
            }

            const player = this.#playersData[this.#currentIndex];
            this.#playerNameDisplay.textContent = player.name;
            this.#playerStatsDisplay.textContent = `Puntuación: ${player.score} | Victorias: ${player.wins} | Estrellas: ${player.stars}`;

            this.#avatarCtx.clearRect(0, 0, this.#avatarCanvas.width, this.#avatarCanvas.height);
            const img = new Image();
            img.crossOrigin = "Anonymous";
            img.onload = () => {
                this.#avatarCtx.drawImage(img, 0, 0, this.#avatarCanvas.width, this.#avatarCanvas.height);
            };
            img.onerror = () => {
                console.warn(`RandomProfileSelector: No se pudo cargar el avatar para ${player.name}.`);
                this.#avatarCtx.fillStyle = '#ccc';
                this.#avatarCtx.fillRect(0, 0, this.#avatarCanvas.width, this.#avatarCanvas.height);
                this.#avatarCtx.font = '10px Arial';
                this.#avatarCtx.fillStyle = '#666';
                this.#avatarCtx.textAlign = 'center';
                this.#avatarCtx.fillText('No Avatar', this.#avatarCanvas.width / 2, this.#avatarCanvas.height / 2);
            };
            img.src = player.avatarUrl;

            this.#prevButton.disabled = this.#currentIndex === 0;
            this.#nextButton.disabled = this.#currentIndex === this.#playersData.length - 1;

            this.#profileDetailsContent.textContent = '';
            this.#profileDetailsContent.style.display = 'none';
        }

        #navigateProfile(direction) {
            this.#currentIndex += direction;
            if (this.#currentIndex < 0) {
                this.#currentIndex = 0;
            } else if (this.#currentIndex >= this.#playersData.length) {
                this.#currentIndex = this.#playersData.length - 1;
            }
            this.#updateProfileDisplay();
            console.log(`RandomProfileSelector: Mostrando perfil: ${this.#playersData[this.#currentIndex].name}`);
        }

        #selectRandomProfile() {
            if (this.#playersData.length === 0) {
                console.warn("RandomProfileSelector: No hay jugadores cargados para seleccionar al azar.");
                return;
            }
            const randomIndex = Math.floor(Math.random() * this.#playersData.length);
            this.#currentIndex = randomIndex;
            this.#updateProfileDisplay();
            console.log(`RandomProfileSelector: Jugador al azar seleccionado: ${this.#playersData[this.#currentIndex].name}`);
        }

        #downloadProfileData() {
            if (this.#playersData.length === 0 || this.#currentIndex === -1) {
                console.warn("RandomProfileSelector: No hay perfil seleccionado para descargar.");
                return;
            }
            const player = this.#playersData[this.#currentIndex];
            const dataStr = JSON.stringify(player, null, 2);
            const blob = new Blob([dataStr], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const filename = `drawaria_player_${player.name.replace(/[^a-zA-Z0-9]/g, '_')}_${player.uid.substring(0, 8)}.json`;

            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
            console.log(`RandomProfileSelector: Datos de ${player.name} descargados.`);
        }

        #openPlayerProfilePage() {
            if (this.#playersData.length === 0 || this.#currentIndex === -1) {
                console.warn("RandomProfileSelector: No hay perfil seleccionado para abrir.");
                return;
            }
            const player = this.#playersData[this.#currentIndex];
            window.open(player.profileUrl, '_blank');
            console.log(`RandomProfileSelector: Abriendo perfil de ${player.name} en una nueva pestaña.`);
        }

        async #displayProfileDetails() {
            if (this.#playersData.length === 0 || this.#currentIndex === -1) {
                console.warn("RandomProfileSelector: No hay perfil seleccionado para mostrar detalles.");
                this.#profileDetailsContent.style.display = 'none';
                return;
            }

            const player = this.#playersData[this.#currentIndex];
            const profileUrl = player.profileUrl;

            this.#profileDetailsContent.textContent = '';
            this.#profileDetailsContent.style.display = 'none';
            this.#modalLoader.style.display = 'block';

            console.log(`RandomProfileSelector: Cargando detalles completos para ${player.name}...`);

            if (!this.#playerProfileExtractorModule || !this.#playerProfileExtractorModule.siblings || this.#playerProfileExtractorModule.siblings.length === 0) {
                console.error("RandomProfileSelector: El módulo 'Extractor de Perfil' no está activo. No se pueden cargar detalles extendidos.");
                this.#profileDetailsContent.textContent = "Error: El módulo Extractor de Perfil no está cargado. Asegúrate de que está habilitado.";
                this.#modalLoader.style.display = 'none';
                this.#profileDetailsContent.style.display = 'block';
                return;
            }

            const extractorInstance = this.#playerProfileExtractorModule.siblings[0];

            if (typeof extractorInstance._parseMainProfileHTML !== 'function') {
                console.error("RandomProfileSelector: El módulo Extractor de Perfil no tiene el método de parseo necesario. Puede que esté desactualizado o modificado incorrectamente.");
                this.#profileDetailsContent.textContent = "Error: El módulo Extractor de Perfil no está completamente funcional.";
                this.#modalLoader.style.display = 'none';
                this.#profileDetailsContent.style.display = 'block';
                return;
            }

            try {
                const response = await fetch(profileUrl);
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const htmlContent = await response.text();
                const parser = new DOMParser();
                const extractedData = extractorInstance._parseMainProfileHTML(htmlContent, parser);

                const combinedData = {
                    ...player,
                    ...extractedData,
                };

                let displayTxt = `--- Detalles Completos del Perfil de ${combinedData.name} ---\n`;
                displayTxt += `UID: ${combinedData.uid || 'N/A'}\n`;
                displayTxt += `Nombre: ${combinedData.name || 'N/A'}\n`;
                displayTxt += `Avatar URL: ${combinedData.avatarUrl || 'N/A'}\n`;
                displayTxt += `Rank: ${combinedData.rank || 'N/A'}\n`;
                displayTxt += `Experiencia: ${combinedData.experience || 'N/A'}\n`;

                displayTxt += `--- Estadísticas de Juego ---\n`;
                displayTxt += `Puntuación Total: ${combinedData.score || 'N/A'}\n`;
                displayTxt += `Estrellas: ${combinedData.stars || 'N/A'}\n`;
                displayTxt += `Victorias: ${combinedData.wins || 'N/A'}\n`;
                displayTxt += `Partidas Jugadas: ${combinedData.matches || 'N/A'}\n`;
                displayTxt += `Adivinanzas Correctas: ${combinedData.guesses || 'N/A'}\n`;
                displayTxt += `Tiempo Promedio de Adivinanza: ${combinedData.avgGuessTime || 'N/A'}\n`;

                if (combinedData.pictionaryStats && Object.keys(combinedData.pictionaryStats).length > 0) {
                    displayTxt += `\n--- Estadísticas Detalladas (Pictionary) ---\n`;
                    for (const key in combinedData.pictionaryStats) {
                        const displayKey = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase());
                        displayTxt += `  ${displayKey}: ${combinedData.pictionaryStats[key]}\n`;
                    }
                }

                if (combinedData.accusedTokens && Object.keys(combinedData.accusedTokens).length > 0) {
                    displayTxt += `\n--- Menciones de Homenaje (Tokens) ---\n`;
                    for (const tokenName in combinedData.accusedTokens) {
                        displayTxt += `  ${tokenName}: ${combinedData.accusedTokens[tokenName]}\n`;
                    }
                }

                this.#profileDetailsContent.textContent = displayTxt;
                console.log(`RandomProfileSelector: Detalles completos de ${player.name} cargados.`);

            } catch (error) {
                console.error(`RandomProfileSelector: Error al cargar detalles de ${player.name}: ${error.message}`);
                this.#profileDetailsContent.textContent = `Error al cargar detalles: ${error.message}.`;
            } finally {
                this.#modalLoader.style.display = 'none';
                this.#profileDetailsContent.style.display = 'block';
            }
        }

        async #analyzeCurrentAvatar() {
            if (this.#playersData.length === 0 || this.#currentIndex === -1) {
                console.warn("RandomProfileSelector: No hay avatar seleccionado para analizar.");
                return;
            }

            const player = this.#playersData[this.#currentIndex];
            const avatarUrl = player.avatarUrl;

            if (!this.#imageAnalyzerModule || !this.#imageAnalyzerModule.siblings || this.#imageAnalyzerModule.siblings.length === 0) {
                console.error("RandomProfileSelector: El módulo 'Analizador de Imágenes' no está activo. No se puede analizar el avatar.");
                return;
            }

            const analyzerInstance = this.#imageAnalyzerModule.siblings[0];
            if (typeof analyzerInstance.performAnalysisFromExternalUrl === 'function') {
                console.log(`RandomProfileSelector: Enviando avatar de ${player.name} al Analizador de Imágenes...`);
                analyzerInstance.performAnalysisFromExternalUrl(avatarUrl);
            } else {
                console.error("RandomProfileSelector: El módulo 'Analizador de Imágenes' no tiene el método de análisis requerido (performAnalysisFromExternalUrl).");
            }
        }
    }
})("QBit");

// --- END RANDOM PROFILE

// START NAVIGATOR

(function RoomNavigatorModule() {
    const QBit = globalThis[arguments[0]];
function fetchJson(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onload = () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                try {
                    let parsedData = JSON.parse(xhr.responseText);
                    // Handle potential double-encoded JSON
                    if (typeof parsedData === 'string') {
                        parsedData = JSON.parse(parsedData);
                    }
                    resolve(parsedData);
                } catch (e) {
                    reject(new Error('Fallo al analizar la respuesta JSON: ' + e.message + ` (Raw: ${xhr.responseText.substring(0, 100)}...)`));
                }
            } else {
                reject(new Error(`La respuesta de la red no fue correcta, estado: ${xhr.status} ${xhr.statusText}`));
            }
        };
        xhr.onerror = () => reject(new Error('Fallo la solicitud de red. Revisa tu conexión o el servidor.'));
        xhr.send();
    });
}

class RoomNavigator extends QBit {
    static dummy1 = QBit.register(this);
    static dummy2 = QBit.bind(this, "CubeEngine");

    #roomListContainer;
    #refreshButton;
    #loadingIndicator;
    #filterNameInput;
    #filterMinPlayersInput;
    #filterMaxPlayersInput;
    #roomDataCache = []; // Store fetched raw room data

    constructor() {
        super("🔎Room Explorer", '<i class="fas fa-search-location"></i>');
        this.#onStartup();
    }

    #onStartup() {
        this.#loadInterface();
        this.#fetchAndApplyFilters();
    }

    #loadInterface() {
        const container = domMake.Tree("div");

        const headerRow = domMake.Row();
        headerRow.style.marginBottom = "10px";

        this.#refreshButton = domMake.Button('<i class="fas fa-sync-alt"></i> Actualizar Salas');
        this.#refreshButton.title = "Actualiza la lista de salas disponibles.";
        this.#refreshButton.addEventListener("click", () => this.#fetchAndApplyFilters());
        headerRow.appendChild(this.#refreshButton);

        this.#loadingIndicator = domMake.Tree("span", { style: "margin-left: 10px; color: var(--info); display: none;" }, ['Cargando...']);
        headerRow.appendChild(this.#loadingIndicator);
        container.appendChild(headerRow);

        const filtersRow = domMake.Row();
        filtersRow.style.flexWrap = "wrap";
        filtersRow.style.gap = "5px";

        this.#filterNameInput = domMake.Tree("input", { type: "text", placeholder: "Filtrar por nombre", style: "flex: 1 1 120px;" });
        this.#filterNameInput.addEventListener("input", () => this.#applyFiltersAndDisplayRooms());
        filtersRow.appendChild(this.#filterNameInput);

        this.#filterMinPlayersInput = domMake.Tree("input", { type: "number", min: "0", placeholder: "Min Jugadores", style: "width: 80px;" });
        this.#filterMinPlayersInput.addEventListener("input", () => this.#applyFiltersAndDisplayRooms());
        filtersRow.appendChild(this.#filterMinPlayersInput);

        this.#filterMaxPlayersInput = domMake.Tree("input", { type: "number", min: "0", placeholder: "Max Jugadores", style: "width: 80px;" });
        this.#filterMaxPlayersInput.addEventListener("input", () => this.#applyFiltersAndDisplayRooms());
        filtersRow.appendChild(this.#filterMaxPlayersInput);

        container.appendChild(filtersRow);

        this.#roomListContainer = domMake.Tree("div", {
            class: "room-list-display",
            style: `
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
                gap: 10px;
                padding: 5px;
                max-height: 400px; /* Limits height and adds scrollbar */
                overflow-y: auto;
                border: 1px solid var(--CE-color);
                border-radius: .25rem;
                margin-top: 10px;
            `
        });
        container.appendChild(this.#roomListContainer);

        this.htmlElements.section.appendChild(container);
    }

    async #fetchAndApplyFilters() {
        this.#roomListContainer.innerHTML = '';
        this.#loadingIndicator.style.display = 'inline';
        this.#refreshButton.disabled = true;

        try {
            const roomData = await fetchJson('https://drawaria.online/getroomlist');
            if (!Array.isArray(roomData)) {
                 throw new Error('La respuesta no es un array de salas válido.');
            }
            this.#roomDataCache = roomData;
            this.#applyFiltersAndDisplayRooms();
        } catch (error) {
            this.#roomListContainer.appendChild(domMake.TextNode("Error al cargar las salas. Inténtalo de nuevo."));
            console.error("Fetch room list error:", error);
        } finally {
            this.#loadingIndicator.style.display = 'none';
            this.#refreshButton.disabled = false;
        }
    }

    #applyFiltersAndDisplayRooms() {
        let filteredRooms = [...this.#roomDataCache]; // Start with cached data

        const nameFilter = this.#filterNameInput.value.toLowerCase();
        const minPlayers = parseInt(this.#filterMinPlayersInput.value);
        const maxPlayers = parseInt(this.#filterMaxPlayersInput.value);

        if (nameFilter) {
            filteredRooms = filteredRooms.filter(room => {
                const roomName = room[3] ? room[3].toLowerCase() : ''; // Access roomName by index 3
                return roomName.includes(nameFilter);
            });
        }

        if (!isNaN(minPlayers) && minPlayers >= 0) {
            filteredRooms = filteredRooms.filter(room => room[1] >= minPlayers); // Access currentPlayers by index 1
        }

        if (!isNaN(maxPlayers) && maxPlayers >= 0) {
            filteredRooms = filteredRooms.filter(room => room[1] <= maxPlayers); // Access currentPlayers by index 1
        }

        this.#displayRooms(filteredRooms);
    }


    #displayRooms(rooms) {
        this.#roomListContainer.innerHTML = '';
        if (rooms.length === 0) {
            this.#roomListContainer.appendChild(domMake.TextNode("No hay salas que coincidan con los filtros."));
            return;
        }

        rooms.forEach(roomArray => {

            const roomId = roomArray[0];
            const currentPlayers = roomArray[1];
            const maxPlayers = roomArray[2];
            const roomName = roomArray[3];
            const gameModeType = roomArray[4];
            const flags = roomArray[6];
            const roundsPlayed = roomArray[7];
            const serverId = roomArray[8];

            let roomModeLabel = 'Desconocido';
            if (gameModeType === 2) {
                roomModeLabel = 'Público';
            } else if (gameModeType === 3) {
                roomModeLabel = 'Amigos/Privado';
            }

            const isPasswordProtected = flags && flags.length > 0 && flags[0] === true;

            const isFull = currentPlayers >= maxPlayers;
            const statusColor = isFull ? 'var(--danger)' : 'var(--success)';
            const joinableStatus = isFull ? 'LLENA' : 'DISPONIBLE';

            const roomCard = domMake.Tree("div", {
                class: "room-card",
                style: `
                    border: 1px solid var(--CE-color);
                    border-radius: .25rem;
                    padding: 8px;
                    background-color: var(--CE-bg_color);
                    display: flex;
                    flex-direction: column;
                    justify-content: space-between;
                    gap: 5px;
                    font-size: 0.85em;
                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                `
            });

            const nodesToAppend = [
                domMake.Tree("div", { style: "font-weight: bold; color: var(--dark-blue-title); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;", title: roomName }, [`Sala: ${roomName || '(Sin Nombre)'}`]),
                domMake.Tree("div", {}, [`Jugadores: ${currentPlayers}/${maxPlayers}`]),
                domMake.Tree("div", {}, [`Rondas jugadas: ${roundsPlayed || 'N/A'}`]),
                domMake.Tree("div", {}, [`Modo: ${roomModeLabel}`]),
                isPasswordProtected ? domMake.Tree("div", {style: "color: var(--warning);"}, [`Contraseña: Sí`]) : null,
                domMake.Tree("div", { style: `color: ${statusColor}; font-weight: bold;` }, [`Estado: ${joinableStatus}`])
            ].filter(node => node instanceof Node);

            roomCard.appendAll(...nodesToAppend);

            const joinButton = domMake.Button("Unirse");
            joinButton.classList.add("btn-primary");
            joinButton.style.width = "100%";
            joinButton.style.marginTop = "5px";
            joinButton.addEventListener("click", () => this.#joinRoom(roomId, serverId));
            roomCard.appendChild(joinButton);

            this.#roomListContainer.appendChild(roomCard);
        });
    }

    #joinRoom(roomId, serverId) {
        let fullRoomIdentifier = roomId;
        // Construct full room ID, e.g., "uuid.serverId"
        if (serverId !== null && serverId !== undefined && !String(roomId).includes(`.${serverId}`)) {
            fullRoomIdentifier = `${roomId}.${serverId}`;
        }
        const roomUrl = `https://drawaria.online/room/${fullRoomIdentifier}`;
        window.location.assign(roomUrl); // Navigate to the room
    }
}

})("QBit");





(function UserInterfaceCMDSysyem() {
    const QBit = globalThis[arguments[0]];


    let _originalJQueryFnModal;
    let _originalJQueryFnPopover;
    let _originalJQueryFnDropdown;
    let _originalJQueryFnCollapse;


    QBit.Styles.addRules([

        `#${QBit.identifier} .cmd-center-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
            margin-top: 10px; /* Add some space from the previous section */
        }`,
        `#${QBit.identifier} .cmd-center-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .cmd-center-links {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }`,
        `#${QBit.identifier} .cmd-center-links .btn {
            width: 100%;
            padding: 8px 15px;
            box-sizing: border-box;
            font-size: 0.95em;
            display: flex;
            align-items: center;
            justify-content: center;
        }`,
        `#${QBit.identifier} .cmd-center-links .btn i {
            margin-right: 8px;
        }`,
        `#${QBit.identifier} .client-cmd-controls input[type="number"],
         #${QBit.identifier} .client-cmd-controls input[type="text"] {
            width: 100%;
            padding: 5px;
            box-sizing: border-box;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
        }`,
        `#${QBit.identifier} .client-cmd-controls .btn {
            width: 100%;
            padding: 5px;
        }`,


        `#${QBit.identifier} .ui-persistence-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .ui-persistence-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .ui-persistence-toggle-button {
            background-color: var(--secondary);
            color: var(--dark);
            width: 100%;
            min-width: unset;
            padding: 10px 15px;
            box-sizing: border-box;
            font-size: 1rem;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            cursor: pointer;
            text-align: center;
            flex: none;
            min-height: 70px;
        }`,
        `#${QBit.identifier} .ui-persistence-toggle-button i {
            margin-right: 0;
            margin-bottom: 5px;
            font-size: 1.5em;
        }`,
        `#${QBit.identifier} .ui-persistence-toggle-button.active {
            background-color: var(--info);
            color: white;
        }`,
        `#${QBit.identifier} .ui-persistence-toggle-button.active i {
            color: #fff;
        }`,
        `#${QBit.identifier} .ui-persistence-element-list {
            display: flex;
            flex-direction: column;
            flex-wrap: nowrap;
            gap: 10px;
            margin-top: 5px;
            justify-content: flex-start;
            align-items: stretch;
        }`,
    ]);

    class UserInterfaceCMDSysyem extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");


        _cmdIdInput;
        _param1Input;
        _param2Input;
        _param3Input;


        _persistentElements = new Map();
        _chatattopNewMessageElement;
        _drawControlsPopuplistElement;
        _friendsTabFriendlistElement;
        _friendsContainerElement;


        constructor() {
            super("🖥️Terminal Interface", '<i class="fas fa-desktop"></i>');
            this._onStartup();
        }

        _onStartup() {

            this._captureOriginalFunctions();
            this._findSpecificElements();
            this._patchJQueryFunctions();


            this._loadInterface();


            this._setupObservers();
        }

        _loadInterface() {
            const container = domMake.Tree("div");


            const clientCmdSection = domMake.Tree("div", { class: "cmd-center-section" });
            clientCmdSection.appendChild(domMake.Tree("div", { class: "cmd-center-title" }, ["Enviar Client Cmd"]));
            clientCmdSection.classList.add("client-cmd-controls");

            const row1 = domMake.Row();
            this._cmdIdInput = domMake.Tree("input", { type: "number", placeholder: "Command ID (e.g., 10)" });
            row1.appendChild(this._cmdIdInput);
            clientCmdSection.appendChild(row1);

            const row2 = domMake.Row();
            this._param1Input = domMake.Tree("input", { type: "text", placeholder: "Param 1 (e.g., true)" });
            this._param2Input = domMake.Tree("input", { type: "text", placeholder: "Param 2 (e.g., 1)" });
            row2.appendAll(this._param1Input, this._param2Input);
            clientCmdSection.appendChild(row2);

            const row3 = domMake.Row();
            this._param3Input = domMake.Tree("input", { type: "text", placeholder: "Param 3 (e.g., 'text')" });
            const sendButton = domMake.Button("Send CMD");
            sendButton.addEventListener("click", () => this.sendCommand());
            row3.appendAll(this._param3Input, sendButton);
            clientCmdSection.appendChild(row3);

            container.appendChild(clientCmdSection);


            const cmdCenterSection = domMake.Tree("div", { class: "cmd-center-section" });
            cmdCenterSection.appendChild(domMake.Tree("div", { class: "cmd-center-title" }, ["CMD Center (Recursos)"]));
            cmdCenterSection.appendChild(domMake.Tree("div", { style: "font-size: 0.85em; margin-bottom: 10px; text-align: center;" }, ["Haz clic para abrir recursos de comandos y propiedades del juego en una nueva pestaña."]));

            const linksContainer = domMake.Tree("div", { class: "cmd-center-links" });

            const listCmdButton = domMake.Button('<i class="fas fa-list"></i> List Command');
            listCmdButton.title = "Abre una lista de comandos comunes de Drawaria.";
            listCmdButton.addEventListener("click", () => this._openExternalLink('https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaWordList/main/list%20command.txt', 'Comandos de Drawaria'));
            linksContainer.appendChild(listCmdButton);

            const dataCenterButton = domMake.Button('<i class="fas fa-database"></i> Data Center (Propiedades del Juego)');
            dataCenterButton.title = "Abre un listado de propiedades y datos internos del juego.";
            dataCenterButton.addEventListener("click", () => this._openExternalLink('https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaWordList/main/data%20center.txt', 'Propiedades de Juego Drawaria'));
            linksContainer.appendChild(dataCenterButton);

            const linksCenterButton = domMake.Button('<i class="fas fa-link"></i> Links Center (Enlaces del Juego)');
            linksCenterButton.title = "Abre una lista de enlaces útiles relacionados con el juego.";
            linksCenterButton.addEventListener("click", () => this._openExternalLink('https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaWordList/main/links%20center.txt', 'Enlaces Útiles Drawaria'));
            linksContainer.appendChild(linksCenterButton);

            cmdCenterSection.appendChild(linksContainer);
            container.appendChild(cmdCenterSection);


            const uiPersistenceSection = domMake.Tree("div", { class: "ui-persistence-section" });
            uiPersistenceSection.appendChild(domMake.Tree("div", { class: "ui-persistence-section-title" }, ["Control de Visibilidad de UI"]));

            const elementList = domMake.Tree("div", { class: "ui-persistence-element-list" });

            const addToggleButton = (id, labelHtml, iconClass) => {
                const button = domMake.Button('');
                button.classList.add("ui-persistence-toggle-button");
                button.setAttribute("data-persistence-id", id);
                button.setAttribute("data-original-label", labelHtml);

                const icon = domMake.Tree("i");
                icon.className = `fas ${iconClass}`;
                const labelSpan = domMake.Tree("span");
                labelSpan.innerHTML = labelHtml;

                button.appendChild(icon);
                button.appendChild(labelSpan);

                if (this._persistentElements.has(id) && this._persistentElements.get(id) === true) {
                    button.classList.add("active");
                    labelSpan.innerHTML = labelHtml.replace('Mantener', 'Liberar');
                }
                button.addEventListener("click", (e) => this._toggleElementPersistence(id, button, labelHtml));
                elementList.appendChild(button);
                return button;
            };

            addToggleButton('all-popovers', 'Mantener Todos los<br>Popovers', 'fa-comment-dots');
            addToggleButton('all-dropdowns', 'Mantener Todos los<br>Dropdowns', 'fa-caret-square-down');
            addToggleButton('draw-controls-popup', 'Mantener Panel de<br>Pincel', 'fa-paint-brush');
            addToggleButton('chat-new-message-notification', 'Mantener Notif. Chat<br>Nueva', 'fa-bell');
            addToggleButton('top-messages', 'Mantener Mensajes<br>Superiores', 'fa-comment-alt');
            addToggleButton('friends-tabfriendlist', 'Mantener Lista de<br>Amigos', 'fa-user-friends');

            uiPersistenceSection.appendChild(elementList);
            container.appendChild(uiPersistenceSection);

            this.htmlElements.section.appendChild(container);
        }


        parseParam(paramStr) {
            if (paramStr.toLowerCase() === 'true') return true;
            if (paramStr.toLowerCase() === 'false') return false;
            if (!isNaN(paramStr) && paramStr.trim() !== '') return Number(paramStr);
            if (paramStr.startsWith('[') && paramStr.endsWith(']')) {
                try {
                    return JSON.parse(paramStr);
                } catch (e) {
                    return paramStr;
                }
            }
            if (paramStr.startsWith('{') && paramStr.endsWith('}')) {
                try {
                    return JSON.parse(paramStr);
                } catch (e) {
                    return paramStr;
                }
            }
            return paramStr;
        }

        sendCommand() {
            const cmdId = parseInt(this._cmdIdInput.value);
            if (isNaN(cmdId)) {

                return;
            }

            const params = [];
            const param1 = this._param1Input.value.trim();
            const param2 = this._param2Input.value.trim();
            const param3 = this._param3Input.value.trim();

            if (param1) params.push(this.parseParam(param1));
            if (param2) params.push(this.parseParam(param2));
            if (param3) params.push(this.parseParam(param3));

            if (globalThis.sockets && globalThis.sockets.length > 0) {
                const payload = ["clientcmd", cmdId, params];
                const dataToSend = `${42}${JSON.stringify(payload)}`;

                globalThis.sockets[0].send(dataToSend);

            } else {

            }
        }

        _openExternalLink(url, title) {
            try {
                const newWindow = window.open(url, '_blank');
                if (newWindow) {
                    this.notify("success", `Abriendo ${title} en una nueva pestaña.`);
                } else {

                }
            } catch (error) {

                console.error(`Error opening external link for ${title}:`, error);
            }
        }


        _captureOriginalFunctions() {
            if (typeof jQuery !== 'undefined' && jQuery.fn) {
                _originalJQueryFnModal = jQuery.fn.modal;
                _originalJQueryFnPopover = jQuery.fn.popover;
                _originalJQueryFnDropdown = jQuery.fn.dropdown;
                _originalJQueryFnCollapse = jQuery.fn.collapse;
            } else {

            }
        }

        _patchJQueryFunctions() {
            const self = this;

            if (_originalJQueryFnModal) {
                jQuery.fn.modal = function(action, ...args) {
                    if (action === 'hide' && self._isPersistent(this, 'all-modals')) {
                        self.notify('info', `[UI Persistencia] Bloqueando intento de ocultar modal: #${this.attr('id') || this.attr('class')}.`);
                        if (this.hasClass('show')) {
                            self._forceVisibility(this);
                            const backdrop = jQuery('.modal-backdrop');
                            if (backdrop.length) {
                                backdrop.off('click.dismiss.bs.modal');
                                self._forceVisibility(backdrop);
                            }
                        }
                        return this;
                    }
                    return _originalJQueryFnModal.apply(this, [action, ...args]);
                };
            }

            if (_originalJQueryFnPopover) {
                jQuery.fn.popover = function(action, ...args) {
                    if (typeof action === 'string' && (action === 'hide' || action === 'destroy') && self._isPersistent(this, 'all-popovers')) {
                        self.notify('info', `[UI Persistencia] Bloqueando intento de ocultar popover: ${this.attr('aria-describedby') || this.attr('title')}.`);
                        self._forceVisibility(jQuery(this.attr('aria-describedby') ? `#${this.attr('aria-describedby')}` : this));
                        return this;
                    }
                    return _originalJQueryFnPopover.apply(this, [action, ...args]);
                };
            }

            if (_originalJQueryFnDropdown) {
                jQuery.fn.dropdown = function(action, ...args) {
                    if (typeof action === 'string' && (action === 'hide' || (action === 'toggle' && this.hasClass('show'))) && self._isPersistent(this, 'all-dropdowns')) {
                        self.notify('info', `[UI Persistencia] Bloqueando intento de ocultar dropdown: ${this.attr('id') || this.attr('class')}.`);
                        const menuId = this.attr('aria-labelledby') || this.next('.dropdown-menu').attr('id');
                        self._forceVisibility(jQuery(`#${menuId}, .${menuId}`));
                        return this;
                    }
                    return _originalJQueryFnDropdown.apply(this, [action, ...args]);
                };
            }

            if (_originalJQueryFnCollapse) {
                jQuery.fn.collapse = function(action, ...args) {
                    if (this.is(self._friendsContainerElement) && self._isPersistent(this, 'friends-tabfriendlist')) {
                        if (action === 'hide') {
                            self.notify('info', '[UI Persistencia] Bloqueando intento de ocultar el panel de amigos.');
                            self._forceVisibility(this);
                            return this;
                        }
                    }
                    return _originalJQueryFnCollapse.apply(this, [action, ...args]);
                };
            }
        }

        _findSpecificElements() {
            this._chatattopNewMessageElement = document.getElementById('chatattop-newmessage');
            this._drawControlsPopuplistElement = document.querySelector('.drawcontrols-popuplist');
            this._friendsTabFriendlistElement = document.getElementById('friends-tabfriendlist');
            this._friendsContainerElement = document.getElementById('friends-container');
        }

        _setupObservers() {
            const self = this;

            if (this._drawControlsPopuplistElement) {
                jQuery(this._drawControlsPopuplistElement).off('focusout.persistence').on('focusout.persistence', function(e) {
                    if (self._isPersistent(this, 'draw-controls-popup')) {
                        e.stopImmediatePropagation();
                        self.notify('info', '[UI Persistencia] Previniendo focusout en panel de pincel.');
                        self._forceVisibility(jQuery(this));
                    }
                });
            }

            if (this._chatattopNewMessageElement) {
                const chatNotificationObserver = new MutationObserver((mutations) => {
                    if (self._isPersistent(self._chatattopNewMessageElement, 'chat-new-message-notification')) {
                        for (const mutation of mutations) {
                            if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
                                if (jQuery(self._chatattopNewMessageElement).is(':hidden')) {
                                    self.notify('info', '[UI Persistencia] Forzando visibilidad de notificación de chat.');
                                    self._forceVisibility(jQuery(self._chatattopNewMessageElement));
                                }
                            }
                        }
                    }
                });
                chatNotificationObserver.observe(this._chatattopNewMessageElement, { attributes: true, attributeFilter: ['style'] });
            }

            if (this._friendsTabFriendlistElement && this._friendsContainerElement) {
                const friendsVisibilityObserver = new MutationObserver((mutations) => {
                    if (self._isPersistent(self._friendsTabFriendlistElement, 'friends-tabfriendlist')) {
                        for (const mutation of mutations) {
                            if (mutation.type === 'attributes' && (mutation.attributeName === 'style' || mutation.attributeName === 'class')) {
                                if (jQuery(self._friendsTabFriendlistElement).is(':hidden')) {
                                    self.notify('info', '[UI Persistencia] Forzando visibilidad de la lista de amigos.');
                                    self._forceVisibility(jQuery(self._friendsTabFriendlistElement));
                                }
                                if (jQuery(self._friendsContainerElement).is(':hidden')) {
                                    self.notify('info', '[UI Persistencia] Forzando apertura del contenedor de amigos.');
                                    jQuery(self._friendsContainerElement).collapse('show');
                                }
                            }
                        }
                    }
                });
                friendsVisibilityObserver.observe(this._friendsTabFriendlistElement, { attributes: true, attributeFilter: ['style', 'class'] });
                friendsVisibilityObserver.observe(this._friendsContainerElement, { attributes: true, attributeFilter: ['style', 'class'] });

                const friendsWgElement = jQuery('#friends-wg');
                if (friendsWgElement.length) {
                    friendsWgElement.off('focusout.friendsHide').on('focusout.friendsHide', function(e) {
                         if (self._isPersistent(self._friendsTabFriendlistElement, 'friends-tabfriendlist')) {
                             e.stopImmediatePropagation();
                             self.notify('debug', '[UI Persistencia] Bloqueando focusout de #friends-wg para mantener la lista abierta.');
                             jQuery(self._friendsContainerElement).collapse('show');
                         }
                    });
                }
            }
        }

        _toggleElementPersistence(id, buttonElement, originalLabelHtml) {
            const isCurrentlyPersistent = this._persistentElements.has(id);
            const newActiveState = !isCurrentlyPersistent;

            if (newActiveState) {
                this._persistentElements.set(id, true);

            } else {
                this._persistentElements.delete(id);

            }

            buttonElement.classList.toggle("active", newActiveState);

            const labelSpan = buttonElement.querySelector("span");
            if (labelSpan) {
                labelSpan.innerHTML = newActiveState
                    ? originalLabelHtml.replace('Mantener', 'Liberar')
                    : originalLabelHtml.replace('Liberar', 'Mantener');
            }

            this._applyPersistenceRulesForElement(id, newActiveState);
        }

        _applyPersistenceRulesForElement(id, isActive) {
            const targetElements = this._getElementsForPersistenceId(id);

            targetElements.forEach(el => {
                if (isActive) {
                    if (id === 'all-modals' && jQuery(el).hasClass('modal')) {
                        if (jQuery(el).hasClass('show')) {
                            this._forceVisibility(jQuery(el));
                        }
                    } else if (id === 'all-popovers' || id === 'all-dropdowns' || id === 'draw-controls-popup' || id === 'chat-new-message-notification' || id === 'top-messages') {
                        this._forceVisibility(jQuery(el));
                    } else if (id === 'friends-tabfriendlist' && jQuery(el).is(this._friendsTabFriendlistElement)) {
                        this._forceVisibility(jQuery(el));
                        jQuery(this._friendsContainerElement).collapse('show');
                    }

                    if (el.tagName === 'DETAILS') {
                        jQuery(el).attr('open', true);
                    }
                } else {
                    this._revertVisibility(jQuery(el));
                    if (el.tagName === 'DETAILS') {
                        jQuery(el).attr('open', false);
                    }
                }
            });
        }

        _getElementsForPersistenceId(id) {
            switch (id) {
                case 'all-modals':
                    return [...document.querySelectorAll('.modal.show, .modal-backdrop.show')];
                case 'all-popovers':
                    return [...document.querySelectorAll('.popover.show')];
                case 'all-dropdowns':
                    return [...document.querySelectorAll('.dropdown-menu.show')];
                case 'draw-controls-popup':
                    return this._drawControlsPopuplistElement ? [this._drawControlsPopuplistElement] : [];
                case 'chat-new-message-notification':
                    return this._chatattopNewMessageElement ? [this._chatattopNewMessageElement] : [];
                case 'top-messages':
                    return [...document.querySelectorAll('.topbox')];
                case 'friends-tabfriendlist':
                    return this._friendsTabFriendlistElement ? [this._friendsTabFriendlistElement] : [];
                default:
                    return [];
            }
        }

        _isPersistent(element, categoryId) {
            return this._persistentElements.has(categoryId) && this._persistentElements.get(categoryId) === true;
        }

        _forceVisibility($element) {
            if ($element && $element.length > 0) {
                $element.each((idx, el) => {
                    jQuery(el).css({ 'display': 'block', 'visibility': 'visible', 'opacity': '1' });
                    jQuery(el).removeClass('hide').addClass('show');
                });
            }
        }

        _revertVisibility($element) {
             if ($element && $element.length > 0) {
                 $element.each((idx, el) => {
                     jQuery(el).css({ 'display': '', 'visibility': '', 'opacity': '' });
                     jQuery(el).removeClass('show').addClass('hide');
                 });
             }
        }
    }
})("QBit");





(function AdvancedTranslateTelemetryModule() {
    const QBit = globalThis[arguments[0]];


    function _xhrGetJson(url, callback) {
        const req = new XMLHttpRequest();
        req.onload = (e) => {
            const response = req.response;
            if (!callback) return;
            try {
                callback(JSON.parse(response));
            } catch (error) {
                console.error("AdvancedTranslateTelemetry: Error parsing JSON response", error);
            }
        };
        req.onerror = (e) => {
            console.error("AdvancedTranslateTelemetry: XHR error", e);
        };
        req.open("GET", url);
        req.send();
    }


    const _translationLanguages = {
        "en": "English", "es": "Spanish", "fr": "French", "de": "German", "it": "Italian", "pt": "Portuguese",
        "ru": "Russian", "zh-CN": "Chinese (Simplified)", "ja": "Japanese", "ko": "Korean", "ar": "Arabic",
        "hi": "Hindi", "bn": "Bengali", "tr": "Turkish", "pl": "Polish", "nl": "Dutch", "sv": "Swedish",
        "da": "Danish", "no": "Norwegian", "fi": "Finnish", "el": "Greek", "he": "Hebrew", "id": "Indonesian",
        "ms": "Malay", "th": "Thai", "vi": "Vietnamese", "uk": "Ukrainian", "cs": "Czech", "hu": "Hungarian",
        "ro": "Romanian", "af": "Afrikaans", "sq": "Albanian", "am": "Amharic", "hy": "Armenian", "az": "Azerbaijani",
        "eu": "Basque", "be": "Belarusian", "bs": "Bosnian", "bg": "Bulgarian", "ca": "Catalan", "ceb": "Cebuano",
        "ny": "Chichewa", "co": "Corsican", "hr": "Croatian", "cz": "Czech (Legacy)", "eo": "Esperanto", "et": "Estonian",
        "tl": "Filipino", "fy": "Frisian", "gl": "Galician", "ka": "Georgian", "gu": "Gujarati", "ht": "Haitian Creole",
        "ha": "Hausa", "haw": "Hawaiian", "iw": "Hebrew (Legacy)", "hmn": "Hmong", "is": "Icelandic", "ig": "Igbo",
        "ga": "Irish", "jw": "Javanese", "kn": "Kannada", "kk": "Kazakh", "km": "Khmer", "ku": "Kurdish (Kurmanji)",
        "ky": "Kyrgyz", "lo": "Lao", "la": "Latin", "lv": "Latvian", "lt": "Lithuanian", "lb": "Luxembourgish",
        "mk": "Macedonian", "mg": "Malagasy", "ml": "Malayalam", "mt": "Maltese", "mi": "Maori", "mr": "Marathi",
        "mn": "Mongolian", "my": "Myanmar (Burmese)", "ne": "Nepali", "ps": "Pashto", "fa": "Persian", "pa": "Punjabi",
        "sm": "Samoan", "gd": "Scots Gaelic", "sr": "Serbian", "st": "Sesotho", "sn": "Shona", "sd": "Sindhi",
        "si": "Sinhala", "sk": "Slovak", "sl": "Slovenian", "so": "Somali", "su": "Sundanese", "sw": "Swahili",
        "tg": "Tajik", "ta": "Tamil", "te": "Telugu", "uz": "Uzbek", "xh": "Xhosa", "yi": "Yiddish", "yo": "Yoruba",
        "zu": "Zulu"
    };

    QBit.Styles.addRules([
        `#${QBit.identifier} .telemetry-section {
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            padding: 5px;
            margin-bottom: 10px;
            background-color: var(--CE-bg_color);
            max-width: 350px; /* NEW: Limit the maximum width of each section */
            margin-left: auto; /* Center the sections within their parent */
            margin-right: auto;
        }`,
        `#${QBit.identifier} .telemetry-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,

        `#${QBit.identifier} .auto-translate-status {
            font-size: 0.9em;
            color: var(--success);
            text-align: center;
            margin-bottom: 10px;
        }`,

        `#${QBit.identifier} .dtr-textarea {
            width: 100%;
            padding: 8px;
            box-sizing: border-box;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            resize: vertical;
            min-height: 60px;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
            font-size: 0.95em;
            line-height: 1.4;
        }`,
        `#${QBit.identifier} .dtr-custom-dropdown {
            position: relative;
            width: 100%; /* Occupy full width of its parent section */
            font-size: 0.95em;
            margin-bottom: 10px;
        }`,
        `#${QBit.identifier} .dtr-selected-language-display {
            width: calc(100% - 18px);
            padding: 8px 10px;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }`,
        `#${QBit.identifier} .dtr-dropdown-panel {
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%; /* Occupy full width of dtr-custom-dropdown */
    /* max-width is now handled by the parent .telemetry-section */
    background-color: var(--CE-bg_color);
    border: 1px solid var(--CE-color);
    border-radius: .25rem;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    z-index: 1000;
    max-height: 200px;
    overflow-y: auto;
    overflow-x: hidden;
    display: none;
    flex-direction: column;
    padding: 5px;
        }`,
`#${QBit.identifier} .dtr-language-search-input {
    width: calc(100% - 12px);
    padding: 6px;
    box-sizing: border-box;
    border: 1px solid var(--CE-color);
    border-radius: .25rem;
    margin: 4px 6px 6px 6px;
    background-color: var(--light-gray-bg);
    color: var(--dark-text);
    font-size: 0.85em;
}`,
        `#${QBit.identifier} .dtr-lang-item {
            padding: 8px 10px;
            cursor: pointer;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            border-radius: .20rem;
        }`,
        `#${QBit.identifier} .dtr-lang-item:hover {
            background-color: var(--light);
        }`,
        `#${QBit.identifier} .dtr-button {
            padding: 10px 15px;
            border: none;
            border-radius: .25rem;
            color: white;
            cursor: pointer;
            transition: background-color 0.2s ease;
            font-size: 1em;
            font-weight: 600;
            margin-bottom: 5px;
        }`,
        `#${QBit.identifier} .dtr-translate-button { background-color: var(--info); }`,
        `#${QBit.identifier} .dtr-translate-button:hover { background-color: var(--dark-info); }`,
        `#${QBit.identifier} .dtr-send-button { background-color: var(--primary); }`,
        `#${QBit.identifier} .dtr-send-button:hover { background-color: var(--dark-primary); }`,


        `#${QBit.identifier} .player-metrics-list {
            max-height: 150px;
            overflow-y: auto;
            border: 1px solid var(--CE-color);
            padding: 5px;
            font-size: 0.85em;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
        }`,
        `#${QBit.identifier} .snapshot-previews {
            display: flex;
            flex-wrap: wrap;
            gap: 5px;
            margin-top: 5px;
            border: 1px dashed var(--CE-color);
            padding: 5px;
            min-height: 60px;
            align-items: center;
        }`,
        `#${QBit.identifier} .snapshot-previews img {
            width: 50px;
            height: 50px;
            border: 1px solid #ccc;
            cursor: pointer;
            object-fit: cover;
        }`,
        `#${QBit.identifier} .hud-controls {
            display: flex;
            gap: 10px;
            align-items: center;
            margin-top: 5px;
        }`,
        `#${QBit.identifier} .hud-controls label {
            flex-shrink: 0;
        }`,
        `#${QBit.identifier} .hud-controls input[type="color"] {
            flex-grow: 1;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
        }`,
`#${QBit.identifier} .dtr-language-list::-webkit-scrollbar {
    width: 6px;
}`,
`#${QBit.identifier} .dtr-language-list::-webkit-scrollbar-track {
    background: var(--light-gray-bg);
    border-radius: 3px;
    border: 1px solid #ccc;
}`,
`#${QBit.identifier} .dtr-language-list::-webkit-scrollbar-thumb {
    background: var(--CE-color);
    border-radius: 3px;
    border: 1px solid #999;
}`,
`#${QBit.identifier} .dtr-language-list::-webkit-scrollbar-thumb:hover {
    background: var(--info);
    border: 1px solid #666;
}`
    ]);

    class AdvancedTranslateTelemetry extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");


        _chatObserver = null;


        _currentSelectedLanguageCode = "es";
        _translatorUi = {
            inputText: null,
            selectedLanguageDisplay: null,
            dropdownPanel: null,
            languageSearchInput: null,
            languageList: null,
            translateButton: null,
            outputText: null,
            sendButton: null,
        };
        _languageItems = {};


        _playerMetricsContainer;
        _snapshotContainer;
        _snapshots = [];
        _maxSnapshots = 3;

        constructor() {
            super("🌐Smart Translator", '<i class="fas fa-globe"></i>');
            this._onStartup();
        }

        _onStartup() {
            this._loadInterface();
            this._setupAutoTranslateHooks();
            this._attachMenuEventListeners();
            this._listenToGameEvents();
            this.updatePlayerMetrics();
            this.updateSnapshotPreviews();

        }

        _loadInterface() {
            const container = domMake.Tree("div");


            const autoTranslateSection = domMake.Tree("div", { class: "telemetry-section" });
            autoTranslateSection.appendChild(domMake.Tree("div", { class: "telemetry-section-title" }, ["Traducción Automática de Chat"]));
            autoTranslateSection.appendChild(domMake.Tree("div", { class: "auto-translate-status" }, ["Estado: Activo y escuchando mensajes."]));
            container.appendChild(autoTranslateSection);


            const translatorMenuSection = domMake.Tree("div", { class: "telemetry-section" });
            translatorMenuSection.appendChild(domMake.Tree("div", { class: "telemetry-section-title" }, ["Menú de Traducción Completo"]));

            this._translatorUi.inputText = domMake.Tree('textarea', {
                placeholder: "Ingresa texto para traducir...", rows: 3, id: "dtrInputText",
                class: "dtr-textarea"
            });
            this._translatorUi.inputText.addEventListener('keydown', (e) => e.stopPropagation());
            translatorMenuSection.appendChild(this._translatorUi.inputText);

            const dtrCustomLanguageDropdown = domMake.Tree('div', { class: 'dtr-custom-dropdown' });
            this._translatorUi.selectedLanguageDisplay = domMake.Tree('div', { class: 'dtr-selected-language-display' }, [
                domMake.Tree('span', {}, [_translationLanguages[this._currentSelectedLanguageCode]]),
                domMake.Tree('span', {style: 'font-size: 0.8em; margin-left: 5px;'}, ['▼'])
            ]);
            this._translatorUi.dropdownPanel = domMake.Tree('div', { class: 'dtr-dropdown-panel' });
            this._translatorUi.languageSearchInput = domMake.Tree('input', {
                type: 'text', placeholder: 'Buscar idiomas...', id: 'dtrLanguageSearchInput',
                class: 'dtr-language-search-input'
            });
            this._translatorUi.languageSearchInput.addEventListener('keydown', (e) => e.stopPropagation());
            this._translatorUi.languageList = domMake.Tree('div', {
                id: 'dtrLanguageList',
                class: 'dtr-language-list',
                style: 'max-height: 150px; overflow-y: auto; overflow-x: hidden; padding: 5px;'
            });


            this._populateLanguageList();

            this._translatorUi.dropdownPanel.appendAll(this._translatorUi.languageSearchInput, this._translatorUi.languageList);
            dtrCustomLanguageDropdown.appendAll(this._translatorUi.selectedLanguageDisplay, this._translatorUi.dropdownPanel);
            translatorMenuSection.appendChild(dtrCustomLanguageDropdown);

            this._translatorUi.translateButton = domMake.Tree('button', { class: "dtr-button dtr-translate-button" }, ["Traducir"]);
            translatorMenuSection.appendChild(this._translatorUi.translateButton);

            this._translatorUi.outputText = domMake.Tree('textarea', {
                placeholder: "La traducción aparecerá aquí...", rows: 3, readonly: true, id: "dtrOutputText",
                class: "dtr-textarea"
            });
            translatorMenuSection.appendChild(this._translatorUi.outputText);

            this._translatorUi.sendButton = domMake.Tree('button', { class: "dtr-button dtr-send-button" }, ["Enviar Traducción"]);
            translatorMenuSection.appendChild(this._translatorUi.sendButton);

            container.appendChild(translatorMenuSection);


            const playerMetricsSection = domMake.Tree("div", { class: "telemetry-section" });
            playerMetricsSection.appendChild(domMake.Tree("div", { class: "telemetry-section-title" }, ["Métricas de Jugadores"]));
            this._playerMetricsContainer = domMake.Tree("div", { class: "player-metrics-list" });
            playerMetricsSection.appendChild(this._playerMetricsContainer);
            container.appendChild(playerMetricsSection);


            const visualHistorySection = domMake.Tree("div", { class: "telemetry-section" });
            visualHistorySection.appendChild(domMake.Tree("div", { class: "telemetry-section-title" }, ["Historial Visual de Lienzo"]));
            const captureSnapshotButton = domMake.Button("Capturar Lienzo");
            captureSnapshotButton.title = "Guarda una imagen del lienzo actual.";
            captureSnapshotButton.addEventListener("click", () => this.captureCanvasSnapshot());
            visualHistorySection.appendChild(captureSnapshotButton);
            this._snapshotContainer = domMake.Tree("div", { class: "snapshot-previews icon-list" });
            visualHistorySection.appendChild(this._snapshotContainer);
            container.appendChild(visualHistorySection);


            const hudThemesSection = domMake.Tree("div", { class: "telemetry-section" });
            hudThemesSection.appendChild(domMake.Tree("div", { class: "telemetry-section-title" }, ["Temas Dinámicos del HUD"]));
            const hudColorControls = domMake.Tree("div", { class: "hud-controls" });
            const hudColorLabel = domMake.Tree("label", {}, ["Color del HUD:"]);
            const hudColorInput = domMake.Tree("input", { type: "color", value: "#007bff" });
            hudColorInput.addEventListener("change", (e) => {
                const newColor = e.target.value;
                document.documentElement.style.setProperty('--primary', newColor);
                document.documentElement.style.setProperty('--success', newColor);

            });
            hudColorControls.appendAll(hudColorLabel, hudColorInput);
            hudThemesSection.appendChild(hudColorControls);
            container.appendChild(hudThemesSection);

            this.htmlElements.section.appendChild(container);
        }


        _setupAutoTranslateHooks() {
            const chatboxMessages = document.querySelector("#chatbox_messages");
            if (chatboxMessages) {
                this._chatObserver = new MutationObserver((mutations) => {
                    mutations.forEach((record) => {
                        record.addedNodes.forEach((node) => {
                            if (node.nodeType === 1 && !node.classList.contains("systemchatmessage5")) {
                                this._processChatMessageNode(node);
                            }
                        });
                    });
                });
                this._chatObserver.observe(chatboxMessages, { childList: true, subtree: false });
                this._addSystemMessage("Traducción automática de chat: activada (con info. de idioma).");
            } else {

            }
        }

        _processChatMessageNode(node) {
            const messageElement = node.querySelector(".playerchatmessage-text");
            if (messageElement) {
                const textToTranslate = messageElement.textContent;
                this._translateText(textToTranslate, "auto", "en", (translation, detectedLang) => {
                    this._applyTranslationAsTooltip(translation, detectedLang, messageElement);
                });
            }

            const nameElement = node.querySelector(".playerchatmessage-name");
            if (nameElement) {
                const nameToTranslate = nameElement.textContent;
                this._translateText(nameToTranslate, "auto", "en", (translation, detectedLang) => {
                    this._applyTranslationAsTooltip(translation, detectedLang, nameElement);
                });
            }

            const selfnameElement = node.querySelector(".playerchatmessage-selfname");
            if (selfnameElement) {
                const nameToTranslate = selfnameElement.textContent;
                this._translateText(nameToTranslate, "auto", "en", (translation, detectedLang) => {
                    this._applyTranslationAsTooltip(translation, detectedLang, selfnameElement);
                });
            }
        }

        _applyTranslationAsTooltip(translatedText, detectedLangCode, targetNode) {
            const langName = new Intl.DisplayNames(['en'], { type: 'language' }).of(detectedLangCode);
            let tooltipText = translatedText;

            if (detectedLangCode !== 'en' && detectedLangCode !== 'auto' && langName && langName !== detectedLangCode) {
                tooltipText += ` (${langName})`;
            }
            targetNode.title = tooltipText;
        }

        _addSystemMessage(message) {
            const loggingContainer = document.getElementById("chatbox_messages");
            if (loggingContainer) {
                const chatmessage = domMake.Tree(
                    "div",
                    { class: `chatmessage systemchatmessage5`, "data-ts": Date.now(), style: `color: var(--info);` },
                    [message]
                );
                loggingContainer.appendChild(chatmessage);
                loggingContainer.scrollTop = loggingContainer.scrollHeight;
            }
        }


        _translateText(textToTranslate, fromLang = "auto", toLang = "en", callback) {
            const url =
                "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" +
                fromLang +
                "&tl=" +
                toLang +
                "&dt=t&q=" +
                encodeURI(textToTranslate);

            _xhrGetJson(url, (data) => {
                if (data && data[0] && data[0][0] && data[0][0][0]) {
                    const translatedText = data[0][0][0];
                    const detectedSourceLanguage = data[2] || 'unknown';
                    callback(translatedText, detectedSourceLanguage);
                } else {
                    console.warn("AdvancedTranslateTelemetry: Could not get translation for:", textToTranslate, data);
                    callback(textToTranslate, 'unknown');
                }
            });
        }


        _populateLanguageList() {
            this._translatorUi.languageList.innerHTML = '';
            for (const langCode in _translationLanguages) {
                const langName = _translationLanguages[langCode];
                const langItem = domMake.Tree('div', {
                    class: 'dtr-lang-item',
                    'data-lang-code': langCode,
                }, [langName]);

                langItem.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this._currentSelectedLanguageCode = langCode;
                    this._translatorUi.selectedLanguageDisplay.querySelector('span:first-child').textContent = langName;
                    this._translatorUi.dropdownPanel.style.display = 'none';

                });
                this._translatorUi.languageList.appendChild(langItem);
                this._languageItems[langCode] = langItem;
            }
        }

        _attachMenuEventListeners() {
            this._translatorUi.selectedLanguageDisplay.addEventListener('click', (e) => {
                e.stopPropagation();
                this._translatorUi.dropdownPanel.style.display = this._translatorUi.dropdownPanel.style.display === 'none' ? 'flex' : 'none';
                if (this._translatorUi.dropdownPanel.style.display === 'flex') {
                    this._translatorUi.languageSearchInput.focus();
                    this._translatorUi.languageSearchInput.value = '';
                    this._translatorUi.languageSearchInput.dispatchEvent(new Event('input'));
                }
            });

            document.addEventListener('click', (e) => {
                const dropdownContainer = this._translatorUi.selectedLanguageDisplay.parentNode;
                if (dropdownContainer && !dropdownContainer.contains(e.target) && this._translatorUi.dropdownPanel.style.display === 'flex') {
                    this._translatorUi.dropdownPanel.style.display = 'none';
                }
            });

            this._translatorUi.languageSearchInput.addEventListener('input', () => {
                const searchTerm = this._translatorUi.languageSearchInput.value.toLowerCase();
                for (const langCode in _translationLanguages) {
                    const langName = _translationLanguages[langCode].toLowerCase();
                    if (langName.startsWith(searchTerm)) {
                        this._languageItems[langCode].style.display = 'block';
                    } else {
                        this._languageItems[langCode].style.display = 'none';
                    }
                }
            });

            this._translatorUi.translateButton.addEventListener("click", () => {
                const textToTranslate = this._translatorUi.inputText.value.trim();
                const toLang = this._currentSelectedLanguageCode;
                if (textToTranslate) {

                    this._translateText(textToTranslate, "auto", toLang, (translatedText) => {
                        this._translatorUi.outputText.value = translatedText;
                        this.notify("success", "Traducción completa.");
                    });
                } else {
                    this._translatorUi.outputText.value = "Por favor, introduce texto para traducir.";

                }
            });

            this._translatorUi.sendButton.addEventListener("click", () => {
                const translatedText = this._translatorUi.outputText.value;
                if (translatedText) {
                    const chatInput = document.getElementById('chatbox_textinput');
                    if (chatInput) {
                        chatInput.value = translatedText;
                        const event = new KeyboardEvent('keydown', {
                            key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true
                        });
                        chatInput.dispatchEvent(event);


                        const originalText = this._translatorUi.sendButton.textContent;
                        this._translatorUi.sendButton.textContent = "¡Enviado!";
                        setTimeout(() => {
                            this._translatorUi.sendButton.textContent = originalText;
                            this._translatorUi.inputText.value = "";
                            this._translatorUi.outputText.value = "";
                        }, 1500);

                    } else {

                        const originalText = this._translatorUi.sendButton.textContent;
                        this._translatorUi.sendButton.textContent = "¡Chat no encontrado!";
                        setTimeout(() => { this._translatorUi.sendButton.textContent = originalText; }, 1500);
                    }
                } else {

                }
            });
        }


        _listenToGameEvents() {
            const playerListElement = document.getElementById("playerlist");
            if (playerListElement) {
                const observer = new MutationObserver(() => this.updatePlayerMetrics());
                observer.observe(playerListElement, { childList: true, subtree: true });
            }

            const chatboxMessages = document.getElementById("chatbox_messages");
            if (chatboxMessages) {
                const chatObserver = new MutationObserver((mutations) => {
                    mutations.forEach(mutation => {
                        mutation.addedNodes.forEach(node => {
                            if (node.classList && node.classList.contains('chatmessage') && !node.classList.contains('systemchatmessage5')) {
                                const playerNameElement = node.querySelector('.playerchatmessage-name a');
                                const playerName = playerNameElement ? playerNameElement.textContent : 'Unknown';

                            }
                        });
                    });
                });
                chatObserver.observe(chatboxMessages, { childList: true });
            }
        }

        updatePlayerMetrics() {
            this._playerMetricsContainer.innerHTML = '';
            const playerRows = document.querySelectorAll("#playerlist .playerlist-row");
            if (playerRows.length === 0) {
                this._playerMetricsContainer.appendChild(domMake.TextNode("No hay jugadores en la sala."));
                return;
            }

            playerRows.forEach(playerRow => {
                const playerId = playerRow.dataset.playerid;
                const playerName = playerRow.querySelector(".playerlist-name a")?.textContent || `Player ${playerId}`;
                const score = playerRow.querySelector(".playerlist-rank")?.textContent || 'N/A';
                const turnScore = playerRow.querySelector(".playerlist-turnscore")?.textContent || 'N/A';

                const metricItem = domMake.Tree("div", { style: "margin: 2px 0; font-size: 0.8rem;" }, [
                    domMake.Tree("strong", {}, [`${playerName} (ID: ${playerId}): `]),
                    domMake.TextNode(`Puntuación: ${score}, Turno: ${turnScore}`)
                ]);
                this._playerMetricsContainer.appendChild(metricItem);
            });
        }














        captureCanvasSnapshot() {
            const gameCanvas = document.body.querySelector("canvas#canvas");
            if (!gameCanvas) {

                return;
            }

            try {
                const base64Image = gameCanvas.toDataURL("image/png");
                const timestamp = new Date().toLocaleString();

                this._snapshots.push({ data: base64Image, timestamp: timestamp });
                if (this._snapshots.length > this._maxSnapshots) {
                    this._snapshots.shift();
                }
                this.updateSnapshotPreviews();
                this.notify("success", `Instantánea del lienzo capturada: ${timestamp}`);
            } catch (e) {

                console.error("Canvas snapshot error:", e);
            }
        }

        updateSnapshotPreviews() {
            this._snapshotContainer.innerHTML = '';
            if (this._snapshots.length === 0) {
                this._snapshotContainer.appendChild(domMake.TextNode("No hay instantáneas guardadas."));
                return;
            }

            this._snapshots.forEach((snapshot, index) => {
                const img = domMake.Tree("img", {
                    src: snapshot.data,
                    style: "width: 50px; height: 50px; border: 1px solid #ccc; margin: 2px; cursor: pointer;",
                    title: `Instantánea ${index + 1}: ${snapshot.timestamp}`
                });
                img.addEventListener("click", () => this.displaySnapshot(snapshot.data));
                this._snapshotContainer.appendChild(img);
            });
        }

        displaySnapshot(imageData) {
            const overlay = domMake.Tree("div", {
                style: `
                    position: fixed; top: 0; left: 0; width: 100%; height: 100%;
                    background: rgba(0,0,0,0.8); z-index: 10000;
                    display: flex; justify-content: center; align-items: center;
                `
            });
            const img = domMake.Tree("img", {
                src: imageData,
                style: `max-width: 90%; max-height: 90%; border: 2px solid white;`
            });
            overlay.appendChild(img);
            overlay.addEventListener("click", () => overlay.remove());
            document.body.appendChild(overlay);
        }
    }
})("QBit");





(function AdvancedChatEnhancementsModule() {
    const QBit = globalThis.QBit;


    const FANCY_TEXT_MAPS = window.FANCY_TEXT_MAPS;


    const ASCII_MOJIS = window.ASCII_MOJIS;


    const GLITCH_TEXT_STRINGS = window.GLITCH_TEXT_STRINGS;


    Object.keys(GLITCH_TEXT_STRINGS).forEach(key => {
        GLITCH_TEXT_STRINGS[key] = GLITCH_TEXT_STRINGS[key].replace(/\\n/g, '\n');
    });

    class AdvancedChatEnhancements extends QBit {
        static dummy1 = QBit.register(this);
        static dummy2 = QBit.bind(this, "CubeEngine");

        #originalChatInput = null;
        #textarea = null;
        #chatInputReplaced = false;
        #isModuleActive = false;

        #selectedFancyStyle = '';

        #spamIntervalId = null;


        #PACKS = [
            { num: 1, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack1.json' },
            { num: 2, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack2.json' },
            { num: 3, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack3.json' },
            { num: 4, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack4.json' },
            { num: 5, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack5.json' },
            { num: 6, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack6.json' },
            { num: 7, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack7.json' },
            { num: 8, url: 'https://raw.githubusercontent.com/NuevoMundoOficial/DrawariaASCIIPacks/main/AsciiPack8.json' }
        ];
        #packCache = {};
        #PAGE_SIZE = 10;

        #currentPackNum = 1;
        #currentUrl = this.#PACKS.url;
        #currentAscii = [];
        #filteredAscii = [];
        #currentPage = 0;


        #asciiListContainer = null;
        #asciiSearchInput = null;
        #asciiPageInfo = null;
        #asciiBtnPrev = null;
        #asciiBtnNext = null;

        constructor() {
            super("🗪Creative Chat", '<i class="fas fa-comments-dollar"></i>');
            this.#onStartup();
        }

        #onStartup() {

            setTimeout(() => {
                this.#originalChatInput = document.querySelector('#chatbox_textinput');
                if (!this.#originalChatInput) {

                    return;
                }
                this.#loadInterface();
                this.#setupHotkeys();
            }, 1000);
        }

        #loadInterface() {
            const container = domMake.Tree("div");


            const moduleToggleRow = domMake.Row();
            const toggleButton = domMake.Button('<i class="fas fa-power-off"></i> Activar Chat Creativo');
            toggleButton.classList.add('module-toggle-button');
            toggleButton.addEventListener('click', () => this.#toggleModuleActive(toggleButton));
            moduleToggleRow.appendChild(toggleButton);
            container.appendChild(moduleToggleRow);

            const basicActionsRow = domMake.Row();
            basicActionsRow.style.gap = '5px';
            this.fillButton = domMake.Button("Rellenar");
            this.fillButton.title = "Rellena el chat con líneas en blanco (útil para limpiar visualmente)";
            this.fillButton.onclick = () => this.#fillTextareaWithBlankLines();
            this.clearButton = domMake.Button("Limpiar");
            this.clearButton.title = "Limpia todo el texto del área de chat";
            this.clearButton.onclick = () => this.#clearTextarea();
            this.saveButton = domMake.Button("Guardar");
            this.saveButton.title = "Guarda el texto actual como borrador";
            this.saveButton.onclick = () => this.#saveDraft();
            this.loadButton = domMake.Button("Cargar");
            this.loadButton.title = "Carga el último borrador guardado";
            this.loadButton.onclick = () => this.#loadDraft();
            basicActionsRow.appendAll(this.fillButton, this.clearButton, this.saveButton, this.loadButton);
            container.appendChild(basicActionsRow);


            const formattingRow = domMake.Row();
            formattingRow.style.gap = '5px';
            this.boldButton = domMake.Button("<strong>B</strong>");
            this.boldButton.title = "Poner texto seleccionado en negrita (**texto**)";
            this.boldButton.onclick = () => this.#wrapSelection('**', '**');
            this.italicButton = domMake.Button("<em>I</em>");
            this.italicButton.title = "Poner texto seleccionado en cursiva (*texto*)";
            this.italicButton.onclick = () => this.#wrapSelection('*', '*');
            this.strikeButton = domMake.Button("<del>S</del>");
            this.strikeButton.title = "Poner texto seleccionado en tachado (~~texto~~)";
            this.strikeButton.onclick = () => this.#wrapSelection('~~', '~~');
            formattingRow.appendAll(this.boldButton, this.italicButton, this.strikeButton);
            container.appendChild(formattingRow);


            const creativeRow = domMake.Row();
            creativeRow.style.gap = '5px';
            this.fancySelect = domMake.Tree("select", { title: "Convertir texto a un estilo Fancy" });
            this.fancySelect.appendAll(
                domMake.Tree("option", { value: "" }, ["-- Estilo Fancy --"]),
                domMake.Tree("option", { value: "script" }, ["Script"]),
                domMake.Tree("option", { value: "fraktur" }, ["Fraktur"]),
                domMake.Tree("option", { value: "monospace" }, ["Monospace"]),
                domMake.Tree("option", { value: "bold" }, ["Bold"]),
                domMake.Tree("option", { value: "italic" }, ["Italic"])
            );
            this.fancySelect.onchange = (e) => this.#setFancyStyle(e.target.value);
            this.emojiButton = domMake.Button("😊 Emojis");
            this.emojiButton.title = "Abrir selector de Emojis y ASCIImojis";
            this.emojiButton.onclick = () => this.#openEmojiPicker();
            creativeRow.appendAll(this.fancySelect, this.emojiButton);
            container.appendChild(creativeRow);


            const countersRow = domMake.Row();
            countersRow.style.marginTop = '10px';
            countersRow.style.fontSize = '0.9em';
            this.wordCountDisplay = domMake.Tree("span");
            this.charCountDisplay = domMake.Tree("span");
            countersRow.appendAll(this.wordCountDisplay, this.charCountDisplay);
            container.appendChild(countersRow);


            const moreOptionsDetails = domMake.Tree("details");
            moreOptionsDetails.style.marginTop = '15px';
            const moreOptionsSummary = domMake.Tree("summary", { style: 'font-weight: bold; cursor: pointer;' }, ["Más Opciones"]);
            moreOptionsDetails.appendChild(moreOptionsSummary);

            const glitchButtonsRow = domMake.Row();
            glitchButtonsRow.style.gap = '5px';
            glitchButtonsRow.style.marginTop = '10px';
            glitchButtonsRow.style.flexWrap = 'wrap';
            glitchButtonsRow.style.justifyContent = 'flex-start';

            const addGlitchButton = (key, text) => {
                const btn = domMake.Button(text, {
                    style: `
                        padding: 4px 8px; /* Smaller padding */
                        font-size: 0.85em; /* Smaller font size */
                        line-height: 1.2; /* Adjust line height for compactness */
                        min-width: auto; /* Allow width to shrink */
                        background: #f0f0f0;
                        color: #333;
                        border: 1px solid #ccc;
                        border-radius: 5px;
                        cursor: pointer;
                        transition: background 0.1s ease;
                    `
                });
                btn.title = `Insertar texto '${key}'`;
                btn.onclick = () => this.#insertPredefinedText(GLITCH_TEXT_STRINGS[key]);
                btn.onmouseenter = () => btn.style.background = "#e0e0e0";
                btn.onmouseleave = () => btn.style.background = "#f0f0f0";
                glitchButtonsRow.appendChild(btn);
                this[`${key}Button`] = btn;
            };

            addGlitchButton('weird', 'Weird');
            addGlitchButton('corrupted', 'Corrupted');
            addGlitchButton('glitch', 'Glitch');
            addGlitchButton('distorted', 'Distorted');
            addGlitchButton('wow', 'Wow');
            addGlitchButton('nyan', 'Nyan Cat');
            addGlitchButton('pika', 'Pikachu');
            addGlitchButton('tiger', 'Tiger');
            addGlitchButton('cool cat', 'Cool Cat');
            addGlitchButton('hi box', 'Hi Box');

            moreOptionsDetails.appendChild(glitchButtonsRow);
            container.appendChild(moreOptionsDetails);


            const spamDetails = domMake.Tree("details");
            spamDetails.style.marginTop = '15px';
            const spamSummary = domMake.Tree("summary", { style: 'font-weight: bold; cursor: pointer;' }, ["Spam de Mensajes"]);
            spamDetails.appendChild(spamSummary);

            const spamControlsRow = domMake.Row();
            spamControlsRow.style.gap = '5px';
            spamControlsRow.style.marginTop = '10px';

            this.spamMessageSelect = domMake.Tree("select", { title: "Selecciona un mensaje para spamear" });
            this.spamMessageSelect.appendChild(domMake.Tree("option", { value: "random" }, ["-- Random --"]));

            Object.keys(GLITCH_TEXT_STRINGS).forEach(key => {
                this.spamMessageSelect.appendChild(domMake.Tree("option", { value: key }, [key.charAt(0).toUpperCase() + key.slice(1)]));
            });

            this.spamButton = domMake.Button("Spamear");
            this.spamButton.title = "Activar/Desactivar el envío repetido del mensaje seleccionado (700ms)";
            this.spamButton.onclick = () => this.#toggleSpam();

            spamControlsRow.appendAll(this.spamMessageSelect, this.spamButton);
            spamDetails.appendChild(spamControlsRow);
            container.appendChild(spamDetails);


            const asciiArtDetails = domMake.Tree("details", { id: "ascii-art-module-details" });
            asciiArtDetails.style.marginTop = '15px';
            const asciiArtSummary = domMake.Tree("summary", { style: 'font-weight: bold; cursor: pointer;' }, ["ASCII Art Packs (con buscador y paginación)"]);
            asciiArtDetails.appendChild(asciiArtSummary);
            asciiArtDetails.appendChild(this.#createAsciiArtPanelContent());
            container.appendChild(asciiArtDetails);


            this.htmlElements.section.appendChild(container);
            this.#updateCounters();

            this.#setUIEnabled(false);
        }

        #createAsciiArtPanelContent() {
            const panelContent = domMake.Tree("div", {
                style: `
                    padding: 8px;
                    background: #f2f2f2; /* Lighter background */
                    border-radius: 5px;
                    margin-top: 10px;
                `
            });


            const btnsBar = domMake.Tree("div", {
                style: `
                    margin: 10px 0 6px 0;
                    display: flex;
                    justify-content: center;
                    gap: 7px;
                    flex-wrap: wrap;
                `
            });

            this.#PACKS.forEach(pack => {
                const btn = domMake.Button(String(pack.num), {
                    class: 'ascii-pack-button',
                    title: "Pack " + pack.num,
                    style: `
                        font-weight: bold;
                        width: 34px;
                        height: 34px;
                        color: #000; /* Text color for pack buttons */
                        background: #FFFFFF; /* Changed to white */
                        border: 1px solid #999; /* Adjusted border for white background */
                        border-radius: 7px;
                        cursor: pointer;
                        transition: background .16s;
                    `
                });
                btn.onmouseenter = () => btn.style.background = "#F0F0F0";
                btn.onmouseleave = () => btn.style.background = "#FFFFFF";
                btn.onclick = () => {
                    this.#showPack(pack.num, pack.url);
                };
                btnsBar.appendChild(btn);
            });
            panelContent.appendChild(btnsBar);


            const searchDiv = domMake.Tree("div", {
                style: `
                    margin-bottom: 9px;
                    text-align: center;
                `
            });
            this.#asciiSearchInput = domMake.Tree("input", {
                type: 'text',
                placeholder: 'Buscar en este pack...',
                style: `
                    width: 88%;
                    padding: 6px;
                    font-size: 1em;
                    background: #FFFFFF; /* Explicitly white for search input */
                    color: #000; /* Black text for search input */
                    border: 1px solid #999; /* Adjusted border */
                    border-radius: 6px;
                `
            });
            this.#asciiSearchInput.addEventListener('input', () => {
                this.#filterAsciiList(this.#asciiSearchInput.value);
            });
            searchDiv.appendChild(this.#asciiSearchInput);
            panelContent.appendChild(searchDiv);


            this.#asciiListContainer = domMake.Tree("div", {
                id: "ascii-art-list",
                style: `
                    overflow-y: auto;
                    max-height: 250px;
                    padding: 4px;
                    border-top: 1px solid #999; /* Changed to lighter border */
                    background: #E0E0E0; /* Changed to a slightly darker light background */
                    border-radius: 8px;
                `
            });
            panelContent.appendChild(this.#asciiListContainer);


            const pager = domMake.Tree("div", {
                style: `
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    margin-top: 10px;
                    gap: 10px;
                    padding: 5px;
                `
            });

            this.#asciiBtnPrev = domMake.Button('⬅', {
                style: `
                    font-size: 1.2em;
                    background: #FFFFFF; /* Changed to white */
                    color: #000; /* Black text */
                    border: 1px solid #999; /* Adjusted border */
                    border-radius: 6px;
                    cursor: pointer;
                `
            });
            this.#asciiBtnPrev.onmouseenter = () => this.#asciiBtnPrev.style.background = "#F0F0F0";
            this.#asciiBtnPrev.onmouseleave = () => this.#asciiBtnPrev.style.background = "#FFFFFF";
            this.#asciiBtnPrev.onclick = () => {
                if (this.#currentPage > 0) {
                    this.#currentPage--;
                    this.#renderAsciiList(this.#getCurrentPageAsciis());
                    this.#updateAsciiPagination();
                }
            };

            this.#asciiPageInfo = domMake.Tree('span', {
                style: 'font-size: 1em; color: #000;'
            });

            this.#asciiBtnNext = domMake.Button('➡', {
                style: `
                    font-size: 1.2em;
                    background: #FFFFFF; /* Changed to white */
                    color: #000; /* Black text */
                    border: 1px solid #999; /* Adjusted border */
                    border-radius: 6px;
                    cursor: pointer;
                `
            });
            this.#asciiBtnNext.onmouseenter = () => this.#asciiBtnNext.style.background = "#F0F0F0";
            this.#asciiBtnNext.onmouseleave = () => this.#asciiBtnNext.style.background = "#FFFFFF";
            this.#asciiBtnNext.onclick = () => {
                if ((this.#currentPage + 1) * this.#PAGE_SIZE < this.#filteredAscii.length) {
                    this.#currentPage++;
                    this.#renderAsciiList(this.#getCurrentPageAsciis());
                    this.#updateAsciiPagination();
                }
            };

            pager.appendAll(this.#asciiBtnPrev, this.#asciiPageInfo, this.#asciiBtnNext);
            panelContent.appendChild(pager);


            this.#showPack(1, this.#PACKS[0].url);

            return panelContent;
        }

        #toggleModuleActive(button) {
            this.#isModuleActive = !this.#isModuleActive;
            if (this.#isModuleActive) {
                button.innerHTML = '<i class="fas fa-power-off"></i> Desactivar Chat Avanzado';
                button.classList.add('active');
                this.#replaceChatInput();
                this.#setUIEnabled(true);
            } else {
                button.innerHTML = '<i class="fas fa-power-off"></i> Activar Chat Avanzado';
                button.classList.remove('active');
                this.#restoreOriginalChatInput();
                this.#setUIEnabled(false);
                this.#stopSpam();
            }
        }

        #setUIEnabled(enabled) {
            const buttons = [
                this.fillButton, this.clearButton, this.saveButton, this.loadButton,
                this.boldButton, this.italicButton, this.strikeButton,
                this.emojiButton, this.spamButton, this.spamMessageSelect,
                this.fancySelect, this.weirdButton, this.corruptedButton,
                this.glitchButton, this.distortedButton, this.wowButton,
                this.nyanButton, this.pikaButton, this.tigerButton,
                this['cool catButton'], this['hi boxButton'],
                this.#asciiSearchInput, this.#asciiBtnPrev, this.#asciiBtnNext
            ];
            buttons.forEach(btn => {
                if (btn) {
                    btn.disabled = !enabled;
                }
            });

            const asciiPackButtons = this.htmlElements.section.querySelectorAll('.ascii-pack-button');
            asciiPackButtons.forEach(btn => {
                if (btn) {
                    btn.disabled = !enabled;
                }
            });
        }

        #replaceChatInput() {
            if (this.#chatInputReplaced) return;

            if (this.#originalChatInput) {
                const textarea = domMake.Tree('textarea', {
                    id: this.#originalChatInput.id,
                    className: this.#originalChatInput.className,
                    placeholder: this.#originalChatInput.placeholder,
                    maxLength: this.#originalChatInput.maxLength
                });
                textarea.style.cssText = this.#originalChatInput.style.cssText;
                textarea.style.height = 'auto';
                textarea.style.resize = 'vertical';

                this.#originalChatInput.parentNode.replaceChild(textarea, this.#originalChatInput);
                this.#textarea = textarea;
                this.#chatInputReplaced = true;
                this.#textarea.addEventListener('input', () => this.#handleTextInput());


                this.#textarea.addEventListener('keydown', (event) => {
                    if (event.key === 'Enter' && !event.shiftKey) {
                        event.preventDefault();
                        const sendButton = document.querySelector('#chatattop-sendbutton');
                        if (sendButton) {
                            sendButton.click();
                        }
                    }
                });
            }
        }

        #restoreOriginalChatInput() {
            if (!this.#chatInputReplaced) return;

            if (this.#textarea && this.#originalChatInput && this.#textarea.parentNode) {
                this.#textarea.parentNode.replaceChild(this.#originalChatInput, this.#textarea);
                this.#textarea = null;
                this.#chatInputReplaced = false;
            }
        }

        #setupHotkeys() {
            document.addEventListener('keydown', (event) => {

                if (!this.#isModuleActive || !this.#textarea || document.activeElement !== this.#textarea) return;

                if (event.ctrlKey && event.shiftKey) {
                    const keyMap = {
                        'B': () => this.#wrapSelection('**', '**'),
                        'I': () => this.#wrapSelection('*', '*'),
                        'S': () => this.#wrapSelection('~~', '~~'),
                        'C': () => this.#clearTextarea(),
                        'F': () => this.#fillTextareaWithBlankLines(),
                        'E': () => this.#openEmojiPicker(),
                    };
                    if (keyMap[event.key.toUpperCase()]) {
                        event.preventDefault();
                        keyMap[event.key.toUpperCase()]();
                    }
                }
            });
        }

        #updateCounters() {
            if (!this.#textarea || !this.wordCountDisplay) return;
            const text = this.#textarea.value;
            const wordCount = text.trim() ? text.trim().split(/\s+/).length : 0;
            const charCount = text.length;
            this.wordCountDisplay.textContent = `Palabras: ${wordCount}`;
            this.charCountDisplay.textContent = ` | Caracteres: ${charCount}`;
        }

        #fillTextareaWithBlankLines() {
            if (!this.#textarea) return;
            this.#textarea.value = "\n".repeat(199) + "\u00AD";

            this.#textarea.dispatchEvent(new Event('input', { bubbles: true }));
        }

        #clearTextarea() {
            if (!this.#textarea) return;
            this.#textarea.value = '';
            this.#textarea.dispatchEvent(new Event('input', { bubbles: true }));
        }

        #saveDraft() {
            if (!this.#textarea) return;
            localStorage.setItem('chatDraft', this.#textarea.value);
        }

        #loadDraft() {
            if (!this.#textarea) return;
            const draft = localStorage.getItem('chatDraft');
            if (draft) {
                this.#textarea.value = draft;
            } else {

            }
            this.#textarea.dispatchEvent(new Event('input', { bubbles: true }));
        }

        #getSelection() {
            if (!this.#textarea) return null;
            return {
                start: this.#textarea.selectionStart,
                end: this.#textarea.selectionEnd,
                text: this.#textarea.value.substring(this.#textarea.selectionStart, this.#textarea.selectionEnd)
            };
        }

        #replaceSelection(newText, selectionStart, selectionEnd) {
            if (!this.#textarea) return;
            const currentText = this.#textarea.value;
            this.#textarea.value = currentText.substring(0, selectionStart) + newText + currentText.substring(selectionEnd);
            this.#textarea.selectionStart = selectionStart;
            this.#textarea.selectionEnd = selectionStart + newText.length;
            this.#textarea.focus();
            this.#textarea.dispatchEvent(new Event('input', { bubbles: true }));
        }

        #wrapSelection(prefix, suffix) {
            const sel = this.#getSelection();
            if (sel && sel.text) {
                this.#replaceSelection(prefix + sel.text + suffix, sel.start, sel.end);
            }
        }

        #setFancyStyle(style) {
            this.#selectedFancyStyle = style;
            if (style) {

                this.#handleTextInput();
            } else {




            }
        }

        #handleTextInput() {
            this.#updateCounters();

            if (!this.#selectedFancyStyle || !this.#textarea) return;

            const currentText = this.#textarea.value;
            const currentCursorPos = this.#textarea.selectionStart;

            const map = FANCY_TEXT_MAPS[this.#selectedFancyStyle];
            if (!map) return;

            let convertedText = '';
            let newCursorPos = currentCursorPos;

            for (let i = 0; i < currentText.length; i++) {
                const originalChar = currentText[i];
                const convertedChar = map[originalChar] || originalChar;

                convertedText += convertedChar;

                if (i < currentCursorPos && convertedChar.length !== originalChar.length) {
                    newCursorPos += (convertedChar.length - originalChar.length);
                }
            }

            if (this.#textarea.value === convertedText) {
                return;
            }

            const originalDispatch = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value').set;
            originalDispatch.call(this.#textarea, convertedText);

            this.#textarea.selectionStart = newCursorPos;
            this.#textarea.selectionEnd = newCursorPos;
        }

        #insertPredefinedText(text) {
            if (!this.#textarea) return;
            const sendButton = document.querySelector('#chatattop-sendbutton');
            if (!sendButton) {

                return;
            }


            let processedTxt = text.replace(/@Facemojikeyboard/g, '').trim();


            this.#textarea.value = (processedTxt ? '\n' : '') + processedTxt;

            this.#textarea.dispatchEvent(new Event('input', { bubbles: true }));
            sendButton.click();
        }

        #openEmojiPicker() {

            const existingPicker = document.getElementById('advanced-emoji-picker');
            if (existingPicker) {
                existingPicker.remove();
                return;
            }

            const picker = domMake.Tree('div', {
                id: 'advanced-emoji-picker',
                style: `
                    position: absolute;
                    bottom: 50px;
                    left: 10px;
                    width: 300px;
                    max-height: 400px;
                    background: #f9f9f9;
                    border: 1px solid #ccc;
                    border-radius: 8px;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                    z-index: 1001;
                    display: flex;
                    flex-direction: column;
                `
            });

            const header = domMake.Tree('div', { style: 'padding: 8px; border-bottom: 1px solid #ddd; display: flex; align-items: center; gap: 8px;' });
            const searchInput = domMake.Tree('input', { type: 'text', placeholder: 'Buscar emoji...', style: 'width: 100%; padding: 5px; border: 1px solid #ccc; border-radius: 4px;' });
            const closeButton = domMake.Button('×', { style: 'border: none; background: none; font-size: 20px; cursor: pointer;' });
            closeButton.onclick = () => picker.remove();
            header.appendAll(searchInput, closeButton);

            const content = domMake.Tree('div', { style: 'overflow-y: auto; padding: 8px; flex-grow: 1;' });

            Object.entries(ASCII_MOJIS).forEach(([category, emojis]) => {
                const categoryContainer = domMake.Tree('div', { 'data-category': category.toLowerCase() });
                categoryContainer.appendChild(domMake.Tree('h4', { style: 'margin: 10px 0 5px; font-size: 14px; color: #333;' }, [category]));
                const emojiGrid = domMake.Tree('div', { style: 'display: flex; flex-wrap: wrap; gap: 5px;' });
                emojis.forEach(emoji => {
                    const btn = domMake.Button(emoji, { class: 'emoji-btn', 'data-emoji': emoji.toLowerCase(), style: 'background: #fff; border: 1px solid #ddd; border-radius: 4px; padding: 5px; cursor: pointer; font-size: 16px;' });
                    btn.onclick = () => {
                        this.#insertEmoji(emoji);
                        picker.remove();
                    };
                    emojiGrid.appendChild(btn);
                });
                categoryContainer.appendChild(emojiGrid);
                content.appendChild(categoryContainer);
            });

            searchInput.oninput = () => {
                const term = searchInput.value.toLowerCase();
                content.querySelectorAll('.emoji-btn').forEach(btn => {
                    const match = btn.dataset.emoji.includes(term);
                    btn.style.display = match ? '' : 'none';
                });
            };

            picker.appendAll(header, content);
            document.body.appendChild(picker);
        }

        #insertEmoji(emoji) {
            if (!this.#textarea) return;
            const sendButton = document.querySelector('#chatattop-sendbutton');
            if (!sendButton) {

                return;
            }

            this.#textarea.value = emoji;
            this.#textarea.dispatchEvent(new Event('input', { bubbles: true }));
            sendButton.click();
        }

        #toggleSpam() {
            if (this.#spamIntervalId) {
                this.#stopSpam();
            } else {
                this.#startSpam();
            }
        }

        #startSpam() {
            const selectedOption = this.spamMessageSelect.value;
            let messageToSend;

            if (selectedOption === "random") {
                const glitchTexts = Object.values(GLITCH_TEXT_STRINGS);
                if (glitchTexts.length === 0) {

                    return;
                }
                messageToSend = glitchTexts[Math.floor(Math.random() * glitchTexts.length)];
            } else {
                messageToSend = GLITCH_TEXT_STRINGS[selectedOption];
            }

            if (!messageToSend) {

                return;
            }

            const sendButton = document.querySelector('#chatattop-sendbutton');
            if (!sendButton || !this.#originalChatInput) {

                return;
            }


            this.spamMessageSelect.disabled = true;
            this.spamButton.textContent = "Detener Spam";
            this.spamButton.classList.add('active');



            this.#spamIntervalId = setInterval(() => {

                if (selectedOption === "random") {
                    const glitchTexts = Object.values(GLITCH_TEXT_STRINGS);
                    if (glitchTexts.length === 0) {

                        this.#stopSpam();
                        return;
                    }
                    messageToSend = glitchTexts[Math.floor(Math.random() * glitchTexts.length)];
                }


                let processedMessage = messageToSend.replace(/@Facemojikeyboard/g, '').trim();
                processedMessage = (processedMessage ? '\n' : '') + processedMessage;

                if (this.#originalChatInput && sendButton) {

                    if (this.#textarea && this.#chatInputReplaced) {
                        this.#textarea.value = processedMessage;
                    } else {
                        this.#originalChatInput.value = processedMessage;
                    }
                    sendButton.click();
                } else {

                    this.#stopSpam();
                }
            }, 700);
        }

        #stopSpam() {
            if (this.#spamIntervalId) {
                clearInterval(this.#spamIntervalId);
                this.#spamIntervalId = null;



                this.spamMessageSelect.disabled = false;
                this.spamButton.textContent = "Spamear";
                this.spamButton.classList.remove('active');
            }
        }



        #getCurrentPageAsciis() {
            const start = this.#currentPage * this.#PAGE_SIZE;
            return this.#filteredAscii.slice(start, start + this.#PAGE_SIZE);
        }

        #updateAsciiPagination() {
            let total = Math.ceil(this.#filteredAscii.length / this.#PAGE_SIZE) || 1;
            this.#asciiPageInfo.textContent = ` ${this.#currentPage + 1} / ${total} `;
            this.#asciiBtnPrev.disabled = this.#currentPage === 0;
            this.#asciiBtnNext.disabled = (this.#currentPage + 1) * this.#PAGE_SIZE >= this.#filteredAscii.length;
        }

        #showPack(packNum, url) {
            this.#currentPackNum = packNum;
            this.#currentUrl = url;
            this.#currentAscii = [];
            this.#filteredAscii = [];
            this.#currentPage = 0;

            if (this.#asciiSearchInput) this.#asciiSearchInput.value = "";
            if (this.#asciiListContainer) {
                this.#asciiListContainer.innerHTML = `<div style="color:#333;margin:20px auto;text-align:center;font-size:17px">Cargando pack #${packNum}...</div>`;
            }

            if (this.#packCache[url]) {
                this.#currentAscii = this.#packCache[url].slice();
                this.#filterAsciiList("");
                return;
            }
            fetch(url)
                .then(r => r.json())
                .then(data => {
                    this.#currentAscii = data.slice();
                    this.#packCache[url] = data.slice();
                    this.#filterAsciiList("");
                })
                .catch(e => {
                    if (this.#asciiListContainer) {
                        this.#asciiListContainer.innerHTML = `<div style='color:#ff7878'>Error cargando pack</div>`;
                    }
                });
        }

        #filterAsciiList(query) {
            if (!this.#currentAscii) return;
            const q = (query || "").toLowerCase();
            this.#filteredAscii = !q ? this.#currentAscii.slice() : this.#currentAscii.filter(txt =>
                txt.toLowerCase().includes(q));
            this.#currentPage = 0;
            this.#renderAsciiList(this.#getCurrentPageAsciis());
            this.#updateAsciiPagination();
        }

        #renderAsciiList(asciiList) {
            if (!this.#asciiListContainer) return;
            this.#asciiListContainer.innerHTML = "";
            if (!asciiList.length) {
                this.#asciiListContainer.innerHTML = '<div style="color:#555;text-align:center;padding:26px">Sin resultados en esta página.</div>';
                return;
            }
            asciiList.forEach(ascii => {
                const asciiDiv = domMake.Tree('div', {
                    className: "ascii-art-entry",
                    style: `
                        background: #FFFFFF; /* Changed to white */
                        margin: 9px 0;
                        padding: 10px 10px 10px 45px;
                        border-radius: 8px;
                        box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 6px; /* Adjusted box shadow for lighter background */
                        white-space: pre;
                        position: relative;
                        font-family: monospace;
                        font-size: 1.05em;
                        cursor: pointer;
                        transition: background 0.12s;
                    `,
                    title: "Haz clic para enviar al chat"
                });


                const copyBtn = domMake.Button("📋", {
                    title: "Copiar",
                    style: `
                        position: absolute;
                        left: 10px;
                        top: 9px;
                        background: #F0F0F0; /* Lighter background for copy button */
                        color: #000; /* Black text for copy button */
                        border: 1px solid #999;
                        border-radius: 5px;
                        padding: 2px 7px;
                        font-size: 1em;
                        cursor: pointer;
                        transition: background 0.14s;
                    `
                });
                copyBtn.onmouseenter = () => copyBtn.style.background = "#E0E0E0";
                copyBtn.onmouseleave = () => copyBtn.style.background = "#F0F0F0";
                copyBtn.onclick = (e) => {
                    e.stopPropagation();
                    navigator.clipboard.writeText(ascii).then(
                        () => {
                            copyBtn.innerHTML = "✅";
                            setTimeout(() => copyBtn.innerHTML = "📋", 1000);
                        }
                    );
                };


                asciiDiv.onclick = () => this.#sendAsciiToChat(ascii);


                asciiDiv.onmouseenter = () => asciiDiv.style.background = "#F8F8F8";
                asciiDiv.onmouseleave = () => asciiDiv.style.background = "#FFFFFF";

                asciiDiv.appendChild(copyBtn);


                const pre = domMake.Tree('pre', {
                    style: `
                        margin: 0;
                        user-select: text;
                        color: #000000; /* Changed to black for better visibility */
                    `
                }, [ascii]);
                asciiDiv.appendChild(pre);

                this.#asciiListContainer.appendChild(asciiDiv);
            });
        }

        #sendAsciiToChat(txt) {
            if (!this.#textarea) return;

            const sendButton = document.querySelector('#chatattop-sendbutton');
            if (!sendButton) {

                return;
            }


            let processedTxt = txt.replace(/@Facemojikeyboard/g, '').trim();


            this.#textarea.value = (processedTxt ? '\n' : '') + processedTxt;

            this.#textarea.dispatchEvent(new Event('input', { bubbles: true }));
            sendButton.click();
        }
    }
})("QBit");


(function () {


  const QBit = window.QBit;





  function getGameSocket() {
    if (globalThis.sockets && globalThis.sockets.length > 0) {
      const gameSocket = globalThis.sockets.find(s =>
        s.url.includes("drawaria.online/socket.io") && s.readyState === WebSocket.OPEN
      );
      if (gameSocket) {
        return gameSocket;
      }
    }
    return null;
  }



  QBit.Styles.addRules([

    `#${QBit.identifier} .module-section {
        display: flex;
        flex-direction: column;
        gap: 10px;
        padding: 5px;
        border: 1px solid var(--CE-color);
        border-radius: .25rem;
        background-color: var(--CE-bg_color);
        margin-bottom: 10px; /* Space between modules */
    }`,
    `#${QBit.identifier} .module-section-title {
        font-weight: bold;
        margin-bottom: 5px;
        color: var(--dark-blue-title);
        text-align: center;
    }`,


    `#${QBit.identifier} .module-form-group label {
        display: block;
        margin-bottom: 5px;
        font-size: 0.8em;
        color: var(--CE-color);
    }`,
    `#${QBit.identifier} .module-form-control {
        width: 100%;
        padding: 5px;
        box-sizing: border-box;
        border: 1px solid var(--CE-color);
        border-radius: .25rem;
        background-color: var(--CE-bg_color);
        color: var(--CE-color);
    }`,
    `#${QBit.identifier} .module-btn-group {
        display: flex;
        gap: 5px;
    }`,
    `#${QBit.identifier} .module-btn-group .btn {
        flex: 1;
    }`,
    `#${QBit.identifier} .module-slider-container {
        margin-bottom: 10px;
    }`,
    `#${QBit.identifier} .module-slider-value {
        display: flex;
        justify-content: space-between;
        margin-bottom: 5px;
        font-size: 0.7em;
        color: var(--CE-color);
    }`,
    `#${QBit.identifier} .module-slider-container input[type="range"] {
        width: 100%;
        height: 6px;
        -webkit-appearance: none;
        border-radius: 3px;
        background: var(--light-gray-bg);
        margin: 5px 0;
    }`,
    `#${QBit.identifier} .module-slider-container input[type="range"]::-webkit-slider-thumb {
        -webkit-appearance: none;
        width: 16px;
        height: 16px;
        background: var(--info);
        border-radius: 50%;
        cursor: pointer;
    }`,
    `#${QBit.identifier} .module-status-indicator {
        display: inline-block;
        width: 10px;
        height: 10px;
        border-radius: 50%;
        margin-right: 5px;
        vertical-align: middle;
    }`,
    `#${QBit.identifier} .module-status-connected {
        background-color: var(--success);
    }`,
    `#${QBit.identifier} .module-status-disconnected {
        background-color: var(--danger);
    }`,



    `#${QBit.identifier} .artfx-section-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 8px;
    }`,
    `#${QBit.identifier} .artfx-section-header h4 {
        margin: 0;
        color: var(--info);
        font-size: 1em;
    }`,
    `#${QBit.identifier} .artfx-input-row {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        margin-bottom: 8px;
        align-items: center;
    }`,
    `#${QBit.identifier} .artfx-input-row label {
        flex-basis: 80px;
        flex-shrink: 0;
        font-size: 0.9em;
        color: var(--CE-color);
    }`,
    `#${QBit.identifier} .artfx-input-row input[type="text"],
     #${QBit.identifier} .artfx-input-row input[type="number"] {
        flex-grow: 1;
        padding: 6px;
        background-color: var(--light-gray-bg);
        border: 1px solid var(--CE-color);
        color: var(--CE-color);
        border-radius: 4px;
        box-sizing: border-box;
        min-width: 0;
    }`,
    `#${QBit.identifier} .artfx-button-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
        gap: 8px;
        margin-top: 10px;
    }`,
    `#${QBit.identifier} .artfx-button {
        padding: 8px 10px;
        background-color: var(--primary);
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        text-align: center;
        font-size: 0.85em;
        transition: background-color 0.2s;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 5px;
    }`,
    `#${QBit.identifier} .artfx-button:hover {
        background-color: var(--dark-primary);
    }`,
    `#${QBit.identifier} .artfx-button.danger {
        background-color: var(--danger);
    }`,
    `#${QBit.identifier} .artfx-button.danger:hover {
        background-color: var(--dark-danger);
    }`,
    `#${QBit.identifier} .artfx-button.special {
        background-color: var(--info);
    }`,
    `#${QBit.identifier} .artfx-button.special:hover {
        background-color: var(--dark-info);
    }`,
    `#${QBit.identifier} .artfx-clear-button {
        padding: 8px 10px;
        background-color: var(--warning);
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        text-align: center;
        font-size: 0.85em;
        transition: background-color 0.2s;
        width: 100%;
        margin-top: 8px;
    }`,
    `#${QBit.identifier} .artfx-clear-button:hover {
        background-color: var(--dark-warning);
    }`
  ]);






(function() {
    'use strict';
    const QBit = globalThis["QBit"];

    if (typeof QBit === 'undefined') {
        console.error("[Drawaria Modules] QBit no está definido. Los scripts no pueden inicializarse.");
        return;
    }


    QBit.Styles.addRules([
        `#transparentBrushControlPanel, #imageUploadControlPanel, #avatarUploadControlPanel {
            background: none;
            padding: 5px 10px;
            border-radius: 0;
            font-family: sans-serif;
            color: #fff;
            width: 100%;
            box-sizing: border-box;
            line-height: normal;
        }`,
        `#transparentBrushControlPanel hr, #imageUploadControlPanel hr, #avatarUploadControlPanel hr {
            border-color: rgba(255, 255, 255, 0.2);
            margin: 5px 0;
        }`,
        `#transparentBrushControlPanel h6, #imageUploadControlPanel h6, #avatarUploadControlPanel h6 {
            margin: 5px 0 8px;
            text-align: left;
            font-size: 0.9em;
            color: #ccc;
            line-height: normal;
        }`,

        `#transparentBrushControlPanel .transparent-brush-toggle-btn {
            width: 100%;
            padding: 6px 0;
            border: none;
            border-radius: 5px;
            color: #fff;
            font-weight: bold;
            cursor: pointer;
            background: #492;
        }`,
        `#transparentBrushControlPanel .transparent-brush-toggle-btn.active {
            background: #2a5;
        }`,
        `#transparentBrushControlPanel .slider-container {
            margin-top: 10px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            font-size: 0.9em;
            color: var(--CE-color);
        }`,
        `#transparentBrushControlPanel .slider-container label {
            margin-right: 8px;
            margin-bottom: 0;
            line-height: normal;
        }`,
        `#transparentBrushControlPanel .opacity-slider {
            flex-grow: 1;
            margin: 0 8px;
            -webkit-appearance: none;
            height: 6px;
            background: #ddd;
            outline: none;
            border-radius: 3px;
        }`,
        `#transparentBrushControlPanel .opacity-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: #666;
            cursor: grab;
        }`,
        `#transparentBrushControlPanel .opacity-slider::-moz-range-thumb {
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: #666;
            cursor: grab;
        }`,
        `#transparentBrushControlPanel .percent-display {
            display: inline-block;
            min-width: 35px;
            text-align: right;
            font-size: 0.9em;
        }`,

        `@import url('https://fonts.googleapis.com/icon?family=Material+Icons');`,
        `.material-icons {
            font-family: 'Material Icons';
            font-weight: normal;
            font-style: normal;
            line-height: 1;
            letter-spacing: normal;
            text-transform: none;
            display: inline-block;
            white-space: nowrap;
        }`,
        `#imageUploadControlPanel .upload-btn, #avatarUploadControlPanel .upload-btn {
            width: 100%;
            padding: 6px 0;
            border: none;
            border-radius: 5px;
            color: #fff;
            font-weight: bold;
            cursor: pointer;
            background: #007bff; /* Primary color */
        }`,
        `#imageUploadControlPanel .upload-btn i, #avatarUploadControlPanel .upload-btn i {
            font-size: 16px;
            vertical-align: middle;
            margin-right: 5px;
        }`,
        `#imageUploadControlPanel .upload-area, #avatarUploadControlPanel .upload-area {
            border: 2px dashed #ffffff80;
            border-radius: 5px;
            padding: 15px;
            text-align: center;
            font-size: 0.8em;
            color: #FF0000;
            margin-top: 10px;
            cursor: pointer;
            transition: border-color 0.2s ease, background-color 0.2s ease;
        }`,
        `#imageUploadControlPanel .upload-area.drag-over, #avatarUploadControlPanel .upload-area.drag-over {
            border-color: #00ff00;
            background-color: #ffffff1a;
        }`,

        `#avatarUploadControlPanel .upload-button {
            width: 100%;
            padding: 6px 0;
            border: none;
            border-radius: 5px;
            color: #fff;
            font-weight: bold;
            cursor: pointer;
            background: #007bff; /* Primary color, matching other buttons */
            text-align: center;
            display: block; /* Make it block level to take full width */
            margin-top: 10px; /* Spacing */
            box-sizing: border-box; /* Include padding/border in width */
        }`,
        `#avatarUploadControlPanel .upload-button.drag-over {
            border: 2px dashed #3ad73d !important;
            background-color: #e0ffe0 !important;
            box-shadow: 0 0 5px rgba(58, 215, 61, 0.5);
            color: #333 !important; /* Adjust text color for better contrast on light background */
        }`
    ]);


    class TransparentBrushFunctionality {
        _transparentMode = false;
        _customAlpha = 0.3;
        _ctx = null;


        _ui = {};

        constructor() {
            this._toggleTransparentMode = this._toggleTransparentMode.bind(this);
            this._updateSliderValue = this._updateSliderValue.bind(this);
            this._hookBrush = this._hookBrush.bind(this);
        }


        _createUI() {
            const targetInsertionPoint = document.getElementById('downloadcanvas');
            if (!targetInsertionPoint) {


                return false;
            }

            const wrapper = document.createElement('div');
            wrapper.id = 'transparentBrushControlPanel';

            const separator = document.createElement('hr');
            separator.style.cssText = 'border-color: rgba(255, 255, 255, 0.2); margin: 5px 0;';
            wrapper.appendChild(separator);

            const title = document.createElement('h6');
            title.style.cssText = 'margin: 5px 0 8px; text-align: left; font-size: 0.9em; color: #ccc; line-height: normal;';
            title.textContent = 'Pincel Transparente';
            wrapper.appendChild(title);

            this._ui.toggleBtn = document.createElement('button');
            this._ui.toggleBtn.className = 'btn btn-block btn-sm transparent-brush-toggle-btn';
            this._ui.toggleBtn.style.cssText = 'width: 100%; padding: 6px 0; border: none; border-radius: 5px; color: #fff; font-weight: bold; cursor: pointer; background: #492;';
            this._ui.toggleBtn.textContent = 'Pincel: OFF';
            this._ui.toggleBtn.addEventListener('click', this._toggleTransparentMode);
            wrapper.appendChild(this._ui.toggleBtn);

            const sliderContainer = document.createElement('div');
            sliderContainer.className = 'slider-container';
            sliderContainer.style.cssText = 'margin-top: 10px; display: flex; align-items: center; justify-content: space-between; font-size: 0.9em; color: var(--CE-color);';

            const label = document.createElement('label');
            label.style.cssText = 'margin-right: 8px; margin-bottom: 0; line-height: normal;';
            label.textContent = 'Opacidad:';
            this._ui.slider = document.createElement('input');
            this._ui.slider.type = 'range';
            this._ui.slider.min = '0.05';
            this._ui.slider.max = '1';
            this._ui.slider.step = '0.01';
            this._ui.slider.value = '0.3';
            this._ui.slider.className = 'opacity-slider';
            this._ui.slider.style.cssText = 'flex-grow: 1; margin: 0 8px; -webkit-appearance: none; height: 6px; background: #ddd; outline: none; border-radius: 3px;';
            this._ui.slider.addEventListener('input', this._updateSliderValue);
            this._ui.percent = document.createElement('span');
            this._ui.percent.className = 'percent-display';
            this._ui.percent.style.cssText = 'display: inline-block; min-width: 35px; text-align: right; font-size: 0.9em;';
            this._ui.percent.textContent = '30%';

            sliderContainer.append(label, this._ui.slider, this._ui.percent);
            wrapper.appendChild(sliderContainer);

            targetInsertionPoint.parentNode.insertBefore(wrapper, targetInsertionPoint.nextSibling);

            if (typeof QBit !== 'undefined' && QBit.notify) {
                QBit.notify("info", "Panel de control de Pincel Transparente insertado en el menú.");
            } else {
                console.log("[Drawaria Transparency Script] Panel de control insertado en el menú.");
            }


            this._tryHookCanvas();
            return true;
        }


        _toggleTransparentMode() {
            this._transparentMode = !this._transparentMode;
            this._ui.toggleBtn.textContent = this._transparentMode ? 'Pincel: ON' : 'Pincel: OFF';
            this._ui.toggleBtn.classList.toggle('active', this._transparentMode);
            if (typeof QBit !== 'undefined' && QBit.notify) {
                QBit.notify("info", `Pincel transparente: ${this._transparentMode ? 'ON' : 'OFF'}`);
            } else {
                console.log(`[Drawaria Transparency Script] Pincel transparente: ${this._transparentMode ? 'ON' : 'OFF'}`);
            }
        }

        _updateSliderValue() {
            this._customAlpha = parseFloat(this._ui.slider.value);
            this._ui.percent.textContent = Math.round(this._customAlpha * 100) + "%";

        }


        _hookBrush() {
            const canvas = document.querySelector('canvas');
            if (!canvas) {
                return false;
            }
            if (!canvas.getContext) {
                return false;
            }

            this._ctx = canvas.getContext('2d');
            if (!this._ctx) {
                return false;
            }
            if (this._ctx._pincelTransparenteHooked) {
                return true;
            }

            const origStroke = this._ctx.stroke;
            const origFill = this._ctx.fill;
            const self = this;

            this._ctx.stroke = function() {
                let prevAlpha = this.globalAlpha;
                if (self._transparentMode) {
                    this.globalAlpha = self._customAlpha;
                }
                origStroke.apply(this, arguments);
                this.globalAlpha = prevAlpha;
            };

            this._ctx.fill = function() {
                let prevAlpha = this.globalAlpha;
                if (self._transparentMode) {
                    this.globalAlpha = self._customAlpha;
                }
                origFill.apply(this, arguments);
                this.globalAlpha = prevAlpha;
            };

            this._ctx._pincelTransparenteHooked = true;
            if (typeof QBit !== 'undefined' && QBit.notify) {
                QBit.notify("info", "Canvas context enganchado exitosamente!");
            } else {
                console.log("[Drawaria Transparency Script] Canvas context enganchado exitosamente!");
            }
            return true;
        }

        _tryHookCanvas() {
            let hookAttempts = 0;
            const maxHookAttempts = 60;

            const attemptHook = () => {
                if (!this._hookBrush()) {
                    hookAttempts++;
                    if (hookAttempts < maxHookAttempts) {
                        setTimeout(attemptHook, 1000);
                    } else {
                        if (typeof QBit !== 'undefined' && QBit.notify) {
                            QBit.notify("error", "No se pudo enganchar el canvas después de varios intentos. Asegúrate de estar en Drawaria.online.");
                        } else {
                            console.error("[Drawaria Transparency Script] No se pudo enganchar el canvas después de varios intentos. Asegúrate de estar en Drawaria.online.");
                        }
                    }
                }
            };

            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', attemptHook);
            } else {
                attemptHook();
            }
        }
    }


    class ImageUploadModule {
        _mainCanvas = null;
        _ctx = null;
        _ui = {};

        constructor() {

        }


        _createUI() {
            this._mainCanvas = document.getElementById('canvas');
            if (this._mainCanvas) {
                this._ctx = this._mainCanvas.getContext('2d');
            } else {
                console.warn("[Drawaria Image Upload] Canvas principal no encontrado al inicio. Algunas funciones pueden no estar disponibles.");
            }


            this._resizeCanvas();


            const transparentBrushPanel = document.getElementById('transparentBrushControlPanel');
            const targetInsertionPoint = transparentBrushPanel || document.getElementById('downloadcanvas');

            if (!targetInsertionPoint) {


                return false;
            }

            const wrapper = document.createElement('div');
            wrapper.id = 'imageUploadControlPanel';

            const separator = document.createElement('hr');
            separator.style.cssText = 'border-color: rgba(255, 255, 255, 0.2); margin: 5px 0;';
            wrapper.appendChild(separator);

            const title = document.createElement('h6');
            title.style.cssText = 'margin: 5px 0 8px; text-align: left; font-size: 0.9em; color: #ccc; line-height: normal;';
            title.textContent = 'Subir Imagen (Dibujo)';
            wrapper.appendChild(title);

            this._ui.uploadBtn = document.createElement('button');
            this._ui.uploadBtn.className = 'btn btn-block btn-sm upload-btn';
            this._ui.uploadBtn.innerHTML = '<i class="fas fa-upload"></i><span>Subir Imagen</span>';
            this._ui.uploadBtn.addEventListener('click', () => this._triggerFileUpload());
            wrapper.appendChild(this._ui.uploadBtn);

            const dragDropArea = document.createElement('div');
            dragDropArea.className = 'upload-area';
            dragDropArea.textContent = 'O arrastra una imagen aquí (imagen de internet)';
            dragDropArea.addEventListener('dragover', this._handleDragOver.bind(this));
            dragDropArea.addEventListener('drop', this._handleDrop.bind(this));
            this._ui.dragDropArea = dragDropArea;
            wrapper.appendChild(dragDropArea);



            const nextSiblingElement = document.getElementById('downloadsavedcanvas');
            if (nextSiblingElement && nextSiblingElement.parentNode) {
                nextSiblingElement.parentNode.insertBefore(wrapper, nextSiblingElement);
            } else {

                targetInsertionPoint.parentNode.insertBefore(wrapper, targetInsertionPoint.nextSibling);
                console.warn("[Drawaria Image Upload] Could not find #downloadsavedcanvas, falling back to inserting after transparentBrushControlPanel/downloadcanvas.");
            }

            if (typeof QBit !== 'undefined' && QBit.notify) {
                QBit.notify("info", "Panel de Subida de Imagen insertado en el menú.");
            } else {
                console.log("[Drawaria Image Upload] Panel de Subida de Imagen insertado en el menú.");
            }


            this._setupDragAndDrop();
            return true;
        }

        _resizeCanvas() {
            if (this._mainCanvas) {
                this._mainCanvas.height = 650;
                this._mainCanvas.width = 780;
                if (typeof QBit !== 'undefined' && QBit.notify) {
                    QBit.notify("info", `Canvas redimensionado a ${this._mainCanvas.width}x${this._mainCanvas.height}.`);
                }
            }
        }

        _triggerFileUpload() {
            let input = document.createElement('input');
            input.type = 'file';
            input.accept = 'image/*';
            input.onchange = () => {
                let file = input.files[0];
                if (file) {
                    this._handleFileSelect(file);
                }
            };
            input.click();
        }

        _handleFileSelect(file) {
            let reader = new FileReader();
            reader.onload = (e) => {
                let img = new Image();
                img.onload = () => {
                    this._handleImageLoad(img, this._mainCanvas);
                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("success", "Imagen cargada y dibujada en el canvas.");
                    }
                };
                img.onerror = () => {
                     if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("error", "Error al cargar el archivo de imagen.");
                    }
                };
                img.src = e.target.result;
            };
            reader.readAsDataURL(file);
        }

        _handleImageLoad(img, targetCanvas) {
            if (!targetCanvas || !this._ctx) {
                if (typeof QBit !== 'undefined' && QBit.notify) {
                    QBit.notify("error", "Canvas no disponible para dibujar la imagen.");
                }
                return;
            }
            let ctx = this._ctx;
            ctx.clearRect(0, 0, targetCanvas.width, targetCanvas.height);

            let scaleX = targetCanvas.width / img.width;
            let scaleY = targetCanvas.height / img.height;
            let scale = Math.min(scaleX, scaleY);

            let xOffset = (targetCanvas.width - img.width * scale) / 2;
            let yOffset = (targetCanvas.height - img.height * scale) / 2;

            ctx.drawImage(img, xOffset, yOffset, img.width * scale, img.height * scale);
        }

        _setupDragAndDrop() {

            if (this._mainCanvas) {
                this._mainCanvas.addEventListener('dragover', this._handleDragOver.bind(this));
                this._mainCanvas.addEventListener('drop', this._handleDrop.bind(this));


                if (this._ui.dragDropArea) {
                    this._ui.dragDropArea.addEventListener('dragenter', (e) => {
                        e.preventDefault();
                        this._ui.dragDropArea.classList.add('drag-over');
                    });
                    this._ui.dragDropArea.addEventListener('dragleave', (e) => {
                        e.preventDefault();
                        this._ui.dragDropArea.classList.remove('drag-over');
                    });
                    this._ui.dragDropArea.addEventListener('drop', (e) => {
                        this._ui.dragDropArea.classList.remove('drag-over');
                    });
                }
            } else {
                console.warn("[Drawaria Image Upload] Canvas principal no disponible para configurar Drag & Drop.");
            }
        }

        _handleDragOver(event) {
            event.preventDefault();
            event.dataTransfer.dropEffect = 'copy';
        }

        async _handleDrop(event) {
            event.preventDefault();

            if (this._ui.dragDropArea) {
                 this._ui.dragDropArea.classList.remove('drag-over');
            }


            if (event.dataTransfer.files && event.dataTransfer.files.length > 0) {

                const file = event.dataTransfer.files[0];
                if (file.type.startsWith('image/')) {
                    this._handleFileSelect(file);
                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("info", `Archivo de imagen '${file.name}' arrastrado.`);
                    }
                } else {
                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("warning", "El archivo arrastrado no es una imagen.");
                    }
                }
            } else if (event.dataTransfer.getData('text/uri-list')) {

                const imageUrl = event.dataTransfer.getData('text/uri-list');
                this._loadImageFromUrl(imageUrl);
                if (typeof QBit !== 'undefined' && QBit.notify) {
                    QBit.notify("info", `URL de imagen '${imageUrl.substring(0,50)}...' arrastrada.`);
                }
            } else if (event.dataTransfer.getData('text/html')) {

                const html = event.dataTransfer.getData('text/html');
                const parser = new DOMParser();
                const doc = parser.parseFromString(html, 'text/html');
                const img = doc.querySelector('img');
                if (img && img.src) {
                    this._loadImageFromUrl(img.src);
                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("info", `Imagen HTML arrastrada desde '${img.src.substring(0,50)}...'.`);
                    }
                } else {
                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("warning", "Contenido arrastrado no es un archivo de imagen ni una URL de imagen directamente.");
                    }
                }
            } else {
                if (typeof QBit !== 'undefined' && QBit.notify) {
                    QBit.notify("warning", "Tipo de contenido arrastrado no soportado.");
                }
            }
        }

        async _loadImageFromUrl(url) {
            try {

                const response = await fetch(url, { mode: 'cors' });
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                const blob = await response.blob();
                if (!blob.type.startsWith('image/')) {
                    throw new Error('La URL no apunta a un tipo de imagen válido.');
                }

                const reader = new FileReader();
                reader.onload = (e) => {
                    let img = new Image();
                    img.onload = () => {
                        this._handleImageLoad(img, this._mainCanvas);
                        if (typeof QBit !== 'undefined' && QBit.notify) {
                            QBit.notify("success", "Imagen de URL cargada y dibujada.");
                        }
                    };
                    img.onerror = () => {
                        if (typeof QBit !== 'undefined' && QBit.notify) {
                            QBit.notify("error", "Error al cargar la imagen desde la URL. Puede ser un problema de CORS.");
                        }
                    };
                    img.src = e.target.result;
                };
                reader.onerror = (e) => {
                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("error", `Error al leer la URL como blob: ${e.message}`);
                    }
                };
                reader.readAsDataURL(blob);

            } catch (error) {
                if (typeof QBit !== 'undefined' && QBit.notify) {
                    QBit.notify("error", `Fallo al cargar imagen desde URL: ${error.message}. Puede ser un problema de CORS.`);
                } else {
                     console.error(`[Drawaria Image Upload] Fallo al cargar imagen desde URL: ${error.message}. Puede ser un problema de CORS.`, error);
                }
            }
        }
    }


    class AvatarUploadModule {
        _ui = {};

        constructor() {

            this._processImageFile = this._processImageFile.bind(this);
            this._handleDragOver = this._handleDragOver.bind(this);
            this._handleDragLeave = this._handleDragLeave.bind(this);
            this._handleDrop = this._handleDrop.bind(this);
        }


        _createUI() {


            if (!window.location.pathname.includes('/avatar/builder/')) {


                return false;
            }



            const headerElement = document.querySelector('header');
            if (!headerElement) {
                console.warn("[Drawaria Avatar Upload] Header element not found, cannot insert UI.");
                return false;
            }


            const wrapper = document.createElement('div');
            wrapper.id = 'avatarUploadControlPanel';



            const separator = document.createElement('hr');

            separator.style.cssText = 'border-color: rgba(255, 255, 255, 0.2); margin: 5px 0;';
            wrapper.appendChild(separator);


            const title = document.createElement('h6');
            title.style.cssText = 'margin: 5px 0 8px; text-align: left; font-size: 0.9em; color: #ccc; line-height: normal;';
            title.textContent = 'Subir Avatar';
            wrapper.appendChild(title);


            this._ui.imageInput = document.createElement('input');
            this._ui.imageInput.type = 'file';
            this._ui.imageInput.accept = 'image/jpeg, image/png, image/webp, image/gif';
            this._ui.imageInput.style.display = 'none';
            this._ui.imageInput.id = 'avatarImageInput';


            this._ui.uploadButton = document.createElement('label');
            this._ui.uploadButton.className = 'upload-button';
            this._ui.uploadButton.setAttribute('for', 'avatarImageInput');
            this._ui.uploadButton.textContent = 'Subir Imagen';


            wrapper.appendChild(this._ui.imageInput);
            wrapper.appendChild(this._ui.uploadButton);


            this._ui.imageInput.addEventListener('change', (event) => {
                const file = event.target.files[0];
                this._processImageFile(file);
            });
            this._ui.uploadButton.addEventListener('dragover', this._handleDragOver);
            this._ui.uploadButton.addEventListener('dragleave', this._handleDragLeave);
            this._ui.uploadButton.addEventListener('drop', this._handleDrop);


            headerElement.appendChild(wrapper);


            if (typeof QBit !== 'undefined' && QBit.notify) {
                QBit.notify("info", "Panel de Subida de Avatar insertado.");
            } else {
                console.log("[Drawaria Avatar Upload] Panel de Subida de Avatar insertado.");
            }
            return true;
        }


        async _processImageFile(file) {
            if (!file) {
                return;
            }


            if (!file.type.startsWith('image/')) {
                if (typeof QBit !== 'undefined' && QBit.notify) {
                    QBit.notify("error", "Por favor, arrastra o selecciona un archivo de imagen (JPEG, PNG, WebP, GIF).");
                } else {
                    alert('Please drop an image file (JPEG, PNG, WebP, GIF).');
                }
                this._ui.imageInput.value = '';
                return;
            }

            const reader = new FileReader();
            reader.addEventListener('load', async () => {

                const uploadedImage = reader.result.replace(/^data:[^;]+;/, 'data:image/jpeg;');
                const button = this._ui.uploadButton;


                button.textContent = 'Subiendo...';
                button.style.pointerEvents = 'none';

                try {

                    const xhr = new XMLHttpRequest();
                    xhr.open('POST', window.LOGGEDIN ? '/saveavatar' : '/uploadavatarimage', true);
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');


                    xhr.upload.addEventListener('progress', (evt) => {
                        if (evt.lengthComputable) {
                            const percentComplete = (evt.loaded / evt.total) * 100;

                            button.style.background = `linear-gradient(180deg, #f6f9fc 85%, transparent 15%), linear-gradient(90deg, #3ad73d ${percentComplete.toFixed(0)}%, #808386 0%`;
                            button.style.color = '#333';
                        }
                    }, false);


                    const postData = new URLSearchParams();

                    postData.append('avatarsave_builder', JSON.stringify(window.ACCOUNT_AVATARSAVE));
                    postData.append('imagedata', uploadedImage);
                    postData.append('fromeditor', 'true');


                    const responseText = await new Promise((resolve, reject) => {
                        xhr.onload = () => {
                            if (xhr.status >= 200 && xhr.status < 300) {
                                resolve(xhr.responseText);
                            } else {
                                reject(new Error(xhr.responseText || xhr.statusText || `HTTP error! status: ${xhr.status}`));
                            }
                        };
                        xhr.onerror = () => reject(new Error('Network error during avatar upload.'));
                        xhr.send(postData.toString());
                    });


                    button.textContent = 'Guardando...';
                    button.style.pointerEvents = 'none';



                    const imageHash = responseText.endsWith('.jpg') ? responseText.slice(0, -4) : responseText;
                    await fetch(`${window.location.origin}/avatar/cache/${imageHash}.jpg`, { method: 'GET', mode: 'cors', cache: 'reload' });


                    button.textContent = '¡Guardado OK!';
                    button.removeAttribute('style');
                    this._ui.imageInput.value = '';


                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("success", "Avatar cargado y guardado exitosamente!");
                    }

                    window.location.href = new URL(window.location.href).origin;

                } catch (error) {

                    button.textContent = 'Subir Imagen';
                    button.removeAttribute('style');
                    this._ui.imageInput.value = '';
                    if (typeof QBit !== 'undefined' && QBit.notify) {
                        QBit.notify("error", `Error al subir avatar: ${error.message}`);
                    } else {
                        alert(`An error occurred during avatar upload: ${error.message}`);
                    }
                }
            });

            reader.readAsDataURL(file);
        }


        _handleDragOver(event) {
            event.preventDefault();
            event.stopPropagation();
            event.currentTarget.classList.add('drag-over');
            event.dataTransfer.dropEffect = 'copy';
        }


        _handleDragLeave(event) {
            event.preventDefault();
            event.stopPropagation();
            event.currentTarget.classList.remove('drag-over');
        }


        _handleDrop(event) {
            event.preventDefault();
            event.stopPropagation();
            event.currentTarget.classList.remove('drag-over');

            if (event.dataTransfer.files && event.dataTransfer.files.length > 0) {
                const file = event.dataTransfer.files[0];
                this._processImageFile(file);
            } else {

                if (typeof QBit !== 'undefined' && QBit.notify) {
                    QBit.notify("warning", "Contenido arrastrado no es un archivo de imagen. Por favor, arrastra un archivo de imagen directamente.");
                }
            }
        }
    }




    window._cubeEngineDomModules = window._cubeEngineDomModules || [];


    window._cubeEngineDomModules.push({
        name: "TransparentBrush",
        instance: new TransparentBrushFunctionality(),
        priority: 10
    });


    window._cubeEngineDomModules.push({
        name: "ImageUpload",
        instance: new ImageUploadModule(),
        priority: 20
    });


    window._cubeEngineDomModules.push({
        name: "AvatarUpload",
        instance: new AvatarUploadModule(),
        priority: 30
    });




    if (!window._domInsertionObserversInitialized) {
        window._domInsertionObserversInitialized = true;

        const initDOMModulesWhenReady = () => {



            const downloadCanvasBtn = document.getElementById('downloadcanvas');
            const headerElement = document.querySelector('header');


            const isAnyTargetPresent = downloadCanvasBtn || (headerElement && window.location.pathname.includes('/avatar/builder/'));

            if (isAnyTargetPresent) {

                window._cubeEngineDomModules.sort((a, b) => a.priority - b.priority);

                let allApplicableUIsCreated = true;


                window._cubeEngineDomModules.forEach(moduleInfo => {
                    const instance = moduleInfo.instance;

                    if (!moduleInfo.uiCreated) {
                        if (instance && typeof instance._createUI === 'function') {
                            const uiCreatedSuccessfully = instance._createUI();
                            if (uiCreatedSuccessfully) {
                                moduleInfo.uiCreated = true;
                            } else {



                                if (moduleInfo.name === "AvatarUpload" && window.location.pathname.includes('/avatar/builder/')) {
                                    allApplicableUIsCreated = false;
                                } else if (moduleInfo.name !== "AvatarUpload" && window.location.pathname.includes('/room/')) {
                                    allApplicableUIsCreated = false;
                                }
                            }
                        } else {
                            console.warn(`[Global DOM Initializer] Module instance for ${moduleInfo.name} not found or _createUI not callable.`);
                            allApplicableUIsCreated = false;
                        }
                    }
                });

                if (!allApplicableUIsCreated) {


                    setTimeout(initDOMModulesWhenReady, 500);
                } else {

                    console.log("[Global DOM Initializer] All applicable registered DOM UIs attempted creation.");
                }

            } else {


                setTimeout(initDOMModulesWhenReady, 500);
            }
        };


        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initDOMModulesWhenReady);
        } else {
            initDOMModulesWhenReady();
        }
    }

})();



const fixCursorCSS = document.createElement('style');
fixCursorCSS.textContent = `
    button, a, [role="button"], .dropdown-item, label[for], input[type="file"], .no-drag {
        cursor: pointer !important;
    }
`;
document.head.appendChild(fixCursorCSS);




(function() {
    'use strict';


    (function() {
        if (!document.querySelector('link[href*="fontawesome"]')) {
            const fa = document.createElement('link');
            fa.rel = 'stylesheet';
            fa.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css';
            document.head.appendChild(fa);
        }
    })();


    function makeDraggable(element, handleElement = element) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        handleElement.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;

            if (e.button !== 0) return;


            if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.tagName === 'SELECT' || e.target.tagName === 'TEXTAREA' || e.target.closest('.no-drag')) {
                return;
            }

            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();

            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;

            element.style.top = (element.offsetTop - pos2) + "px";
            element.style.left = (element.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }


    let simpleGradientPanel, simpleGradientEffect, avatarPanel, pixelationPanel, avatarCopyPanel;
    let observer;
    let isPixelated = false;


    function hideAllPanels() {
        [simpleGradientPanel, avatarPanel, pixelationPanel, avatarCopyPanel].forEach(panel => {
            if (panel) {
                panel.style.display = 'none';
            }
        });

        if (simpleGradientEffect) {
            simpleGradientEffect.style.opacity = 0;
        }
    }


    function hideDefaultAvatarButtons() {
        ['openAvatarButton', 'downloadIconButton', 'avatarRandomizerButton'].forEach(function(id) {
            var btn = document.getElementById(id);
            if (btn) btn.remove();
        });
    }


    function observeAvatarButtons() {
        const midcol = document.getElementById('login-midcol');
        if (!midcol) return;
        const observer = new MutationObserver(function() {
            hideDefaultAvatarButtons();
        });
        observer.observe(midcol, { childList: true, subtree: true });
        hideDefaultAvatarButtons();
    }
    observeAvatarButtons();



    function hideSocButtons() {
        var socButtons = document.getElementById('socbuttons');
        if (socButtons) {
            socButtons.style.display = 'none';
        }
    }


    function removeDiscordButton() {
        var discordButton = document.getElementById('discordprombox');
        if (discordButton) {
            discordButton.style.display = 'none';
        }
    }


    function stars() {
        var existingStarsContainer = document.getElementById('starsContainer');
        if (existingStarsContainer) {
            existingStarsContainer.remove();
        }

        var starsContainer = document.createElement('div');
        starsContainer.id = 'starsContainer';
        starsContainer.style.position = 'fixed';
        starsContainer.style.top = '0';
        starsContainer.style.left = '0';
        starsContainer.style.width = '100%';
        starsContainer.style.height = '100%';
        starsContainer.style.zIndex = '-1';
        starsContainer.style.background = 'black';
        document.body.appendChild(starsContainer);

        for (var i = 0; i < 500; i++) {
            var star = document.createElement('div');
            star.className = 'star';
            star.style.position = 'absolute';
            star.style.width = '2px';
            star.style.height = '2px';
            star.style.background = 'white';
            star.style.borderRadius = '50%';
            star.style.boxShadow = '0 0 10px white';
            star.style.top = Math.random() * document.body.clientHeight + 'px';
            star.style.left = Math.random() * document.body.clientWidth + 'px';
            starsContainer.appendChild(star);

            hideSocButtons();
            window.addEventListener('resize', makeLoginboxBlack);
            makeElementsBlack();
            makeLoginboxBlack();
            removeDiscordButton();
        }
    }


    function makeLoginboxBlack() {
        var loginbox = document.querySelector('.loginbox');
        if (loginbox) {
            var elements = loginbox.querySelectorAll('*');
            elements.forEach(function(element) {
                element.style.color = 'white';
                element.style.backgroundColor = 'black';
                element.style.borderColor = 'white';
                loginbox.style.borderRadius = '1px';
                loginbox.style.background = '#000000';
            });
        }
    }


    function makeElementsBlack() {
        var loginMidCol = document.getElementById('login-midcol');
        if (loginMidCol) {
            var elements = loginMidCol.querySelectorAll('*');
            elements.forEach(function(element) {
                element.style.color = 'white';
                element.style.backgroundColor = 'black';
                element.style.borderColor = 'white';
                loginMidCol.style.background = '#000000';
            });
        }
    }


    var modsMenuContainer = document.createElement('div');
    modsMenuContainer.className = 'dropdown d-inline';
    modsMenuContainer.style.marginLeft = '10px';


    var modsMenuButton = document.createElement('button');
    modsMenuButton.className = 'btn btn-primary dropdown-toggle';
    modsMenuButton.type = 'button';
    modsMenuButton.id = 'modsMenuButton';
    modsMenuButton.textContent = 'Custom Options';
    modsMenuButton.setAttribute('data-toggle', 'dropdown');
    modsMenuButton.setAttribute('aria-haspopup', 'true');
    modsMenuButton.setAttribute('aria-expanded', 'false');


    var modsMenuDropdown = document.createElement('div');
    modsMenuDropdown.className = 'dropdown-menu';
    modsMenuDropdown.setAttribute('aria-labelledby', 'modsMenuButton');


    function playSong() {
        var input = document.createElement('input');
        input.type = 'file';
        input.accept = '.mp3';
        input.onchange = function(e) {
            var file = e.target.files[0];
            if (file) {
                var audio = new Audio(URL.createObjectURL(file));
                audio.loop = true;
                audio.play();
            }
        };
        input.click();
    }


    function fetchJson(url) {
        return new Promise((resolve, reject) => {

            GM.xmlHttpRequest({
                method: "GET",
                url: url + '?v=' + new Date().getTime(),
                onload: function(res) {
                    if (res.status === 200) {
                        try {
                            let parsedData = JSON.parse(res.responseText);
                            if (typeof parsedData === 'string') {
                                parsedData = JSON.parse(parsedData);
                            }
                            resolve(parsedData);
                        } catch (e) {
                            reject(new Error('Failed to parse JSON response: ' + e.message + ` (Raw: ${res.responseText.substring(0, 100)}...)`));
                        }
                    } else {
                        reject(new Error(`Network response not OK, status: ${res.status} ${res.statusText}`));
                    }
                },
                onerror: function(err) {
                    reject(new Error('Network request failed: ' + err.statusText || 'Unknown error'));
                }
            });
        });
    }


    async function joinRandomRoom() {
        try {
            const roomData = await fetchJson('https://drawaria.online/getroomlist');
            if (!Array.isArray(roomData)) {
                throw new Error('La respuesta no es un array de salas válido.');
            }

            const availableRooms = roomData.filter(roomArray => {
                const currentPlayers = roomArray[1];
                const maxPlayers = roomArray[2];
                const flags = roomArray[6];
                const isPasswordProtected = flags && flags.length > 0 && flags[0] === true;
                return currentPlayers < maxPlayers && !isPasswordProtected;
            });

            if (availableRooms.length === 0) {
                alert('No se encontraron salas disponibles para unirse.');
                console.log('No available rooms found.');
                return;
            }

            const randomIndex = Math.floor(Math.random() * availableRooms.length);
            const selectedRoom = availableRooms[randomIndex];
            const roomId = selectedRoom[0];
            const serverId = selectedRoom[8];

            let fullRoomIdentifier = roomId;
            if (serverId !== null && serverId !== undefined && !String(roomId).includes(`.${serverId}`)) {
                fullRoomIdentifier = `${roomId}.${serverId}`;
            }
            const roomUrl = `https://drawaria.online/room/${fullRoomIdentifier}`;
            console.log(`Joining room: ${roomUrl}`);
            window.location.assign(roomUrl);
        } catch (error) {
            alert(`Error al unirse a una sala aleatoria: ${error.message}`);
            console.error("Error joining random room:", error);
        }
    }


    function replaceLogo() {
        var logoImage = document.querySelector('.img-fluid');
        if (logoImage) {
            var input = document.createElement('input');
            input.type = 'file';
            input.accept = 'image/png, image/jpeg';
            input.onchange = function(e) {
                var file = e.target.files[0];
                if (file) {
                    var reader = new FileReader();
                    reader.onload = function(e) {
                        logoImage.src = e.target.result;
                    };
                    reader.readAsDataURL(file);
                }
            };
            input.click();
        }
    }


    const THEME_KEY = 'drawaria_dark_theme_enabled';
    const CSS_ID = 'drawaria-darktheme-style-tag';
    const JSON_URL_DARK_THEME = 'https://raw.githubusercontent.com/NuevoMundoOficial/Drawaria-CanvasPic/main/DarkTheme.json';
    let darkThemeCssContent = null;

    async function loadCssFromJSON() {
        if (darkThemeCssContent) {
            console.log('DM Script: CSS already loaded from JSON. Reusing.');
            return darkThemeCssContent;
        }
        console.log('DM Script: Starting CSS load from JSON:', JSON_URL_DARK_THEME);
        try {
            const response = await fetchJson(JSON_URL_DARK_THEME);
            darkThemeCssContent = response.css;
            console.log('DM Script: CSS successfully loaded and parsed from JSON.');
            return darkThemeCssContent;
        } catch (error) {
            console.error('DM Script: Error loading or parsing JSON:', error);
            darkThemeCssContent = null;
            return null;
        }
    }

    async function setDarkTheme(enabled) {
        let styleTag = document.getElementById(CSS_ID);
        if (enabled) {
            if (!styleTag) {
                const cssContent = await loadCssFromJSON();
                if (!cssContent) {
                    console.error('DM Script: Could not get CSS content to apply dark theme. Operation cancelled.');
                    return;
                }
                styleTag = document.createElement('style');
                styleTag.type = 'text/css';
                styleTag.id = CSS_ID;
                styleTag.textContent = cssContent;
                document.head.appendChild(styleTag);
                console.log('DM Script: Dark theme <style> tag added to <head>.');
            } else {
                console.log('DM Script: <style> tag already exists, dark theme is already applied or ready.');
            }
        } else {
            if (styleTag) {
                styleTag.remove();
                console.log('DM Script: Dark theme <style> tag removed from <head>.');
            } else {
                console.log('DM Script: <style> tag does not exist, dark theme is already disabled.');
            }
        }
        localStorage.setItem(THEME_KEY, enabled ? '1' : '0');
    }

    function isDarkThemeEnabled() {
        return localStorage.getItem(THEME_KEY) === '1';
    }

    async function toggleDarkTheme() {
        const currentlyEnabled = isDarkThemeEnabled();
        await setDarkTheme(!currentlyEnabled);
    }



const AUTO_SETTINGS_KEY = 'drawaria_auto_room_settings';
let hasAutoExecuted = false;


function getAutoSettingsState() {
    const saved = localStorage.getItem(AUTO_SETTINGS_KEY);
    return saved ? JSON.parse(saved) : {
        enabled: false,
        avatarSpawn: true,
        privateRoom: true,
        autoCreate: true
    };
}


function saveAutoSettingsState(settings) {
    localStorage.setItem(AUTO_SETTINGS_KEY, JSON.stringify(settings));
}


function enableAvatarSpawn() {
    const checkbox = document.getElementById('avatarspawnwnabled');
    if (checkbox && !checkbox.checked) {
        checkbox.checked = true;
        checkbox.dispatchEvent(new Event('change', { bubbles: true }));
        console.log('Avatar spawn habilitado automáticamente');
        return true;
    }
    return checkbox && checkbox.checked;
}

function selectPrivateRoom() {
    const radioButton = document.getElementById('customRadio1');
    if (radioButton && !radioButton.checked) {
        radioButton.checked = true;
        radioButton.dispatchEvent(new Event('change', { bubbles: true }));
        console.log('Private room seleccionado automáticamente');
        return true;
    }
    return radioButton && radioButton.checked;
}

function clickCreateRoomButton() {
    const createButton = document.querySelector('button[type="submit"].btn.btn-primary.btn-warning.btn-lg');
    if (createButton && createButton.textContent.trim() === 'Create Room') {
        createButton.click();
        console.log('Create Room button clicked automatically');
        return true;
    }
    return false;
}

function executeAutoRoomSettings() {
    if (hasAutoExecuted) return;

    const settings = getAutoSettingsState();
    if (!settings.enabled) return;

    let actionsCompleted = 0;
    let totalActions = 0;

    if (settings.avatarSpawn) {
        totalActions++;
        if (enableAvatarSpawn()) actionsCompleted++;
    }

    if (settings.privateRoom) {
        totalActions++;
        if (selectPrivateRoom()) actionsCompleted++;
    }

    if (settings.autoCreate && actionsCompleted === totalActions && totalActions > 0) {
        setTimeout(() => {
            clickCreateRoomButton();
            hasAutoExecuted = true;
            console.log('Auto room settings completed.');
        }, 500);
    }
}

function addAutoSettingsTriggerListener() {
    const triggerButton = document.getElementById('createroom');
    if (triggerButton && !triggerButton.hasAttribute('data-auto-listener')) {
        triggerButton.addEventListener('click', function() {
            console.log('Create room button pressed. Executing auto-settings...');
            setTimeout(executeAutoRoomSettings, 100);
        });
        triggerButton.setAttribute('data-auto-listener', 'true');
        console.log('Auto-settings listener added to create room button');
    }
}


function createAutoRoomSettingsPanel() {
    if (document.getElementById('autoRoomSettingsPanel')) return;

    const settings = getAutoSettingsState();

    const panel = document.createElement('div');
    panel.id = 'autoRoomSettingsPanel';
    panel.style.cssText = `
    display: none;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #ffffff;
    border: 2px solid #e0e0e0;
    padding: 25px;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    z-index: 10000;
    min-width: 350px;
    color: #333333;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
`;

    panel.innerHTML = `
    <h3 style="
        margin: 0 0 20px 0;
        text-align: center;
        color: #2c3e50;
        font-weight: normal;
        font-size: 18px;
        border-bottom: 1px solid #ecf0f1;
        padding-bottom: 10px;
    ">
        🎮 Auto Room Settings
    </h3>

    <div style="margin-bottom: 15px;">
        <label style="
            display: flex;
            align-items: center;
            cursor: pointer;
            margin-bottom: 10px;
            padding: 8px;
            border-radius: 4px;
            transition: background-color 0.2s;
        " onmouseover="this.style.backgroundColor='#f8f9fa'" onmouseout="this.style.backgroundColor='transparent'">
            <input type="checkbox" id="autoSettingsEnabled" ${settings.enabled ? 'checked' : ''}
                   style="
                       margin-right: 10px;
                       accent-color: #3498db;
                   ">
            <span style="font-weight: 600; color: #2c3e50;">Habilitar configuración automática</span>
        </label>
    </div>

    <div style="margin-bottom: 15px; padding-left: 20px; border-left: 3px solid #ecf0f1;">
        <label style="
            display: flex;
            align-items: center;
            cursor: pointer;
            margin-bottom: 8px;
            padding: 5px;
            border-radius: 4px;
        " onmouseover="this.style.backgroundColor='#f8f9fa'" onmouseout="this.style.backgroundColor='transparent'">
            <input type="checkbox" id="autoAvatarSpawn" ${settings.avatarSpawn ? 'checked' : ''}
                   style="margin-right: 10px; accent-color: #3498db;">
            <span style="color: #34495e;">Auto-habilitar Avatar Spawn</span>
        </label>

        <label style="
            display: flex;
            align-items: center;
            cursor: pointer;
            margin-bottom: 8px;
            padding: 5px;
            border-radius: 4px;
        " onmouseover="this.style.backgroundColor='#f8f9fa'" onmouseout="this.style.backgroundColor='transparent'">
            <input type="checkbox" id="autoPrivateRoom" ${settings.privateRoom ? 'checked' : ''}
                   style="margin-right: 10px; accent-color: #3498db;">
            <span style="color: #34495e;">Auto-seleccionar sala privada</span>
        </label>

        <label style="
            display: flex;
            align-items: center;
            cursor: pointer;
            padding: 5px;
            border-radius: 4px;
        " onmouseover="this.style.backgroundColor='#f8f9fa'" onmouseout="this.style.backgroundColor='transparent'">
            <input type="checkbox" id="autoCreateRoom" ${settings.autoCreate ? 'checked' : ''}
                   style="margin-right: 10px; accent-color: #3498db;">
            <span style="color: #34495e;">Auto-crear sala</span>
        </label>
    </div>

    <div style="
        display: flex;
        justify-content: space-between;
        margin-top: 25px;
        gap: 10px;
        border-top: 1px solid #ecf0f1;
        padding-top: 15px;
    ">
        <button id="saveAutoRoomSettings" style="
            background: #3498db;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            font-size: 14px;
            transition: background-color 0.2s;
            flex: 1;
        " onmouseover="this.style.backgroundColor='#2980b9'" onmouseout="this.style.backgroundColor='#3498db'">
            💾 Guardar
        </button>

        <button id="closeAutoRoomPanel" style="
            background: #95a5a6;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            font-size: 14px;
            transition: background-color 0.2s;
            flex: 1;
        " onmouseover="this.style.backgroundColor='#7f8c8d'" onmouseout="this.style.backgroundColor='#95a5a6'">
            ❌ Cerrar
        </button>
    </div>
`;


    document.body.appendChild(panel);
    makeDraggable(panel);


    document.getElementById('saveAutoRoomSettings').addEventListener('click', function() {
        const newSettings = {
            enabled: document.getElementById('autoSettingsEnabled').checked,
            avatarSpawn: document.getElementById('autoAvatarSpawn').checked,
            privateRoom: document.getElementById('autoPrivateRoom').checked,
            autoCreate: document.getElementById('autoCreateRoom').checked
        };
        saveAutoSettingsState(newSettings);
        console.log('Auto room settings saved:', newSettings);
        panel.style.display = 'none';
    });

    document.getElementById('closeAutoRoomPanel').addEventListener('click', function() {
        panel.style.display = 'none';
    });
}

function showAutoRoomSettingsPanel() {
    hideAllPanels();
    createAutoRoomSettingsPanel();
    document.getElementById('autoRoomSettingsPanel').style.display = 'block';
}


function initializeAutoRoomSettings() {
    addAutoSettingsTriggerListener();


    const observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && !hasAutoExecuted) {
                addAutoSettingsTriggerListener();
            }
        });
    });

    if (document.body) {
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
}





    let isGradientActive = false;
    let animationId = null;
    let speed = 5;
    let gradientType = 'linear';
    let linearDirection = 'to right';
    let conicStartAngle = 0;
    const colorInputs = [];
    let colorCount = 2;
    const defaultColors = ['#FF00FF', '#00FFFF'];

    simpleGradientPanel = document.createElement("div");
    simpleGradientPanel.style.cssText = `
        position: fixed;
        top: 20px; /* Consistent fixed positioning */
        right: 20px; /* Consistent fixed positioning */
        z-index: 99999;
        background: rgba(240, 240, 240, 0.96);
        padding: 15px;
        border-radius: 8px;
        border: 2px solid #ccc;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
        width: 280px;
        font-family: Arial, sans-serif;
        display: none;
    `;
    simpleGradientPanel.setAttribute('id', 'simple-gradient-panel');

    simpleGradientEffect = document.createElement("div");
    simpleGradientEffect.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        z-index: -1;
        pointer-events: none;
        opacity: 0;
        transition: opacity 1.5s ease;
    `;
    simpleGradientEffect.setAttribute('id', 'simple-gradient-effect');

    document.body.appendChild(simpleGradientPanel);
    document.body.appendChild(simpleGradientEffect);

    simpleGradientPanel.innerHTML = `
        <div class="panel-header" style="border-bottom: 1px solid #ccc; margin-bottom: 15px; padding-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
            <div style="font-weight: bold; font-size: 16px;">
                 Gradient Menu
            </div>
            <div class="no-drag" id="simple-close-btn" style="cursor: pointer; font-size: 20px;">×</div>
        </div>
        <div id="simple-color-container" style="margin-bottom: 15px;"></div>
        <button class="no-drag" id="simple-add-color" style="background: #e9e9e9; color: #333; border: 1px solid #d4d4d4; padding: 8px; border-radius: 5px; margin-bottom: 15px; cursor: pointer; width: 100%; box-shadow: 0 1px 2px rgba(0,0,0,0.1);">
            + Add Color
        </button>
        <div style="margin-bottom: 15px;">
            <label for="simple-gradient-type" style="display: block; font-size: 14px; margin-bottom: 5px;">Gradient Type:</label>
            <select class="no-drag" id="simple-gradient-type" style="width: 100%; padding: 5px; border-radius: 3px; border: 1px solid #ccc; background: white;">
                <option value="linear">Linear</option>
                <option value="radial">Radial</option>
                <option value="conic">Conic</option>
            </select>
        </div>
        <div id="simple-linear-direction-control" style="margin-bottom: 15px;">
            <label for="simple-linear-direction" style="display: block; font-size: 14px; margin-bottom: 5px;">Linear Direction:</label>
            <select class="no-drag" id="simple-linear-direction" style="width: 100%; padding: 5px; border-radius: 3px; border: 1px solid #ccc; background: white;">
                <option value="to right">Right</option>
                <option value="to left">Left</option>
                <option value="to top">Top</option>
                <option value="to bottom">Bottom</option>
            </select>
        </div>
        <div id="simple-conic-angle-control" style="margin-bottom: 15px; display: none;">
            <label for="simple-conic-angle-slider" style="display: block; font-size: 14px; margin-bottom: 5px;">Conic Start Angle: <span id="simple-conic-angle-value">0</span>°</label>
            <input class="no-drag" type="range" id="simple-conic-angle-slider" min="0" max="360" value="0" style="width: 100%;">
        </div>
        <div style="margin-bottom: 15px;">
            <label for="simple-speed-slider" style="display: block; font-size: 14px; margin-bottom: 5px;">Speed: <span id="simple-speed-value">5</span></label>
            <input class="no-drag" type="range" id="simple-speed-slider" min="1" max="10" value="5" style="width: 100%;">
        </div>
        <button class="no-drag" id="simple-toggle-btn" style="background: #007bff; color: white; border: none; padding: 10px; border-radius: 5px; cursor: pointer; width: 100%; font-size: 16px; font-weight: bold; box-shadow: 0 2px 4px rgba(0,0,0,0.2);">
            Activate Gradient
        </button>
    `;

    const colorContainer = simpleGradientPanel.querySelector("#simple-color-container");
    const addColorBtn = simpleGradientPanel.querySelector("#simple-add-color");
    const toggleBtn = simpleGradientPanel.querySelector("#simple-toggle-btn");
    const closeBtn = simpleGradientPanel.querySelector("#simple-close-btn");
    const speedSlider = simpleGradientPanel.querySelector("#simple-speed-slider");
    const speedValue = simpleGradientPanel.querySelector("#simple-speed-value");
    const gradientTypeSelect = simpleGradientPanel.querySelector("#simple-gradient-type");
    const linearDirectionSelect = simpleGradientPanel.querySelector("#simple-linear-direction");
    const linearDirectionControl = simpleGradientPanel.querySelector("#simple-linear-direction-control");
    const conicAngleControl = simpleGradientPanel.querySelector("#simple-conic-angle-control");
    const conicAngleSlider = simpleGradientPanel.querySelector("#simple-conic-angle-slider");
    const conicAngleValue = simpleGradientPanel.querySelector("#simple-conic-angle-value");

    function createColorElement(index) {
        const wrapper = document.createElement("div");
        wrapper.style.marginBottom = "10px";
        wrapper.style.display = "flex";
        wrapper.style.alignItems = "center";

        const input = document.createElement("input");
        input.type = "color";
        input.value = defaultColors[index % defaultColors.length];
        input.style.width = "calc(100% - 30px)";
        input.style.height = "30px";
        input.style.cursor = "pointer";
        input.style.marginRight = "5px";
        input.classList.add('no-drag');

        input.addEventListener("input", updateGradient);

        const deleteBtn = document.createElement("button");
        deleteBtn.textContent = "🗑️";
        deleteBtn.style.background = "none";
        deleteBtn.style.border = "none";
        deleteBtn.style.color = "#dc3545";
        deleteBtn.style.fontSize = "18px";
        deleteBtn.style.cursor = "pointer";
        deleteBtn.style.width = "25px";
        deleteBtn.style.height = "30px";
        deleteBtn.style.lineHeight = "1";
        deleteBtn.title = "Remove color";
        deleteBtn.classList.add('no-drag');
        deleteBtn.onclick = () => {
            const idxToRemove = colorInputs.indexOf(input);
            if (idxToRemove > -1) {
                colorInputs.splice(idxToRemove, 1);
                wrapper.remove();
                colorCount--;
                if (colorInputs.length < 2 && isGradientActive) {
                    toggleBtn.click();
                }
                updateGradient();
            }
        };

        wrapper.appendChild(input);
        wrapper.appendChild(deleteBtn);
        colorContainer.appendChild(wrapper);
        colorInputs.push(input);
    }

    function updateControlVisibility() {
        linearDirectionControl.style.display = gradientType === 'linear' ? 'block' : 'none';
        conicAngleControl.style.display = gradientType === 'conic' ? 'block' : 'none';
    }

    let currentRotationOffset = 0;

    function updateGradient() {
        if (colorInputs.length < 2) {
            simpleGradientEffect.style.background = 'none';
            cancelAnimationFrame(animationId);
            return;
        }

        const colors = colorInputs.map(input => input.value);

        const gradientStops = colors.join(', ');

        cancelAnimationFrame(animationId);

        const animate = () => {
            if (!isGradientActive) {
                cancelAnimationFrame(animationId);
                return;
            }

            const animationSpeedFactor = speed * 0.1;
            currentRotationOffset += animationSpeedFactor;

            let backgroundCSS = '';
            if (gradientType === 'linear') {
                backgroundCSS = `linear-gradient(${linearDirection}, ${gradientStops})`;
                let bgSize = '100% 100%';
                let bgPosition = '0% 0%';

                if (linearDirection.includes('right') || linearDirection.includes('left')) {
                    bgSize = `${100 * colors.length}% 100%`;
                    bgPosition = `${-(currentRotationOffset % (100 * colors.length / (colors.length - 1)))}% 0%`;
                } else if (linearDirection.includes('top') || linearDirection.includes('bottom')) {
                    bgSize = `100% ${100 * colors.length}%`;
                    bgPosition = `0% ${-(currentRotationOffset % (100 * colors.length / (colors.length - 1)))}%`;
                }

                simpleGradientEffect.style.background = backgroundCSS;
                simpleGradientEffect.style.backgroundSize = bgSize;
                simpleGradientEffect.style.backgroundPosition = bgPosition;
            } else if (gradientType === 'radial') {
                const centerOffset = 20 + Math.sin(currentRotationOffset * 0.05) * 15;
                backgroundCSS = `radial-gradient(circle at ${50 + Math.sin(currentRotationOffset * 0.03) * centerOffset}% ${50 + Math.cos(currentRotationOffset * 0.04) * centerOffset}%, ${gradientStops})`;
                simpleGradientEffect.style.background = backgroundCSS;
            } else if (gradientType === 'conic') {
                const animatedAngle = (conicStartAngle + currentRotationOffset * 3) % 360;
                backgroundCSS = `conic-gradient(from ${animatedAngle}deg at center, ${gradientStops})`;
                simpleGradientEffect.style.background = backgroundCSS;
            }
            animationId = requestAnimationFrame(animate);
        };

        if (isGradientActive) {
            animate();
        }
    }

    function openSimpleGradientPanel() {
        hideAllPanels();
        simpleGradientPanel.style.top = '20px';
        simpleGradientPanel.style.right = '20px';
        simpleGradientPanel.style.left = 'auto';
        simpleGradientPanel.style.transform = '';
        simpleGradientPanel.style.display = 'block';

        simpleGradientEffect.style.opacity = isGradientActive ? '0.8' : '0';
        if (colorInputs.length === 0) {
            for (let i = 0; i < defaultColors.length; i++) {
                createColorElement(i);
            }
            colorCount = defaultColors.length;
        }
        updateControlVisibility();
        toggleBtn.textContent = isGradientActive ? "Deactivate Gradient" : "Activate Gradient";
        speedValue.textContent = speed;
        speedSlider.value = speed;
        gradientTypeSelect.value = gradientType;
        linearDirectionSelect.value = linearDirection;
        conicAngleValue.textContent = conicStartAngle;
        conicAngleSlider.value = conicStartAngle;

        if (isGradientActive) {
            updateGradient();
        }
    }

    addColorBtn.addEventListener("click", () => {
        if (colorCount >= 10) {
            alert("Maximum 10 colors allowed!");
            return;
        }
        createColorElement(colorCount);
        colorCount++;
        if (isGradientActive) updateGradient();
    });

    toggleBtn.addEventListener("click", () => {
        if (colorInputs.length < 2) {
            alert("Need at least 2 colors!");
            return;
        }
        isGradientActive = !isGradientActive;
        if (isGradientActive) {
            simpleGradientEffect.style.opacity = 0.8;
            toggleBtn.textContent = "Deactivate Gradient";
            updateGradient();
        } else {
            simpleGradientEffect.style.opacity = 0;
            toggleBtn.textContent = "Activate Gradient";
            cancelAnimationFrame(animationId);
        }
    });

    speedSlider.addEventListener("input", () => {
        speed = parseInt(speedSlider.value);
        speedValue.textContent = speed;
        if (isGradientActive) updateGradient();
    });

    gradientTypeSelect.addEventListener("change", (e) => {
        gradientType = e.target.value;
        updateControlVisibility();
        if (isGradientActive) updateGradient();
    });

    linearDirectionSelect.addEventListener("change", (e) => {
        linearDirection = e.target.value;
        if (isGradientActive) updateGradient();
    });

    conicAngleSlider.addEventListener("input", () => {
        conicStartAngle = parseInt(conicAngleSlider.value);
        conicAngleValue.textContent = conicStartAngle;
        if (isGradientActive) updateGradient();
    });

    closeBtn.addEventListener("click", () => {
        simpleGradientPanel.style.display = 'none';
    });
    makeDraggable(simpleGradientPanel, simpleGradientPanel.querySelector('.panel-header'));







    const avatarIds = window.avatarIds || [];


    function getAvatarUrlFromId(id) {
        return window.getAvatarUrlFromId ? window.getAvatarUrlFromId(id) : `/avatar/cache/${id}.jpg`;
    }

    function changeAvatarRandomly() {

        return window.changeAvatarRandomly ? window.changeAvatarRandomly() : false;
    }

    function downloadCurrentAvatar() {
        const avatarImage = document.querySelector('#selfavatarimage');
        if (avatarImage) {
            const downloadLink = document.createElement('a');
            downloadLink.href = avatarImage.src;
            downloadLink.download = 'avatar.jpg';
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);
            console.log('Downloading current avatar.');
        } else {
            console.error('Self avatar image element (#selfavatarimage) not found.');
        }
    }

    function openCurrentAvatarInNewTab() {
        const avatarImage = document.querySelector('#selfavatarimage');
        if (avatarImage) {
            window.open(avatarImage.src, '_blank');
            console.log('Opening current avatar in new tab.');
        } else {
            console.error('Self avatar image element (#selfavatarimage) not found.');
        }
    }

    function goToPrevAvatar() {
        if (avatarIds.length === 0) return;
        const avatarImage = document.querySelector('#selfavatarimage');
        if (!avatarImage) return;

        const currentSrc = avatarImage.src;
        let currentIndex = -1;

        for(let i = 0; i < avatarIds.length; i++) {
            if (currentSrc.includes(`/avatar/cache/${avatarIds[i]}.jpg`)) {
                currentIndex = i;
                break;
            }
        }

        let newIndex = (currentIndex === -1 || currentIndex === 0) ? avatarIds.length - 1 : currentIndex - 1;
        avatarImage.src = getAvatarUrlFromId(avatarIds[newIndex]);
        console.log('Changed avatar to previous:', avatarIds[newIndex]);
    }

    function goToNextAvatar() {
        if (avatarIds.length === 0) return;
        const avatarImage = document.querySelector('#selfavatarimage');
        if (!avatarImage) return;

        const currentSrc = avatarImage.src;
        let currentIndex = -1;

        for(let i = 0; i < avatarIds.length; i++) {
            if (currentSrc.includes(`/avatar/cache/${avatarIds[i]}.jpg`)) {
                currentIndex = i;
                break;
            }
        }

        let newIndex = (currentIndex === -1 || currentIndex === avatarIds.length - 1) ? 0 : currentIndex + 1;
        avatarImage.src = getAvatarUrlFromId(avatarIds[newIndex]);
        console.log('Changed avatar to next:', avatarIds[newIndex]);
    }


    const avatarTranslations = {
        en: {
            'Avatar Randomizer': 'Random Avatar',
            'Download Avatar': 'Download Avatar',
            'Open in New Tab': 'Open in New Tab',
            'Avatar Menu': 'Avatar Menu'
        },
        ru: {
            'Avatar Randomizer': 'Случайный аватар',
            'Download Avatar': 'Скачать аватар',
            'Open in New Tab': 'Открыть в новой вкладке',
            'Avatar Menu': 'Меню аватара'
        },
        es: {
            'Avatar Randomizer': 'Cambiar Avatar Aleatoriamente',
            'Download Avatar': 'Descargar Avatar',
            'Open in New Tab': 'Abrir en Nueva Pestaña',
            'Avatar Menu': 'Menú de Avatar'
        }
    };


    avatarPanel = document.createElement("div");
    avatarPanel.style.cssText = `
    position: fixed;
    top: 20px;
    right: 320px;
    z-index: 99999;
    background: rgba(240, 240, 240, 0.96);
    padding: 15px;
    border-radius: 8px;
    border: 2px solid #ccc;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    width: 280px;
    font-family: Arial, sans-serif;
    display: none;
`;
    avatarPanel.setAttribute('id', 'avatar-menu-panel');
    document.body.appendChild(avatarPanel);

    avatarPanel.innerHTML = `
    <div class="panel-header" style="border-bottom: 1px solid #ccc; margin-bottom: 15px; padding-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
        <div id="avatar-menu-title" style="font-weight: bold; font-size: 16px;">
             Avatar Menu
        </div>
        <div class="no-drag" id="avatar-close-btn" style="cursor: pointer; font-size: 20px;">×</div>
    </div>
    <div id="avatar-buttons-container"></div>
`;

    const avatarNavButtonStyle = `
    background: #e9e9e9;
    border: 1px solid #d4d4d4;
    color: #555;
    font-size: 20px;
    border-radius: 5px;
    margin: 0 5px;
    padding: 7px 12px;
    cursor: pointer;
    box-shadow: 0 1px 2px rgba(0,0,0,0.1);
    transition: background 0.2s;
`;

    const avatarButtonsContainer = avatarPanel.querySelector("#avatar-buttons-container");
    const avatarCloseBtn = avatarPanel.querySelector("#avatar-close-btn");
    const avatarMenuTitle = avatarPanel.querySelector("#avatar-menu-title");

    const avatarButtons = [
        { id: 'avatarRandomizerButton', text: 'Avatar Randomizer', clickHandler: changeAvatarRandomly },
        { id: 'downloadIconButton', text: 'Download Avatar', clickHandler: downloadCurrentAvatar },
        { id: 'openAvatarButton', text: 'Open in New Tab', clickHandler: openCurrentAvatarInNewTab }
    ];

    function createAvatarButtons() {
        avatarButtonsContainer.innerHTML = '';
        avatarButtons.forEach((buttonData) => {
            const newButton = document.createElement('button');
            newButton.id = buttonData.id;
            newButton.type = 'button';
            newButton.role = 'button';
            newButton.classList.add('btn', 'btn-secondary', 'btn-block', 'no-drag');
            newButton.textContent = buttonData.text;
            newButton.style.marginBottom = '10px';
            newButton.addEventListener('click', buttonData.clickHandler);
            avatarButtonsContainer.appendChild(newButton);
        });


        const navRow = document.createElement('div');
        navRow.style.display = 'flex';
        navRow.style.justifyContent = 'center';
        navRow.style.gap = '8px';
        navRow.style.marginTop = '10px';

        const prevBtn = document.createElement('button');
        prevBtn.type = 'button';
        prevBtn.title = 'Anterior avatar';
        prevBtn.innerHTML = '<i class="fas fa-arrow-left"></i>';
        prevBtn.style.cssText = avatarNavButtonStyle;
        prevBtn.classList.add('no-drag');
        prevBtn.onclick = goToPrevAvatar;

        const nextBtn = document.createElement('button');
        nextBtn.type = 'button';
        nextBtn.title = 'Siguiente avatar';
        nextBtn.innerHTML = '<i class="fas fa-arrow-right"></i>';
        nextBtn.style.cssText = avatarNavButtonStyle;
        nextBtn.classList.add('no-drag');
        nextBtn.onclick = goToNextAvatar;

        navRow.appendChild(prevBtn);
        navRow.appendChild(nextBtn);
        avatarButtonsContainer.appendChild(navRow);
        translateAvatarButtonTexts();
    }

    function translateAvatarButtonTexts() {
        const langSelector = document.querySelector('#langselector');
        if (langSelector) {
            const selectedLanguage = langSelector.value;
            const translationSet = avatarTranslations[selectedLanguage] || avatarTranslations.en;

            avatarMenuTitle.textContent = translationSet['Avatar Menu'];

            avatarButtons.forEach((buttonData) => {
                const buttonElement = document.getElementById(buttonData.id);
                if (buttonElement) {
                    buttonElement.textContent = translationSet[buttonData.text];
                }
            });
        }
    }

    function openAvatarPanel() {
        hideAllPanels();
        avatarPanel.style.top = '20px';
        avatarPanel.style.right = '320px';
        avatarPanel.style.left = 'auto';
        avatarPanel.style.transform = '';
        avatarPanel.style.display = 'block';
        createAvatarButtons();
    }

    avatarCloseBtn.addEventListener("click", () => {
        avatarPanel.style.display = 'none';
    });


    document.querySelector('#langselector').addEventListener('change', translateAvatarButtonTexts);
    makeDraggable(avatarPanel, avatarPanel.querySelector('.panel-header'));


    console.log('✅ Avatar system integrated with local library');
    console.log(`📊 Available avatars from library: ${window.getAvatarCount ? window.getAvatarCount() : avatarIds.length}`);





    const PIXEL_SIZE_KEY = 'drawaria_pixel_size';
    const PIXELATION_ENABLED_KEY = 'drawaria_pixelation_enabled';

    let pixelSize = parseInt(localStorage.getItem(PIXEL_SIZE_KEY) || '8', 10);
    isPixelated = localStorage.getItem(PIXELATION_ENABLED_KEY) === 'true';

    pixelationPanel = document.createElement('div');
    pixelationPanel.id = "pixelationUI";
    pixelationPanel.style.cssText = `
        position: fixed;
        top: 20px; /* Consistent fixed positioning */
        right: 620px; /* Staggered from right */
        background: rgba(240, 240, 240, 0.96);
        color: #333;
        padding: 15px;
        border-radius: 8px;
        border: 2px solid #ccc;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
        z-index: 10000;
        font-family: Arial, sans-serif;
        width: 280px;
        display: none;
    `;
    document.body.appendChild(pixelationPanel);

    pixelationPanel.innerHTML = `
        <div class="panel-header" style="border-bottom: 1px solid #ccc; margin-bottom: 15px; padding-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
            <div style="font-weight: bold; font-size: 16px;">Pixelator Controls</div>
            <div class="no-drag" id="pixelation-close-btn" style="cursor: pointer; font-size: 20px;">×</div>
        </div>
        <div style="display: flex; gap: 10px; margin-bottom: 10px;">
            <button class="no-drag" id="pixelationEnableButton" style="background-color: #4CAF50; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer; flex: 1;">Enable Pixelation</button>
            <button class="no-drag" id="pixelationDisableButton" style="background-color: #f44336; color: white; border: none; padding: 8px 16px; border-radius: 5px; cursor: pointer; flex: 1;">Disable Pixelation (Reload)</button>
        </div>
        <div style="margin-bottom: 15px; display: flex; align-items: center; justify-content: space-between;">
            <label for="pixelSizeInput" style="font-size: 14px;">Pixel Size:</label>
            <input class="no-drag" type="number" id="pixelSizeInput" min="1" style="width: 80px; padding: 5px; border-radius: 5px; border: 1px solid #ccc; background-color: white; color: #333;">
        </div>
    `;

    const pixelationEnableButton = pixelationPanel.querySelector("#pixelationEnableButton");
    const pixelationDisableButton = pixelationPanel.querySelector("#pixelationDisableButton");
    const pixelSizeInput = pixelationPanel.querySelector("#pixelSizeInput");
    const pixelationCloseBtn = pixelationPanel.querySelector("#pixelation-close-btn");

    function pixelateCanvasData(canvas, ctx) {
        const width = canvas.width;
        const height = canvas.height;
        const pixelatedCanvas = document.createElement('canvas');
        pixelatedCanvas.width = width;
        pixelatedCanvas.height = height;
        const ctxPixelated = pixelatedCanvas.getContext('2d');
        ctxPixelated.imageSmoothingEnabled = false;

        for (let y = 0; y < height; y += pixelSize) {
            for (let x = 0; x < width; x += pixelSize) {
                const pixelData = ctx.getImageData(x, y, 1, 1).data;
                const color = `rgba(${pixelData[0]}, ${pixelData[1]}, ${pixelData[2]}, ${pixelData[3] / 255})`;
                ctxPixelated.fillStyle = color;
                ctxPixelated.fillRect(x, y, pixelSize, pixelSize);
            }
        }
        return pixelatedCanvas;
    }

    function pixelateImageOrCanvas(element) {
        if (!element || !element.width || !element.height) return;

        const canvas = document.createElement('canvas');
        canvas.width = element.width;
        canvas.height = element.height;
        const ctx = canvas.getContext('2d');
        ctx.imageSmoothingEnabled = false;

        if (element.tagName === "CANVAS") {
            ctx.drawImage(element, 0, 0, element.width, element.height);
        } else if (element.tagName === "IMG" && element.src) {
            if (!element.complete) {
                element.onload = () => pixelateImageOrCanvas(element);
                return;
            }
            ctx.drawImage(element, 0, 0, element.width, element.height);
        } else {
            return;
        }

        const pixelatedCanvas = pixelateCanvasData(canvas, ctx);

        if (element.tagName === "CANVAS") {
            const elementCtx = element.getContext('2d');
            elementCtx.clearRect(0, 0, element.width, element.height);
            elementCtx.drawImage(pixelatedCanvas, 0, 0, element.width, element.height);
        } else if (element.tagName === "IMG") {
            element.src = pixelatedCanvas.toDataURL();
        }
    }

    function pixelateText(element) {
        element.style.font = `bold ${pixelSize}px monospace`;
    }

    function pixelateBackgroundImage(element) {
        let originalBackgroundImage = window.getComputedStyle(element).backgroundImage;
        if (originalBackgroundImage && originalBackgroundImage !== 'none' && originalBackgroundImage.includes('url(')) {
            const imageUrlMatch = originalBackgroundImage.match(/url\("?(.+?)"?\)/);
            if (imageUrlMatch) {
                const imageUrl = imageUrlMatch[1];
                const img = new Image();
                img.crossOrigin = "anonymous";
                img.src = imageUrl;
                img.onload = () => {
                    const tempCanvas = document.createElement('canvas');
                    tempCanvas.width = img.width;
                    tempCanvas.height = img.height;
                    const tempCtx = tempCanvas.getContext('2d');
                    tempCtx.drawImage(img, 0, 0);
                    const pixelatedCanvas = pixelateCanvasData(tempCanvas, tempCtx);
                    element.style.backgroundImage = `url(${pixelatedCanvas.toDataURL()})`;
                    element.style.backgroundSize = `${img.width}px ${img.height}px`;
                };
                img.onerror = () => {
                    console.error("Error loading background image:", imageUrl);
                };
            }
        }
    }

    function pixelateBorderImage(element) {
        let originalBorderImage = window.getComputedStyle(element).borderImageSource;
        if (originalBorderImage && originalBorderImage !== 'none' && originalBorderImage.includes('url(')) {
            const imageUrlMatch = originalBorderImage.match(/url\("?(.+?)"?\)/);
            if (imageUrlMatch) {
                const imageUrl = imageUrlMatch[1];
                const img = new Image();
                img.crossOrigin = "anonymous";
                img.src = imageUrl;
                img.onload = () => {
                    const tempCanvas = document.createElement('canvas');
                    tempCanvas.width = img.width;
                    tempCanvas.height = img.height;
                    const tempCtx = tempCanvas.getContext('2d');
                    tempCtx.drawImage(img, 0, 0);
                    const pixelatedCanvas = pixelateCanvasData(tempCanvas, tempCtx);
                    element.style.borderImageSource = `url(${pixelatedCanvas.toDataURL()})`;
                };
                img.onerror = () => {
                    console.error("Error loading border image:", imageUrl);
                };
            }
        }
    }

    function applyPixelation() {
        if (!isPixelated) return;
        document.querySelectorAll('canvas, img').forEach(pixelateImageOrCanvas);
        document.querySelectorAll('*').forEach(element => {
            pixelateText(element);
            pixelateBackgroundImage(element);
            pixelateBorderImage(element);
        });
    }

    function enablePixelation() {
        if (!isPixelated) {
            isPixelated = true;
            localStorage.setItem(PIXELATION_ENABLED_KEY, 'true');
            applyPixelation();
            startObservingPixelation();
            console.log('Pixelation enabled.');
            pixelationEnableButton.textContent = 'Pixelation Enabled';
            pixelationDisableButton.textContent = 'Disable Pixelation (Reload)';
        }
    }

    function disablePixelation() {
        if (isPixelated) {
            isPixelated = false;
            localStorage.setItem(PIXELATION_ENABLED_KEY, 'false');
            stopObservingPixelation();
            console.log('Pixelation disabled. Reloading page...');
            location.reload();
        }
    }

    function openPixelationPanel() {
        hideAllPanels();
        pixelationPanel.style.top = '20px';
        pixelationPanel.style.right = '620px';
        pixelationPanel.style.left = 'auto';
        pixelationPanel.style.transform = '';
        pixelationPanel.style.display = 'block';

        pixelSizeInput.value = pixelSize;
        pixelationEnableButton.textContent = isPixelated ? 'Pixelation Enabled' : 'Enable Pixelation';
        pixelationDisableButton.textContent = isPixelated ? 'Disable Pixelation (Reload)' : 'Pixelation Disabled';
    }

    function startObservingPixelation() {
        if (observer) return;
        observer = new MutationObserver(mutations => {
            if (isPixelated) {
                mutations.forEach(mutation => {
                    if (mutation.type === 'childList') {
                        mutation.addedNodes.forEach(node => {
                            if (node.nodeType === Node.ELEMENT_NODE) {
                                pixelateImageOrCanvas(node);
                                node.querySelectorAll('canvas, img').forEach(pixelateImageOrCanvas);
                                pixelateText(node);
                                pixelateBackgroundImage(node);
                                pixelateBorderImage(node);
                            }
                        });
                    } else if (mutation.type === 'attributes' && (mutation.target.tagName === 'IMG' || mutation.target.tagName === 'CANVAS')) {
                        pixelateImageOrCanvas(mutation.target);
                    }
                });
            }
        });
        observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src', 'style'] });
    }

    function stopObservingPixelation() {
        if (observer) {
            observer.disconnect();
            observer = null;
        }
    }


    pixelationEnableButton.addEventListener('click', enablePixelation);
    pixelationDisableButton.addEventListener('click', disablePixelation);
    pixelSizeInput.addEventListener('change', (event) => {
        const newValue = parseInt(event.target.value, 10);
        if (!isNaN(newValue) && newValue > 0) {
            pixelSize = newValue;
            localStorage.setItem(PIXEL_SIZE_KEY, pixelSize);
            if (isPixelated) applyPixelation();
        }
    });
    pixelationCloseBtn.addEventListener('click', () => {
        pixelationPanel.style.display = 'none';
    });
    makeDraggable(pixelationPanel, pixelationPanel.querySelector('.panel-header'));


    if (isPixelated) {
        applyPixelation();
        startObservingPixelation();
    }




    avatarCopyPanel = document.createElement("div");
    avatarCopyPanel.style.cssText = `
        position: fixed;
        top: 20px; /* Consistent fixed positioning */
        right: 920px; /* Staggered from right */
        z-index: 99999;
        background: rgba(240, 240, 240, 0.96);
        padding: 15px;
        border-radius: 8px;
        border: 2px solid #ccc;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
        font-family: Arial, sans-serif;
        width: 280px;
        display: none;
    `;
    avatarCopyPanel.setAttribute('id', 'avatar-switcher-panel');
    document.body.appendChild(avatarCopyPanel);

    avatarCopyPanel.innerHTML = `
        <div class="panel-header" style="border-bottom: 1px solid #ccc; margin-bottom: 15px; padding-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
            <h3 style="margin:0; color:#1e90ff; font-weight: bold; font-size: 16px;">Avatar Copy Players</h3>
            <div class="no-drag" id="avatar-copy-close-btn" style="cursor: pointer; font-size: 20px;">×</div>
        </div>
        <div class="no-drag" id="avatar-drop-zone" style="border: 2px dashed #4682b4; border-radius: 5px; padding: 20px; text-align: center; margin-top: 10px; color: #4682b4; font-weight: bold; transition: background-color 0.2s;">Arrastra y suelta imagen aquí</div>

        <div id="player-name-display-container" style="display: flex; align-items: center; margin-top: 10px;">
            <input class="no-drag" type="text" id="player-name-field" value="" disabled style="width: calc(100% - 30px); padding: 8px; border: 1px solid #ccc; border-radius: 4px; background-color: #eee; color: #555; font-size: 14px;">
            <i class="no-drag" id="copy-name-icon" class="fas fa-copy" style="cursor: pointer; margin-left: 5px; color: #555; font-size: 18px;"></i>
        </div>

        <input class="no-drag" type="file" id="avatar-file-input-copy" accept="image/*" style="display: none;">
        <label class="no-drag" for="avatar-file-input-copy" style="display: block; width: 100%; padding: 10px; margin-top: 10px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; text-align: center; font-size: 16px;">Subir Avatar Descargado</label>
        <div id="avatar-copy-status-message" style="margin-top: 10px; font-size: 14px; text-align: center; color: #333;">Haz clic en un avatar para descargarlo.</div>
    `;

    const dropZone = avatarCopyPanel.querySelector('#avatar-drop-zone');
    const fileInputCopy = avatarCopyPanel.querySelector('#avatar-file-input-copy');
    const statusMessage = avatarCopyPanel.querySelector('#avatar-copy-status-message');
    const playerNameDisplayContainer = avatarCopyPanel.querySelector('#player-name-display-container');
    const playerNameField = avatarCopyPanel.querySelector('#player-name-field');
    const copyNameIcon = avatarCopyPanel.querySelector('#copy-name-icon');
    const avatarCopyCloseBtn = avatarCopyPanel.querySelector('#avatar-copy-close-btn');

    function copyTextToClipboard(text) {
        if (!navigator.clipboard) {
            console.error('La API del portapapeles no está disponible.');
            statusMessage.textContent = 'Tu navegador no soporta copiar.';
            return;
        }
        navigator.clipboard.writeText(text).then(function() {
            statusMessage.textContent = `"${text}" copiado al portapapeles.`;
            console.log('Nombre copiado al portapapeles.');
        }, function(err) {
            console.error('Falló al copiar el nombre:', err);
            statusMessage.textContent = '¡Fallo al copiar el nombre!';
        });
    }

    function downloadImage(url, filename, playerName) {
        statusMessage.textContent = `Descargando ${filename}...`;

        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            responseType: 'blob',
            onload: function(response) {
                const urlCreator = window.URL || window.webkitURL;
                const imageUrl = urlCreator.createObjectURL(response.response);
                const a = document.createElement('a');
                a.href = imageUrl;
                a.download = filename;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                urlCreator.revokeObjectURL(imageUrl);

                playerNameField.value = playerName;
                playerNameDisplayContainer.style.display = 'flex';
                localStorage.setItem('lastDownloadedPlayerName', playerName);
                localStorage.setItem('autoPasteNameFlag', 'true');

                statusMessage.textContent = `${filename} descargado. Nombre "${playerName}" listo para pegar.`;
            },
            onerror: function() {
                statusMessage.textContent = '¡Fallo en la descarga!';
            }
        });
    }

    async function handleFileUpload(file) {
        if (!file || !file.type.startsWith('image/')) {
            statusMessage.textContent = 'Por favor, selecciona un archivo de imagen.';
            return;
        }

        statusMessage.textContent = 'Subiendo...';
        const reader = new FileReader();
        reader.onload = async function(e) {
            let base64ImageData = e.target.result;
            base64ImageData = base64ImageData.replace(/^data:image\/\w+;/, "data:image/jpeg;");

            const isLoggedIn = typeof window.LOGGEDIN !== 'undefined' && window.LOGGEDIN;
            const uploadUrl = isLoggedIn ? 'https://drawaria.online/saveavatar' : 'https://drawaria.online/uploadavatarimage';

            try {
                const response = await fetch(uploadUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
                    },
                    body: 'imagedata=' + encodeURIComponent(base64ImageData)
                });

                if (response.ok && response.status === 200) {
                    const responseText = await response.text();
                    if (responseText && responseText.trim()) {
                        statusMessage.textContent = '¡Subida exitosa!';
                        alert('¡Avatar subido con éxito! La página se recargará para ver los cambios.');
                        location.reload();
                    } else {
                        throw new Error('El servidor devolvió una respuesta vacía o inválida.');
                    }
                } else {
                    throw new Error(`El servidor respondió con estado: ${response.status}`);
                }
            } catch (error) {
                console.error('Fallo en la subida:', error);
                statusMessage.textContent = '¡Fallo en la subida! Revisa la consola.';
                alert('Fallo en la subida. El servidor pudo haber rechazado la imagen. Revisa la consola (F12) para detalles.');
            }
        };
        reader.readAsDataURL(file);
    }

    playerNameDisplayContainer.style.display = 'none';

    dropZone.addEventListener('dragover', (e) => {
        e.preventDefault();
        dropZone.classList.add('hover');
    });
    dropZone.addEventListener('dragleave', () => {
        dropZone.classList.remove('hover');
    });
    dropZone.addEventListener('drop', (e) => {
        e.preventDefault();
        dropZone.classList.remove('hover');
        const file = e.dataTransfer.files[0];
        handleFileUpload(file);
    });

    fileInputCopy.addEventListener('change', (e) => {
        const file = e.target.files[0];
        handleFileUpload(file);
    });

    copyNameIcon.addEventListener('click', () => {
        copyTextToClipboard(playerNameField.value);
    });

    document.body.addEventListener('click', async function(event) {
        const playerRow = event.target.closest('.playerlist-row');
        if (playerRow) {
            const playerAvatarImg = playerRow.querySelector('.playerlist-avatar');
            const playerNameLink = playerRow.querySelector('.playerlist-name a');
            if (playerAvatarImg && playerNameLink) {
                event.preventDefault();
                event.stopPropagation();
                const avatarSrc = playerAvatarImg.src;
                const playerName = playerNameLink.textContent.trim();
                const filename = `${playerName.replace(/[^a-z0-9]/gi, '_')}.jpg`;

            }
        }
    });

    function initializeAvatarCopyPanel() {
        const lastDownloadedName = localStorage.getItem('lastDownloadedPlayerName');
        const autoPasteFlag = localStorage.getItem('autoPasteNameFlag');

        if (lastDownloadedName && autoPasteFlag === 'true') {
            const selfPlayerNameInput = document.getElementById('playername');
            if (selfPlayerNameInput) {
                selfPlayerNameInput.value = lastDownloadedName;
                selfPlayerNameInput.dispatchEvent(new Event('input', { bubbles: true }));
                statusMessage.textContent = `Nombre "${lastDownloadedName}" establecido automáticamente.`;
                console.log(`Nombre de perfil establecido automáticamente a: ${lastDownloadedName}`);
            } else {
                playerNameField.value = lastDownloadedName;
                playerNameDisplayContainer.style.display = 'flex';
                statusMessage.textContent = `Nombre "${lastDownloadedName}" listo para pegar.`;
                console.warn('Campo de nombre de perfil (#playername) no encontrado en la carga automática.');
            }
            localStorage.removeItem('lastDownloadedPlayerName');
            localStorage.removeItem('autoPasteNameFlag');
        } else if (lastDownloadedName) {
            playerNameField.value = lastDownloadedName;
            playerNameDisplayContainer.style.display = 'flex';
            statusMessage.textContent = `Nombre "${lastDownloadedName}" listo para pegar.`;
        }
    }

    function openAvatarCopyPanel() {
        hideAllPanels();
        avatarCopyPanel.style.top = '20px';
        avatarCopyPanel.style.right = '920px';
        avatarCopyPanel.style.left = 'auto';
        avatarCopyPanel.style.transform = '';
        avatarCopyPanel.style.display = 'block';
        initializeAvatarCopyPanel();
    }

    avatarCopyCloseBtn.addEventListener("click", () => {
        avatarCopyPanel.style.display = 'none';
    });
    makeDraggable(avatarCopyPanel, avatarCopyPanel.querySelector('.panel-header h3'));




    var modsMenuItems = [
        { text: 'Stars Background', id: 'stars', action: stars },
        { text: 'Play Song', id: 'playSong', action: playSong },
        { text: 'Join Random Room', id: 'joinRandomRoom', action: joinRandomRoom },
        { text: 'Toggle Dark Theme', id: 'toggleDarkTheme', action: toggleDarkTheme },
        { text: 'Gradient Menu', id: 'simpleGradient', action: openSimpleGradientPanel },
        { text: 'Pixelation Menu', id: 'pixelationMenu', action: openPixelationPanel },
        { text: 'Avatar Menu', id: 'randomAvatarMenu', action: openAvatarPanel },
        { text: 'Avatar Copy Tool', id: 'avatarCopyTool', action: openAvatarCopyPanel },
        { text: 'Replace Logo', id: 'replaceLogo', action: replaceLogo }
    ];


    modsMenuItems.forEach(function(item) {
        var menuItem = document.createElement('a');
        menuItem.className = 'dropdown-item';
        menuItem.href = '#';
        menuItem.id = item.id;
        menuItem.textContent = item.text;
        menuItem.onclick = item.action;
        modsMenuDropdown.appendChild(menuItem);
    });


var autoRoomSettingsOption = document.createElement('a');
autoRoomSettingsOption.className = 'dropdown-item';
autoRoomSettingsOption.href = '#';
autoRoomSettingsOption.innerHTML = '<i class="fas fa-cog"></i> Auto Room Settings';
autoRoomSettingsOption.onclick = function(e) {
    e.preventDefault();
    showAutoRoomSettingsPanel();
};
modsMenuDropdown.appendChild(autoRoomSettingsOption);


document.addEventListener('DOMContentLoaded', initializeAutoRoomSettings);
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initializeAutoRoomSettings);
} else {
    initializeAutoRoomSettings();
}



    modsMenuContainer.appendChild(modsMenuButton);
    modsMenuContainer.appendChild(modsMenuDropdown);


    var loginbox = document.querySelector('.loginbox');
    if (loginbox) {
        loginbox.appendChild(modsMenuContainer);
    } else {

        console.warn('Loginbox not found. Appending mods menu to body.');
        document.body.appendChild(modsMenuContainer);
    }


    setDarkTheme(isDarkThemeEnabled());


    translateAvatarButtonTexts();






})();





    (function() {
        'use strict';

        let canvas = document.getElementById('canvas');
        let ctx = canvas ? canvas.getContext('2d') : null;


        if (!canvas) {
            const observer = new MutationObserver((mutations, obs) => {
                canvas = document.getElementById('canvas');
                if (canvas) {
                    ctx = canvas.getContext('2d');
                    initVideoUpload();
                    obs.disconnect();
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
            return;
        } else {
            initVideoUpload();
        }


        function initVideoUpload() {
            if (!canvas || !ctx) {
                console.error("Video Upload: Canvas no disponible para inicializar el módulo.");
                return;
            }





            let target = document.getElementById('downloadcanvas');
            if (!target) {
                console.warn("Video Upload: Punto de inserción 'downloadcanvas' no encontrado. Reintentando...");
                setTimeout(initVideoUpload, 500);
                return;
            }

            let uploadContainer = document.createElement('div');
            uploadContainer.id = 'iUploadVideoPanel';
            uploadContainer.style.marginTop = '10px';


            let uploadButton = document.createElement('button');
            uploadButton.id = 'videoupload-btn';
            uploadButton.type = 'button';
            uploadButton.classList.add('btn', 'btn-light', 'btn-sm', 'btn-block');
            uploadButton.innerHTML = '<i class="material-icons" style="font-size: 16px;">&#xe038;</i><span>Upload Video</span>';


            let dropArea = document.createElement('div');
            dropArea.classList.add('upload-video-area');
            dropArea.innerHTML = 'O arrastra un video aqui para reproducirlo';


            let linkElm = document.createElement("link");
            linkElm.href = "https://fonts.googleapis.com/icon?family=Material+Icons";
            linkElm.rel = "stylesheet";
            document.head.appendChild(linkElm);


            let styleElem = document.createElement("style");
            styleElem.textContent = `
            .material-icons {
                font-family: 'Material Icons';
                font-weight: normal;
                font-style: normal;
                line-height: 1;
                letter-spacing: normal;
                text-transform: none;
                display: inline-block;
                white-space: nowrap;
            }
            #iUploadVideoPanel .upload-video-area {
                border: 2px dashed #ffffff80;
                border-radius: 5px;
                padding: 15px;
                text-align: center;
                font-size: 0.8em;
                color: #FF0000;
                margin-top: 10px;
                cursor: pointer;
                transition: border-color 0.2s ease, background-color 0.2s ease;
            }
            #iUploadVideoPanel .upload-video-area.dragover {
                border-color: #007bff; /* Color al arrastrar sobre el área */
                background-color: #ffffff1a;
            }
        `;
            document.head.appendChild(styleElem);

            target.parentNode.insertBefore(uploadContainer, target.nextSibling);
            uploadContainer.appendChild(uploadButton);
            uploadContainer.appendChild(dropArea);


            let currentAnimationId = null;
            let currentVideoElement = null;

            function stopPreviousVideo() {
                if (currentAnimationId) {
                    cancelAnimationFrame(currentAnimationId);
                    currentAnimationId = null;
                }
                if (currentVideoElement) {
                    currentVideoElement.pause();
                    currentVideoElement.currentTime = 0;
                    currentVideoElement.src = '';
                    currentVideoElement.remove();
                    currentVideoElement = null;
                }
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                canvas.onclick = null;
            }

            function processVideoFile(file) {
                const allowedTypes = ['video/mp4', 'video/webm'];
                const isValidType = allowedTypes.some(type => file.type.startsWith(type));

                if (!isValidType) {
                    alert('Solo se admiten archivos de video MP4 y WebM.');
                    return;
                }

                stopPreviousVideo();

                let video = document.createElement('video');
                video.loop = true;
            video.autoplay = true;
            video.muted = true;

            video.onloadedmetadata = function() {
                let scaleX = canvas.width / video.videoWidth;
                let scaleY = canvas.height / video.videoHeight;
                let scale = Math.min(scaleX, scaleY);
                let xOffset = (canvas.width - video.videoWidth * scale) / 2;
                let yOffset = (canvas.height - video.videoHeight * scale) / 2;

                function drawVideoFrame() {
                    if (currentVideoElement === video && !video.paused && !video.ended) {
                        ctx.clearRect(0, 0, canvas.width, canvas.height);
                        ctx.drawImage(video, xOffset, yOffset, video.videoWidth * scale, video.videoHeight * scale);
                        currentAnimationId = requestAnimationFrame(drawVideoFrame);
                    } else {
                        currentAnimationId = null;
                    }
                }

                video.play().then(() => {
                    currentAnimationId = requestAnimationFrame(drawVideoFrame);
                    video.muted = false;
                }).catch(e => {
                    console.warn("Autoplay con audio bloqueado. Haz clic en el lienzo para iniciar el video.", e);

                    video.muted = false;
                    canvas.onclick = () => {
                        if (video.paused) {
                            video.play().then(() => {
                                currentAnimationId = requestAnimationFrame(drawVideoFrame);
                                canvas.onclick = null;
                            }).catch(err => console.error("Error al reproducir manualmente:", err));
                        }
                    };
                });
            };
            video.src = URL.createObjectURL(file);
            document.body.appendChild(video);
            video.style.position = 'absolute';
            video.style.left = '-9999px';
            video.style.top = '-9999px';
            currentVideoElement = video;
        }


        uploadButton.addEventListener('click', function() {
            let input = document.createElement('input');
            input.type = 'file';
            input.accept = 'video/mp4';
            input.onchange = function() {
                if (input.files.length > 0) {
                    processVideoFile(input.files[0]);
                }
            };
            input.click();
        });


        dropArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            e.stopPropagation();
            dropArea.classList.add('dragover');
        }, false);

        dropArea.addEventListener('dragleave', (e) => {
            e.preventDefault();
            e.stopPropagation();
            dropArea.classList.remove('dragover');
        }, false);

        dropArea.addEventListener('drop', (e) => {
            e.preventDefault();
            e.stopPropagation();
            dropArea.classList.remove('dragover');

            let files = e.dataTransfer.files;
            if (files.length > 0) {
                processVideoFile(files[0]);
            }
        }, false);
    }
})();





(function() {
    'use strict';


    const CONFIG_KEY = 'moreColorPalettes';

    const DEFAULT_CONFIG = {
        enabled: false,
        colors: [
            { name: "Teal", hex: "#D4FFF0", enabled: true },
            { name: "Gray", hex: "#F2F2F2", enabled: true },
            { name: "Skin", hex: "#FFE9C2", enabled: true },
            { name: "Purple", hex: "#5231EB", enabled: true },
            { name: "Maroon", hex: "#800000", enabled: true },
            { name: "Pink", hex: "#FF0048", enabled: true }
        ]
    };


    function loadConfig() {
        try {
            const saved = localStorage.getItem(CONFIG_KEY);
            return saved ? { ...DEFAULT_CONFIG, ...JSON.parse(saved) } : DEFAULT_CONFIG;
        } catch (e) {
            console.warn("Error loading color palette config:", e);
            return DEFAULT_CONFIG;
        }
    }

    function saveConfig(config) {
        try {
            localStorage.setItem(CONFIG_KEY, JSON.stringify(config));
        } catch (e) {
            console.warn("Error saving color palette config:", e);
        }
    }

    let currentConfig = loadConfig();
    let isInitialized = false;
    let styleElement = null;
    let gameTriangleElement = null;


    function createConfigPanel() {
        if (document.getElementById('colorPaletteConfigPanel')) return;

        const panel = document.createElement('div');
        panel.id = 'colorPaletteConfigPanel';
        panel.style.cssText = `
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #ffffff;
            border: 2px solid #e0e0e0;
            padding: 25px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            z-index: 10000;
            min-width: 400px;
            color: #333333;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-height: 80vh;
            overflow-y: auto;
        `;

        const colorsHTML = currentConfig.colors.map((color, index) => `
            <div style="
                display: flex;
                align-items: center;
                margin-bottom: 8px;
                padding: 8px;
                border-radius: 4px;
                border: 1px solid #e0e0e0;
            ">
                <input type="checkbox"
                       id="color_${index}"
                       ${color.enabled ? 'checked' : ''}
                       style="margin-right: 10px; accent-color: #3498db;">
                <div style="
                    width: 30px;
                    height: 30px;
                    background: ${color.hex};
                    border: 1px solid #ccc;
                    border-radius: 4px;
                    margin-right: 10px;
                "></div>
                <span style="flex: 1; color: #34495e;">${color.name}</span>
                <input type="color"
                       id="colorPicker_${index}"
                       value="${color.hex}"
                       style="width: 40px; height: 30px; border: none; border-radius: 4px;">
            </div>
        `).join('');

        panel.innerHTML = `
            <h3 style="
                margin: 0 0 20px 0;
                text-align: center;
                color: #2c3e50;
                font-weight: normal;
                font-size: 18px;
                border-bottom: 1px solid #ecf0f1;
                padding-bottom: 10px;
            ">
                🎨 Configuración de Paleta de Colores
            </h3>

            <div style="margin-bottom: 20px;">
                <label style="
                    display: flex;
                    align-items: center;
                    cursor: pointer;
                    padding: 10px;
                    border-radius: 4px;
                    background: #f8f9fa;
                    border: 1px solid #e0e0e0;
                ">
                    <input type="checkbox"
                           id="enableColorPalettes"
                           ${currentConfig.enabled ? 'checked' : ''}
                           style="margin-right: 10px; accent-color: #3498db;">
                    <span style="font-weight: 600; color: #2c3e50;">
                        Habilitar colores personalizados
                    </span>
                </label>
            </div>

            <div id="colorsContainer" style="${!currentConfig.enabled ? 'opacity: 0.5; pointer-events: none;' : ''}">
                <h4 style="margin: 0 0 15px 0; color: #34495e; font-size: 16px;">
                    Colores disponibles:
                </h4>
                ${colorsHTML}
            </div>

            <div style="
                display: flex;
                justify-content: space-between;
                margin-top: 25px;
                gap: 10px;
                border-top: 1px solid #ecf0f1;
                padding-top: 15px;
            ">
                <button id="saveColorPaletteConfig" style="
                    background: #3498db;
                    color: white;
                    border: none;
                    padding: 10px 20px;
                    border-radius: 4px;
                    cursor: pointer;
                    font-weight: 500;
                    font-size: 14px;
                    transition: background-color 0.2s;
                    flex: 1;
                ">💾 Guardar</button>

                <button id="resetColorPaletteConfig" style="
                    background: #e74c3c;
                    color: white;
                    border: none;
                    padding: 10px 20px;
                    border-radius: 4px;
                    cursor: pointer;
                    font-weight: 500;
                    font-size: 14px;
                    transition: background-color 0.2s;
                    flex: 1;
                ">🔄 Restablecer</button>

                <button id="closeColorPalettePanel" style="
                    background: #95a5a6;
                    color: white;
                    border: none;
                    padding: 10px 20px;
                    border-radius: 4px;
                    cursor: pointer;
                    font-weight: 500;
                    font-size: 14px;
                    transition: background-color 0.2s;
                    flex: 1;
                ">❌ Cerrar</button>
            </div>
        `;

        document.body.appendChild(panel);


        const enableCheckbox = document.getElementById('enableColorPalettes');
        const colorsContainer = document.getElementById('colorsContainer');

        enableCheckbox.addEventListener('change', function() {
            colorsContainer.style.opacity = this.checked ? '1' : '0.5';
            colorsContainer.style.pointerEvents = this.checked ? 'auto' : 'none';
        });


        currentConfig.colors.forEach((color, index) => {
            const colorPicker = document.getElementById(`colorPicker_${index}`);
            const colorPreview = colorPicker.previousElementSibling;

            colorPicker.addEventListener('change', function() {
                colorPreview.style.background = this.value;
            });
        });

        document.getElementById('saveColorPaletteConfig').addEventListener('click', saveConfigFromPanel);
        document.getElementById('resetColorPaletteConfig').addEventListener('click', resetConfig);
        document.getElementById('closeColorPalettePanel').addEventListener('click', closeConfigPanel);
    }

    function showConfigPanel() {
        createConfigPanel();
        document.getElementById('colorPaletteConfigPanel').style.display = 'block';
    }

    function closeConfigPanel() {
        const panel = document.getElementById('colorPaletteConfigPanel');
        if (panel) panel.style.display = 'none';
    }

    function saveConfigFromPanel() {
        const newConfig = {
            enabled: document.getElementById('enableColorPalettes').checked,
            colors: currentConfig.colors.map((color, index) => ({
                ...color,
                enabled: document.getElementById(`color_${index}`).checked,
                hex: document.getElementById(`colorPicker_${index}`).value
            }))
        };

        currentConfig = newConfig;
        saveConfig(currentConfig);


        cleanup();
        if (currentConfig.enabled) {
            initialize();
        }

        closeConfigPanel();


        if (window.cubeEngine && window.cubeEngine.notify) {
            window.cubeEngine.notify("success", "Configuración de colores guardada correctamente.");
        }
    }

    function resetConfig() {
        currentConfig = { ...DEFAULT_CONFIG };
        saveConfig(currentConfig);
        closeConfigPanel();
        cleanup();

        if (window.cubeEngine && window.cubeEngine.notify) {
            window.cubeEngine.notify("info", "Configuración de colores restablecida.");
        }
    }


    function addConfigButton() {

        const controlsContainer = document.querySelector('.cube-engine-controls') ||
                                document.querySelector('#cubeEngineControls') ||
                                document.querySelector('.drawcontrols');

        if (controlsContainer && !document.getElementById('colorPaletteConfigBtn')) {
            const configBtn = document.createElement('button');
            configBtn.id = 'colorPaletteConfigBtn';
            configBtn.innerHTML = '🎨';
            configBtn.title = 'Configurar Paleta de Colores';
            configBtn.style.cssText = `
                background: #3498db;
                color: white;
                border: none;
                padding: 8px 12px;
                border-radius: 4px;
                cursor: pointer;
                margin: 2px;
                font-size: 16px;
            `;

            configBtn.addEventListener('click', showConfigPanel);
            controlsContainer.appendChild(configBtn);
        }
    }


    const style = document.createElement('style');
    style.textContent = `
        #drawcontrols {
            flex-wrap: wrap;
        }
        .drawcontrols-button.custom-color-button {
            box-shadow: 0 0 2px rgba(0,0,0,0.3);
        }
        .drawcontrols-button.custom-color-button.custom-active-color {
            box-shadow: 0 0 5px 2px #007bff;
            border: 1px solid #007bff;
        }
        #colorpicker-cursor {
            transition: left 0.1s ease-out;
        }
    `;

    function initialize() {
        if (!currentConfig.enabled || isInitialized) return;

        document.head.appendChild(style);
        styleElement = style;

        const interval = setInterval(() => {
            if (addCustomColors()) {
                clearInterval(interval);
                isInitialized = true;
            }
        }, 100);


        setTimeout(() => clearInterval(interval), 10000);
    }

    function cleanup() {
        if (styleElement && styleElement.parentNode) {
            styleElement.parentNode.removeChild(styleElement);
            styleElement = null;
        }


        document.querySelectorAll('.custom-color-button').forEach(btn => {
            btn.remove();
        });

        isInitialized = false;
    }

    function findGameTriangle() {
        if (gameTriangleElement && document.body.contains(gameTriangleElement)) {
            return gameTriangleElement;
        }
        gameTriangleElement = document.getElementById('colorpicker-cursor');
        return gameTriangleElement;
    }

    function updateTrianglePosition(targetButton) {
        const triangle = findGameTriangle();
        if (!triangle || !targetButton) return;

        const buttonContainer = document.getElementById('drawcontrols-colors') ||
                              document.getElementById('drawcontrols');
        if (!buttonContainer) return;

        const buttonRect = targetButton.getBoundingClientRect();
        const containerRect = buttonContainer.getBoundingClientRect();

        const buttonCenterRelativeToContainer = (buttonRect.left - containerRect.left) + (buttonRect.width / 2);
        const triangleWidth = triangle.offsetWidth || 8;
        const newLeft = buttonCenterRelativeToContainer - (triangleWidth / 2);

        triangle.style.left = `${newLeft}px`;
    }

    function addCustomColors() {
        if (!currentConfig.enabled) return false;

        const drawControlsContainer = document.getElementById('drawcontrols');
        if (!drawControlsContainer) return false;

        const actualButtonContainer = document.getElementById('drawcontrols-colors') || drawControlsContainer;

        if (document.getElementById('custom-color-teal')) return true;

        const colorPickerButtonOriginal = actualButtonContainer.querySelector('.drawcontrols-colorpicker');
        const proxyGameButton = actualButtonContainer.querySelector('.drawcontrols-button.drawcontrols-color:not(.drawcontrols-colorpicker)');

        if (!proxyGameButton) {
            console.error("Cube Engine More Color Palettes: Proxy game button not found.");
            return false;
        }

        findGameTriangle();


        const enabledColors = currentConfig.colors.filter(color => color.enabled);

        enabledColors.forEach(colorInfo => {
            const newButton = document.createElement('div');
            newButton.className = 'drawcontrols-button drawcontrols-color custom-color-button';
            newButton.style.backgroundColor = colorInfo.hex;
            newButton.dataset.ctrlgroup = 'color';
            newButton.id = `custom-color-${colorInfo.name.toLowerCase().replace(/\s/g, '-')}`;

            newButton.addEventListener('click', function(event) {
                const originalProxyColor = proxyGameButton.style.backgroundColor;
                proxyGameButton.style.backgroundColor = this.style.backgroundColor;
                proxyGameButton.click();

                requestAnimationFrame(() => {
                    proxyGameButton.style.backgroundColor = originalProxyColor;
                    updateTrianglePosition(this);
                });

                document.querySelectorAll('.custom-color-button.custom-active-color').forEach(btn => {
                    btn.classList.remove('custom-active-color');
                });
                this.classList.add('custom-active-color');

                actualButtonContainer.querySelectorAll('.drawcontrols-button.drawcontrols-color:not(.custom-color-button):not(.drawcontrols-colorpicker)').forEach(gameBtn => {
                    gameBtn.classList.remove('active');
                });
            });

            if (colorPickerButtonOriginal) {
                actualButtonContainer.insertBefore(newButton, colorPickerButtonOriginal);
            } else {
                actualButtonContainer.appendChild(newButton);
            }
        });


        actualButtonContainer.querySelectorAll('.drawcontrols-button.drawcontrols-color:not(.custom-color-button):not(.drawcontrols-colorpicker)').forEach(gameBtn => {
            gameBtn.addEventListener('click', function() {
                document.querySelectorAll('.custom-color-button.custom-active-color').forEach(customBtn => {
                    customBtn.classList.remove('custom-active-color');
                });
            });
        });

        console.log("Cube Engine More Color Palettes: Initialized with custom options.");
        return true;
    }


    window.cubeEngineColorPalettes = {
        showConfig: showConfigPanel,
        enable: () => {
            currentConfig.enabled = true;
            saveConfig(currentConfig);
            initialize();
        },
        disable: () => {
            currentConfig.enabled = false;
            saveConfig(currentConfig);
            cleanup();
        },
        isEnabled: () => currentConfig.enabled,
        getConfig: () => currentConfig
    };


    if (currentConfig.enabled) {
        initialize();
    }


    setTimeout(addConfigButton, 1000);


    const observer = new MutationObserver(() => {
        addConfigButton();
    });
    observer.observe(document.body, { childList: true, subtree: true });

})();



(function insertMoreColorPalettesMenuItem() {
  const tryInsert = () => {

    const menu = document.querySelector('.dropdown-menu.show');
    if (!menu) return false;


    const items = Array.from(menu.querySelectorAll('.dropdown-item'));
    const autoRoomItem = items.find(a => a.textContent.trim().includes('Auto Room Settings'));
    if (!autoRoomItem) return false;


    if (menu.querySelector('#moreColorPalettesMenuItem')) return true;


    const li = document.createElement('a');
    li.className = 'dropdown-item';
    li.href = '#';
    li.id = 'moreColorPalettesMenuItem';
    li.innerHTML = '<i class="fas fa-palette"></i> More Color Palettes';

    autoRoomItem.insertAdjacentElement('afterend', li);


    li.addEventListener('click', (e) => {
      e.preventDefault();
      if (window.cubeEngineColorPalettes?.showConfig) {
        window.cubeEngineColorPalettes.showConfig();
      } else if (window.cubeEngineColorPalettes?.enable) {
        window.cubeEngineColorPalettes.enable();
      }
    });

    return true;
  };


  if (tryInsert()) return;


  const observer = new MutationObserver(() => {
    if (tryInsert()) observer.disconnect();
  });
  observer.observe(document.body, { childList: true, subtree: true });


  let attempts = 0;
  const timer = setInterval(() => {
    attempts++;
    if (tryInsert() || attempts > 100) clearInterval(timer);
  }, 100);
})();


(function() {
    'use strict';


    const CONFIG_KEY = 'drawariaSimulatedBrusher';
    const DEFAULT_CONFIG = {
        enabled: false,
        brushColor: '#000000',
        brushSize: 15,
        sendToServer: true
    };


    function loadConfig() {
        try {
            const saved = localStorage.getItem(CONFIG_KEY);
            return saved ? { ...DEFAULT_CONFIG, ...JSON.parse(saved) } : DEFAULT_CONFIG;
        } catch (e) {
            console.warn("Error loading brusher config:", e);
            return DEFAULT_CONFIG;
        }
    }

    function saveConfig(config) {
        try {
            localStorage.setItem(CONFIG_KEY, JSON.stringify(config));
        } catch (e) {
            console.warn("Error saving brusher config:", e);
        }
    }

    let currentConfig = loadConfig();
    let isInitialized = false;
    let socket = null;
    let modalOverlay = null;
    let modalContent = null;
    let canvas = null;
    let ctx = null;
    let drawing = false;
    let lastX, lastY;


    function initWebSocketHook() {
        const originalSend = WebSocket.prototype.send;
        WebSocket.prototype.send = function (...args) {
            if (!socket) {
                socket = this;
            }
            return originalSend.apply(this, args);
        };
    }


    function createModal() {
        if (modalOverlay) return;


        modalOverlay = document.createElement('div');
        modalOverlay.id = 'brusherModalOverlay';
        modalOverlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.6);
            z-index: 10000;
            display: none;
            justify-content: center;
            align-items: center;
            backdrop-filter: blur(3px);
            animation: fadeIn 0.3s ease-out;
        `;


        modalContent = document.createElement('div');
        modalContent.id = 'brusherModalContent';
        modalContent.style.cssText = `
            background: white;
            border-radius: 12px;
            padding: 25px;
            max-width: 400px;
            width: 90%;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            position: relative;
            animation: modalSlideIn 0.3s ease-out;
        `;

        modalContent.innerHTML = `
            <div style="
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 20px;
                border-bottom: 2px solid #f1f1f1;
                padding-bottom: 15px;
            ">
                <h3 style="
                    margin: 0;
                    color: #2c3e50;
                    font-size: 20px;
                    font-weight: 600;
                    display: flex;
                    align-items: center;
                    gap: 10px;
                ">
                    🎨 Simulated Brusher
                </h3>
                <button id="closeBrusherModal" style="
                    background: #e74c3c;
                    color: white;
                    border: none;
                    padding: 8px 12px;
                    border-radius: 6px;
                    cursor: pointer;
                    font-size: 14px;
                    font-weight: 500;
                    transition: background-color 0.2s;
                ">✕ Cerrar</button>
            </div>

            <div style="display: flex; flex-direction: column; gap: 20px;">
                <div style="
                    display: flex;
                    align-items: center;
                    gap: 15px;
                    padding: 15px;
                    background: #f8f9fa;
                    border-radius: 8px;
                    border: 1px solid #e9ecef;
                ">
                    <label style="
                        font-weight: 600;
                        color: #495057;
                        min-width: 60px;
                    ">Color:</label>
                    <input type="color" id="brushColor" value="${currentConfig.brushColor}" style="
                        width: 50px;
                        height: 40px;
                        border: 2px solid #dee2e6;
                        border-radius: 6px;
                        cursor: pointer;
                        background: transparent;
                    ">
                    <span id="colorValue" style="
                        font-family: monospace;
                        background: #ffffff;
                        padding: 5px 10px;
                        border-radius: 4px;
                        border: 1px solid #dee2e6;
                        font-size: 12px;
                        color: #6c757d;
                    ">${currentConfig.brushColor}</span>
                </div>

                <div style="
                    padding: 15px;
                    background: #f8f9fa;
                    border-radius: 8px;
                    border: 1px solid #e9ecef;
                ">
                    <div style="display: flex; align-items: center; gap: 15px; margin-bottom: 10px;">
                        <label style="
                            font-weight: 600;
                            color: #495057;
                            min-width: 60px;
                        ">Grosor:</label>
                        <input type="range" id="brushSize" min="1" max="20" value="${currentConfig.brushSize}" style="
                            flex: 1;
                            height: 6px;
                            border-radius: 3px;
                            background: #dee2e6;
                            outline: none;
                            cursor: pointer;
                        ">
                        <span id="sizeValue" style="
                            background: #ffffff;
                            padding: 5px 10px;
                            border-radius: 4px;
                            border: 1px solid #dee2e6;
                            font-weight: 600;
                            color: #495057;
                            min-width: 50px;
                            text-align: center;
                        ">${currentConfig.brushSize}px</span>
                    </div>
                    <div style="
                        width: 100%;
                        height: 20px;
                        background: #ffffff;
                        border: 1px solid #dee2e6;
                        border-radius: 4px;
                        position: relative;
                        overflow: hidden;
                    ">
                        <div id="brushPreview" style="
                            width: ${currentConfig.brushSize * 2}px;
                            height: ${currentConfig.brushSize * 2}px;
                            background: ${currentConfig.brushColor};
                            border-radius: 50%;
                            position: absolute;
                            top: 50%;
                            left: 10px;
                            transform: translateY(-50%);
                            transition: all 0.2s ease;
                        "></div>
                    </div>
                </div>

                <div style="
                    display: flex;
                    align-items: center;
                    gap: 15px;
                    padding: 15px;
                    background: #e3f2fd;
                    border-radius: 8px;
                    border: 1px solid #bbdefb;
                ">
                    <input type="checkbox" id="sendToServer" ${currentConfig.sendToServer ? 'checked' : ''} style="
                        width: 18px;
                        height: 18px;
                        cursor: pointer;
                        accent-color: #2196f3;
                    ">
                    <label for="sendToServer" style="
                        font-weight: 500;
                        color: #1565c0;
                        cursor: pointer;
                        flex: 1;
                    ">📡 Enviar trazos al servidor (otros usuarios verán tus dibujos)</label>
                </div>

                <div style="
                    display: flex;
                    gap: 10px;
                    margin-top: 10px;
                ">
                    <button id="clearCanvas" style="
                        flex: 1;
                        background: #f44336;
                        color: white;
                        border: none;
                        padding: 12px 20px;
                        border-radius: 6px;
                        cursor: pointer;
                        font-size: 14px;
                        font-weight: 500;
                        transition: background-color 0.2s;
                    ">🗑️ Limpiar Canvas</button>

                    <button id="minimizeBrusher" style="
                        background: #4caf50;
                        color: white;
                        border: none;
                        padding: 12px 20px;
                        border-radius: 6px;
                        cursor: pointer;
                        font-size: 14px;
                        font-weight: 500;
                        transition: background-color 0.2s;
                    ">📦 Minimizar</button>
                </div>
            </div>
        `;


        const style = document.createElement('style');
        style.textContent = `
            @keyframes fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }

            @keyframes modalSlideIn {
                from {
                    transform: scale(0.7) translateY(-50px);
                    opacity: 0;
                }
                to {
                    transform: scale(1) translateY(0);
                    opacity: 1;
                }
            }

            #brushSize::-webkit-slider-thumb {
                appearance: none;
                width: 20px;
                height: 20px;
                border-radius: 50%;
                background: #2196f3;
                cursor: pointer;
                box-shadow: 0 2px 4px rgba(0,0,0,0.2);
            }

            #closeBrusherModal:hover, #clearCanvas:hover, #minimizeBrusher:hover {
                transform: translateY(-1px);
                box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            }
        `;
        document.head.appendChild(style);

        modalOverlay.appendChild(modalContent);
        document.body.appendChild(modalOverlay);


        setupModalEvents();
    }

    function setupModalEvents() {

        document.getElementById('brushSize').addEventListener('input', function() {
            const value = this.value;
            document.getElementById('sizeValue').textContent = value + 'px';
            const preview = document.getElementById('brushPreview');
            preview.style.width = (value * 2) + 'px';
            preview.style.height = (value * 2) + 'px';
            currentConfig.brushSize = parseInt(value);
            saveConfig(currentConfig);
        });


        document.getElementById('brushColor').addEventListener('change', function() {
            const value = this.value;
            document.getElementById('colorValue').textContent = value;
            document.getElementById('brushPreview').style.background = value;
            currentConfig.brushColor = value;
            saveConfig(currentConfig);
        });


        document.getElementById('sendToServer').addEventListener('change', function() {
            currentConfig.sendToServer = this.checked;
            saveConfig(currentConfig);
        });


        document.getElementById('clearCanvas').addEventListener('click', clearCanvas);
        document.getElementById('closeBrusherModal').addEventListener('click', hideBrusher);
        document.getElementById('minimizeBrusher').addEventListener('click', hideBrusher);


        modalOverlay.addEventListener('click', function(e) {
            if (e.target === modalOverlay) {
                hideBrusher();
            }
        });


        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape' && modalOverlay && modalOverlay.style.display === 'flex') {
                hideBrusher();
            }
        });
    }


    function sendDrawCommand(x1, y1, x2, y2, color, thickness) {
        if (!socket || !document.getElementById('sendToServer').checked) return;

        const normX1 = (x1 / canvas.width).toFixed(4);
        const normY1 = (y1 / canvas.height).toFixed(4);
        const normX2 = (x2 / canvas.width).toFixed(4);
        const normY2 = (y2 / canvas.height).toFixed(4);

        const command = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - thickness},"${color}",0,0,{}]]`;
        socket.send(command);
    }

    function applyBrushSettings() {
        if (!ctx) return;
        ctx.strokeStyle = document.getElementById('brushColor').value;
        ctx.lineWidth = document.getElementById('brushSize').value;
        ctx.lineCap = 'round';
        ctx.lineJoin = 'round';
    }

    function setupCanvasEvents() {
        canvas = document.querySelector('#canvas');
        if (!canvas) return false;

        ctx = canvas.getContext('2d');
        canvas.style.pointerEvents = 'auto';

        canvas.addEventListener('mousedown', function(event) {
            drawing = true;
            lastX = event.offsetX;
            lastY = event.offsetY;
            applyBrushSettings();
        });

        canvas.addEventListener('mousemove', function(event) {
            if (drawing) {
                applyBrushSettings();

                ctx.beginPath();
                ctx.moveTo(lastX, lastY);
                ctx.lineTo(event.offsetX, event.offsetY);
                ctx.stroke();

                sendDrawCommand(
                    lastX,
                    lastY,
                    event.offsetX,
                    event.offsetY,
                    document.getElementById('brushColor').value,
                    parseInt(document.getElementById('brushSize').value)
                );

                lastX = event.offsetX;
                lastY = event.offsetY;
            }
        });

        canvas.addEventListener('mouseup', function() {
            drawing = false;
        });

        return true;
    }

    function clearCanvas() {
        if (!ctx) return;
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        if (socket && document.getElementById('sendToServer').checked) {
            const clearCommand = `42["clearcmd",0,[]]`;
            socket.send(clearCommand);
        }


        if (window.cubeEngine && window.cubeEngine.notify) {
            window.cubeEngine.notify("info", "Canvas limpiado correctamente");
        }
    }

    function initialize() {
        if (isInitialized) return;

        initWebSocketHook();
        createModal();

        const interval = setInterval(() => {
            if (setupCanvasEvents()) {
                clearInterval(interval);
                isInitialized = true;
                console.log("Drawaria Simulated Brusher: Initialized successfully");
            }
        }, 500);

        setTimeout(() => clearInterval(interval), 10000);
    }

    function cleanup() {
        if (modalOverlay && modalOverlay.parentNode) {
            modalOverlay.parentNode.removeChild(modalOverlay);
            modalOverlay = null;
            modalContent = null;
        }

        const canvasElement = document.querySelector('#canvas');
        if (canvasElement) {
            canvasElement.style.pointerEvents = '';
        }

        isInitialized = false;
        socket = null;
        canvas = null;
        ctx = null;
    }

    function showBrusher() {
        if (!currentConfig.enabled) {
            enable();
        }
        if (modalOverlay) {
            modalOverlay.style.display = 'flex';
        }
    }

    function hideBrusher() {
        if (modalOverlay) {
            modalOverlay.style.display = 'none';
        }
    }

    function enable() {
        currentConfig.enabled = true;
        saveConfig(currentConfig);
        initialize();
        showBrusher();
    }

    function disable() {
        currentConfig.enabled = false;
        saveConfig(currentConfig);
        cleanup();
    }

    function toggle() {
        if (currentConfig.enabled) {
            disable();
        } else {
            enable();
        }
    }


    window.drawariaSimulatedBrusher = {
        show: showBrusher,
        hide: hideBrusher,
        enable: enable,
        disable: disable,
        toggle: toggle,
        isEnabled: () => currentConfig.enabled,
        getConfig: () => currentConfig
    };

    if (currentConfig.enabled) {
        initialize();
    }

})();


(function insertSimulatedBrusherMenuItem() {
    const tryInsert = () => {
        const menu = document.querySelector('.dropdown-menu.show');
        if (!menu) return false;

        const items = Array.from(menu.querySelectorAll('.dropdown-item'));
        const moreColorPalettesItem = items.find(a => a.textContent.trim().includes('More Color Palettes'));
        if (!moreColorPalettesItem) return false;

        if (menu.querySelector('#simulatedBrusherMenuItem')) return true;

        const li = document.createElement('a');
        li.className = 'dropdown-item';
        li.href = '#';
        li.id = 'simulatedBrusherMenuItem';
        li.innerHTML = '<i class="fas fa-paint-brush"></i> Simulated Brusher';

        moreColorPalettesItem.insertAdjacentElement('afterend', li);

        li.addEventListener('click', (e) => {
            e.preventDefault();
            if (window.drawariaSimulatedBrusher?.toggle) {
                window.drawariaSimulatedBrusher.toggle();

                const icon = li.querySelector('i');
                const isEnabled = window.drawariaSimulatedBrusher.isEnabled();
                icon.className = isEnabled ? 'fas fa-paint-brush' : 'far fa-paint-brush';
                li.innerHTML = `${icon.outerHTML} Simulated Brusher ${isEnabled ? '(ON)' : '(OFF)'}`;

                if (window.cubeEngine && window.cubeEngine.notify) {
                    window.cubeEngine.notify(
                        isEnabled ? "success" : "info",
                        `Simulated Brusher ${isEnabled ? 'habilitado' : 'deshabilitado'}`
                    );
                }
            }
        });

        const isEnabled = window.drawariaSimulatedBrusher?.isEnabled() || false;
        li.innerHTML = `<i class="fa${isEnabled ? 's' : 'r'} fa-paint-brush"></i> Simulated Brusher ${isEnabled ? '(ON)' : '(OFF)'}`;

        return true;
    };

    if (tryInsert()) return;

    const observer = new MutationObserver(() => {
        if (tryInsert()) observer.disconnect();
    });
    observer.observe(document.body, { childList: true, subtree: true });

    let attempts = 0;
    const timer = setInterval(() => {
        attempts++;
        if (tryInsert() || attempts > 100) clearInterval(timer);
    }, 100);
})();







(function() {
  'use strict';


    const selectors = ['.chatmessage.systemchatmessage7',
        '.chatmessage.systemchatmessage'
    ];

    function removeMessages() {
        selectors.forEach(selector => {
            document.querySelectorAll(selector).forEach(el => el.remove());
        });
    }


    new MutationObserver(removeMessages).observe(document.documentElement, {childList: true, subtree: true});


    removeMessages();

})();








var FastManagerTool;









function getFastManagerGameRoomId() {

    const invUrlInput = document.querySelector("#invurl");
    if (invUrlInput && invUrlInput.value) {
        const match = String(invUrlInput.value).match(/([a-f0-9.-]+?)$/gi);
        if (match && match[0]) {
            return match[0];
        }
    }


    const pathname = window.location.pathname;
    if (pathname.startsWith("/room/")) {
        const roomId = pathname.substring(6);
        if (roomId && roomId.length > 0) {
            return roomId;
        }
    }
    return null;
}


function parseFastManagerServerUrl(serverUrlSegment) {
    var prefix = String(serverUrlSegment).length == 1 ? `sv${serverUrlSegment}.` : "";
    let baseUrl = `wss://${prefix}drawaria.online/socket.io/?`;
    let params = `EIO=3&transport=websocket`;



    if (prefix === "") {
        params = `EIO=3&transport=websocket`;
    } else {
        params = `sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`;
    }
    return baseUrl + params;
}


function parseFastManagerRoomId(any) {
    const sAny = String(any);
    const match = sAny.match(/([a-f0-9.-]+?)$/gi);
    if (match && match[0]) {
        return match[0];
    }
    return sAny;
}


function parseFastManagerSocketIOEvent(prefix_length, event_data) {
    try {
        return JSON.parse(event_data.slice(prefix_length));
    } catch (error) {

        return null;
    }
}


function parseFastManagerAvatarURL(arr = []) {
    return `https://drawaria.online/avatar/cache/${arr.length > 0 ? arr.join(".") : "default"}.jpg`;
}


const FastManagerEmits = {
    chatmsg: function (message) { return `42${JSON.stringify(["chatmsg", message])}`; },
    startplay: function (room, name, avatar) {
        return `420${JSON.stringify([
            "startplay", name, room.type, "en", room.id, null,
            [null, "https://drawaria.online/", 1000, 1000, [null, avatar[0], avatar[1]], null]
        ])}`;
    },

    playerafk: function () { return `42${JSON.stringify(["playerafk"])}`; },
    setstatusflag: function (flagid, isactive) { return `42${JSON.stringify(["clientcmd", 3, [flagid, isactive]])}`; },
    moveavatar: function (positionX, positionY) {
        return `42${JSON.stringify([
            "clientcmd", 103,
            [1e4 * Math.floor((positionX / 100) * 1e4) + Math.floor((positionY / 100) * 1e4), false]
        ])}`;
    },


    disconnectSignal: function() { return `41`; }
};



class FastManagerBotClientInterface {
    constructor(name = "FastBot", avatar = ["cf19b8f0-cf31-11ed-9ece-d584b24f60dc", "1680377222354"]) {
        this.name = name;
        this.avatar = avatar;

        this.socket = null;
        this.interval_id = 0;
        this.isReconnecting = false;

        this.room = {
            id: null,
            config: null,
            type: 2,
            players: []
        };
        this.customObservers = [];

        this._onSocketOpenHandler = this._onSocketOpenHandler.bind(this);
        this._onSocketMessageHandler = this._onSocketMessageHandler.bind(this);
        this._onSocketCloseHandler = this._onSocketCloseHandler.bind(this);
        this._onSocketErrorHandler = this._onSocketErrorHandler.bind(this);
    }

    getReadyState() { return this.socket && this.socket.readyState === WebSocket.OPEN; }
    getClientName() { return this.name; }
    setClientName(newName) { this.name = newName; }
    setClientIcon(newAvatar) { this.avatar = newAvatar; }

    _onSocketOpenHandler() {
        clearInterval(this.interval_id);
        this.interval_id = setInterval(() => {
            if (!this.getReadyState()) { clearInterval(this.interval_id); return; }
            this.send('2');
        }, 25000);

        if (this.isReconnecting) {
            this.send('41');
            this.send('40');
            this.isReconnecting = false;
        }
    }

    _onSocketMessageHandler(message_event) {
        var prefix = String(message_event.data).match(/(^\d+)/)?.[0] || "";

        if (prefix === "40") {
            if (this.room.id) {
                this.send(FastManagerEmits.startplay(this.room, this.name, this.avatar));
            }
        }

        var data = parseFastManagerSocketIOEvent(prefix.length, message_event.data) || [];
        if (data && data.length === 1 && data[0] && data[0].players) { this.room.players = data[0].players; }
        else if (data && data.length > 1) { var eventName = data.shift(); this.customObservers.forEach((listener) => { if (listener.event === eventName) { listener.callback(data); } }); }
    }

    _onSocketCloseHandler(event) { clearInterval(this.interval_id); this.socket = null; this.isReconnecting = false; }
    _onSocketErrorHandler(event) { clearInterval(this.interval_id); this.socket = null; this.isReconnecting = false; }

    connect(serverUrlSegment = "") {
        if (this.socket && (this.socket.readyState === WebSocket.OPEN || this.socket.readyState === WebSocket.CONNECTING)) {
            return;
        }

        const fullServerUrl = parseFastManagerServerUrl(serverUrlSegment);
        this.socket = new WebSocket(fullServerUrl);

        this.socket.addEventListener("open", this._onSocketOpenHandler);
        this.socket.addEventListener("message", this._onSocketMessageHandler);
        this.socket.addEventListener("close", this._onSocketCloseHandler);
        this.socket.addEventListener("error", this._onSocketErrorHandler);
    }

    disconnect() {
        if (!this.socket || this.socket.readyState === WebSocket.CLOSED) { return; }
        clearInterval(this.interval_id);
        this.send(FastManagerEmits.disconnectSignal());
        this.socket.close();
        this.socket = null;
        this.isReconnecting = false;
    }

    reconnect() {
        if (!this.room.id) { return; }

        this.isReconnecting = true;

        if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.send('41');
            this.send('40');
            this.isReconnecting = false;
        } else {
            const serverIdSegment = this.room.id.includes(".") ? this.room.id.slice(-1) : "";
            this.connect(serverIdSegment);
        }
    }

    enterRoom(roomid, roomTypeOverride = null) {
        this.room.id = parseFastManagerRoomId(roomid);
        if (roomTypeOverride !== null) { this.room.type = roomTypeOverride; }
        else { const parts = String(roomid).split('.'); if (parts.length === 1 && !isNaN(parseInt(parts[0], 16))) { this.room.type = 0; } else { this.room.type = 2; } }

        this.reconnect();
    }

    send(data) { if (this.getReadyState()) { this.socket.send(data); } }
    emit(event, ...data) { var emitter = FastManagerEmits[event]; if (emitter) { this.send(emitter(...data)); } }
    _destroy() { this.disconnect(); }
}










FastManagerTool = class extends QBit {
    #fastBots = [];
    #ui = {};
    #botReconnectTimer = null;
    #autoEnterActive = false;
    #autoEnterIntervalId = null;

    constructor() {
        super("🚚Bot Fast Manager", '<i class="fas fa-shipping-fast"></i>');
        this._onStartup();
    }

    _onStartup() {
        this.#loadInterface();
        this.#setupEventListeners();
        this.#refreshStatus();
        this.#setupBotReconnectCheck();


    }


    _createToggleButton(id, labelText) {
        const wrapper = domMake.Tree("div", { class: "module-form-group" });
        const button = domMake.Button(labelText);
        button.id = `${this.identifier}-${id}`;
        button.classList.add('module-toggle-button');
        wrapper.appendChild(button);
        return wrapper;
    }

    #loadInterface() {
        const container = domMake.Tree("div", { id: `${this.identifier}-container`, class: "module-section" });
        this.htmlElements.section.appendChild(container);

        container.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Control Rápido de Bots"]));

        const addControlsRow = domMake.Row({ class: "module-btn-group" });
        this.#ui.addBotBtn = domMake.Button('<i class="fas fa-sign-in-alt"></i> Unir Bot (Enter)');
        addControlsRow.append(this.#ui.addBotBtn);
        container.appendChild(addControlsRow);

        const actionControlsRow = domMake.Row({ class: "module-btn-group" });
        this.#ui.disconnectBtn = domMake.Button('<i class="fas fa-plug"></i> Desconectar Bot');
        this.#ui.removeBtn = domMake.Button('<i class="fas fa-trash"></i> Eliminar Bot');
        actionControlsRow.append(this.#ui.disconnectBtn, this.#ui.removeBtn);
        container.appendChild(actionControlsRow);


        const autoEnterToggleRow = this._createToggleButton('auto-enter-toggle', 'Auto-Enter');
        this.#ui.autoEnterToggle = autoEnterToggleRow.querySelector('.module-toggle-button');
        container.appendChild(autoEnterToggleRow);

        container.appendChild(domMake.Tree("div", { class: "module-section-title", style: "margin-top: 15px;" }, ["Estado del Bot Principal"]));
        this.#ui.statusDiv = domMake.Tree("div", { id: `${this.identifier}-status-display`, class: "module-status-area", style: "text-align: center; font-weight: bold;" });
        container.appendChild(this.#ui.statusDiv);

        container.appendChild(domMake.Tree("p", { style: "font-size: 0.8em; margin-top: 15px; color: var(--info); text-align: center;" },
            ["Este módulo controla un único bot principal para reconexiones rápidas."]));
    }

    #setupEventListeners() {
        this.#ui.addBotBtn.addEventListener("click", () => this.#handleEnterButton());
        this.#ui.disconnectBtn.addEventListener("click", () => this.#handleDisconnectButton());
        this.#ui.removeBtn.addEventListener("click", () => this.#handleRemoveButton());
        this.#ui.autoEnterToggle.addEventListener("click", () => this.#toggleAutoEnter(this.#ui.autoEnterToggle));
    }

    #handleEnterButton() {
        const currentRoomId = getFastManagerGameRoomId();
        if (!currentRoomId) {

            return;
        }

        let botToUse = this.#fastBots[0];
        if (!botToUse) {
            const botName = `ௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌௌ`;
            const botAvatar = ["17805610-2c39-11f0-851d-194ff4d727c0"];
            botToUse = new FastManagerBotClientInterface(botName, botAvatar);
            this.#fastBots.push(botToUse);
            this.notify("success", `Nuevo bot "${botToUse.getClientName()}" creado y conectándose.`);
        }

        botToUse.enterRoom(currentRoomId);

        this.#refreshStatus();
    }

    #handleDisconnectButton() {
        const botToDisconnect = this.#fastBots[0];
        if (botToDisconnect && botToDisconnect.getReadyState()) {
            botToDisconnect.disconnect();

        } else {

        }
        this.#stopAutoEnter();
        this.#refreshStatus();
    }

    #handleRemoveButton() {
        const botToRemove = this.#fastBots.shift();
        if (botToRemove) {
            if (botToRemove.getReadyState()) {
                botToRemove.disconnect();
            }

        } else {

        }
        this.#stopAutoEnter();
        this.#refreshStatus();
    }

    #toggleAutoEnter(button) {
        this.#autoEnterActive = !this.#autoEnterActive;
        button.classList.toggle('active', this.#autoEnterActive);

        if (this.#autoEnterActive) {
            this.#startAutoEnter();
        } else {
            this.#stopAutoEnter();
        }
    }

    #startAutoEnter() {
        const botToControl = this.#fastBots[0];
        if (!botToControl) {

            this.#autoEnterActive = false;
            this.#ui.autoEnterToggle.classList.remove('active');
            return;
        }

        const currentRoomId = getFastManagerGameRoomId();
        if (!currentRoomId) {

            this.#autoEnterActive = false;
            this.#ui.autoEnterToggle.classList.remove('active');
            return;
        }


        this.#autoEnterIntervalId = setInterval(() => {
            if (this.#autoEnterActive && botToControl) {
                const roomToJoin = getFastManagerGameRoomId();
                if (roomToJoin) {
                    botToControl.enterRoom(roomToJoin);
                } else {

                    this.#stopAutoEnter();
                }
            } else {
                this.#stopAutoEnter();
            }
        }, 300);
    }

    #stopAutoEnter() {
        if (this.#autoEnterIntervalId) {
            clearInterval(this.#autoEnterIntervalId);
            this.#autoEnterIntervalId = null;
            this.#autoEnterActive = false;

            this.#ui.autoEnterToggle.classList.remove('active');
        }
    }

    #setupBotReconnectCheck() {

        setInterval(() => this.#refreshStatus(), 2000);
    }

    #refreshStatus() {
        if (!this.#ui || !this.#ui.statusDiv) return;

        this.#ui.statusDiv.innerHTML = "";

        const bot = this.#fastBots[0];

        if (!bot) {
            this.#ui.statusDiv.textContent = "No hay un bot principal activo. Pulsa 'Unir Bot (Enter)'.";
            this.#ui.disconnectBtn.disabled = true;
            this.#ui.removeBtn.disabled = true;
            this.#ui.addBotBtn.disabled = false;
            this.#ui.autoEnterToggle.disabled = true;
            this.#stopAutoEnter();
            return;
        }

        const isConnected = bot.getReadyState();
        const statusColor = isConnected ? "green" : "red";
        const statusText = isConnected ? "Conectado" : "Desconectado";

        this.#ui.statusDiv.innerHTML = `<span style="color: ${statusColor};">${statusText}</span> - ${bot.getClientName()}`;

        this.#ui.disconnectBtn.disabled = !isConnected;
        this.#ui.removeBtn.disabled = false;
        this.#ui.addBotBtn.disabled = false;


        this.#ui.autoEnterToggle.disabled = false;
    }
}






  class DrawariaTools extends QBit {
    static dummy1 = QBit.register(this);
    static dummy2 = QBit.bind(this, "CubeEngine");

    constructor() {
      super("🛠️Drawaria Tools", '<i class="fas fa-tools"></i>');
      this._onStartup();
    }

    _onStartup() {

      this.loadExtension(FlagAutodrawTool);
      this.loadExtension(PlayerSentinelTool);
      this.loadExtension(AnimatorTool);
      this.loadExtension(SolarSmashModTool);
      this.loadExtension(LayerAnimationTool);
      this.loadExtension(ElementalAnimationsTool);
      this.loadExtension(GenerativeAnimatorTool);
      this.loadExtension(AdvancedPhysicsTool);
      this.loadExtension(DynamicVisualEffectsTool);
      this.loadExtension(DraggableActionMenuTool);
      this.loadExtension(PermanentRoomBotTool);
      this.loadExtension(GeometryDashCubeOnlineDrawer);
      this.loadExtension(MinecraftDrawer);
      this.loadExtension(WordHelperTool);
      this.loadExtension(FastManagerTool);
      this.loadExtension(LuminousFlowGeneratorTool);
      this.loadExtension(AvatarPhysicsPlatformerTool);


    }
  }





class FlagAutodrawTool extends QBit {




    _canvas = null;
    _ctx = null;
    _previewCanvas = null;
    _previewCtx = null;
    _imgDataGlobal = null;
    _executionLine = [];
    _drawingActive = false;
    _socketStatus = 'disconnected';

    _ui = {};


    _autodrawImageSize = 4;
    _autodrawBrushSize = 13;
    _autodrawPixelSize = 2;
    _autodrawOffsetX = 0;
    _autodrawOffsetY = 0;



    _flags = {
      Afghanistan: 'https://flagcdn.com/w320/af.png',
      Albania: 'https://flagcdn.com/w320/al.png',
      Algeria: 'https://flagcdn.com/w320/dz.png',
      Andorra: 'https://flagcdn.com/w320/ad.png',
      Angola: 'https://flagcdn.com/w320/ao.png',
      'Antigua and Barbuda': 'https://flagcdn.com/w320/ag.png',
      Argentina: 'https://flagcdn.com/w320/ar.png',
      Armenia: 'https://flagcdn.com/w320/am.png',
      Australia: 'https://flagcdn.com/w320/au.png',
      Austria: 'https://flagcdn.com/w320/at.png',
      Azerbaijan: 'https://flagcdn.com/w320/az.png',
      Bahamas: 'https://flagcdn.com/w320/bs.png',
      Bahrain: 'https://flagcdn.com/w320/bh.png',
      Bangladesh: 'https://flagcdn.com/w320/bd.png',
      Barbados: 'https://flagcdn.com/w320/bb.png',
      Belarus: 'https://flagcdn.com/w320/by.png',
      Belgium: 'https://flagcdn.com/w320/be.png',
      Belize: 'https://flagcdn.com/w320/bz.png',
      Benin: 'https://flagcdn.com/w320/bj.png',
      Bhutan: 'https://flagcdn.com/w320/bt.png',
      Bolivia: 'https://flagcdn.com/w320/bo.png',
      'Bosnia and Herzegovina': 'https://flagcdn.com/w320/ba.png',
      Botswana: 'https://flagcdn.com/w320/bw.png',
      Brazil: 'https://flagcdn.com/w320/br.png',
      Brunei: 'https://flagcdn.com/w320/bn.png',
      Bulgaria: 'https://flagcdn.com/w320/bg.png',
      'Burkina Faso': 'https://flagcdn.com/w320/bf.png',
      Burundi: 'https://flagcdn.com/w320/bi.png',
      Cambodia: 'https://flagcdn.com/w320/kh.png',
      Cameroon: 'https://flagcdn.com/w320/cm.png',
      Canada: 'https://flagcdn.com/w320/ca.png',
      'Cape Verde': 'https://flagcdn.com/w320/cv.png',
      'Central African Republic': 'https://flagcdn.com/w320/cf.png',
      Chad: 'https://flagcdn.com/w320/td.png',
      Chile: 'https://flagcdn.com/w320/cl.png',
      China: 'https://flagcdn.com/w320/cn.png',
      Colombia: 'https://flagcdn.com/w320/co.png',
      Comoros: 'https://flagcdn.com/w320/km.png',
      'Democratic Republic of the Congo': 'https://flagcdn.com/w320/cd.png',
      'Republic of the Congo': 'https://flagcdn.com/w320/cg.png',
      'Costa Rica': 'https://flagcdn.com/w320/cr.png',
      Croatia: 'https://flagcdn.com/w320/hr.png',
      Cuba: 'https://flagcdn.com/w320/cu.png',
      Cyprus: 'https://flagcdn.com/w320/cy.png',
      'Czech Republic': 'https://flagcdn.com/w320/cz.png',
      Denmark: 'https://flagcdn.com/w320/dk.png',
      Djibouti: 'https://flagcdn.com/w320/dj.png',
      Dominica: 'https://flagcdn.com/w320/dm.png',
      'Dominican Republic': 'https://flagcdn.com/w320/do.png',
      Ecuador: 'https://flagcdn.com/w320/ec.png',
      Egypt: 'https://flagcdn.com/w320/eg.png',
      'El Salvador': 'https://flagcdn.com/w320/sv.png',
      'Equatorial Guinea': 'https://flagcdn.com/w320/gq.png',
      Eritrea: 'https://flagcdn.com/w320/er.png',
      Estonia: 'https://flagcdn.com/w320/ee.png',
      Eswatini: 'https://flagcdn.com/w320/sz.png',
      Ethiopia: 'https://flagcdn.com/w320/et.png',
      Fiji: 'https://flagcdn.com/w320/fj.png',
      Finland: 'https://flagcdn.com/w320/fi.png',
      France: 'https://flagcdn.com/w320/fr.png',
      Gabon: 'https://flagcdn.com/w320/ga.png',
      Gambia: 'https://flagcdn.com/w320/gm.png',
      Georgia: 'https://flagcdn.com/w320/ge.png',
      Germany: 'https://flagcdn.com/w320/de.png',
      Ghana: 'https://flagcdn.com/w320/gh.png',
      Greece: 'https://flagcdn.com/w320/gr.png',
      Grenada: 'https://flagcdn.com/w320/gd.png',
      Guatemala: 'https://flagcdn.com/w320/gt.png',
      Guinea: 'https://flagcdn.com/w320/gn.png',
      'Guinea-Bissau': 'https://flagcdn.com/w320/gw.png',
      Guyana: 'https://flagcdn.com/w320/gy.png',
      Haiti: 'https://flagcdn.com/w320/ht.png',
      Honduras: 'https://flagcdn.com/w320/hn.png',
      Hungary: 'https://flagcdn.com/w320/hu.png',
      Iceland: 'https://flagcdn.com/w320/is.png',
      India: 'https://flagcdn.com/w320/in.png',
      Indonesia: 'https://flagcdn.com/w320/id.png',
      Iran: 'https://flagcdn.com/w320/ir.png',
      Iraq: 'https://flagcdn.com/w320/iq.png',
      Ireland: 'https://flagcdn.com/w320/ie.png',
      Israel: 'https://flagcdn.com/w320/il.png',
      Italy: 'https://flagcdn.com/w320/it.png',
      'Ivory Coast': 'https://flagcdn.com/w320/ci.png',
      Jamaica: 'https://flagcdn.com/w320/jm.png',
      Japan: 'https://flagcdn.com/w320/jp.png',
      Jordan: 'https://flagcdn.com/w320/jo.png',
      Kazakhstan: 'https://flagcdn.com/w320/kz.png',
      Kenya: 'https://flagcdn.com/w320/ke.png',
      Kiribati: 'https://flagcdn.com/w320/ki.png',
      Kuwait: 'https://flagcdn.com/w320/kw.png',
      Kyrgyzstan: 'https://flagcdn.com/w320/kg.png',
      Laos: 'https://flagcdn.com/w320/la.png',
      Latvia: 'https://flagcdn.com/w320/lv.png',
      Lebanon: 'https://flagcdn.com/w320/lb.png',
      Lesotho: 'https://flagcdn.com/w320/ls.png',
      Liberia: 'https://flagcdn.com/w320/lr.png',
      Libya: 'https://flagcdn.com/w320/ly.png',
      Liechtenstein: 'https://flagcdn.com/w320/li.png',
      Lithuania: 'https://flagcdn.com/w320/lt.png',
      Luxembourg: 'https://flagcdn.com/w320/lu.png',
      Madagascar: 'https://flagcdn.com/w320/mg.png',
      Malawi: 'https://flagcdn.com/w320/mw.png',
      Malaysia: 'https://flagcdn.com/w320/my.png',
      Maldives: 'https://flagcdn.com/w320/mv.png',
      Mali: 'https://flagcdn.com/w320/ml.png',
      Malta: 'https://flagcdn.com/w320/mt.png',
      'Marshall Islands': 'https://flagcdn.com/w320/mh.png',
      Mauritania: 'https://flagcdn.com/w320/mr.png',
      Mauritius: 'https://flagcdn.com/w320/mu.png',
      Mexico: 'https://flagcdn.com/w320/mx.png',
      Micronesia: 'https://flagcdn.com/w320/fm.png',
      Moldova: 'https://flagcdn.com/w320/md.png',
      Monaco: 'https://flagcdn.com/w320/mc.png',
      Mongolia: 'https://flagcdn.com/w320/mn.png',
      Montenegro: 'https://flagcdn.com/w320/me.png',
      Morocco: 'https://flagcdn.com/w320/ma.png',
      Mozambique: 'https://flagcdn.com/w320/mz.png',
      Myanmar: 'https://flagcdn.com/w320/mm.png',
      Namibia: 'https://flagcdn.com/w320/na.png',
      Nauru: 'https://flagcdn.com/w320/nr.png',
      Nepal: 'https://flagcdn.com/w320/np.png',
      Netherlands: 'https://flagcdn.com/w320/nl.png',
      'New Zealand': 'https://flagcdn.com/w320/nz.png',
      Nicaragua: 'https://flagcdn.com/w320/ni.png',
      Niger: 'https://flagcdn.com/w320/ne.png',
      Nigeria: 'https://flagcdn.com/w320/ng.png',
      'North Korea': 'https://flagcdn.com/w320/kp.png',
      'North Macedonia': 'https://flagcdn.com/w320/mk.png',
      Norway: 'https://flagcdn.com/w320/no.png',
      Oman: 'https://flagcdn.com/w320/om.png',
      Pakistan: 'https://flagcdn.com/w320/pk.png',
      Palau: 'https://flagcdn.com/w320/pw.png',
      Palestine: 'https://flagcdn.com/w320/ps.png',
      Panama: 'https://flagcdn.com/w320/pa.png',
      'Papua New Guinea': 'https://flagcdn.com/w320/pg.png',
      Paraguay: 'https://flagcdn.com/w320/py.png',
      Peru: 'https://flagcdn.com/w320/pe.png',
      Philippines: 'https://flagcdn.com/w320/ph.png',
      Poland: 'https://flagcdn.com/w320/pl.png',
      Portugal: 'https://flagcdn.com/w320/pt.png',
      Qatar: 'https://flagcdn.com/w320/qa.png',
      Romania: 'https://flagcdn.com/w320/ro.png',
      Russia: 'https://flagcdn.com/w320/ru.png',
      Rwanda: 'https://flagcdn.com/w320/rw.png',
      'Saint Kitts and Nevis': 'https://flagcdn.com/w320/kn.png',
      'Saint Lucia': 'https://flagcdn.com/w320/lc.png',
      'Saint Vincent and the Grenadines': 'https://flagcdn.com/w320/vc.png',
      Samoa: 'https://flagcdn.com/w320/ws.png',
      'San Marino': 'https://flagcdn.com/w320/sm.png',
      'São Tomé and Príncipe': 'https://flagcdn.com/w320/st.png',
      'Saudi Arabia': 'https://flagcdn.com/w320/sa.png',
      Senegal: 'https://flagcdn.com/w320/sn.png',
      Serbia: 'https://flagcdn.com/w320/rs.png',
      Seychelles: 'https://flagcdn.com/w320/sc.png',
      'Sierra Leone': 'https://flagcdn.com/w320/sl.png',
      Singapore: 'https://flagcdn.com/w320/sg.png',
      Slovakia: 'https://flagcdn.com/w320/sk.png',
      Slovenia: 'https://flagcdn.com/w320/si.png',
      'Solomon Islands': 'https://flagcdn.com/w320/sb.png',
      Somalia: 'https://flagcdn.com/w320/so.png',
      'South Africa': 'https://flagcdn.com/w320/za.png',
      'South Korea': 'https://flagcdn.com/w320/kr.png',
      'South Sudan': 'https://flagcdn.com/w320/ss.png',
      Spain: 'https://flagcdn.com/w320/es.png',
      'Sri Lanka': 'https://flagcdn.com/w320/lk.png',
      Sudan: 'https://flagcdn.com/w320/sd.png',
      Suriname: 'https://flagcdn.com/w320/sr.png',
      Sweden: 'https://flagcdn.com/w320/se.png',
      Switzerland: 'https://flagcdn.com/w320/ch.png',
      Syria: 'https://flagcdn.com/w320/sy.png',
      Tajikistan: 'https://flagcdn.com/w320/tj.png',
      Tanzania: 'https://flagcdn.com/w320/tz.png',
      Thailand: 'https://flagcdn.com/w320/th.png',
      'Timor-Leste': 'https://flagcdn.com/w320/tl.png',
      Togo: 'https://flagcdn.com/w320/tg.png',
      Tonga: 'https://flagcdn.com/w320/to.png',
      'Trinidad and Tobago': 'https://flagcdn.com/w320/tt.png',
      Tunisia: 'https://flagcdn.com/w320/tn.png',
      Turkey: 'https://flagcdn.com/w320/tr.png',
      Turkmenistan: 'https://flagcdn.com/w320/tm.png',
      Tuvalu: 'https://flagcdn.com/w320/tv.png',
      Uganda: 'https://flagcdn.com/w320/ug.png',
      Ukraine: 'https://flagcdn.com/w320/ua.png',
      'United Arab Emirates': 'https://flagcdn.com/w320/ae.png',
      'United Kingdom': 'https://flagcdn.com/w320/gb.png',
      'United States': 'https://flagcdn.com/w320/us.png',
      Uruguay: 'https://flagcdn.com/w320/uy.png',
      Uzbekistan: 'https://flagcdn.com/w320/uz.png',
      Vanuatu: 'https://flagcdn.com/w320/vu.png',
      'Vatican City': 'https://flagcdn.com/w320/va.png',
      Venezuela: 'https://flagcdn.com/w320/ve.png',
      Vietnam: 'https://flagcdn.com/w320/vn.png',
      Yemen: 'https://flagcdn.com/w320/ye.png',
      Zambia: 'https://flagcdn.com/w320/zm.png',
      Zimbabwe: 'https://flagcdn.com/w320/zw.png',
    };

    constructor() {
        super("🚩Flag Generator", '<i class="fas fa-flag"></i>');
        this._onStartup();
    }

    _onStartup() {
        this._canvas = document.getElementById('canvas');
        if (this._canvas) {
            this._ctx = this._canvas.getContext('2d');
        } else {


            return;
        }


        this._previewCanvas = document.createElement('canvas');
        this._previewCtx = this._previewCanvas.getContext('2d');

        this._loadInterface();
        this._setupEventListeners();

        this._updateConnectionStatus(getGameSocket() && getGameSocket().readyState === WebSocket.OPEN ? 'connected' : 'disconnected');

    }


    _loadInterface() {
        const container = domMake.Tree("div", { id: `${this.identifier}-container`, class: "module-section" });
        this.htmlElements.section.appendChild(container);



        const connectionStatusDiv = domMake.Tree("div", {}, [
            domMake.Tree("span", { id: `${this.identifier}-connectionStatus`, class: `module-status-indicator module-status-${this._socketStatus}` }),
            domMake.Tree("span", { id: `${this.identifier}-statusText` }, [this._socketStatus.charAt(0).toUpperCase() + this._socketStatus.slice(1)])
        ]);



        container.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Dibujo de Bandera"]));


        this._ui.flagSearchInput = domMake.Tree("input", {
            id: `${this.identifier}-flagSearch`,
            type: "text",
            class: "module-form-control",
            placeholder: "Buscar bandera por nombre..."
        });
        container.appendChild(this._ui.flagSearchInput);


        const flagSelectOptions = Object.keys(this._flags).map(flag => domMake.Tree("option", { value: flag }, [flag]));
        this._ui.flagSelect = domMake.Tree("select", { id: `${this.identifier}-flagSelect`, class: "module-form-control" }, [domMake.Tree("option", { value: "" }, ["Seleccionar una Bandera"]), ...flagSelectOptions]);
        container.appendChild(domMake.Tree("div", { class: "module-form-group" }, [domMake.Tree("label", { for: `${this.identifier}-flagSelect` }, ["Seleccionar Bandera:"]), this._ui.flagSelect]));


        this._createSlider(container, "autodraw_imagesize", "Tamaño de Imagen", "1", "10", "4", "Grande", "Pequeña");
        this._GME_ENGINE_IMAGESIZE_SLIDER = this._ui.autodrawImagesizeInput;
        this._createSlider(container, "autodraw_brushsize", "Tamaño del Pincel", "2", "20", "13", "Fino", "Grueso");
        this._createSlider(container, "autodraw_pixelsize", "Distancia del Píxel", "2", "20", "2", "Cerca", "Lejos");
        this._createSlider(container, "autodraw_offset_x", "Desplazamiento Horizontal (%)", "0", "100", "0", "0", "100");
        this._createSlider(container, "autodraw_offset_y", "Desplazamiento Vertical (%)", "0", "100", "0", "100");


        this._ui.startAutodrawButton = domMake.Button('<i class="fas fa-play-circle"></i> Iniciar Autodraw');
        this._ui.stopAutodrawButton = domMake.Button('<i class="fas fa-stop-circle"></i> Detener Autodraw');
        this._ui.clearCanvasButton = domMake.Button('<i class="fas fa-eraser"></i> Limpiar Todo');

        container.appendChild(domMake.Tree("div", { class: "module-btn-group" }, [this._ui.startAutodrawButton, this._ui.stopAutodrawButton]));
        container.appendChild(domMake.Tree("div", { class: "module-btn-group" }, [this._ui.clearCanvasButton]));
    }


    _createSlider(parent, idSuffix, labelText, min, max, value, minLabel, maxLabel) {
        const sliderId = `${this.identifier}-${idSuffix}`;
        const valueId = `${sliderId}Value`;

        const sliderContainer = domMake.Tree("div", { class: "module-slider-container" }, [
            domMake.Tree("label", { for: sliderId }, [labelText]),
            domMake.Tree("div", { class: "module-slider-value" }, [
                domMake.Tree("span", {}, [minLabel]),
                domMake.Tree("span", { id: valueId }, [value]),
                domMake.Tree("span", {}, [maxLabel])
            ]),
            domMake.Tree("input", { type: "range", id: sliderId, min: min, max: max, value: value })
        ]);
        parent.appendChild(sliderContainer);

        this._ui[idSuffix.replace(/_([a-z])/g, (g) => g[1].toUpperCase()) + "Input"] = sliderContainer.querySelector('input');
        this._ui[idSuffix.replace(/_([a-z])/g, (g) => g[1].toUpperCase()) + "Value"] = sliderContainer.querySelector(`#${valueId}`);
    }


    _setupEventListeners() {

        this._ui.flagSelect.addEventListener('change', (e) => {
            const selectedFlagName = e.target.value;
            if (selectedFlagName && this._flags[selectedFlagName]) {
                this._loadImageForAutodraw(this._flags[selectedFlagName]);
            } else if (!selectedFlagName) {
                this._imgDataGlobal = null;
                this.notify("debug", "No flag selected, image data cleared.");
            }
        });


        this._ui.flagSearchInput.addEventListener('keyup', (e) => {
            const searchTerm = e.target.value.toLowerCase();
            const flagSelect = this._ui.flagSelect;
            const options = flagSelect.getElementsByTagName('option');


            for (let i = 1; i < options.length; i++) {
                const optionText = options[i].textContent || options[i].innerText;
                if (optionText.toLowerCase().includes(searchTerm)) {
                    options[i].style.display = '';
                } else {
                    options[i].style.display = 'none';
                }
            }
        });



        this._ui.startAutodrawButton.addEventListener('click', () => {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            if (!this._imgDataGlobal) {

                return;
            }


            this._autodrawImageSize = parseInt(this._ui.autodrawImagesizeInput.value, 10);
            this._autodrawBrushSize = parseInt(this._ui.autodrawBrushsizeInput.value, 10);
            this._autodrawPixelSize = parseInt(this._ui.autodrawPixelsizeInput.value, 10);
            this._autodrawOffsetX = parseInt(this._ui.autodrawOffsetXInput.value, 10);
            this._autodrawOffsetY = parseInt(this._ui.autodrawOffsetYInput.value, 10);


            this._processImageForAutodraw(this._autodrawImageSize, this._autodrawPixelSize, this._autodrawBrushSize, { x: this._autodrawOffsetX, y: this._autodrawOffsetY });
            this._executeAutodraw(socket);
        });


        this._ui.stopAutodrawButton.addEventListener('click', () => {
            this._drawingActive = false;

        });


        this._ui.clearCanvasButton.addEventListener('click', async () => {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }

            this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);

            const clearThickness = 1000;
            const clearColor = '#ffffff';
            const steps = 5;


            for (let i = 0; i <= steps; i++) {
                this._drawLineAndSendCommand(socket, 0, (i / steps) * this._canvas.height, this._canvas.width, (i / steps) * this._canvas.height, clearColor, clearThickness);
                await this._delay(5);
                this._drawLineAndSendCommand(socket, (i / steps) * this._canvas.width, 0, (i / steps) * this._canvas.width, this._canvas.height, clearColor, clearThickness);
                await this._delay(5);
            }
            this.notify("success", "El lienzo ha sido limpiado.");
        });


        for (const key in this._ui) {
            if (key.endsWith("Input") && this._ui[key].type === "range") {
                const valueSpanKey = key.replace("Input", "Value");
                if (this._ui[valueSpanKey]) {
                    this._ui[key].addEventListener('input', () => {
                        this._ui[valueSpanKey].textContent = this._ui[key].value;
                    });
                }
            }
        }
    }


    _updateConnectionStatus(status) {
        this._socketStatus = status;
        const statusIndicator = document.getElementById(`${this.identifier}-connectionStatus`);
        const statusText = document.getElementById(`${this.identifier}-statusText`);

        if (statusIndicator && statusText) {
            statusIndicator.className = `module-status-indicator module-status-${status}`;
            statusText.textContent = status.charAt(0).toUpperCase() + status.slice(1);
        }
    }


    _delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }


    _drawLineAndSendCommand(socket, x1, y1, x2, y2, color, thickness) {
        if (!this._canvas || !this._ctx) return;


        this._ctx.strokeStyle = color;
        this._ctx.lineWidth = thickness;
        this._ctx.lineCap = 'round';
        this._ctx.beginPath();
        this._ctx.moveTo(x1, y1);
        this._ctx.lineTo(x2, y2);
        this._ctx.stroke();


        if (socket && socket.readyState === WebSocket.OPEN) {

            const normX1 = (x1 / this._canvas.width).toFixed(4);
            const normY1 = (y1 / this._canvas.height).toFixed(4);
            const normX2 = (x2 / this._canvas.width).toFixed(4);
            const normY2 = (y2 / this._canvas.height).toFixed(4);


            socket.send(`42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - thickness},"${color}",0,0,{}]]`);
        } else {

        }
    }


    _recalc(value, offset) {
        const cw = this._canvas.width;
        const ch = this._canvas.height;
        return [
            Math.min(1, Math.max(0, (value[0] / cw + offset.x / 100))),
            Math.min(1, Math.max(0, (value[1] / ch + offset.y / 100)))
        ];
    }


    _loadImageForAutodraw(url) {
        let img = new Image();
        img.crossOrigin = 'anonymous';
        img.src = url;
        img.addEventListener('load', () => {
            if (!this._canvas) {

                return;
            }
            const cw = this._canvas.width;
            const ch = this._canvas.height;

            this._previewCanvas.width = cw;
            this._previewCanvas.height = ch;
            this._previewCtx.clearRect(0, 0, cw, ch);


            let scale = Math.min(cw / img.width, ch / img.height);
            let scaledWidth = img.width * scale;
            let scaledHeight = img.height * scale;
            let dx = (cw - scaledWidth) / 2;
            let dy = (ch - scaledHeight) / 2;

            this._previewCtx.drawImage(img, dx, dy, scaledWidth, scaledHeight);
            this._imgDataGlobal = this._previewCtx.getImageData(0, 0, cw, ch).data;

        });
        img.addEventListener('error', () => {

        });
    }


    _processImageForAutodraw(size, modifier = 1, thickness = 5, offset = { x: 0, y: 0 }, ignoreColors = []) {
        if (!this._imgDataGlobal || !this._canvas) {

            return;
        }
        this._executionLine = [];
        const cw = this._canvas.width;
        const ch = this._canvas.height;
        const step = size * modifier;


        for (let y = 0; y < ch; y += step) {
            let startX = 0;
            let currentColor = null;


            for (let x = 0; x < cw; x += 1) {
                const currentPixelX = Math.floor(x / step) * step; // Ensure sampling is on the grid
                const currentPixelY = Math.floor(y / step) * step;

                const index = (Math.floor(currentPixelY) * cw + Math.floor(currentPixelX)) * 4;
                const a = this._imgDataGlobal[index + 3] || 0;

                if (a > 20) {
                    const r = this._imgDataGlobal[index + 0] || 0;
                    const g = this._imgDataGlobal[index + 1] || 0;
                    const b = this._imgDataGlobal[index + 2] || 0;
                    const color = `rgb(${r},${g},${b})`;

                    if (!ignoreColors.includes(color)) {
                        if (color !== currentColor) {
                            if (currentColor !== null && x > startX) {
                                this._executionLine.push({
                                    pixelPos1: [startX, y],
                                    pixelPos2: [x, y],
                                    color: currentColor,
                                    thickness: thickness,
                                });
                            }
                            currentColor = color;
                            startX = x;
                        }
                    } else {
                        if (currentColor !== null && x > startX) {
                            this._executionLine.push({
                                pixelPos1: [startX, y],
                                pixelPos2: [x, y],
                                color: currentColor,
                                thickness: thickness,
                            });
                        }
                        currentColor = null;
                    }
                } else {
                    if (currentColor !== null && x > startX) {
                        this._executionLine.push({
                            pixelPos1: [startX, y],
                            pixelPos2: [x, y],
                            color: currentColor,
                            thickness: thickness,
                        });
                    }
                    currentColor = null;
                }
            }

            if (currentColor !== null && cw > startX) {
                this._executionLine.push({
                    pixelPos1: [startX, y],
                    pixelPos2: [cw, y],
                    color: currentColor,
                    thickness: thickness,
                });
            }
        }
        this.notify("debug", `Procesamiento de imagen completo, líneas: ${this._executionLine.length}`);
    }


    async _executeAutodraw(socket) {
        if (!socket || socket.readyState !== WebSocket.OPEN) {

            return;
        }
        if (this._executionLine.length === 0) {

            return;
        }

        this._drawingActive = true;
        for (let i = 0; i < this._executionLine.length; i++) {
            if (!this._drawingActive) {

                break;
            }
            const line = this._executionLine[i];

            const p1 = this._recalc(line.pixelPos1, { x: this._autodrawOffsetX, y: this._autodrawOffsetY });
            const p2 = this._recalc(line.pixelPos2, { x: this._autodrawOffsetX, y: this._autodrawOffsetY });
            const color = line.color;
            const thickness = line.thickness;


            this._drawLineAndSendCommand(socket, line.pixelPos1[0], line.pixelPos1[1], line.pixelPos2[0], line.pixelPos2[1], color, thickness);
            await this._delay(1);
        }
        this._drawingActive = false;
        this.notify("success", "Autodraw completo.");
    }
}



class AdvancedPhysicsTool extends QBit {
    _canvas = null;
    _ctx = null;
    _isActive = false;
    _physicsObjects = new Map();
    _objectIdCounter = 0;
    _lastRenderTime = 0;
    _renderInterval = 1000 / 60;
    _gameSocket = null;
    _ui = {};
    _statsInterval = null;


    _commandQueue = [];
    _batchProcessor = null;
    _positionCache = new Map();
    _playerTracker = {
        players: new Map(),
        detectionRadius: 0,
        lastUpdateTime: 0,
        detectionInterval: 1000 / 60
    };


    BATCH_SIZE = 12;
    BATCH_INTERVAL = 45;
    MOVEMENT_THRESHOLD = 3;


    PHYSICS_CONSTANTS = {
        GRAVITY: 400,
        BALL_MASS: 0.2,
        BALL_RADIUS: 40,
        TIMESTEP: 1/60,
        MAX_VELOCITY: 1000,
        AIR_RESISTANCE: 0.003,
        RESTITUTION_BALL: 0.9,
        RESTITUTION_WALL: 0.85,
        FRICTION_GRASS: 0.95,
        PLAYER_INTERACTION_FORCE: 500,
        PLAYER_PUSH_MULTIPLIER: 3.5,
        PLAYER_RESTITUTION: 1.4,
        PLAYER_DETECTION_RADIUS_MULTIPLIER: 1.5
    };


    FIELD_CONSTANTS = {
        GOAL_P1_COLOR: '#555555',
        GOAL_P2_COLOR: '#787878',
        TEXT_COLOR: '#000000',
        GRASS_COLOR: '#228B22',
        GOAL_THICKNESS: 10
    };


    matchMode = {
        active: false,
        scores: { p1: 0, p2: 0 },
        playArea: null,
        goalCooldown: false,
        maxGoals: 5
    };

    controls = {
        showTrails: false,
        showDebug: false,
        defaultObjectColor: '#000000',
        alternatePhysics: 'normal'
    };

    gameStats = {
        totalCollisions: 0,
        maxVelocityReached: 0,
        objectsCreated: 0,
        totalGoals: 0
    };

    constructor() {
        super("⚽Football Engine", '<i class="fas fa-futbol"></i>');
        this._onStartup();
    }

    _onStartup() {
        this.notify("info", "🚀 Iniciando motor de fútbol...");
        this._findGameCanvas();
        this._setupWebSocketCapture();
        this._startBatchProcessor();
        this._setupPlayerDetection();
        this._loadInterface();
        this._startAdvancedStatsMonitoring();
        this.notify("success", "⚽ Motor de fútbol inicializado y listo.");
    }

    _onClose() {
        this.notify("info", "🔄 Cerrando motor optimizado...");
        this._stopPhysicsLoop();
        this._stopAdvancedStatsMonitoring();

        if (this._batchProcessor) {
            clearInterval(this._batchProcessor);
            this._batchProcessor = null;
        }
        this._commandQueue.length = 0;
        this._positionCache.clear();
        this._playerTracker.players.clear();

        this._physicsObjects.clear();
        this._ctx?.clearRect(0, 0, this._canvas.width, this._canvas.height);
        this.notify("success", "✅ Motor cerrado correctamente.");
    }


    _loadInterface() {
        if (this._ui.panel) {
            this._ui.panel.remove();
        }

        const container = domMake.Tree("div", {
            id: `${this.identifier}-container`,
            style: `
                display: flex; flex-direction: column; gap: 12px;
                padding: 20px; background: #FFFFFF; border-radius: 12px;
                box-shadow: 0 4px 20px rgba(0,0,0,0.1); color: #333333;
                border: 1px solid #E5E5E5;
            `
        });
        this._ui.panel = container;
        this.htmlElements.section.appendChild(container);


        this._addConnectionStatus();


        const controlsDiv = domMake.Tree("div", {
            style: "display: flex; flex-direction: column; gap: 10px;"
        });


        const createFieldBtn = domMake.Tree("button", {
            id: this.identifier + '-create-field-btn',
            style: `width: 100%; padding: 14px; background: #F8F9FA; color: #495057; border: 2px solid #DEE2E6; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.2s;`
        }, "🏟️ Create Football Field");


        const togglePhysicsBtn = domMake.Tree("button", {
            id: this.identifier + '-toggle-physics',
            style: `width: 100%; padding: 14px; background: #E9ECEF; color: #495057; border: 2px solid #CED4DA; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.2s;`
        }, "🚀 Launch Physics Engine");

        controlsDiv.appendChild(createFieldBtn);
        controlsDiv.appendChild(togglePhysicsBtn);


        const ballControlsDiv = domMake.Tree("div", {
            style: "display: flex; gap: 10px;"
        });

        const addBallBtn = domMake.Tree("button", {
            id: this.identifier + '-add-ball-btn',
            style: `flex: 1; padding: 12px; background: #F1F3F4; color: #495057; border: 1px solid #CED4DA; border-radius: 6px; cursor: pointer; font-weight: 500; transition: all 0.2s;`
        }, "⚽ Add Ball");

        const resetAllBtn = domMake.Tree("button", {
            id: this.identifier + '-reset-all-btn',
            style: `flex: 1; padding: 12px; background: #F1F3F4; color: #495057; border: 1px solid #CED4DA; border-radius: 6px; cursor: pointer; font-weight: 500; transition: all 0.2s;`
        }, "🔄 Reset");

        ballControlsDiv.appendChild(addBallBtn);
        ballControlsDiv.appendChild(resetAllBtn);
        controlsDiv.appendChild(ballControlsDiv);


        const matchModeToggle = domMake.Tree("button", {
            id: this.identifier + '-match-mode-toggle',
            style: `width: 100%; padding: 14px; background: #F8F9FA; color: #495057; border: 2px solid #DEE2E6; border-radius: 8px; cursor: pointer; font-weight: 600; transition: all 0.2s;`
        }, "🏆 Match Mode");
        controlsDiv.appendChild(matchModeToggle);


        const clearAllBtn = domMake.Tree("button", {
            id: this.identifier + '-clear-all-btn',
            style: `width: 100%; padding: 12px; background: #F8F9FA; color: #6C757D; border: 1px solid #DEE2E6; border-radius: 6px; cursor: pointer; font-weight: 500; transition: all 0.2s;`
        }, "🗑️ Clear All");
        controlsDiv.appendChild(clearAllBtn);

        container.appendChild(controlsDiv);


        const physicsControlsDiv = domMake.Tree("div", {
            style: "display: flex; flex-direction: column; gap: 10px; border-top: 1px solid #E9ECEF; padding-top: 15px;"
        });

        const physicsModeSelect = domMake.Tree("select", {
            id: this.identifier + '-physics-mode',
            style: `width: 100%; padding: 10px; border: 1px solid #CED4DA; border-radius: 6px; background: #FFFFFF; color: #495057; font-size: 14px;`
        }, [
            domMake.Tree("option", { value: "normal" }, "🌍 Normal"),
            domMake.Tree("option", { value: "moon" }, "🌙 Moon Gravity"),
            domMake.Tree("option", { value: "underwater" }, "🌊 Underwater"),
            domMake.Tree("option", { value: "magnetic" }, "🧲 Magnetic Field"),
        ]);

        const colorPicker = domMake.Tree("input", {
            id: this.identifier + '-object-color-picker',
            type: "color",
            value: "#000000",
            style: `width: 100%; height: 35px; border: 1px solid #CED4DA; border-radius: 6px; cursor: pointer; background: #FFFFFF;`
        });

        physicsControlsDiv.appendChild(domMake.Tree("div", { style: "display: flex; align-items: center; gap: 8px;" }, [
            domMake.Tree("label", { style: "font-size: 14px; color: #6C757D; font-weight: 500;" }, "Physics Mode:"), physicsModeSelect
        ]));
        physicsControlsDiv.appendChild(domMake.Tree("div", { style: "display: flex; align-items: center; gap: 8px;" }, [
            domMake.Tree("label", { style: "font-size: 14px; color: #6C757D; font-weight: 500;" }, "Ball Color:"), colorPicker
        ]));

        container.appendChild(physicsControlsDiv);


        const scoreboard = domMake.Tree("div", {
            id: this.identifier + '-scoreboard',
            style: `display: none; text-align: center; border-top: 1px solid #E9ECEF; padding-top: 15px; font-weight: 600; font-size: 18px; background: #F8F9FA; border-radius: 8px; padding: 20px; border: 2px solid #FFC107; margin-top: 10px;`
        });

        const scoreTitle = domMake.Tree("h4", { style: "margin: 0 0 12px 0; color: #856404; font-size: 16px;" }, "🏆 MATCH SCORE");
        const scoreContainer = domMake.Tree("div", { style: "display: flex; justify-content: space-between; font-size: 20px; font-weight: 700;" });

        const scoreP1 = domMake.Tree("span", { id: this.identifier + '-score-p1', style: "color: #DC3545;" }, "0");
        const scoreP2 = domMake.Tree("span", { id: this.identifier + '-score-p2', style: "color: #007BFF;" }, "0");

        scoreContainer.appendChild(domMake.Tree("span", { style: "color: #495057;" }, "P1: "));
        scoreContainer.appendChild(scoreP1);
        scoreContainer.appendChild(domMake.Tree("span", { style: "color: #ADB5BD;" }, " vs "));
        scoreContainer.appendChild(domMake.Tree("span", { style: "color: #495057;" }, "P2: "));
        scoreContainer.appendChild(scoreP2);

        scoreboard.appendChild(scoreTitle);
        scoreboard.appendChild(scoreContainer);
        container.appendChild(scoreboard);


        this._ui.createFieldBtn = createFieldBtn;
        this._ui.togglePhysicsBtn = togglePhysicsBtn;
        this._ui.addBallBtn = addBallBtn;
        this._ui.resetAllBtn = resetAllBtn;
        this._ui.matchModeToggle = matchModeToggle;
        this._ui.clearAllBtn = clearAllBtn;
        this._ui.colorPicker = colorPicker;
        this._ui.physicsModeSelect = physicsModeSelect;
        this._ui.scoreboard = scoreboard;
        this._ui.scoreP1 = scoreP1;
        this._ui.scoreP2 = scoreP2;


        this._addHoverEffects();
        this._ui.createFieldBtn?.addEventListener('click', () => this.createFootballField());
        this._ui.togglePhysicsBtn?.addEventListener('click', () => this.togglePhysics());
        this._ui.addBallBtn?.addEventListener('click', () => this.addBallAtCenter());
        this._ui.resetAllBtn?.addEventListener('click', () => this.resetAllObjects());
        this._ui.matchModeToggle?.addEventListener('click', () => this.toggleMatchMode());
        this._ui.clearAllBtn?.addEventListener('click', () => this.clearAllObjects());
        this._ui.colorPicker?.addEventListener('input', (e) => this.controls.defaultObjectColor = e.target.value);
        this._ui.physicsModeSelect?.addEventListener('change', (e) => this.controls.alternatePhysics = e.target.value);


        if (this._canvas) {
            this._canvas.addEventListener('click', (e) => {
                const rect = this._canvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                this._createBall(x, y);
            });
        }
    }


    _addHoverEffects() {
        const buttons = [
            this._ui.createFieldBtn,
            this._ui.togglePhysicsBtn,
            this._ui.addBallBtn,
            this._ui.resetAllBtn,
            this._ui.matchModeToggle,
            this._ui.clearAllBtn
        ];

        buttons.forEach(btn => {
            if (!btn) return;

            btn.addEventListener('mouseenter', () => {
                btn.style.transform = 'translateY(-2px)';
                btn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
            });

            btn.addEventListener('mouseleave', () => {
                btn.style.transform = 'translateY(0)';
                btn.style.boxShadow = 'none';
            });
        });
    }


    _addConnectionStatus() {
        const statusDiv = domMake.Tree("div", {
            id: this.identifier + '-connection-status',
            style: `text-align: center; padding: 10px; border-radius: 6px; margin-bottom: 15px; font-size: 12px; font-weight: 600; border: 1px solid #E9ECEF;`
        });

        this._ui.panel.appendChild(statusDiv);


        setInterval(() => {
            const socket = getGameSocket();
            const isConnected = socket && socket.readyState === WebSocket.OPEN;

            if (isConnected) {
                statusDiv.textContent = "🟢 SERVER CONNECTED";
                statusDiv.style.background = "#D4EDDA";
                statusDiv.style.color = "#155724";
                statusDiv.style.borderColor = "#C3E6CB";
            } else {
                statusDiv.textContent = "🔴 LOCAL ONLY";
                statusDiv.style.background = "#F8D7DA";
                statusDiv.style.color = "#721C24";
                statusDiv.style.borderColor = "#F5C6CB";
            }
        }, 2000);
    }

    _findGameCanvas() {
        this._canvas = document.getElementById('canvas') || document.querySelector('canvas');
        if (this._canvas) {
            this._ctx = this._canvas.getContext('2d');
            this.notify("success", "🎯 Canvas detectado.");
            return true;
        } else {
            this.notify("error", "❌ Canvas no encontrado.");
            return false;
        }
    }

    _setupWebSocketCapture() {
        if (typeof getGameSocket !== 'function') {
            this.notify("error", "❌ getGameSocket no disponible");
            return false;
        }

        this._gameSocket = getGameSocket();

        setInterval(() => {
            const currentSocket = getGameSocket();
            if (currentSocket && currentSocket.readyState === WebSocket.OPEN) {
                if (!this._gameSocket || this._gameSocket.readyState !== WebSocket.OPEN) {
                    this._gameSocket = currentSocket;

                }
            } else {
                if (this._gameSocket && this._gameSocket.readyState === WebSocket.OPEN) {
                    this.notify("warning", "⚠️ Conexión perdida");
                }
                this._gameSocket = null;
            }
        }, 3000);

        if (this._gameSocket && this._gameSocket.readyState === WebSocket.OPEN) {
            this.notify("success", "🌐 WebSocket conectado");
            return true;
        } else {
            this.notify("warning", "⚠️ WebSocket no disponible - modo local");
            return false;
        }
    }

    _startBatchProcessor() {
        if (this._batchProcessor) return;

        this._batchProcessor = setInterval(() => {
            const socket = getGameSocket();
            if (!socket || socket.readyState !== WebSocket.OPEN || this._commandQueue.length === 0) {
                return;
            }

            const batch = this._commandQueue.splice(0, this.BATCH_SIZE);
            batch.forEach(cmd => {
                try {
                    socket.send(cmd);
                } catch (e) {
                    console.warn('Failed to send command:', e);
                }
            });
        }, this.BATCH_INTERVAL);
    }

    _sendDrawCommand(x1, y1, x2, y2, color, thickness) {
        if (!this._canvas) return false;

        const normX1 = (x1 / this._canvas.width).toFixed(4);
        const normY1 = (y1 / this._canvas.height).toFixed(4);
        const normX2 = (x2 / this._canvas.width).toFixed(4);
        const normY2 = (y2 / this._canvas.height).toFixed(4);

        const command = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${-Math.abs(thickness)},"${color}",0,0,{}]]`;
        this._commandQueue.push(command);

        this._drawLocal(x1, y1, x2, y2, color, thickness);
        return true;
    }

    _drawLocal(x1, y1, x2, y2, color, thickness) {
        if (!this._ctx) return;

        this._ctx.strokeStyle = color;
        this._ctx.lineWidth = thickness;
        this._ctx.lineCap = 'round';
        this._ctx.lineJoin = 'round';
        this._ctx.beginPath();
        this._ctx.moveTo(x1, y1);
        this._ctx.lineTo(x2, y2);
        this._ctx.stroke();
    }

    _setupPlayerDetection() {
        this._playerTracker.detectionRadius = this.PHYSICS_CONSTANTS.BALL_RADIUS * this.PHYSICS_CONSTANTS.PLAYER_DETECTION_RADIUS_MULTIPLIER;
    }

    _getPlayerPositions() {
        const currentTime = performance.now();
        const players = [];
        if (!this._canvas) return players;

        const canvasRect = this._canvas.getBoundingClientRect();
        const deltaTime = currentTime - this._playerTracker.lastUpdateTime;

        const selfPlayer = document.querySelector('body > div.spawnedavatar.spawnedavatar-self');
        if (selfPlayer) {
            const rect = selfPlayer.getBoundingClientRect();
            const currentPos = {
                type: 'self',
                id: selfPlayer.dataset.playerid || 'self',
                x: rect.left - canvasRect.left + rect.width / 2,
                y: rect.top - canvasRect.top + rect.height / 2,
                width: rect.width,
                height: rect.height,
                radius: Math.max(rect.width, rect.height) / 2,
                vx: 0,
                vy: 0
            };

            const prevPlayer = this._playerTracker.players.get('self');
            if (prevPlayer && deltaTime > 0) {
                currentPos.vx = (currentPos.x - prevPlayer.x) / (deltaTime / 1000);
                currentPos.vy = (currentPos.y - prevPlayer.y) / (deltaTime / 1000);
            }

            players.push(currentPos);
            this._playerTracker.players.set('self', currentPos);
        }

        const otherPlayers = document.querySelectorAll('body > div.spawnedavatar.spawnedavatar-otherplayer');
        otherPlayers.forEach((player, index) => {
            const rect = player.getBoundingClientRect();
            const playerId = player.dataset.playerid || `other_${index}`;
            const currentPos = {
                type: 'other',
                id: playerId,
                x: rect.left - canvasRect.left + rect.width / 2,
                y: rect.top - canvasRect.top + rect.height / 2,
                width: rect.width,
                height: rect.height,
                radius: Math.max(rect.width, rect.height) / 2,
                vx: 0,
                vy: 0
            };

            const prevPlayer = this._playerTracker.players.get(playerId);
            if (prevPlayer && deltaTime > 0) {
                currentPos.vx = (currentPos.x - prevPlayer.x) / (deltaTime / 1000);
                currentPos.vy = (currentPos.y - prevPlayer.y) / (deltaTime / 1000);
            }

            players.push(currentPos);
            this._playerTracker.players.set(playerId, currentPos);
        });

        this._playerTracker.lastUpdateTime = currentTime;

        return players.filter(p =>
            p.x >= -this._playerTracker.detectionRadius &&
            p.x <= this._canvas.width + this._playerTracker.detectionRadius &&
            p.y >= -this._playerTracker.detectionRadius &&
            p.y <= this._canvas.height + this._playerTracker.detectionRadius
        );
    }

    _updatePlayerDetection() {
        const currentTime = performance.now();
        if (currentTime - this._playerTracker.lastUpdateTime < this._playerTracker.detectionInterval) return;

        this._getPlayerPositions();
    }

    _handleBallPlayerCollisions() {
        const players = this._getPlayerPositions();
        if (players.length === 0) return;

        this._physicsObjects.forEach(ball => {
            if (ball.type !== 'ball') return;

            players.forEach(player => {
                const dx = ball.x - player.x;
                const dy = ball.y - player.y;
                const distance = Math.sqrt(dx * dx + dy * dy);
                const detectionDistance = this._playerTracker.detectionRadius;

                if (distance < detectionDistance && distance > 0) {
                    const intensity = Math.max(0, (detectionDistance - distance) / detectionDistance);

                    if (distance < ball.radius + player.radius) {
                        this._resolveBallPlayerCollision(ball, player, dx, dy, distance, 'collision');
                    } else if (intensity > 0.3) {
                        this._resolveBallPlayerCollision(ball, player, dx, dy, distance, 'push', intensity);
                    }
                }
            });
        });
    }

    _resolveBallPlayerCollision(ball, player, dx, dy, distance, interactionType = 'collision', intensity = 1.0) {
        const normalX = dx / distance;
        const normalY = dy / distance;

        if (interactionType === 'collision') {
            const overlap = (ball.radius + player.radius) - distance;
            ball.x += normalX * overlap * 1.1;
            ball.y += normalY * overlap * 1.1;

            const relativeVelX = ball.vx - (player.vx || 0);
            const relativeVelY = ball.vy - (player.vy || 0);
            const velAlongNormal = relativeVelX * normalX + relativeVelY * normalY;

            if (velAlongNormal > 0) return;

            const restitution = this.PHYSICS_CONSTANTS.PLAYER_RESTITUTION;
            const impulse = -(1 + restitution) * velAlongNormal;

            ball.vx += impulse * normalX;
            ball.vy += impulse * normalY;

            if (player.vx || player.vy) {
                const transferFactor = 0.7;
                ball.vx += (player.vx || 0) * transferFactor;
                ball.vy += (player.vy || 0) * transferFactor;
            }

            const additionalForce = this.PHYSICS_CONSTANTS.PLAYER_INTERACTION_FORCE * 1.5;
            ball.vx += normalX * additionalForce * this.PHYSICS_CONSTANTS.TIMESTEP;
            ball.vy += normalY * additionalForce * this.PHYSICS_CONSTANTS.TIMESTEP;

        } else if (interactionType === 'push') {
            const pushForce = this.PHYSICS_CONSTANTS.PLAYER_INTERACTION_FORCE *
                             this.PHYSICS_CONSTANTS.PLAYER_PUSH_MULTIPLIER *
                             intensity * intensity;

            ball.vx += normalX * pushForce * this.PHYSICS_CONSTANTS.TIMESTEP;
            ball.vy += normalY * pushForce * this.PHYSICS_CONSTANTS.TIMESTEP;

            if (player.vx || player.vy) {
                const playerSpeed = Math.sqrt((player.vx || 0) ** 2 + (player.vy || 0) ** 2);
                if (playerSpeed > 50) {
                    const moveTransfer = intensity * 0.4;
                    ball.vx += (player.vx || 0) * moveTransfer;
                    ball.vy += (player.vy || 0) * moveTransfer;
                }
            }
        }

        const maxSpeed = this.PHYSICS_CONSTANTS.MAX_VELOCITY * 1.2;
        const currentSpeed = Math.sqrt(ball.vx ** 2 + ball.vy ** 2);
        if (currentSpeed > maxSpeed) {
            ball.vx = (ball.vx / currentSpeed) * maxSpeed;
            ball.vy = (ball.vy / currentSpeed) * maxSpeed;
        }

        console.log(`⚽ ${interactionType} with ${player.type} player: ${player.id} (intensity: ${intensity.toFixed(2)})`);
    }

    async createFootballField() {
        if (!this._canvas) {
            this.notify("error", "❌ Canvas no disponible.");
            return;
        }

        this.notify("info", "🏟️ Creando campo de fútbol...");

        await this._cleanCanvas();
        await this._delay(200);

        const fieldCoords = this._calculateFieldCoordinates();

        await this._drawGrass(fieldCoords);
        await this._delay(300);

        await this._drawGoal(fieldCoords.leftGoal, this.FIELD_CONSTANTS.GOAL_P1_COLOR);
        await this._drawGoal(fieldCoords.rightGoal, this.FIELD_CONSTANTS.GOAL_P2_COLOR);
        await this._delay(300);

        await this._drawFootballText(fieldCoords);

        this.notify("success", "🏆 ¡Campo creado correctamente!");
    }

    _calculateFieldCoordinates() {
        const width = this._canvas.width;
        const height = this._canvas.height;

        return {
            leftGoal: {
                x: Math.floor(width * -0.05),
                y: Math.floor(height * 0.67),
                width: Math.floor(width * 0.25),
                height: Math.floor(height * 0.42)
            },
            rightGoal: {
                x: Math.floor(width * 0.80),
                y: Math.floor(height * 0.67),
                width: Math.floor(width * 0.25),
                height: Math.floor(height * 0.42)
            },
            grass: {
                y: height - 2,
                triangleSpacing: Math.max(8, Math.floor(width * 0.015)),
                triangleHeight: Math.max(10, Math.floor(height * 0.025))
            },
            text: {
                x: Math.floor(width * 0.5),
                y: Math.floor(height * 0.15),
                pixelSize: Math.max(2, Math.floor(width * 0.006))
            }
        };
    }

    async _drawGrass(coords) {
        const width = this._canvas.width;
        const lineThickness = Math.max(4, Math.floor(width * 0.01));

        this._sendDrawCommand(0, coords.grass.y, width, coords.grass.y, this.FIELD_CONSTANTS.GRASS_COLOR, lineThickness);
        await this._delay(60);

        for (let x = 0; x < width; x += coords.grass.triangleSpacing) {
            const height = coords.grass.triangleHeight + (Math.random() * 6 - 3);
            const tipX = x + (Math.random() * 4 - 2);
            const tipY = coords.grass.y - height;

            this._sendDrawCommand(x, coords.grass.y, tipX, tipY, this.FIELD_CONSTANTS.GRASS_COLOR, 2);
            this._sendDrawCommand(tipX, tipY, x + coords.grass.triangleSpacing, coords.grass.y, this.FIELD_CONSTANTS.GRASS_COLOR, 2);
            await this._delay(25);
        }
    }

    async _drawGoal(goalCoords, color) {
        const frameThickness = this.FIELD_CONSTANTS.GOAL_THICKNESS;
        const lineThickness = Math.max(1, Math.floor(frameThickness/2));
        const lineMargin = Math.max(8, goalCoords.width * 0.08);
        const topOffset = goalCoords.height * 0.05;

        this._sendDrawCommand(goalCoords.x, goalCoords.y, goalCoords.x, goalCoords.y + goalCoords.height, color, frameThickness);
        this._sendDrawCommand(goalCoords.x, goalCoords.y, goalCoords.x + goalCoords.width, goalCoords.y, color, frameThickness);
        this._sendDrawCommand(goalCoords.x + goalCoords.width, goalCoords.y, goalCoords.x + goalCoords.width, goalCoords.y + goalCoords.height, color, frameThickness);
        await this._delay(80);

        for (let i = 1; i <= 3; i++) {
            const x = goalCoords.x + (goalCoords.width * i / 4);
            this._sendDrawCommand(x, goalCoords.y + topOffset, x, goalCoords.y + goalCoords.height - topOffset, color, lineThickness);
            await this._delay(60);
        }

        for (let i = 1; i <= 4; i++) {
            const y = goalCoords.y + (goalCoords.height * i / 5);
            this._sendDrawCommand(goalCoords.x + lineMargin, y, goalCoords.x + goalCoords.width - lineMargin, y, color, lineThickness);
            await this._delay(60);
        }
    }

    PIXEL_LETTERS = {
        'F': [[1,1,1,1],[1,0,0,0],[1,1,1,0],[1,0,0,0],[1,0,0,0]],
        'O': [[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,0,0,1],[1,1,1,1]],
        'T': [[1,1,1,1,1],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0]],
        'B': [[1,1,1,1],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,1,1,1]],
        'A': [[0,1,1,0],[1,0,0,1],[1,1,1,1],[1,0,0,1],[1,0,0,1]],
        'L': [[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,0,0,0],[1,1,1,1]]
    };

    async _drawFootballText(coords) {
        const text = "FOOTBALL";
        const letterSpacing = coords.text.pixelSize * 6;
        const textWidth = text.length * letterSpacing;
        let currentX = coords.text.x - (textWidth / 2);

        for (let i = 0; i < text.length; i++) {
            const letter = text[i].toUpperCase();
            const pattern = this.PIXEL_LETTERS[letter];
            if (!pattern) continue;

            for (let row = 0; row < pattern.length; row++) {
                for (let col = 0; col < pattern[row].length; col++) {
                    if (pattern[row][col] === 1) {
                        const pixelX = currentX + (col * coords.text.pixelSize);
                        const pixelY = coords.text.y + (row * coords.text.pixelSize);
                        this._sendDrawCommand(pixelX, pixelY, pixelX + 1, pixelY + 1, this.FIELD_CONSTANTS.TEXT_COLOR, coords.text.pixelSize);
                        await this._delay(20);
                    }
                }
            }
            currentX += letterSpacing;
            await this._delay(50);
        }
    }

    async _cleanCanvas() {
        if (!this._canvas) return;

        const socket = getGameSocket();
        if (!socket || socket.readyState !== WebSocket.OPEN) {
            if (this._ctx) {
                this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
            }
            return;
        }

        const clearThickness = Math.max(this._canvas.width, this._canvas.height);
        const clearColor = '#FFFFFF';

        for (let i = 0; i < 10; i++) {
            const y = (i / 10) * this._canvas.height;
            this._sendDrawCommand(0, y, this._canvas.width, y, clearColor, clearThickness);
            await this._delay(50);
        }

        for (let i = 0; i < 10; i++) {
            const x = (i / 10) * this._canvas.width;
            this._sendDrawCommand(x, 0, x, this._canvas.height, clearColor, clearThickness);
            await this._delay(50);
        }

        if (this._ctx) {
            this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
        }
    }

    toggleMatchMode() {
        const button = this._ui.matchModeToggle;
        const scoreboard = this._ui.scoreboard;

        this.matchMode.active = !this.matchMode.active;

        if (this.matchMode.active) {
            button.style.background = '#FFF3CD';
            button.style.borderColor = '#FFC107';
            button.style.color = '#856404';
            scoreboard.style.display = 'block';
            this._setupMatchMode();
            this.notify('success', '🏆 ¡Modo partido activado!');
        } else {
            button.style.background = '#F8F9FA';
            button.style.borderColor = '#DEE2E6';
            button.style.color = '#495057';
            scoreboard.style.display = 'none';
            this._resetMatch();
            this.notify('info', '🏆 Modo partido desactivado.');
        }
    }

    async _setupMatchMode() {
        await this.createFootballField();

        this.matchMode.scores = { p1: 0, p2: 0 };
        this._updateScoreboard();

        this.matchMode.playArea = {
            leftGoalX: this._canvas.width * -0.05,
            rightGoalX: this._canvas.width * 0.80,
            goalY: this._canvas.height * 0.67,
            goalWidth: this._canvas.width * 0.25,
            goalHeight: this._canvas.height * 0.42
        };

        setTimeout(() => {
            this.addBallAtCenter();
        }, 500);
    }

    _resetMatch() {
        this.matchMode.scores = { p1: 0, p2: 0 };
        this._updateScoreboard();
        this.matchMode.goalCooldown = false;
    }

    _updateScoreboard() {
        if (this._ui.scoreP1) this._ui.scoreP1.textContent = this.matchMode.scores.p1;
        if (this._ui.scoreP2) this._ui.scoreP2.textContent = this.matchMode.scores.p2;
    }

    _checkGoals() {
        if (!this.matchMode.active || this.matchMode.goalCooldown) return;

        const playArea = this.matchMode.playArea;
        const extraPadding = this.PHYSICS_CONSTANTS.BALL_RADIUS + 10;

        Array.from(this._physicsObjects.values()).forEach(obj => {
            if (obj.type !== 'ball') return;

            if (obj.x >= playArea.leftGoalX - extraPadding &&
                obj.x <= playArea.leftGoalX + playArea.goalWidth + extraPadding &&
                obj.y >= playArea.goalY - extraPadding &&
                obj.y <= playArea.goalY + playArea.goalHeight + extraPadding) {
                this._scoreGoal('p2', obj);
            }

            if (obj.x >= playArea.rightGoalX - extraPadding &&
                obj.x <= playArea.rightGoalX + playArea.goalWidth + extraPadding &&
                obj.y >= playArea.goalY - extraPadding &&
                obj.y <= playArea.goalY + playArea.goalHeight + extraPadding) {
                this._scoreGoal('p1', obj);
            }
        });
    }

    async _scoreGoal(player, ballObject) {
        if (this.matchMode.goalCooldown) return;

        this.matchMode.goalCooldown = true;
        this.matchMode.scores[player]++;
        this.gameStats.totalGoals++;

        this._updateScoreboard();
        this._physicsObjects.delete(ballObject.id);

        this.notify("success", `⚽ ¡GOOOOL! ${player.toUpperCase()} anota!`);

        await this.createFootballField();

        if (this.matchMode.scores[player] >= this.matchMode.maxGoals) {
            this.notify("success", `🏆 ¡${player.toUpperCase()} GANA EL PARTIDO!`);
            setTimeout(() => this._resetMatch(), 3000);
        } else {
            setTimeout(() => this.addBallAtCenter(), 1000);
        }

        setTimeout(() => {
            this.matchMode.goalCooldown = false;
        }, 2000);
    }

    addBallAtCenter() {
        if (!this._canvas) {
            this.notify("error", "❌ Canvas no disponible.");
            return;
        }

        this._createBall(this._canvas.width / 2, this._canvas.height / 2);
        this.notify('success', '⚽ Pelota añadida en el centro.');
    }


    _createBall(x, y) {
        const id = `ball_${this._objectIdCounter++}`;
        const ball = {
            id: id,
            type: 'ball',
            x: x,
            y: y,
            vx: (Math.random() - 0.5) * 200,
            vy: (Math.random() - 0.5) * 200,
            radius: this.PHYSICS_CONSTANTS.BALL_RADIUS,
            color: this.controls.defaultObjectColor,
            mass: this.PHYSICS_CONSTANTS.BALL_MASS,
            lastRenderX: -9999,
            lastRenderY: -9999
        };

        this._physicsObjects.set(id, ball);
        this.gameStats.objectsCreated++;
        return ball;
    }

    togglePhysics() {
        if (this._isActive) {
            this._stopPhysicsLoop();
            this._ui.togglePhysicsBtn.textContent = '🚀 Launch Physics Engine';
            this._ui.togglePhysicsBtn.style.background = '#E9ECEF';
            this._ui.togglePhysicsBtn.style.borderColor = '#CED4DA';
            this._ui.togglePhysicsBtn.style.color = '#495057';
            this.notify('info', '🛑 Motor detenido.');
        } else {
            this._startPhysicsLoop();
            this._ui.togglePhysicsBtn.textContent = '⏸️ Stop Physics Engine';
            this._ui.togglePhysicsBtn.style.background = '#F8D7DA';
            this._ui.togglePhysicsBtn.style.borderColor = '#F5C6CB';
            this._ui.togglePhysicsBtn.style.color = '#721C24';
            this.notify('success', '🚀 Motor iniciado.');
        }
    }

    _startPhysicsLoop() {
        if (this._isActive) return;
        this._isActive = true;
        this._lastRenderTime = performance.now();
        this._update(performance.now());
    }

    _stopPhysicsLoop() {
        this._isActive = false;
    }

    _update(currentTime) {
        if (!this._isActive) return;

        const deltaTime = (currentTime - this._lastRenderTime) / 1000;
        this._lastRenderTime = currentTime;

        this._updatePhysics(deltaTime);
        this._checkCollisions();
        if (this.matchMode.active) {
            this._checkGoals();
        }
        this._drawObjects();

        requestAnimationFrame(this._update.bind(this));
    }


    _updatePhysics(deltaTime) {
        const gravity = this.PHYSICS_CONSTANTS.GRAVITY;
        const airResistance = this.PHYSICS_CONSTANTS.AIR_RESISTANCE;

        this._updatePlayerDetection();

        this._physicsObjects.forEach(obj => {
            let gravityMultiplier = 1;
            let airResistanceMultiplier = 1;

            switch(this.controls.alternatePhysics) {
                case 'moon':
                    gravityMultiplier = 0.16;
                    break;
                case 'underwater':
                    airResistanceMultiplier = 15;
                    gravityMultiplier = 0.3;
                    break;
                case 'magnetic':
                    const centerX = this._canvas.width / 2;
                    const centerY = this._canvas.height / 2;
                    const dx = centerX - obj.x;
                    const dy = centerY - obj.y;
                    const distance = Math.sqrt(dx * dx + dy * dy);
                    if (distance > 0) {
                        const magnetForce = 1000;
                        const forceX = (dx / distance) * magnetForce;
                        const forceY = (dy / distance) * magnetForce;
                        obj.vx += forceX * deltaTime;
                        obj.vy += forceY * deltaTime;
                    }
                    break;
            }


            if (obj.y + obj.radius >= this._canvas.height - 1) {
                obj.vx *= this.PHYSICS_CONSTANTS.FRICTION_GRASS;
            } else {

                obj.vx *= (1 - airResistance * airResistanceMultiplier);
                obj.vy *= (1 - airResistance * airResistanceMultiplier);
            }

            obj.vy += gravity * gravityMultiplier * deltaTime;

            const speed = Math.sqrt(obj.vx ** 2 + obj.vy ** 2);
            if (speed > this.PHYSICS_CONSTANTS.MAX_VELOCITY) {
                const scale = this.PHYSICS_CONSTANTS.MAX_VELOCITY / speed;
                obj.vx *= scale;
                obj.vy *= scale;
            }

            obj.x += obj.vx * deltaTime;
            obj.y += obj.vy * deltaTime;
        });

        this._handleBallPlayerCollisions();
    }

    _checkCollisions() {
        const gameWidth = this._canvas.width;
        const gameHeight = this._canvas.height;
        const restitution = this.PHYSICS_CONSTANTS.RESTITUTION_WALL;

        this._physicsObjects.forEach(obj => {
            if (obj.x - obj.radius < 0) {
                obj.vx = -obj.vx * restitution;
                obj.x = obj.radius;
            }
            if (obj.x + obj.radius > gameWidth) {
                obj.vx = -obj.vx * restitution;
                obj.x = gameWidth - obj.radius;
            }
            if (obj.y - obj.radius < 0) {
                obj.vy = -obj.vy * restitution;
                obj.y = obj.radius;
            }
            if (obj.y + obj.radius > gameHeight) {
                obj.vy = -obj.vy * restitution;
                obj.y = gameHeight - obj.radius;
            }

            this._physicsObjects.forEach(otherObj => {
                if (obj.id === otherObj.id) return;
                const dx = otherObj.x - obj.x;
                const dy = otherObj.y - obj.y;
                const distance = Math.sqrt(dx * dx + dy * dy);
                const minDistance = obj.radius + otherObj.radius;

                if (distance < minDistance) {
                    const angle = Math.atan2(dy, dx);
                    const overlap = minDistance - distance;
                    obj.x -= (overlap / 2) * Math.cos(angle);
                    obj.y -= (overlap / 2) * Math.sin(angle);
                    otherObj.x += (overlap / 2) * Math.cos(angle);
                    otherObj.y += (overlap / 2) * Math.sin(angle);

                    const v1 = { x: obj.vx, y: obj.vy };
                    const v2 = { x: otherObj.vx, y: otherObj.vy };
                    obj.vx = v2.x * this.PHYSICS_CONSTANTS.RESTITUTION_BALL;
                    obj.vy = v2.y * this.PHYSICS_CONSTANTS.RESTITUTION_BALL;
                    otherObj.vx = v1.x * this.PHYSICS_CONSTANTS.RESTITUTION_BALL;
                    otherObj.vy = v1.y * this.PHYSICS_CONSTANTS.RESTITUTION_BALL;

                    this.gameStats.totalCollisions++;
                }
            });
        });
    }

    _drawObjects() {
        this._physicsObjects.forEach(obj => {
            if (obj.type !== 'ball') return;

            const dx = Math.abs(obj.x - obj.lastRenderX);
            const dy = Math.abs(obj.y - obj.lastRenderY);

            if (dx > this.MOVEMENT_THRESHOLD || dy > this.MOVEMENT_THRESHOLD) {
                const cacheKey = `${obj.id}_old`;
                if (this._positionCache.has(cacheKey)) {
                    const oldPos = this._positionCache.get(cacheKey);
                    this._drawBall(oldPos.x, oldPos.y, obj.radius, '#FFFFFF');
                }

                this._drawBall(obj.x, obj.y, obj.radius, obj.color);

                this._positionCache.set(cacheKey, { x: obj.lastRenderX, y: obj.lastRenderY });

                obj.lastRenderX = obj.x;
                obj.lastRenderY = obj.y;
            }
        });
    }

    _drawBall(x, y, radius, color) {
        const thickness = radius * 2.5;
        this._sendDrawCommand(x, y, x + 0.1, y + 0.1, color, thickness);
    }

    resetAllObjects() {
        if (this._canvas) {
            this._physicsObjects.forEach(obj => {
                obj.x = this._canvas.width / 2 + (Math.random() - 0.5) * 100;
                obj.y = this._canvas.height / 2 + (Math.random() - 0.5) * 100;
                obj.vx = 0;
                obj.vy = 0;
                obj.lastRenderX = -9999;
                obj.lastRenderY = -9999;
            });
            this.notify('success', '🔄 ¡Objetos reiniciados!');
        }
    }

    clearAllObjects() {
        this._physicsObjects.clear();
        this._positionCache.clear();
        this._cleanCanvas();
        this.notify('info', '🗑️ Todos los objetos eliminados.');
    }

    _delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    _startAdvancedStatsMonitoring() {

        this._stopAdvancedStatsMonitoring();
        this._statsInterval = setInterval(() => {

            this.gameStats.maxVelocityReached = Math.max(
                this.gameStats.maxVelocityReached,
                ...Array.from(this._physicsObjects.values()).map(obj =>
                    Math.sqrt(obj.vx ** 2 + obj.vy ** 2)
                )
            );
        }, 1000);
    }

    _stopAdvancedStatsMonitoring() {
        if (this._statsInterval) {
            clearInterval(this._statsInterval);
            this._statsInterval = null;
        }
    }
}



class GenerativeAnimatorTool extends QBit {
    _isAnimating = false;
    _generationIntervalId = null;

    // Acceso directo como el script original
    _canvas = null;
    _ctx = null;
    _socket = null;

    _ui = {
        textInput: null,
        charCounter: null,
        letterSizeRange: null,
        letterSizeValue: null,
        letterSpacingRange: null,
        letterSpacingValue: null,
        thicknessRange: null,
        thicknessValue: null,
        posXSelect: null,
        posYSelect: null,
        colorSchemeSelect: null,
        colorPreview: null,
        sparkleAreaRange: null,
        sparkleAreaValue: null,
        sparkleDelayRange: null,
        sparkleDelayValue: null,
        finalIntensityRange: null,
        finalIntensityValue: null,
        textSpeedRange: null,
        textSpeedValue: null,
        createButton: null,
        randomButton: null,
        resetButton: null,
        stopButton: null
    };

    _letterPatterns = {
        'A': [[0,4,1,0], [1,0,2,0], [2,0,3,4], [0,2,3,2]],
        'B': [[0,0,0,4], [0,0,2,0], [0,2,2,2], [2,0,2,2], [2,2,2,4], [0,4,2,4]],
        'C': [[0,0,0,4], [0,0,2,0], [0,4,2,4]],
        'D': [[0,0,0,4], [0,0,2,1], [2,1,2,3], [2,3,0,4]],
        'E': [[0,0,0,4], [0,0,2,0], [0,2,1,2], [0,4,2,4]],
        'F': [[0,0,0,4], [0,0,2,0], [0,2,1,2]],
        'G': [[0,0,0,4], [0,0,2,0], [0,4,2,4], [2,2,2,4], [1,2,2,2]],
        'H': [[0,0,0,4], [2,0,2,4], [0,2,2,2]],
        'I': [[0,0,2,0], [1,0,1,4], [0,4,2,4]],
        'J': [[0,0,2,0], [1,0,1,3], [1,3,0,4]],
        'K': [[0,0,0,4], [0,2,2,0], [0,2,2,4]],
        'L': [[0,0,0,4], [0,4,2,4]],
        'M': [[0,0,0,4], [0,0,1,2], [1,2,2,0], [2,0,2,4]],
        'N': [[0,0,0,4], [0,0,2,4], [2,0,2,4]],
        'O': [[0,0,0,4], [0,0,2,0], [2,0,2,4], [2,4,0,4]],
        'P': [[0,0,0,4], [0,0,2,0], [2,0,2,2], [2,2,0,2]],
        'Q': [[0,0,0,4], [0,0,2,0], [2,0,2,4], [2,4,0,4], [1,3,2,4]],
        'R': [[0,0,0,4], [0,0,2,0], [2,0,2,2], [2,2,0,2], [0,2,2,4]],
        'S': [[0,0,2,0], [0,0,0,2], [0,2,2,2], [2,2,2,4], [2,4,0,4]],
        'T': [[0,0,2,0], [1,0,1,4]],
        'U': [[0,0,0,4], [0,4,2,4], [2,4,2,0]],
        'V': [[0,0,1,4], [2,0,1,4]],
        'W': [[0,0,0,4], [0,4,1,2], [1,2,2,4], [2,4,2,0]],
        'X': [[0,0,2,4], [2,0,0,4]],
        'Y': [[0,0,1,2], [2,0,1,2], [1,2,1,4]],
        'Z': [[0,0,2,0], [2,0,0,4], [0,4,2,4]],
        '0': [[0,0,0,4], [0,0,2,0], [2,0,2,4], [2,4,0,4]],
        '1': [[1,0,1,4], [0,1,1,0], [0,4,2,4]],
        '2': [[0,0,2,0], [2,0,2,2], [2,2,0,2], [0,2,0,4], [0,4,2,4]],
        '3': [[0,0,2,0], [2,0,2,2], [1,2,2,2], [2,2,2,4], [2,4,0,4]],
        '4': [[0,0,0,2], [0,2,2,2], [2,0,2,4]],
        '5': [[2,0,0,0], [0,0,0,2], [0,2,2,2], [2,2,2,4], [2,4,0,4]],
        '6': [[0,0,0,4], [0,0,2,0], [0,2,2,2], [2,2,2,4], [2,4,0,4]],
        '7': [[0,0,2,0], [2,0,1,4]],
        '8': [[0,0,0,4], [0,0,2,0], [2,0,2,4], [2,4,0,4], [0,2,2,2]],
        '9': [[0,0,0,2], [0,0,2,0], [2,0,2,4], [0,2,2,2], [2,4,0,4]],
        ' ': []
    };

    _colorSchemes = {
        golden: {
            text: ['#FFD700', '#FFA500', '#FFFF00', '#FFE4B5'],
            sparkles: ['#FFFFFF', '#FFFFCC', '#F0E68C', '#DAA520'],
            sparkleCount: 8,
            finalSparkles: 50
        },
        rainbow: {
            text: ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#4B0082', '#9400D3'],
            sparkles: ['#FFFFFF', '#FFE4E1', '#F0F8FF', '#FFFAF0'],
            sparkleCount: 12,
            finalSparkles: 75
        },
        neon: {
            text: ['#FF1493', '#00FFFF', '#ADFF2F', '#FF69B4'],
            sparkles: ['#FFFFFF', '#E0E0E0', '#FFFF99', '#FF99FF'],
            sparkleCount: 15,
            finalSparkles: 100
        },
        ice: {
            text: ['#87CEEB', '#B0E0E6', '#ADD8E6', '#E0F6FF'],
            sparkles: ['#FFFFFF', '#F0FFFF', '#E6F3FF', '#CCE7FF'],
            sparkleCount: 10,
            finalSparkles: 60
        },
        fire: {
            text: ['#FF4500', '#FF6347', '#FF0000', '#DC143C'],
            sparkles: ['#FFFF00', '#FFD700', '#FFA500', '#FFFFFF'],
            sparkleCount: 20,
            finalSparkles: 120
        },
        emerald: {
            text: ['#50C878', '#00FF7F', '#32CD32', '#228B22'],
            sparkles: ['#FFFFFF', '#F0FFF0', '#E0FFE0', '#90EE90'],
            sparkleCount: 9,
            finalSparkles: 55
        },
        cosmic: {
            text: ['#4B0082', '#8A2BE2', '#9400D3', '#7B68EE'],
            sparkles: ['#FFFFFF', '#E6E6FA', '#DDA0DD', '#D8BFD8'],
            sparkleCount: 18,
            finalSparkles: 90
        },
        sunset: {
            text: ['#FF6347', '#FF7F50', '#FFD700', '#FFA500'],
            sparkles: ['#FFFFFF', '#FFFAF0', '#FFE4B5', '#FFDAB9'],
            sparkleCount: 14,
            finalSparkles: 70
        }
    };

    constructor() {
        super("✨Glitter Studio", '<i class="fas fa-broom"></i>');
        this._onStartup();
    }

    _onStartup() {
        setTimeout(() => {
            this._setupCanvas();
            this._setupWebSocket();
            this._loadInterface();
            this._setupEventListeners();

            // Verificar conexión después de cargar
            setTimeout(() => {
                this._checkConnection();
                this._updateButtonsState();
            }, 2000);

            setInterval(() => {
                this._checkConnection();
                this._updateButtonsState();
            }, 2000);

            this.notify("success", "✨ Glitter Text Studio cargado exitosamente!");
        }, 1000);
    }

    // MÉTODO ARREGLADO - Setup Canvas exactamente como script original
    _setupCanvas() {
        this._canvas = document.getElementById('canvas');
        if (this._canvas) {
            this._ctx = this._canvas.getContext('2d');
            console.log("✅ Canvas encontrado y configurado");
        } else {
            this.notify("error", "❌ Canvas no encontrado");
        }
    }

    // MÉTODO ARREGLADO - WebSocket setup como script original
    _setupWebSocket() {
        // Variable global para capturar socket como en script original
        if (!window._glitterTextSocket) {
            const self = this;

            const originalSend = WebSocket.prototype.send;
            WebSocket.prototype.send = function (...args) {
                if (!window._glitterTextSocket) {
                    window._glitterTextSocket = this;
                    self._socket = this;
                    console.log("✅ WebSocket capturado para Glitter Text");
                }
                return originalSend.apply(this, args);
            };
        } else {
            this._socket = window._glitterTextSocket;
        }

        // Intentar obtener socket existente
        if (typeof getGameSocket === 'function') {
            const gameSocket = getGameSocket();
            if (gameSocket && gameSocket.readyState === WebSocket.OPEN) {
                this._socket = gameSocket;
                window._glitterTextSocket = gameSocket;
                console.log("✅ Socket obtenido via getGameSocket() para Glitter Text");
            }
        }
    }

    // MÉTODO NUEVO - Verificar conexión activa
    _checkConnection() {
        // Verificar getGameSocket primero
        if (typeof getGameSocket === 'function') {
            const gameSocket = getGameSocket();
            if (gameSocket && gameSocket.readyState === WebSocket.OPEN) {
                this._socket = gameSocket;
                window._glitterTextSocket = gameSocket;
                return true;
            }
        }

        // Verificar socket capturado
        if (window._glitterTextSocket && window._glitterTextSocket.readyState === WebSocket.OPEN) {
            this._socket = window._glitterTextSocket;
            return true;
        }

        // Verificar socket interno
        if (this._socket && this._socket.readyState === WebSocket.OPEN) {
            return true;
        }

        return false;
    }

    // MÉTODO ARREGLADO - SendDrawCommand con dibujo local y servidor
    _sendDrawCommand(x1, y1, x2, y2, color, thickness) {
        // Dibujo local como script original
        if (this._ctx && this._canvas) {
            this._ctx.save();
            this._ctx.strokeStyle = color;
            this._ctx.lineWidth = thickness;
            this._ctx.lineCap = 'round';
            this._ctx.lineJoin = 'round';
            this._ctx.beginPath();
            this._ctx.moveTo(x1, y1);
            this._ctx.lineTo(x2, y2);
            this._ctx.stroke();
            this._ctx.restore();
        }

        // Comando al servidor
        if (!this._socket || !this._canvas) {
            return false;
        }

        if (typeof x1 !== 'number' || typeof y1 !== 'number' ||
            typeof x2 !== 'number' || typeof y2 !== 'number') {
            return false;
        }

        const normX1 = Math.max(0, Math.min(1, (x1 / this._canvas.width))).toFixed(4);
        const normY1 = Math.max(0, Math.min(1, (y1 / this._canvas.height))).toFixed(4);
        const normX2 = Math.max(0, Math.min(1, (x2 / this._canvas.width))).toFixed(4);
        const normY2 = Math.max(0, Math.min(1, (y2 / this._canvas.height))).toFixed(4);

        const safeColor = color && typeof color === 'string' ? color : '#000000';
        const safeThickness = Math.max(1, Math.min(50, thickness || 2));

        const command = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - safeThickness},"${safeColor}",0,0,{}]]`;

        try {
            if (this._socket.readyState === WebSocket.OPEN) {
                this._socket.send(command);
                return true;
            } else {
                return false;
            }
        } catch (e) {
            this.notify("error", "Error de conexión: " + e.message);
            return false;
        }
    }

    _getLetterPattern(letter) {
        return this._letterPatterns[letter.toUpperCase()] || [];
    }

    _getRandomFloat(min, max) {
        return Math.random() * (max - min) + min;
    }

    async _createGlitterText(options) {
        if (!this._socket || this._socket.readyState !== WebSocket.OPEN) {
            this.notify("error", "No hay conexión WebSocket activa. Conéctate al juego primero.");
            this._isAnimating = false;
            this._updateButtonsState();
            return;
        }

        if (!this._canvas || !this._ctx) {
            this.notify("error", "Canvas no disponible.");
            this._isAnimating = false;
            this._updateButtonsState();
            return;
        }

        const {
            text, letterSize, letterSpacing, positionX, positionY,
            colorScheme, textThickness, sparkleDelay, textSpeed,
            sparkleArea, finalSparkleIntensity
        } = options;

        if (!text || text.trim().length === 0) {
            this.notify("warning", "El texto no puede estar vacío.");
            this._isAnimating = false;
            this._updateButtonsState();
            return;
        }

        const textColors = this._colorSchemes[colorScheme]?.text || this._colorSchemes.emerald.text;
        const sparkleColors = this._colorSchemes[colorScheme]?.sparkles || this._colorSchemes.emerald.sparkles;
        const sparkleCount = Math.floor((this._colorSchemes[colorScheme]?.sparkleCount || 9) * (sparkleArea / 100));
        const finalSparkleCount = Math.floor((this._colorSchemes[colorScheme]?.finalSparkles || 55) * (finalSparkleIntensity / 100));

        const totalWidth = text.length * letterSpacing;
        let startX, startY;

        switch(positionX) {
            case 'left': startX = 50; break;
            case 'center': startX = Math.max(50, (this._canvas.width - totalWidth) / 2); break;
            case 'right': startX = this._canvas.width - totalWidth - 50; break;
            default: startX = parseInt(positionX) || (this._canvas.width - totalWidth) / 2;
        }

        switch(positionY) {
            case 'top': startY = letterSize + 50; break;
            case 'middle': startY = this._canvas.height / 2; break;
            case 'bottom': startY = this._canvas.height - letterSize - 50; break;
            default: startY = parseInt(positionY) || this._canvas.height / 2;
        }

        this.notify("info", `✨ Creando texto: "${text}"`);

        try {
            for (let i = 0; i < text.length; i++) {
                if (!this._isAnimating) break;

                const letter = text[i];
                const letterPattern = this._getLetterPattern(letter);
                const letterX = startX + (i * letterSpacing);
                const baseColor = textColors[i % textColors.length];

                for (const line of letterPattern) {
                    if (!this._isAnimating) break;

                    const [x1, y1, x2, y2] = line;
                    const realX1 = letterX + (x1 * letterSize / 3);
                    const realY1 = startY + (y1 * letterSize / 4) - letterSize/2;
                    const realX2 = letterX + (x2 * letterSize / 3);
                    const realY2 = startY + (y2 * letterSize / 4) - letterSize/2;

                    this._sendDrawCommand(realX1, realY1, realX2, realY2, baseColor, textThickness);
                    await new Promise(resolve => setTimeout(resolve, textSpeed));

                    this._sendDrawCommand(realX1-1, realY1-1, realX2-1, realY2-1, '#FFFFFF', Math.max(1, textThickness-2));
                    await new Promise(resolve => setTimeout(resolve, textSpeed/2));
                }

                for (let sparkle = 0; sparkle < sparkleCount; sparkle++) {
                    if (!this._isAnimating) break;

                    const sparkleX = letterX + this._getRandomFloat(-10, letterSize + 10);
                    const sparkleY = startY + this._getRandomFloat(-letterSize, letterSize);
                    const sparkleColor = sparkleColors[Math.floor(Math.random() * sparkleColors.length)];
                    const sparkleSize = this._getRandomFloat(2, 6);

                    this._sendDrawCommand(sparkleX - sparkleSize, sparkleY, sparkleX + sparkleSize, sparkleY, sparkleColor, 2);
                    this._sendDrawCommand(sparkleX, sparkleY - sparkleSize, sparkleX, sparkleY + sparkleSize, sparkleColor, 2);
                    await new Promise(resolve => setTimeout(resolve, sparkleDelay));
                }

                await new Promise(resolve => setTimeout(resolve, 200));
            }

            for (let finalSparkle = 0; finalSparkle < finalSparkleCount; finalSparkle++) {
                if (!this._isAnimating) break;

                const x = this._getRandomFloat(startX - 30, startX + (text.length * letterSpacing) + 30);
                const y = this._getRandomFloat(startY - letterSize, startY + letterSize);
                const color = sparkleColors[Math.floor(Math.random() * sparkleColors.length)];
                const size = this._getRandomFloat(1, 4);

                this._sendDrawCommand(x-size, y, x+size, y, color, 1);
                this._sendDrawCommand(x, y-size, x, y+size, color, 1);
                await new Promise(resolve => setTimeout(resolve, 25));
            }

            this.notify("success", "✨ Texto brillante completado");
        } catch (error) {
            this.notify("error", "Error durante la creación del texto: " + error.message);
        } finally {
            this._isAnimating = false;
            this._updateButtonsState();
        }
    }

    _stopAnimation() {
        this._isAnimating = false;
        if (this._generationIntervalId) {
            clearInterval(this._generationIntervalId);
            this._generationIntervalId = null;
        }
        this._updateButtonsState();
        this.notify("info", "Animación detenida por el usuario.");
    }

    _loadInterface() {
        const container = document.createElement("div");
        container.className = "glitter-text-section";
        container.style.cssText = `
            background: #ffffff;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 15px;
            margin: 10px 0;
            color: #333333;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        `;
        this.htmlElements.section.appendChild(container);

        const textSection = this._createSection("📝 Configuración de Brillo");

        this._ui.textInput = document.createElement("input");
        this._ui.textInput.type = "text";
        this._ui.textInput.placeholder = "Escribe tu texto brillante aquí...";
        this._ui.textInput.value = "write here";
        this._ui.textInput.maxLength = 12;
        this._ui.textInput.style.cssText = `
            width: 100%;
            padding: 10px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            font-size: 14px;
            margin-bottom: 5px;
        `;

        this._ui.charCounter = document.createElement("div");
        this._ui.charCounter.innerHTML = `Caracteres: <span style="color: #28a745;">10</span>/12`;
        this._ui.charCounter.style.cssText = `
            font-size: 12px;
            color: #666;
            text-align: right;
        `;

        textSection.appendChild(this._ui.textInput);
        textSection.appendChild(this._ui.charCounter);
        container.appendChild(textSection);

        const controlsSection = this._createSection("🎮 Controles Principales");

        const buttonRow = document.createElement("div");
        buttonRow.style.cssText = `
            display: flex;
            gap: 8px;
            margin: 10px 0;
        `;

        this._ui.createButton = this._createButton("✨ Crear Texto Brillante", "#28a745");
        this._ui.createButton.addEventListener('click', () => this._executeGlitterText());

        this._ui.randomButton = this._createButton("🎲 Aleatorio", "#ffc107");
        this._ui.randomButton.addEventListener('click', () => this._randomizeSettings());

        this._ui.resetButton = this._createButton("🔄 Reset", "#6c757d");
        this._ui.resetButton.addEventListener('click', () => this._resetToDefaults());

        this._ui.stopButton = this._createButton("⏹️ Detener", "#dc3545");
        this._ui.stopButton.addEventListener('click', () => this._stopAnimation());

        buttonRow.appendChild(this._ui.createButton);
        buttonRow.appendChild(this._ui.randomButton);
        buttonRow.appendChild(this._ui.resetButton);
        buttonRow.appendChild(this._ui.stopButton);
        controlsSection.appendChild(buttonRow);
        container.appendChild(controlsSection);

        const sizeSection = this._createSection("📏 Tamaño y Espaciado");

        sizeSection.appendChild(this._createSlider("Tamaño Letra:", "letterSize", 25, 100, 66, "px"));
        sizeSection.appendChild(this._createSlider("Espaciado:", "letterSpacing", 35, 120, 61, "px"));
        sizeSection.appendChild(this._createSlider("Grosor Trazo:", "thickness", 3, 15, 6, "px"));
        container.appendChild(sizeSection);

        const positionSection = this._createSection("📍 Posicionamiento");

        const posXGroup = document.createElement("div");
        posXGroup.style.marginBottom = "10px";
        posXGroup.appendChild(this._createLabel("Posición Horizontal:"));
        this._ui.posXSelect = this._createSelect([
            {value: 'left', text: '← Izquierda'},
            {value: 'center', text: '↔ Centro'},
            {value: 'right', text: '→ Derecha'}
        ], 'center');
        posXGroup.appendChild(this._ui.posXSelect);

        const posYGroup = document.createElement("div");
        posYGroup.appendChild(this._createLabel("Posición Vertical:"));
        this._ui.posYSelect = this._createSelect([
            {value: 'top', text: '↑ Arriba'},
            {value: 'middle', text: '↕ Centro'},
            {value: 'bottom', text: '↓ Abajo'}
        ], 'middle');
        posYGroup.appendChild(this._ui.posYSelect);

        positionSection.appendChild(posXGroup);
        positionSection.appendChild(posYGroup);
        container.appendChild(positionSection);

        const colorSection = this._createSection("🎨 Esquemas de Color");

        const colorGroup = document.createElement("div");
        colorGroup.appendChild(this._createLabel("Esquema de Colores:"));

        this._ui.colorSchemeSelect = this._createSelect(
            Object.keys(this._colorSchemes).map(scheme => ({
                value: scheme,
                text: `${scheme.charAt(0).toUpperCase() + scheme.slice(1)} (${this._colorSchemes[scheme].sparkleCount} ✨)`
            })), 'emerald'
        );

        this._ui.colorPreview = document.createElement("div");
        this._ui.colorPreview.style.cssText = `
            height: 30px;
            border: 2px solid #dee2e6;
            border-radius: 4px;
            margin-top: 8px;
            display: flex;
        `;

        colorGroup.appendChild(this._ui.colorSchemeSelect);
        colorGroup.appendChild(this._ui.colorPreview);
        colorSection.appendChild(colorGroup);
        container.appendChild(colorSection);

        const sparkleSection = this._createSection("✨ Configuración de Brillos");

        sparkleSection.appendChild(this._createSlider("Área de Brillos:", "sparkleArea", 50, 150, 150, "%"));
        sparkleSection.appendChild(this._createSlider("Velocidad Brillos:", "sparkleDelay", 5, 50, 20, "ms"));
        sparkleSection.appendChild(this._createSlider("Intensidad Final:", "finalIntensity", 25, 200, 200, "%"));
        container.appendChild(sparkleSection);

        const speedSection = this._createSection("⚡ Velocidad y Timing");
        speedSection.appendChild(this._createSlider("Velocidad Texto:", "textSpeed", 5, 80, 30, "ms"));
        container.appendChild(speedSection);
    }

    _createSection(title) {
        const section = document.createElement("div");
        section.style.cssText = `
            margin-bottom: 15px;
            padding: 12px;
            background: #f8f9fa;
            border: 1px solid #e9ecef;
            border-radius: 6px;
        `;

        const titleElement = document.createElement("div");
        titleElement.textContent = title;
        titleElement.style.cssText = `
            font-weight: 600;
            font-size: 14px;
            color: #333;
            margin-bottom: 10px;
        `;

        section.appendChild(titleElement);
        return section;
    }

    _createSlider(label, key, min, max, defaultVal, unit) {
        const container = document.createElement("div");
        container.style.marginBottom = "12px";

        const labelEl = this._createLabel(label);

        const sliderContainer = document.createElement("div");
        sliderContainer.style.cssText = `
            display: flex;
            align-items: center;
            gap: 10px;
        `;

        const slider = document.createElement("input");
        slider.type = "range";
        slider.min = min;
        slider.max = max;
        slider.value = defaultVal;
        slider.style.cssText = `
            flex: 1;
            height: 6px;
        `;

        const valueLabel = document.createElement("span");
        valueLabel.textContent = defaultVal + unit;
        valueLabel.style.cssText = `
            min-width: 60px;
            text-align: center;
            font-size: 12px;
            font-weight: 500;
            color: #007bff;
            background: #f8f9fa;
            padding: 4px 8px;
            border-radius: 4px;
            border: 1px solid #dee2e6;
        `;

        slider.addEventListener('input', () => {
            valueLabel.textContent = slider.value + unit;
        });

        this._ui[key + "Range"] = slider;
        this._ui[key + "Value"] = valueLabel;

        sliderContainer.appendChild(slider);
        sliderContainer.appendChild(valueLabel);
        container.appendChild(labelEl);
        container.appendChild(sliderContainer);

        return container;
    }

    _createSelect(options, defaultVal) {
        const select = document.createElement("select");
        select.style.cssText = `
            width: 100%;
            padding: 8px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            font-size: 13px;
            background: #ffffff;
        `;

        options.forEach(option => {
            const opt = document.createElement("option");
            opt.value = option.value;
            opt.textContent = option.text;
            if (option.value === defaultVal) opt.selected = true;
            select.appendChild(opt);
        });

        return select;
    }

    _createButton(text, color) {
        const button = document.createElement("button");
        button.textContent = text;
        button.style.cssText = `
            flex: 1;
            padding: 10px;
            background: ${color};
            color: white;
            border: none;
            border-radius: 4px;
            font-weight: 500;
            font-size: 12px;
            cursor: pointer;
            transition: all 0.2s ease;
        `;

        button.addEventListener('mouseover', () => {
            if (!button.disabled) {
                button.style.opacity = '0.9';
                button.style.transform = 'translateY(-1px)';
            }
        });
        button.addEventListener('mouseout', () => {
            button.style.opacity = '1';
            button.style.transform = 'translateY(0)';
        });

        return button;
    }

    _createLabel(text) {
        const label = document.createElement("label");
        label.textContent = text;
        label.style.cssText = `
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            font-size: 13px;
            color: #333;
        `;
        return label;
    }

    _setupEventListeners() {
        if (this._ui.textInput) {
            this._ui.textInput.addEventListener('input', () => {
                const count = this._ui.textInput.value.length;
                if (this._ui.charCounter) {
                    this._ui.charCounter.innerHTML = `Caracteres: <span style="color: ${count > 10 ? '#dc3545' : '#28a745'};">${count}</span>/12`;
                }
            });
        }

        if (this._ui.colorSchemeSelect) {
            this._ui.colorSchemeSelect.addEventListener('change', () => this._updateColorPreview());
            this._updateColorPreview();
        }
    }

    _updateColorPreview() {
        if (!this._ui.colorPreview || !this._ui.colorSchemeSelect) return;

        const scheme = this._colorSchemes[this._ui.colorSchemeSelect.value];
        if (!scheme) return;

        this._ui.colorPreview.innerHTML = '';

        scheme.text.forEach((color, index) => {
            const colorBlock = document.createElement('div');
            colorBlock.style.cssText = `
                background-color: ${color};
                flex: 1;
                height: 100%;
                position: relative;
                cursor: help;
                transition: transform 0.2s ease;
            `;

            colorBlock.title = `Color ${index + 1}: ${color}`;
            colorBlock.addEventListener('mouseover', () => {
                colorBlock.style.transform = 'scale(1.1)';
                colorBlock.style.zIndex = '10';
            });
            colorBlock.addEventListener('mouseout', () => {
                colorBlock.style.transform = 'scale(1)';
                colorBlock.style.zIndex = '1';
            });

            this._ui.colorPreview.appendChild(colorBlock);
        });
    }

    async _executeGlitterText() {
        if (this._isAnimating) {
            this.notify("warning", "Ya hay una animación en progreso. Usa el botón 'Detener' para cancelarla.");
            return;
        }

        const text = this._ui.textInput?.value?.trim();
        if (!text || text.length === 0) {
            this.notify("warning", "El texto no puede estar vacío.");
            return;
        }

        if (text.length > 12) {
            this.notify("warning", "El texto no puede exceder 12 caracteres.");
            return;
        }

        const options = {
            text: text,
            letterSize: parseInt(this._ui.letterSizeRange?.value || 66),
            letterSpacing: parseInt(this._ui.letterSpacingRange?.value || 61),
            positionX: this._ui.posXSelect?.value || 'center',
            positionY: this._ui.posYSelect?.value || 'middle',
            colorScheme: this._ui.colorSchemeSelect?.value || 'emerald',
            textThickness: parseInt(this._ui.thicknessRange?.value || 6),
            sparkleDelay: parseInt(this._ui.sparkleDelayRange?.value || 20),
            textSpeed: parseInt(this._ui.textSpeedRange?.value || 30),
            sparkleArea: parseInt(this._ui.sparkleAreaRange?.value || 150),
            finalSparkleIntensity: parseInt(this._ui.finalIntensityRange?.value || 200)
        };

        this._isAnimating = true;
        this._updateButtonsState();

        try {
            await this._createGlitterText(options);
        } catch (error) {
            this.notify("error", "Error creando texto: " + error.message);
        }
    }

    _randomizeSettings() {
        const colorSchemes = Object.keys(this._colorSchemes);
        const positions = ['left', 'center', 'right'];
        const vPositions = ['top', 'middle', 'bottom'];

        if (this._ui.colorSchemeSelect) {
            this._ui.colorSchemeSelect.value = colorSchemes[Math.floor(Math.random() * colorSchemes.length)];
        }
        if (this._ui.posXSelect) {
            this._ui.posXSelect.value = positions[Math.floor(Math.random() * positions.length)];
        }
        if (this._ui.posYSelect) {
            this._ui.posYSelect.value = vPositions[Math.floor(Math.random() * vPositions.length)];
        }

        const sliderUpdates = {
            letterSize: 30 + Math.floor(Math.random() * 50),
            letterSpacing: 40 + Math.floor(Math.random() * 60),
            thickness: 4 + Math.floor(Math.random() * 8),
            sparkleArea: 75 + Math.floor(Math.random() * 50),
            finalIntensity: 50 + Math.floor(Math.random() * 100),
            sparkleDelay: 10 + Math.floor(Math.random() * 30),
            textSpeed: 15 + Math.floor(Math.random() * 40)
        };

        Object.entries(sliderUpdates).forEach(([key, value]) => {
            const rangeEl = this._ui[key + "Range"];
            const valueEl = this._ui[key + "Value"];

            if (rangeEl && valueEl) {
                rangeEl.value = value;
                const unit = valueEl.textContent.replace(/\d+/, '').trim();
                valueEl.textContent = value + unit;
            }
        });

        this._updateColorPreview();
        this.notify("success", "🎲 Configuración randomizada aplicada");
    }

    _resetToDefaults() {
        if (this._ui.textInput) this._ui.textInput.value = 'write here';
        if (this._ui.posXSelect) this._ui.posXSelect.value = 'center';
        if (this._ui.posYSelect) this._ui.posYSelect.value = 'middle';
        if (this._ui.colorSchemeSelect) this._ui.colorSchemeSelect.value = 'emerald';

        const defaultValues = {
            letterSize: { value: 66, unit: 'px' },
            letterSpacing: { value: 61, unit: 'px' },
            thickness: { value: 6, unit: 'px' },
            sparkleDelay: { value: 20, unit: 'ms' },
            textSpeed: { value: 30, unit: 'ms' },
            sparkleArea: { value: 150, unit: '%' },
            finalIntensity: { value: 200, unit: '%' }
        };

        Object.entries(defaultValues).forEach(([key, {value, unit}]) => {
            const rangeEl = this._ui[key + "Range"];
            const valueEl = this._ui[key + "Value"];

            if (rangeEl && valueEl) {
                rangeEl.value = value;
                valueEl.textContent = value + unit;
            }
        });

        if (this._ui.textInput && this._ui.charCounter) {
            const count = this._ui.textInput.value.length;
            this._ui.charCounter.innerHTML = `Caracteres: <span style="color: #28a745;">${count}</span>/12`;
        }

        this._updateColorPreview();
        this.notify("info", "🔄 Configuración restablecida a valores por defecto");
    }

    // MÉTODO ARREGLADO - UpdateButtonsState con detección mejorada
    _updateButtonsState() {
        const isConnected = this._checkConnection();

        if (this._ui.createButton) {
            this._ui.createButton.disabled = !isConnected || this._isAnimating;

            if (!isConnected) {
                this._ui.createButton.textContent = "🔄 Conectando...";
                this._ui.createButton.style.background = "#ffc107";
            } else if (this._isAnimating) {
                this._ui.createButton.textContent = "✨ Creando...";
                this._ui.createButton.style.background = "#17a2b8";
            } else {
                this._ui.createButton.textContent = "✨ Crear Texto Brillante";
                this._ui.createButton.style.background = "#28a745";
            }
        }

        if (this._ui.randomButton) {
            this._ui.randomButton.disabled = this._isAnimating;
            this._ui.randomButton.style.opacity = this._isAnimating ? '0.5' : '1';
        }

        if (this._ui.resetButton) {
            this._ui.resetButton.disabled = this._isAnimating;
            this._ui.resetButton.style.opacity = this._isAnimating ? '0.5' : '1';
        }

        if (this._ui.stopButton) {
            this._ui.stopButton.disabled = !this._isAnimating;
            this._ui.stopButton.style.opacity = this._isAnimating ? '1' : '0.5';
        }

        const controls = [
            this._ui.textInput,
            this._ui.letterSizeRange,
            this._ui.letterSpacingRange,
            this._ui.thicknessRange,
            this._ui.posXSelect,
            this._ui.posYSelect,
            this._ui.colorSchemeSelect,
            this._ui.sparkleAreaRange,
            this._ui.sparkleDelayRange,
            this._ui.finalIntensityRange,
            this._ui.textSpeedRange
        ];

        controls.forEach(control => {
            if (control) {
                control.disabled = this._isAnimating;
                control.style.opacity = this._isAnimating ? '0.5' : '1';
            }
        });
    }

    _validateTextInput(text) {
        if (!text || typeof text !== 'string') {
            return { valid: false, message: "El texto debe ser una cadena válida" };
        }

        if (text.trim().length === 0) {
            return { valid: false, message: "El texto no puede estar vacío" };
        }

        if (text.length > 12) {
            return { valid: false, message: "El texto no puede exceder 12 caracteres" };
        }

        const supportedChars = Object.keys(this._letterPatterns);
        const unsupportedChars = [];

        for (let char of text.toUpperCase()) {
            if (!supportedChars.includes(char)) {
                unsupportedChars.push(char);
            }
        }

        if (unsupportedChars.length > 0) {
            return {
                valid: false,
                message: `Caracteres no soportados: ${unsupportedChars.join(', ')}`
            };
        }

        return { valid: true, message: "Texto válido" };
    }

    _getCanvasInfo() {
        if (!this._canvas) {
            return {
                available: false,
                width: 0,
                height: 0,
                context: null
            };
        }

        return {
            available: true,
            width: this._canvas.width || this._canvas.clientWidth,
            height: this._canvas.height || this._canvas.clientHeight,
            context: this._ctx
        };
    }

    _getConnectionStatus() {
        try {
            if (typeof getGameSocket !== 'function') {
                return {
                    connected: false,
                    status: 'getGameSocket function not available',
                    readyState: null
                };
            }

            const socket = getGameSocket();
            if (!socket) {
                return {
                    connected: false,
                    status: 'No WebSocket instance',
                    readyState: null
                };
            }

            const states = {
                0: 'CONNECTING',
                1: 'OPEN',
                2: 'CLOSING',
                3: 'CLOSED'
            };

            return {
                connected: socket.readyState === WebSocket.OPEN,
                status: states[socket.readyState] || 'UNKNOWN',
                readyState: socket.readyState
            };

        } catch (e) {
            return {
                connected: false,
                status: 'Error: ' + e.message,
                readyState: null
            };
        }
    }

    _debugInfo() {
        const canvasInfo = this._getCanvasInfo();
        const connectionStatus = this._getConnectionStatus();

        const debug = {
            timestamp: new Date().toISOString(),
            canvas: canvasInfo,
            connection: connectionStatus,
            animation: {
                isAnimating: this._isAnimating,
                intervalId: this._generationIntervalId
            },
            ui: {
                elementsLoaded: Object.keys(this._ui).filter(key => this._ui[key] !== null).length,
                totalElements: Object.keys(this._ui).length
            },
            colorSchemes: Object.keys(this._colorSchemes).length,
            letterPatterns: Object.keys(this._letterPatterns).length
        };

        console.log("🎨 Glitter Text Studio Debug Info:", debug);
        return debug;
    }

    _cleanup() {
        this._isAnimating = false;

        if (this._generationIntervalId) {
            clearInterval(this._generationIntervalId);
            this._generationIntervalId = null;
        }

        Object.keys(this._ui).forEach(key => {
            if (this._ui[key] && typeof this._ui[key].removeEventListener === 'function') {
                this._ui[key] = null;
            }
        });

        this.notify("info", "Recursos del Glitter Text Studio limpiados");
    }

    getStatus() {
        return {
            name: "Glitter Text Studio",
            version: "2.0.0",
            isAnimating: this._isAnimating,
            canvasAvailable: this._getCanvasInfo().available,
            connectionStatus: this._getConnectionStatus(),
            supportedCharacters: Object.keys(this._letterPatterns).length,
            availableColorSchemes: Object.keys(this._colorSchemes)
        };
    }
}

class LuminousFlowGeneratorTool extends QBit {
    _isGenerating = false;
    _generationIntervalId = null;

    // Acceso directo como el script original
    _canvas = null;
    _ctx = null;
    _socket = null;
    _canvasCenter = { x: 0, y: 0 };
    _canvasDimensions = { width: 0, height: 0 };

    _ui = {
        patternSelect: null,
        colorSelect: null,
        sizeRange: null,
        sizeValue: null,
        xRange: null,
        yRange: null,
        xValue: null,
        yValue: null,
        previewCanvas: null,
        canvasInfo: null,
        drawButton: null,
        randomButton: null,
        centerButton: null,
        stopButton: null
    };

    _pixelPatterns = {
        'smiley': [
            "  YYYYYYY  ",
            " Y       Y ",
            "Y  BB BB  Y",
            "Y         Y",
            "Y  B   B  Y",
            "Y   BBB   Y",
            " Y       Y ",
            "  YYYYYYY  "
        ],
        'heart': [
            " RR   RR ",
            "RRRR RRRR",
            "RRRRRRRRR",
            " RRRRRRR ",
            "  RRRRR  ",
            "   RRR   ",
            "    R    "
        ],
        'mushroom': [
            "   RRRRR   ",
            "  RRR RRR  ",
            " RRR W RRR ",
            "RRRRRRRRRRR",
            "   WWWWW   ",
            "   WWWWW   ",
            "   WWWWW   ",
            "  WWWWWWW  "
        ],
        'flower': [
            "  R   R  ",
            " RRR RRR ",
            "RRR Y RRR",
            "RRR Y RRR",
            "  RRRRR  ",
            "   GGG   ",
            "   GGG   ",
            "   GGG   "
        ],
        'cat': [
            "W   W W   W",
            " W W   W W ",
            "  WWWWWWW  ",
            " WWWWWWWWW ",
            "WW RR RR WW",
            "W    B    W",
            "W  BBBBB  W",
            " WWWWWWWWW "
        ],
        'tree': [
            "    GGG    ",
            "   GGGGG   ",
            "  GGGGGGG  ",
            " GGGGGGGGG ",
            "GGGGGGGGGGG",
            "    BBB    ",
            "    BBB    ",
            "    BBB    "
        ],
        'house': [
            "   RRRRR   ",
            "  RRRRRRR  ",
            " RRRRRRRRR ",
            "RRRRRRRRRRR",
            "BBBBBBBBBBB",
            "B YY B BB B",
            "B YY B BB B",
            "B    B    B",
            "BBBBBBBBBBB"
        ],
        'star': [
            "    Y    ",
            "   YYY   ",
            "  YYYYY  ",
            " YYYYYYY ",
            "YYYYYYYYY",
            " YYYYYYY ",
            "  YYYYY  ",
            "   YYY   ",
            "    Y    "
        ],
        'diamond': [
            "    R    ",
            "   RRR   ",
            "  RRRRR  ",
            " RRRRRRR ",
            "RRRRRRRRR",
            " RRRRRRR ",
            "  RRRRR  ",
            "   RRR   ",
            "    R    "
        ]
    };

    _colorSchemes = {
        classic: {
            'Y': '#FFFF00', 'B': '#000000', 'R': '#FF0000', 'W': '#FFFFFF', 'G': '#00FF00', ' ': null
        },
        neon: {
            'Y': '#FFFF00', 'B': '#FF00FF', 'R': '#00FFFF', 'W': '#FFFFFF', 'G': '#00FF00', ' ': null
        },
        pastel: {
            'Y': '#FFE4B5', 'B': '#87CEEB', 'R': '#FFB6C1', 'W': '#F5F5F5', 'G': '#98FB98', ' ': null
        },
        dark: {
            'Y': '#DAA520', 'B': '#2F2F2F', 'R': '#8B0000', 'W': '#D3D3D3', 'G': '#006400', ' ': null
        },
        rainbow: {
            'Y': '#FFFF00', 'B': '#4B0082', 'R': '#FF1493', 'W': '#FFFFFF', 'G': '#32CD32', ' ': null
        },
        fire: {
            'Y': '#FFD700', 'B': '#8B0000', 'R': '#FF4500', 'W': '#FFA500', 'G': '#FF6347', ' ': null
        },
        ocean: {
            'Y': '#F0E68C', 'B': '#191970', 'R': '#4682B4', 'W': '#F0F8FF', 'G': '#008B8B', ' ': null
        },
        sunset: {
            'Y': '#FFD700', 'B': '#2F4F4F', 'R': '#DC143C', 'W': '#FFF8DC', 'G': '#FF8C00', ' ': null
        }
    };

    constructor() {
        super("👾Pixel Art Lab", '<i class="fas fa-dungeon"></i>');
        this._onStartup();
    }

    _onStartup() {
        setTimeout(() => {
            this._setupCanvas();
            this._detectCanvasInfo();
            this._setupWebSocket();
            this._loadInterface();
            this._setupEventListeners();

            // Verificar conexión después de cargar
            setTimeout(() => {
                this._checkConnection();
                this._updateButtonsState();
            }, 2000);

            setInterval(() => {
                this._updateCanvasInfo();
                this._checkConnection();
            }, 2000);

            this.notify("success", "🎨 Pixel Art Studio cargado exitosamente!");
        }, 1000);
    }

    // MÉTODO ARREGLADO - Setup Canvas exactamente como script original
    _setupCanvas() {
        this._canvas = document.getElementById('canvas');
        if (this._canvas) {
            this._ctx = this._canvas.getContext('2d');
            console.log("✅ Canvas encontrado y configurado");
        } else {
            this.notify("error", "❌ Canvas no encontrado");
        }
    }

    // MÉTODO ARREGLADO - WebSocket setup como script original
    _setupWebSocket() {
        // Variable global para capturar socket como en script original
        if (!window._pixelArtSocket) {
            const self = this;

            const originalSend = WebSocket.prototype.send;
            WebSocket.prototype.send = function (...args) {
                if (!window._pixelArtSocket) {
                    window._pixelArtSocket = this;
                    self._socket = this;
                    console.log("✅ WebSocket capturado");
                }
                return originalSend.apply(this, args);
            };
        } else {
            this._socket = window._pixelArtSocket;
        }

        // Intentar obtener socket existente
        if (typeof getGameSocket === 'function') {
            const gameSocket = getGameSocket();
            if (gameSocket && gameSocket.readyState === WebSocket.OPEN) {
                this._socket = gameSocket;
                window._pixelArtSocket = gameSocket;
                console.log("✅ Socket obtenido via getGameSocket()");
            }
        }
    }

    // MÉTODO NUEVO - Verificar conexión activa
    _checkConnection() {
        // Verificar getGameSocket primero
        if (typeof getGameSocket === 'function') {
            const gameSocket = getGameSocket();
            if (gameSocket && gameSocket.readyState === WebSocket.OPEN) {
                this._socket = gameSocket;
                window._pixelArtSocket = gameSocket;
                return true;
            }
        }

        // Verificar socket capturado
        if (window._pixelArtSocket && window._pixelArtSocket.readyState === WebSocket.OPEN) {
            this._socket = window._pixelArtSocket;
            return true;
        }

        // Verificar socket interno
        if (this._socket && this._socket.readyState === WebSocket.OPEN) {
            return true;
        }

        return false;
    }

    _detectCanvasInfo() {
        if (this._canvas) {
            const rect = this._canvas.getBoundingClientRect();
            this._canvasDimensions.width = this._canvas.width || rect.width;
            this._canvasDimensions.height = this._canvas.height || rect.height;

            this._canvasCenter.x = Math.floor(this._canvasDimensions.width / 2);
            this._canvasCenter.y = Math.floor(this._canvasDimensions.height / 2);

            return true;
        }
        return false;
    }

    // MÉTODO EXACTO del script original
    _sendDrawCommand(x1, y1, x2, y2, color, thickness) {
        if (!this._socket || !this._canvas) return false;

        const normX1 = (x1 / this._canvasDimensions.width).toFixed(4);
        const normY1 = (y1 / this._canvasDimensions.height).toFixed(4);
        const normX2 = (x2 / this._canvasDimensions.width).toFixed(4);
        const normY2 = (y2 / this._canvasDimensions.height).toFixed(4);

        const command = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - thickness},"${color}",0,0,{}]]`;
        this._socket.send(command);
        return true;
    }

    // MÉTODO EXACTO del script original
    _drawPixelLocally(x, y, width, height, color) {
        if (!this._ctx || !this._canvas) return;
        this._ctx.fillStyle = color;
        this._ctx.fillRect(x, y, width, height);
    }

    // MÉTODO EXACTO del script original
    async _fillPixelAdvanced(x, y, pixelSize, color, thickness = 2) {
        this._drawPixelLocally(x, y, pixelSize, pixelSize, color);

        const step = Math.max(1, Math.floor(thickness));

        for (let i = 0; i < pixelSize; i += step) {
            this._sendDrawCommand(x, y + i, x + pixelSize, y + i, color, thickness);
        }

        for (let i = 0; i < pixelSize; i += step * 2) {
            this._sendDrawCommand(x + i, y, x + i, y + pixelSize, color, Math.max(1, thickness - 1));
        }
    }

    async _createCustomPixelArt(patternName, colorScheme, pixelSize, offsetX = 0, offsetY = 0) {
        if (!this._socket || !this._canvas || !this._ctx) {
            this.notify("error", "No hay conexión activa o canvas no disponible.");
            return;
        }

        const pattern = this._pixelPatterns[patternName];
        const colors = this._colorSchemes[colorScheme];

        if (!pattern || !colors) {
            this.notify("error", "Patrón o esquema de colores no válido.");
            return;
        }

        this._isGenerating = true;
        this._updateButtonsState();

        const totalWidth = pattern.length * pixelSize;
        const totalHeight = pattern.length * pixelSize;

        const startX = Math.max(0, this._canvasCenter.x - totalWidth / 2 + offsetX);
        const startY = Math.max(0, this._canvasCenter.y - totalHeight / 2 + offsetY);

        this.notify("info", `Iniciando ${patternName.toUpperCase()} - Tamaño: ${pixelSize}px`);

        let pixelsDrawn = 0;
        let totalPixels = 0;

        for (let row = 0; row < pattern.length; row++) {
            for (let col = 0; col < pattern[row].length; col++) {
                if (colors[pattern[row][col]]) totalPixels++;
            }
        }

        for (let row = 0; row < pattern.length; row++) {
            if (!this._isGenerating) break;

            for (let col = 0; col < pattern[row].length; col++) {
                if (!this._isGenerating) break;

                const char = pattern[row][col];
                if (!colors[char]) continue;

                const x = startX + col * pixelSize;
                const y = startY + row * pixelSize;

                await this._fillPixelAdvanced(x, y, pixelSize, colors[char], Math.max(1, pixelSize <= 2 ? 1 : 2));

                pixelsDrawn++;

                if (pixelsDrawn % 10 === 0) {
                    const progress = Math.round((pixelsDrawn / totalPixels) * 100);
                    this.notify("info", `Progreso ${patternName.toUpperCase()}: ${progress}%`);
                }

                if (pixelsDrawn % 3 === 0) {
                    await new Promise(resolve => setTimeout(resolve, pixelSize <= 2 ? 2 : 5));
                }
            }

            await new Promise(resolve => setTimeout(resolve, pixelSize <= 2 ? 5 : 10));
        }

        this._isGenerating = false;
        this._updateButtonsState();

        this.notify("success", `${patternName.toUpperCase()} completado - ${pixelsDrawn} píxeles`);
    }

    _stopGeneration() {
        this._isGenerating = false;
        if (this._generationIntervalId) {
            clearInterval(this._generationIntervalId);
            this._generationIntervalId = null;
        }
        this._updateButtonsState();
        this.notify("info", "Generación de pixel art detenida.");
    }

    _loadInterface() {
        const container = document.createElement("div");
        container.className = "pixel-studio-section";
        container.style.cssText = `
            background: #ffffff;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            padding: 5px;
            margin: 10px 0;
            color: #333333;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        `;
        this.htmlElements.section.appendChild(container);

        const header = document.createElement("div");
        header.className = "pixel-studio-title";
        header.innerHTML = "🎨 Pixel Art Studio";
        header.style.cssText = `
            font-size: 18px;
            font-weight: 600;
            text-align: center;
            margin-bottom: 15px;
            color: #2c2c2c;
            padding: 8px;
            background: #f8f9fa;
            border-radius: 4px;
            border: 1px solid #e9ecef;
        `;
        container.appendChild(header);

        const previewSection = document.createElement("div");
        previewSection.style.cssText = `
            text-align: center;
            margin: 15px 0;
            padding: 10px;
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 4px;
        `;

        const previewLabel = document.createElement("div");
        previewLabel.innerHTML = "👁️ Vista Previa:";
        previewLabel.style.cssText = `
            font-weight: 600;
            margin-bottom: 8px;
            color: #333;
        `;

        this._ui.previewCanvas = document.createElement("canvas");
        this._ui.previewCanvas.width = 120;
        this._ui.previewCanvas.height = 120;
        this._ui.previewCanvas.style.cssText = `
            border: 2px solid #dee2e6;
            border-radius: 4px;
            background-color: #ffffff;
            display: block;
            margin: 0 auto;
        `;

        previewSection.appendChild(previewLabel);
        previewSection.appendChild(this._ui.previewCanvas);
        container.appendChild(previewSection);

        const buttonRow = document.createElement("div");
        buttonRow.style.cssText = `
            display: flex;
            gap: 8px;
            margin: 15px 0;
            padding: 10px;
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 4px;
        `;

        this._ui.drawButton = this._createStyledButton("🎨 Dibujar Pixel Art", "#28a745");
        this._ui.drawButton.addEventListener('click', () => this._drawPixelArt());

        this._ui.randomButton = this._createStyledButton("🎲 Aleatorio", "#ffc107");
        this._ui.randomButton.addEventListener('click', () => this._randomizeSettings());

        this._ui.stopButton = this._createStyledButton("⏹️ Detener", "#dc3545");
        this._ui.stopButton.addEventListener('click', () => this._stopGeneration());
        this._ui.stopButton.disabled = true;

        buttonRow.appendChild(this._ui.drawButton);
        buttonRow.appendChild(this._ui.randomButton);
        buttonRow.appendChild(this._ui.stopButton);
        container.appendChild(buttonRow);

        const infoSection = document.createElement("div");
        infoSection.style.cssText = `
            background: #f5f5f5;
            border: 1px solid #ddd;
            border-radius: 4px;
            padding: 8px;
            margin-bottom: 15px;
            font-size: 12px;
            text-align: center;
            color: #666;
        `;

        this._ui.canvasInfo = document.createElement("div");
        this._ui.canvasInfo.innerHTML = `📐 Canvas: ${this._canvasDimensions.width}x${this._canvasDimensions.height} | 🎯 Centro: (${this._canvasCenter.x}, ${this._canvasCenter.y})`;
        infoSection.appendChild(this._ui.canvasInfo);
        container.appendChild(infoSection);

        const patternGroup = this._createControlGroup("🖼️ Patrón:", "select");
        this._ui.patternSelect = patternGroup.querySelector("select");
        Object.keys(this._pixelPatterns).forEach(pattern => {
            const option = document.createElement("option");
            option.value = pattern;
            option.textContent = pattern.charAt(0).toUpperCase() + pattern.slice(1);
            this._ui.patternSelect.appendChild(option);
        });
        container.appendChild(patternGroup);

        const colorGroup = this._createControlGroup("🎨 Esquema de Colores:", "select");
        this._ui.colorSelect = colorGroup.querySelector("select");
        Object.keys(this._colorSchemes).forEach(scheme => {
            const option = document.createElement("option");
            option.value = scheme;
            option.textContent = scheme.charAt(0).toUpperCase() + scheme.slice(1);
            this._ui.colorSelect.appendChild(option);
        });
        container.appendChild(colorGroup);

        const sizeGroup = this._createControlGroup("📏 Tamaño de Píxel:", "range");
        this._ui.sizeRange = sizeGroup.querySelector("input");
        this._ui.sizeRange.min = "1";
        this._ui.sizeRange.max = "50";
        this._ui.sizeRange.value = "15";

        this._ui.sizeValue = document.createElement("span");
        this._ui.sizeValue.textContent = "15px";
        this._ui.sizeValue.style.cssText = `
            margin-left: 10px;
            font-weight: 500;
            color: #007bff;
        `;
        sizeGroup.appendChild(this._ui.sizeValue);
        container.appendChild(sizeGroup);

        const positionSection = document.createElement("div");
        positionSection.style.cssText = `
            background: #fafafa;
            border: 1px solid #e0e0e0;
            border-radius: 4px;
            padding: 10px;
            margin: 10px 0;
        `;

        const positionTitle = document.createElement("div");
        positionTitle.innerHTML = "🎯 Posicionamiento:";
        positionTitle.style.cssText = `
            font-weight: 600;
            margin-bottom: 10px;
            text-align: center;
            color: #333;
        `;
        positionSection.appendChild(positionTitle);

        const xGroup = this._createControlGroup("↔️ Posición X:", "range");
        this._ui.xRange = xGroup.querySelector("input");
        this._ui.xRange.min = -this._canvasDimensions.width / 2;
        this._ui.xRange.max = this._canvasDimensions.width / 2;
        this._ui.xRange.value = "0";

        this._ui.xValue = document.createElement("span");
        this._ui.xValue.textContent = "Centro (0)";
        this._ui.xValue.style.cssText = `
            margin-left: 10px;
            font-size: 12px;
            color: #007bff;
            font-weight: 500;
        `;
        xGroup.appendChild(this._ui.xValue);
        positionSection.appendChild(xGroup);

        const yGroup = this._createControlGroup("↕️ Posición Y:", "range");
        this._ui.yRange = yGroup.querySelector("input");
        this._ui.yRange.min = -this._canvasDimensions.height / 2;
        this._ui.yRange.max = this._canvasDimensions.height / 2;
        this._ui.yRange.value = "0";

        this._ui.yValue = document.createElement("span");
        this._ui.yValue.textContent = "Centro (0)";
        this._ui.yValue.style.cssText = `
            margin-left: 10px;
            font-size: 12px;
            color: #007bff;
            font-weight: 500;
        `;
        yGroup.appendChild(this._ui.yValue);
        positionSection.appendChild(yGroup);

        this._ui.centerButton = this._createStyledButton("🎯 Centrar", "#6c757d");
        this._ui.centerButton.addEventListener('click', () => this._centerPosition());
        positionSection.appendChild(this._ui.centerButton);

        container.appendChild(positionSection);
    }

    _createControlGroup(labelText, inputType) {
        const group = document.createElement("div");
        group.style.cssText = `
            margin-bottom: 12px;
        `;

        const label = document.createElement("label");
        label.innerHTML = labelText;
        label.style.cssText = `
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            font-size: 13px;
            color: #333;
        `;

        const input = document.createElement(inputType === "select" ? "select" : "input");
        if (inputType !== "select") {
            input.type = inputType;
        }

        input.style.cssText = `
            width: 100%;
            padding: 8px;
            border: 1px solid #ced4da;
            border-radius: 4px;
            background: #ffffff;
            color: #333;
            font-size: 13px;
        `;

        if (inputType === "range") {
            input.style.cssText += `
                background: #ffffff;
                height: 6px;
            `;
        }

        group.appendChild(label);
        group.appendChild(input);
        return group;
    }

    _createStyledButton(text, color) {
        const button = document.createElement("button");
        button.innerHTML = text;
        button.style.cssText = `
            flex: 1;
            padding: 10px;
            background: ${color};
            color: white;
            border: none;
            border-radius: 4px;
            font-weight: 500;
            font-size: 13px;
            cursor: pointer;
            transition: all 0.2s ease;
        `;

        button.addEventListener('mouseover', () => {
            if (!button.disabled) {
                button.style.opacity = '0.9';
                button.style.transform = 'translateY(-1px)';
            }
        });

        button.addEventListener('mouseout', () => {
            button.style.opacity = '1';
            button.style.transform = 'translateY(0)';
        });

        return button;
    }

    _setupEventListeners() {
        this._ui.sizeRange.addEventListener('input', () => {
            const value = this._ui.sizeRange.value;
            this._ui.sizeValue.textContent = value + 'px';
            if (parseInt(value) <= 3) {
                this._ui.sizeValue.style.color = '#e74c3c';
                this._ui.sizeValue.textContent += ' (Ultra Preciso)';
            } else {
                this._ui.sizeValue.style.color = '#007bff';
            }
            this._updatePreview();
        });

        this._ui.xRange.addEventListener('input', () => {
            const val = parseInt(this._ui.xRange.value);
            this._ui.xValue.textContent = val === 0 ? 'Centro (0)' :
                             val > 0 ? `Derecha (+${val})` : `Izquierda (${val})`;
        });

        this._ui.yRange.addEventListener('input', () => {
            const val = parseInt(this._ui.yRange.value);
            this._ui.yValue.textContent = val === 0 ? 'Centro (0)' :
                             val > 0 ? `Abajo (+${val})` : `Arriba (${val})`;
        });

        this._ui.patternSelect.addEventListener('change', () => this._updatePreview());
        this._ui.colorSelect.addEventListener('change', () => this._updatePreview());

        setTimeout(() => this._updatePreview(), 100);
    }

    _centerPosition() {
        this._ui.xRange.value = '0';
        this._ui.yRange.value = '0';
        this._ui.xValue.textContent = 'Centro (0)';
        this._ui.yValue.textContent = 'Centro (0)';
        this.notify("info", "🎯 Posicionamiento centrado: X=0, Y=0");
    }

    _randomizeSettings() {
        const patterns = Object.keys(this._pixelPatterns);
        const colors = Object.keys(this._colorSchemes);

        const selectedPattern = patterns[Math.floor(Math.random() * patterns.length)];
        const selectedColor = colors[Math.floor(Math.random() * colors.length)];
        const selectedSize = Math.floor(Math.random() * 30) + 5;

        this._ui.patternSelect.value = selectedPattern;
        this._ui.colorSelect.value = selectedColor;
        this._ui.sizeRange.value = selectedSize;
        this._ui.sizeValue.textContent = selectedSize + 'px';

        const randomX = Math.floor(Math.random() * 200) - 100;
        const randomY = Math.floor(Math.random() * 200) - 100;

        this._ui.xRange.value = randomX;
        this._ui.yRange.value = randomY;
        this._ui.xRange.dispatchEvent(new Event('input'));
        this._ui.yRange.dispatchEvent(new Event('input'));

        this.notify("success", `🎲 Configuración aleatoria: ${selectedPattern} | ${selectedColor} | ${selectedSize}px`);
        this._updatePreview();
    }

    _updatePreview() {
        if (!this._ui.previewCanvas) return;

        const ctx = this._ui.previewCanvas.getContext('2d');
        ctx.clearRect(0, 0, this._ui.previewCanvas.width, this._ui.previewCanvas.height);

        const pattern = this._pixelPatterns[this._ui.patternSelect.value];
        const colors = this._colorSchemes[this._ui.colorSelect.value];

        if (!pattern || !colors) return;

        const cellSize = Math.min(
            this._ui.previewCanvas.width / pattern.length,
            this._ui.previewCanvas.height / pattern.length
        );

        const offsetX = (this._ui.previewCanvas.width - pattern.length * cellSize) / 2;
        const offsetY = (this._ui.previewCanvas.height - pattern.length * cellSize) / 2;

        for (let row = 0; row < pattern.length; row++) {
            for (let col = 0; col < pattern[row].length; col++) {
                const char = pattern[row][col];
                if (!colors[char]) continue;

                ctx.fillStyle = colors[char];
                ctx.fillRect(
                    offsetX + col * cellSize,
                    offsetY + row * cellSize,
                    cellSize,
                    cellSize
                );
            }
        }
    }

    _drawPixelArt() {
        const pattern = this._ui.patternSelect.value;
        const colorScheme = this._ui.colorSelect.value;
        const pixelSize = parseInt(this._ui.sizeRange.value);
        const offsetX = parseInt(this._ui.xRange.value);
        const offsetY = parseInt(this._ui.yRange.value);

        this.notify("info", `🎨 Iniciando pixel art: ${pattern} | ${colorScheme} | ${pixelSize}px`);
        this._createCustomPixelArt(pattern, colorScheme, pixelSize, offsetX, offsetY);
    }

    // MÉTODO ARREGLADO - UpdateButtonsState con detección mejorada
    _updateButtonsState() {
        const isConnected = this._checkConnection();

        this._ui.drawButton.disabled = !isConnected || this._isGenerating;
        this._ui.randomButton.disabled = this._isGenerating;
        this._ui.stopButton.disabled = !this._isGenerating;

        if (!isConnected) {
            this._ui.drawButton.innerHTML = "🔄 Conectando...";
            this._ui.drawButton.style.backgroundColor = "#ffc107";
        } else if (this._isGenerating) {
            this._ui.drawButton.innerHTML = "🎨 Dibujando...";
            this._ui.drawButton.style.backgroundColor = "#17a2b8";
        } else {
            this._ui.drawButton.innerHTML = "🎨 Dibujar Pixel Art";
            this._ui.drawButton.style.backgroundColor = "#28a745";
        }
    }

    _updateCanvasInfo() {
        if (this._detectCanvasInfo() && this._ui.canvasInfo) {
            this._ui.canvasInfo.innerHTML =
                `📐 Canvas: ${this._canvasDimensions.width}x${this._canvasDimensions.height} | 🎯 Centro: (${this._canvasCenter.x}, ${this._canvasCenter.y})`;

            if (this._ui.xRange && this._ui.yRange) {
                this._ui.xRange.min = -this._canvasDimensions.width / 2;
                this._ui.xRange.max = this._canvasDimensions.width / 2;
                this._ui.yRange.min = -this._canvasDimensions.height / 2;
                this._ui.yRange.max = this._canvasDimensions.height / 2;
            }
        }

        this._updateButtonsState();
    }
}





class ElementalAnimationsTool extends QBit {
    _drawingActive = false;
    _canvas = null;
    _ctx = null;

    _ui = {};
    _animationConfig = {};

    constructor() {
        super("🍃Elemental Effects", '<i class="fas fa-leaf"></i>');
        this._onStartup();
    }

    _onStartup() {
        this._canvas = document.getElementById('canvas');
        if (this._canvas) {
            this._ctx = this._canvas.getContext('2d');
        } else {
            this.notify("error", "Canvas del juego no encontrado para Elemental Animations.");
            return;
        }

        this._loadInterface();
        this._setInitialValues();
        this._setupEventListeners();

        setInterval(() => this._updateButtonsState(), 1000);
        this.notify("info", "Módulo 'Elemental Animations' cargado.");
    }

    _loadInterface() {
        const container = domMake.Tree("div", { class: "elemental-section" });
        this.htmlElements.section.appendChild(container);

        container.appendChild(domMake.Tree("h4", {}, ["Animaciones"]));
        const animationsGrid = domMake.Tree("div", { class: "elemental-button-grid" });
        const animationsList = [
            { id: "effect-ocean", label: "Ocean", icon: 'bxs-droplet', type: 'Ocean' },
            { id: "effect-magma", label: "Magma", icon: 'bxs-volcano', type: 'Magma' },
            { id: "effect-poison", label: "Poison", icon: 'bxs-skull', type: 'Poison' },
            { id: "effect-purplegoo", label: "Purple Goo", icon: 'bxs-vial', type: 'PurpleGoo' }
        ];

        animationsList.forEach(anim => {
            const button = domMake.Button(`<i class='bx ${anim.icon}'></i> ${anim.label}`, { class: `elemental-button ${anim.type.toLowerCase()}` });
            button.addEventListener('click', () => this._animateElementalWave(anim.type));
            animationsGrid.appendChild(button);
            this._ui[anim.id] = button;
        });
        container.appendChild(animationsGrid);

        container.appendChild(domMake.Tree("h4", {}, ["Settings"]));
        const settingsDiv = domMake.Tree("div", {});

        const addSetting = (label, id, min, max, value, step, unit = '', isCheckbox = false) => {
            const item = domMake.Tree("div", { class: "elemental-setting-item" });
            let input;
            if (isCheckbox) {
                input = domMake.Tree("input", { type: "checkbox", id: `${this.identifier}-${id}`, checked: value });
                item.appendAll(domMake.Tree("label", { for: `${this.identifier}-${id}`, class: "checkbox-label" }, [label]), input);
            } else {
                input = domMake.Tree("input", { type: "range", id: `${this.identifier}-${id}`, min, max, value, step });
                const valueSpan = domMake.Tree("span", { id: `${this.identifier}-${id}Value`, class: "value-display" }, [`${value}${unit}`]);
                input.addEventListener('input', () => {
                    valueSpan.textContent = `${input.value}${unit}`;

                    if (id === "animationFillDensityInput") {
                        valueSpan.textContent = `${(parseFloat(input.value) / 1000).toFixed(3)}`;
                    }
                });
                item.appendAll(domMake.Tree("label", { for: `${this.identifier}-${id}` }, [label]), input, valueSpan);
            }
            settingsDiv.appendChild(item);
            this._ui[id] = input;
        };

        addSetting("Speed (ms/frame):", "animationSpeedInput", "50", "500", "150", "10");
        addSetting("Wave Detail (Segments):", "animationSegmentsInput", "10", "50", "30", "1");
        addSetting("Particle Density (%):", "animationParticleChanceInput", "0", "100", "15", "1", "%");
        addSetting("Base Fill Density:", "animationFillDensityInput", "5", "100", "8", "1", "");
        addSetting("Randomize Frame Delay:", "animationRandomDelayToggle", null, null, false, null, null, true);

        settingsDiv.appendChild(domMake.Tree("p", { style: "font-size:0.85em;color:var(--warning);margin-top:10px;" }, ""));
        container.appendChild(settingsDiv);

        const stopButton = domMake.Button('<i class="fas fa-stop-circle"></i> Detener Animación', { class: "elemental-button danger" });
        stopButton.style.width = "100%";
        stopButton.addEventListener('click', () => this._stopDrawing());
        container.appendChild(stopButton);
    }

    _setInitialValues() {

        this._ui.animationSpeedInput.value = 150;
        this._ui.animationSegmentsInput.value = 30;
        this._ui.animationParticleChanceInput.value = 15;
        this._ui.animationFillDensityInput.value = 8;
        this._ui.animationRandomDelayToggle.checked = false;



        this._ui.animationSpeedInput.dispatchEvent(new Event('input'));
        this._ui.animationSegmentsInput.dispatchEvent(new Event('input'));
        this._ui.animationParticleChanceInput.dispatchEvent(new Event('input'));
        this._ui.animationFillDensityInput.dispatchEvent(new Event('input'));
    }

    _setupEventListeners() {


    }

    _updateButtonsState() {
        const isConnected = getGameSocket() !== null;

        const settingInputs = [
            this._ui.animationSpeedInput, this._ui.animationSegmentsInput,
            this._ui.animationParticleChanceInput, this._ui.animationFillDensityInput,
            this._ui.animationRandomDelayToggle
        ];
        settingInputs.forEach(input => {
            if (input) input.disabled = !isConnected || this._drawingActive;
        });


        const animationButtons = [
            this._ui['effect-ocean'], this._ui['effect-magma'],
            this._ui['effect-poison'], this._ui['effect-purplegoo']
        ];
        animationButtons.forEach(button => {
            if (button) button.disabled = !isConnected || this._drawingActive;
        });


        const stopButton = document.querySelector(`#${this.identifier} .elemental-button.danger`);
        if (stopButton) {
            stopButton.disabled = !this._drawingActive;
        }
    }

    _delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }

    _getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
    _getRandomFloat(min, max) { return Math.random() * (max - min) + min; }



    _sendAndRenderDrawCmd(start_norm, end_norm, color, thickness, isEraser = false, algo = 0) {
        const gameSocket = getGameSocket();
        if (!gameSocket) {
            this.notify("warning", "WebSocket no conectado. No se puede enviar ni renderizar el comando de dibujo.");
            this._stopDrawing();
            return false;
        }

        const p1x_norm = Math.max(0, Math.min(1, start_norm[0]));
        const p1y_norm = Math.max(0, Math.min(1, start_norm[1]));
        const p2x_norm = Math.max(0, Math.min(1, end_norm[0]));
        const p2y_norm = Math.max(0, Math.min(1, end_norm[1]));
        let numThickness = parseFloat(thickness);
        if (isNaN(numThickness)) {
            numThickness = 10;
        }


        if (this._ctx && this._canvas) {
            const p1x_px = p1x_norm * this._canvas.width;
            const p1y_px = p1y_norm * this._canvas.height;
            const p2x_px = p2x_norm * this._canvas.width;
            const p2y_px = p2y_norm * this._canvas.height;

            this._ctx.strokeStyle = color;
            this._ctx.lineWidth = numThickness;
            this._ctx.lineCap = 'round';
            this._ctx.lineJoin = 'round';
            if (isEraser) {
                this._ctx.globalCompositeOperation = 'destination-out';
            }
            this._ctx.beginPath();
            this._ctx.moveTo(p1x_px, p1y_px);
            this._ctx.lineTo(p2x_px, p2y_px);
            this._ctx.stroke();
            if (isEraser) {
                this._ctx.globalCompositeOperation = 'source-over';
            }
        }

        const gT = isEraser ? numThickness : 0 - numThickness;
        gameSocket.send(`42["drawcmd",0,[${p1x_norm},${p1y_norm},${p2x_norm},${p2y_norm},${isEraser},${gT},"${color}",0,0,{"2":${algo},"3":0.5,"4":0.5}]]`);
        return true;
    }

    _stopDrawing() {
        this._drawingActive = false;
        this.notify("info", "Animación detenida.");
        this._updateButtonsState();
    }


    async _clearCanvas() {
        const gameSocket = getGameSocket();
        if (!gameSocket) {
            this.notify("warning", "No hay conexión WebSocket activa para limpiar el lienzo.");
            return;
        }

        this.notify("info", "Enviando comandos para borrar el lienzo...");

        if (this._ctx && this._canvas) {
            this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
        }

        const clearThickness = 1000;
        const clearColor = '#ffffff';
        const steps = 5;

        for (let i = 0; i <= steps; i++) {
            const yCoord = (i / steps) * 100;

            this._sendAndRenderDrawCmd([0, yCoord], [100, yCoord], clearColor, clearThickness, true);
            await new Promise(resolve => setTimeout(resolve, 5));
        }
        for (let i = 0; i <= steps; i++) {
            const xCoord = (i / steps) * 100;

            this._sendAndRenderDrawCmd([xCoord, 0], [xCoord, 100], clearColor, clearThickness, true);
            await new Promise(resolve => setTimeout(resolve, 5));
        }
        this.notify("success", "Lienzo limpiado.");
    }

    async _animateElementalWave(type) {
        if (!this._canvas || !this._ctx) {
            this.notify("error", "Canvas not ready. Please ensure you are in a Drawaria room.");
            return;
        }
        const gameSocket = getGameSocket();
        if (!gameSocket) {
            this.notify("error", "Not connected to Drawaria room. Please join a room first.");
            return;
        }
        if (this._drawingActive) {
            this.notify("warning", "Animation already in progress. Please wait or stop the current animation.");
            return;
        }

        this._drawingActive = true;
        this._updateButtonsState();
        this.notify("info", `Starting ${type} Animation...`);

        let config;

        switch (type) {
            case 'Ocean':
                config = {
                    baseFillColor: '#00008B',
                    waveColors: ['#1E90FF', '#4682B4'],
                    outlineColor: '#AFEEEE',
                    particleColor: '#00FFFF',
                    particleType: 'blob',
                    particleChance: 0.15,
                    particleSizeMin: 0.01,
                    particleSizeMax: 0.03,
                    particleThickness: 8,
                    startHeight: 0.88,
                    waveAmplitude: 0.06,
                    waveFrequency: 2.5,
                    waveSpeed: 0.02,
                    outlineOffset: 0.005,
                    outlineThicknessReduction: 3,
                    mainThickness: 30,
                    waveStyle: 'smooth',
                    frames: 100,
                };
                break;
            case 'Magma':
                config = {
                    baseFillColor: '#660000',
                    waveColors: ['#FF2400', '#CC5500'],
                    outlineColor: '#282828',
                    particleColor: '#FFA500',
                    particleType: 'ember',
                    particleChance: 0.2,
                    particleSizeMin: 0.008,
                    particleSizeMax: 0.02,
                    particleThickness: 4,
                    startHeight: 0.86,
                    waveAmplitude: 0.04,
                    waveFrequency: 2,
                    waveSpeed: 0.01,
                    outlineOffset: 0.01,
                    outlineThicknessReduction: 1,
                    mainThickness: 28,
                    waveStyle: 'jagged',
                    frames: 100,
                };
                break;
            case 'Poison':
                config = {
                    baseFillColor: '#556B2F',
                    waveColors: ['#6B8E23', '#808000'],
                    outlineColor: '#ADFF2F',
                    particleColor: '#7FFF00',
                    particleType: 'blob',
                    particleChance: 0.25,
                    particleSizeMin: 0.015,
                    particleSizeMax: 0.035,
                    particleThickness: 10,
                    startHeight: 0.87,
                    waveAmplitude: 0.055,
                    waveFrequency: 1.8,
                    waveSpeed: 0.018,
                    outlineOffset: 0.006,
                    outlineThicknessReduction: 2,
                    mainThickness: 26,
                    waveStyle: 'gloopy',
                    frames: 100,
                };
                break;
            case 'PurpleGoo':
                config = {
                    baseFillColor: '#4B0082',
                    waveColors: ['#800080', '#9932CC'],
                    outlineColor: '#FF00FF',
                    particleColor: '#DA70D6',
                    particleType: 'blob',
                    particleChance: 0.2,
                    particleSizeMin: 0.01,
                    particleSizeMax: 0.03,
                    particleThickness: 9,
                    startHeight: 0.88,
                    waveAmplitude: 0.05,
                    waveFrequency: 2.2,
                    waveSpeed: 0.022,
                    outlineOffset: 0.007,
                    outlineThicknessReduction: 2,
                    mainThickness: 27,
                    waveStyle: 'jagged',
                    frames: 100,
                };
                break;
            default:
                this._stopDrawing();
                console.warn(`Unknown animation type: ${type}`);
                return;
        }


        const userFrameDelay = parseInt(this._ui.animationSpeedInput.value);
        const userSegments = parseInt(this._ui.animationSegmentsInput.value);
        const userParticleChance = parseFloat(this._ui.animationParticleChanceInput.value) / 100; // % chance to 0-1

        const userFillDensityInputVal = parseInt(this._ui.animationFillDensityInput.value);
        config.fillStep_norm = Math.max(0.005, Math.min(0.1, userFillDensityInputVal / 1000)); // Divide user input by 1000

        const addRandomDelay = this._ui.animationRandomDelayToggle.checked;


        config.segments = Math.max(10, Math.min(50, userSegments));
        config.particleChance = Math.max(0, Math.min(1, userParticleChance));



        await this._clearCanvas();
        if (!this._drawingActive) { return; }



        const fillThicknessPerLine = 15;
        let fillY = 0.99;
        const targetFillY = config.startHeight + config.waveAmplitude;

        while (fillY > targetFillY && this._drawingActive) {


            if (!this._sendAndRenderDrawCmd([0, fillY], [1, fillY], config.baseFillColor, fillThicknessPerLine, false)) {
                this._stopDrawing();
                console.error("Failed to draw base fill segment.");
                break;
            }
            fillY -= config.fillStep_norm;
            await this._delay(10);
        }
        if (!this._drawingActive) { console.log("Animation stopped during base fill."); return; }
        await this._delay(50);


        for (let frame = 0; frame < config.frames && this._drawingActive; frame++) {
            let wavePoints_norm = [];
            for (let i = 0; i <= config.segments; i++) {
                const x_norm = i / config.segments;
                let yOffset_norm = Math.sin((x_norm * config.waveFrequency * Math.PI) + (frame * config.waveSpeed)) * config.waveAmplitude;

                if (config.waveStyle === 'gloopy') {
                    yOffset_norm += (Math.sin((x_norm * config.waveFrequency * 1.7 * Math.PI) + (frame * config.waveSpeed * 1.3) + 0.5) * config.waveAmplitude * 0.4) * Math.sin(x_norm * Math.PI);
                } else if (config.waveStyle === 'jagged') {
                    yOffset_norm += (this._getRandomFloat(-0.5, 0.5)) * config.waveAmplitude * 0.3;
                }
                const y_norm = config.startHeight - yOffset_norm;
                wavePoints_norm.push([x_norm, y_norm]);
            }

            for (let i = 0; i < wavePoints_norm.length - 1 && this._drawingActive; i++) {
                const p1_norm = wavePoints_norm[i];
                const p2_norm = wavePoints_norm[i + 1];
                const waveColor = config.waveColors[i % config.waveColors.length];


                if (!this._sendAndRenderDrawCmd(p1_norm, p2_norm, waveColor, config.mainThickness, false)) {
                    this._stopDrawing(); break;
                }


                const p1_outline_norm = [p1_norm[0], p1_norm[1] - config.outlineOffset];
                const p2_outline_norm = [p2_norm[0], p2_norm[1] - config.outlineOffset];
                const outlineThickness = Math.max(1, config.mainThickness - config.outlineThicknessReduction);
                if (!this._sendAndRenderDrawCmd(p1_outline_norm, p2_outline_norm, config.outlineColor, outlineThickness, false)) {
                    this._stopDrawing(); break;
                }


                if (Math.random() < config.particleChance) {
                    const particleX_norm = this._getRandomFloat(Math.min(p1_norm[0], p2_norm[0]), Math.max(p1_norm[0], p2_norm[0]));
                    const particleY_norm = Math.min(p1_norm[1], p2_norm[1]) - this._getRandomFloat(0.01, 0.08);
                    const particleSize_norm = this._getRandomFloat(config.particleSizeMin, config.particleSizeMax);

                    const particleFinalThickness = Math.max(1, config.particleThickness * particleSize_norm / config.particleSizeMin);
                    if (!this._sendAndRenderDrawCmd([particleX_norm, particleY_norm], [particleX_norm + 0.001, particleY_norm + 0.001], config.particleColor, particleFinalThickness, false)) {
                        this._stopDrawing(); break;
                    }
                }
                if (!this._drawingActive) break;
            }
            if (!this._drawingActive) break;


            let currentFrameDelay = userFrameDelay;
            if (addRandomDelay) {
                currentFrameDelay += this._getRandomInt(0, 50);
            }
            if (currentFrameDelay > 0) await this._delay(currentFrameDelay);
        }

        this._stopDrawing();
        console.log(`${type} Animation finished.`);
    }
}
















const LS_KEY_EXPAND = 'drawaria_expand_canvas_v3';
const LS_KEY_CIRCULAR = 'drawaria_canvas_circular_v1';




class DynamicVisualEffectsTool extends QBit {
    _canvas = null;
    _ctx = null;
    _socketStatus = 'disconnected';


    _joinExitBotsInterval = null;
    _joinExitBotCount = 0;
    _joinExitBotInstances = [];


    _visualEffectInterval = null;
    _visualEffectFrame = 0;
    _visualEffectType = null;
    _visualEffectColor = "#000000";


    _joinExitTextBotsInterval = null;
    _textSpamBots = [];
    _textSpamLoopTimeout = null;

    _ui = {};


    _originalCanvasWidth = 0;
    _originalCanvasHeight = 0;
    _originalCanvasAspectRatio = 0;

    constructor() {
        super("🌟Dynamic Visuals", '<i class="fas fa-star"></i>');
        this._onStartup();
    }

    _onStartup() {
        this._canvas = document.getElementById('canvas');
        if (!this._canvas) {
            console.warn("Canvas del juego no encontrado al inicio para Dynamic Visual Effects. Se intentará revalidar con el observador.");
        } else {
            this._ctx = this._canvas.getContext('2d');
        }

        this._loadInterface();


        this._createResetCanvasButton();


        const storedExpandedState = localStorage.getItem(LS_KEY_EXPAND);
        const initialStateExpanded = storedExpandedState === 'true';

        const storedCircularState = localStorage.getItem(LS_KEY_CIRCULAR);
        const initialStateCircular = storedCircularState === 'true';


        if (this._ui['expand-canvas']) {
            if (initialStateExpanded) {
                this._ui['expand-canvas'].classList.add('active');
            } else {
                this._ui['expand-canvas'].classList.remove('active');
            }
        }
        if (this._ui['circular-canvas']) {
            if (initialStateCircular) {
                this._ui['circular-canvas'].classList.add('active');
            } else {
                this._ui['circular-canvas'].classList.remove('active');
            }
        }


        this._initializeCanvasStateObserver(initialStateExpanded, initialStateCircular);

        this._setupEventListeners();
        setInterval(() => this._updateConnectionStatus(), 1000);
        this.notify("info", "Módulo 'Dynamic Visual Effects' cargado.");
    }

    _loadInterface() {
        const container = domMake.Tree("div", { id: `${this.identifier}-container`, class: "module-section" });
        this.htmlElements.section.appendChild(container);


        container.appendChild(domMake.Tree("div", { class: "module-section-title" }, [""]));
        const connectionStatusDiv = domMake.Tree("div", {}, [


        ]);



        container.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Efectos Automatizados"]));

        const createToggleButton = (id, labelText) => {
            const wrapper = domMake.Tree("div", { class: "module-form-group" });
            const button = domMake.Button(labelText);
            button.id = `${this.identifier}-${id}`;
            button.classList.add('module-toggle-button');
            button.addEventListener('click', () => this[`_toggle${this._capitalize(this._camelCase(id))}`](button));
            wrapper.appendChild(button);
            container.appendChild(wrapper);
            this._ui[id] = button;
        };

        createToggleButton('join-exit-bots', '<i class="fas fa-robot"></i> Join/Exit Bots');
        createToggleButton('black-white-visuals', '<i class="fas fa-adjust"></i> Black & White Visuals');
        createToggleButton('rainbow-visuals', '<i class="fas fa-palette"></i> Rainbow Visuals');
        createToggleButton('join-exit-text-bots', '<i class="fas fa-comment-dots"></i> Join/Exit Text Bots');
        createToggleButton('linear-loading-effect', '<i class="fas fa-chart-line"></i> Linear Loading Effect');
        createToggleButton('circular-loading-effect', '<i class="fas fa-sync-alt"></i> Circular Loading Effect');

        container.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Control de Canvas"]));
        createToggleButton('expand-canvas', '<i class="fas fa-expand"></i> Hacer Canvas Cuadrado');
        createToggleButton('circular-canvas', '<i class="fas fa-circle"></i> Hacer Canvas Circular');
    }


    _createResetCanvasButton() {

        const style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = `
            #drawaria-reset-canvas-button {
                position: fixed; /* Make it float */
                bottom: 20px;    /* 20px from bottom */
                right: 20px;     /* 20px from right */
                z-index: 99999;  /* Ensure it's on top of almost everything */
                background-color: #f1f9f5;
                border: 1px solid #b0b5b9;
                border-radius: 5px;
                padding: 5px 10px;
                cursor: pointer;
                font-size: 14px;
                display: none; /* Hidden by default, managed by JS */
                align-items: center;
                gap: 6px;
                box-shadow: 0 1px 3px rgba(0,0,0,0.1);
                transition: background-color 0.2s ease;
                white-space: nowrap;
                color: #e74c3c; /* Red color to signify reset/danger */
                font-weight: bold;
            }
            #drawaria-reset-canvas-button:hover {
                background-color: #e0e0e0;
            }
        `;
        document.head.appendChild(style);

        const resetButton = domMake.Button('<span style="font-size: 16px;">🔄</span> Restablecer Canvas');
        resetButton.id = 'drawaria-reset-canvas-button';
        resetButton.title = "Restablece el tamaño y la forma original del canvas.";


        document.body.appendChild(resetButton);


        this._ui['reset-canvas'] = resetButton;
        resetButton.addEventListener('click', () => this._toggleResetCanvas(resetButton));

        console.log("Botón 'Restablecer Canvas' creado e inyectado.");
        this._updateResetButtonVisibility();
    }


    _updateResetButtonVisibility() {
        const resetButton = this._ui['reset-canvas'];
        if (!resetButton) return;

        const isExpanded = localStorage.getItem(LS_KEY_EXPAND) === 'true';
        const isCircular = localStorage.getItem(LS_KEY_CIRCULAR) === 'true';

        if (isExpanded || isCircular) {
            resetButton.style.display = 'flex';
        } else {
            resetButton.style.display = 'none';
        }
    }


    _toggleResetCanvas(button) {

        this._setCanvasExpanded(false, false);


        if (this._ui['expand-canvas']) this._ui['expand-canvas'].classList.remove('active');
        if (this._ui['circular-canvas']) this._ui['circular-canvas'].classList.remove('active');


    }


    _camelCase(kebabCaseString) {
        return kebabCaseString.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
    }

    _capitalize(camelCaseString) {
        return camelCaseString.charAt(0).toUpperCase() + camelCaseString.slice(1);
    }

    _setupEventListeners() {


    }

    _updateConnectionStatus(status) {
        if (typeof status === 'undefined') {
            const gameSocket = getGameSocket();
            status = gameSocket ? gameSocket._socketStatus : 'disconnected';
        }

        this._socketStatus = status;
        const statusIndicator = document.getElementById(`${this.identifier}-connectionStatus`);
        const statusText = document.getElementById(`${this.identifier}-statusText`);

        if (statusIndicator && statusText) {
            statusIndicator.className = `module-status-indicator module-status-${status}`;
            statusText.textContent = status ? status.charAt(0).toUpperCase() + status.slice(1) : 'Unknown';
        }
    }

    _delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

    _sendAndRenderDrawCommand(socket, start_game_coords, end_game_coords, color, thickness_game_units, isEraser = false, algo = 0) {
        if (!socket || socket.readyState !== WebSocket.OPEN) {
            return false;
        }
        if (!this._canvas || !this._ctx) {
            console.warn("Canvas o contexto 2D no disponibles para dibujar.");
            return false;
        }

        const p1x_norm = Math.max(0, Math.min(1, start_game_coords[0] / 100));
        const p1y_norm = Math.max(0, Math.min(1, start_game_coords[1] / 100));
        const p2x_norm = Math.max(0, Math.min(1, end_game_coords[0] / 100));
        const p2y_norm = Math.max(0, Math.min(1, end_game_coords[1] / 100));

        let numThickness = parseFloat(thickness_game_units);
        if (isNaN(numThickness)) {

            numThickness = 10;
        }

        this._ctx.strokeStyle = color;
        this._ctx.lineWidth = numThickness * (this._canvas.width / 100);
        this._ctx.lineCap = 'round';
        this._ctx.lineJoin = 'round';

        if (isEraser) {
            this._ctx.globalCompositeOperation = 'destination-out';
        }
        this._ctx.beginPath();
        this._ctx.moveTo(p1x_norm * this._canvas.width, p1y_norm * this._canvas.height);
        this._ctx.lineTo(p2x_norm * this._canvas.width, p2y_norm * this._canvas.height);
        this._ctx.stroke();

        if (isEraser) {
            this._ctx.globalCompositeOperation = 'source-over';
        }

        const gT = isEraser ? numThickness : 0 - numThickness;
        socket.send(`42["drawcmd",0,[${p1x_norm.toFixed(4)},${p1y_norm.toFixed(4)},${p2x_norm.toFixed(4)},${p2y_norm.toFixed(4)},false,${gT},"${color}",0,0,{}]]`);
        return true;
    }

    async _clearCanvas() {
        const socket = getGameSocket();
        if (!socket) {

            return;
        }
        if (!this._ctx || !this._canvas) {
            console.warn("Canvas o contexto 2D no disponibles para limpiar.");
            return;
        }


        this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);

        const clearThickness = 100;
        const clearColor = '#FFFFFF';
        const steps = 5;

        for (let i = 0; i <= steps; i++) {
            this._sendAndRenderDrawCommand(socket, [0, (i / steps) * 100], [100, (i / steps) * 100], clearColor, clearThickness, true);
            await this._delay(5);
            this._sendAndRenderDrawCommand(socket, [(i / steps) * 100, 0], [(i / steps) * 100, 100], clearColor, clearThickness, true);
            await this._delay(5);
        }
        this.notify("success", "Lienzo limpiado.");
    }

    _getRandomColor(saturation = 100, lightness = 50) {
        const hue = Math.floor(Math.random() * 360);
        return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
    }

    _stopAllVisualEffects() {
        if (this._visualEffectInterval) {
            clearInterval(this._visualEffectInterval);
            this._visualEffectInterval = null;
            this._visualEffectType = null;
            this._visualEffectFrame = 0;
        }

        const visualEffectToggleIds = [
            'black-white-visuals', 'rainbow-visuals',
            'linear-loading-effect', 'circular-loading-effect'
        ];
        visualEffectToggleIds.forEach(id => {
            const btn = this._ui[id];
            if (btn) btn.classList.remove('active');
        });

        this._clearCanvas();
    }


    _toggleJoinExitBots(button) {
        if (this._joinExitBotsInterval) {
            clearInterval(this._joinExitBotsInterval);
            this._joinExitBotsInterval = null;
            this._disconnectBots(this._joinExitBotInstances);
            this._joinExitBotInstances = [];
            button.classList.remove('active');

        } else {
            const botManager = QBit.findGlobal("BotClientManager");
            if (!botManager || !botManager.siblings || botManager.siblings.length === 0) {

                return;
            }
            const managerInstance = botManager.siblings[0];
            if (!getGameSocket()) {

                return;
            }

            button.classList.add('active');
            this._joinExitBotCount = 0;
            this._joinExitBotInstances = [];


            const joinDisconnectCycle = async () => {
                if (!this._joinExitBotsInterval) return;

                const maxBots = 3;
                const currentRoomId = document.querySelector("#invurl")?.value || window.location.pathname.replace('/room/', '');

                for (let i = 0; i < maxBots; i++) {
                    if (!this._joinExitBotsInterval) break;
                    const botInterface = managerInstance.createBotClientInterface();
                    if (botInterface && botInterface.bot) {
                        botInterface.setClientName(`𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫${this._joinExitBotCount++}`);
                        botInterface.bot.enterRoom(currentRoomId);
                        this._joinExitBotInstances.push(botInterface.bot);

                        await this._delay(500);
                    }
                }
                await this._delay(1000);

                for (const bot of this._joinExitBotInstances) {
                    if (!this._joinExitBotsInterval) break;
                    if (bot.getReadyState()) {
                        bot.disconnect();

                        await this._delay(500);
                    }
                }
                this._joinExitBotInstances = [];
                await this._delay(1000);
            };

            this._joinExitBotsInterval = setInterval(joinDisconnectCycle, 3000);
            joinDisconnectCycle();
        }
    }

    _disconnectBots(botInstances) {
        for (const bot of botInstances) {
            if (bot && typeof bot.disconnect === 'function' && bot.getReadyState()) {
                try {
                    bot.disconnect();

                } catch(e) {

                }
            }
        }
    }


    _toggleBlackWhiteVisuals(button) {
        if (this._visualEffectInterval && this._visualEffectType === 'bw') {
            clearInterval(this._visualEffectInterval);
            this._visualEffectInterval = null;
            this._visualEffectType = null;
            this._visualEffectFrame = 0;
            this._clearCanvas();
            button.classList.remove('active');

        } else {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            this._stopAllVisualEffects();
            button.classList.add('active');
            this._visualEffectType = 'bw';
            this._visualEffectFrame = 0;


            this._visualEffectInterval = setInterval(() => {
                const color = (this._visualEffectFrame % 2 === 0) ? "#000000" : "#FFFFFF";
                this._sendAndRenderDrawCommand(socket, [0, 0], [100, 100], color, 2000);
                this._visualEffectFrame++;
            }, 100);
        }
    }


    _toggleRainbowVisuals(button) {
        if (this._visualEffectInterval && this._visualEffectType === 'rainbow') {
            clearInterval(this._visualEffectInterval);
            this._visualEffectInterval = null;
            this._visualEffectType = null;
            this._visualEffectFrame = 0;
            this._clearCanvas();
            button.classList.remove('active');

        } else {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            this._stopAllVisualEffects();
            button.classList.add('active');
            this._visualEffectType = 'rainbow';
            this._visualEffectFrame = 0;
            this._visualEffectColor = this._getRandomColor();


            this._visualEffectInterval = setInterval(() => {
                this._visualEffectColor = this._getRandomColor();
                this._sendAndRenderDrawCommand(socket, [0, 0], [100, 100], this._visualEffectColor, 2000);
                this._visualEffectFrame++;
            }, 100);
        }
    }


    _toggleJoinExitTextBots(button) {
        if (this._joinExitTextBotsInterval) {
            clearInterval(this._joinExitTextBotsInterval);
            clearTimeout(this._textSpamLoopTimeout);
            this._joinExitTextBotsInterval = null;
            this._textSpamLoopTimeout = null;
            this._disconnectBots(this._textSpamBots);
            this._textSpamBots = [];
            button.classList.remove('active');

        } else {
            const botManager = QBit.findGlobal("BotClientManager");
            if (!botManager || !botManager.siblings || botManager.siblings.length === 0) {

                return;
            }
            const managerInstance = botManager.siblings[0];
            if (!getGameSocket()) {

                return;
            }

            button.classList.add('active');


            const cycleTextSpamBots = async () => {
                if (!this._joinExitTextBotsInterval) return;

                const maxBots = 3;
                const currentRoomId = document.querySelector("#invurl")?.value || window.location.pathname.replace('/room/', '');

                this._disconnectBots(this._textSpamBots);
                this._textSpamBots = [];
                await this._delay(1000);

                for (let i = 0; i < maxBots; i++) {
                    if (!this._joinExitTextBotsInterval) break;
                    const botInterface = managerInstance.createBotClientInterface();
                    if (botInterface && botInterface.bot) {
                        botInterface.setClientName(`𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫${i}`);
                        botInterface.bot.enterRoom(currentRoomId);
                        this._textSpamBots.push(botInterface.bot);

                        await this._delay(500);
                    }
                }
                await this._delay(2000);

                let spamCount = 0;
                const spamDuration = 3 * 60 * 1000;
                const spamInterval = 700;

                const spamStartTime = Date.now();
                const spamLoop = setInterval(() => {
                    if (!this._joinExitTextBotsInterval || (Date.now() - spamStartTime) >= spamDuration) {
                        clearInterval(spamLoop);

                        if (this._joinExitTextBotsInterval) {
                            this._textSpamLoopTimeout = setTimeout(cycleTextSpamBots, 1000);
                        }
                        return;
                    }
                    for (const bot of this._textSpamBots) {
                        if (bot.getReadyState()) {
                            bot.emit("chatmsg", "𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫𒐫");
                        }
                    }
                    spamCount++;
                }, spamInterval);

            };

            this._joinExitTextBotsInterval = setInterval(() => {}, 60 * 1000 * 5);
            cycleTextSpamBots();
        }
    }


    _toggleLinearLoadingEffect(button) {
        if (this._visualEffectInterval && this._visualEffectType === 'linear-loading') {
            clearInterval(this._visualEffectInterval);
            this._visualEffectInterval = null;
            this._visualEffectType = null;
            this._visualEffectFrame = 0;
            this._clearCanvas();
            button.classList.remove('active');

        } else {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            this._stopAllVisualEffects();
            button.classList.add('active');
            this._visualEffectType = 'linear-loading';
            this._visualEffectFrame = 0;


            const boxSize = 100;
            const boxStartX = 0;
            const boxStartY = 0;

            const borderThickness = 8;
            const borderColor = "#FFFFFF00";

            const segmentLength = 25;
            const segmentThickness = 25;

            const animationSpeed = 4;
            const delayPerFrame = 150;

            const drawLinearLoadingFrame = () => {
                if (!getGameSocket() || !this._visualEffectInterval || !this._canvas || !this._ctx) {
                    this._stopAllVisualEffects(); return;
                }

                this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);

                this._sendAndRenderDrawCommand(socket, [boxStartX, boxStartY], [boxStartX + boxSize, boxStartY], borderColor, borderThickness);
                this._sendAndRenderDrawCommand(socket, [boxStartX + boxSize, boxStartY], [boxStartX + boxSize, boxStartY + boxSize], borderColor, borderThickness);
                this._sendAndRenderDrawCommand(socket, [boxStartX + boxSize, boxStartY + boxSize], [boxStartX, boxStartY + boxSize], borderColor, borderThickness);
                this._sendAndRenderDrawCommand(socket, [boxStartX, boxStartY + boxSize], [boxStartX, boxStartY], borderColor, borderThickness);

                const pathLength = boxSize * 4;
                const currentPathPos = (this._visualEffectFrame * animationSpeed) % pathLength;

                let segmentStart = [0, 0];
                let segmentEnd = [0, 0];
                let segmentColor = this._getRandomColor();

                if (currentPathPos < boxSize) {
                    segmentStart = [boxStartX + currentPathPos, boxStartY];
                    segmentEnd = [boxStartX + currentPathPos + segmentLength, boxStartY];
                } else if (currentPathPos < boxSize * 2) {
                    const sidePos = currentPathPos - boxSize;
                    segmentStart = [boxStartX + boxSize, boxStartY + sidePos];
                    segmentEnd = [boxStartX + boxSize, boxStartY + sidePos + segmentLength];
                } else if (currentPathPos < boxSize * 3) {
                    const sidePos = currentPathPos - boxSize * 2;
                    segmentStart = [boxStartX + boxSize - sidePos, boxStartY + boxSize];
                    segmentEnd = [boxStartX + boxSize - sidePos - segmentLength, boxStartY + boxSize];
                } else {
                    const sidePos = currentPathPos - boxSize * 3;
                    segmentStart = [boxStartX, boxStartY + boxSize - sidePos];
                    segmentEnd = [boxStartX, boxStartY + boxSize - sidePos - segmentLength];
                }

                this._sendAndRenderDrawCommand(socket, segmentStart, segmentEnd, segmentColor, segmentThickness);

                this._visualEffectFrame++;
            };
            this._visualEffectInterval = setInterval(drawLinearLoadingFrame, delayPerFrame);
        }
    }


    _toggleCircularLoadingEffect(button) {
        if (this._visualEffectInterval && this._visualEffectType === 'circular-loading') {
            clearInterval(this._visualEffectInterval);
            this._visualEffectInterval = null;
            this._visualEffectType = null;
            this._clearCanvas();
            button.classList.remove('active');

        } else {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            this._stopAllVisualEffects();
            button.classList.add('active');
            this._visualEffectType = 'circular-loading';
            this._visualEffectFrame = 0;


            const centerX = 50;
            const centerY = 50;
            const maxRadius = 40;
            const squareSize = 10;
            const thickness = 2;
            const delayPerFrame = 250;

            const numSquaresPerSpiral = 1;
            const spiralTurns = 3;
            const totalSquarePath = numSquaresPerSpiral * spiralTurns;

            const drawCircularLoadingFrame = () => {
                if (!getGameSocket() || !this._visualEffectInterval || !this._canvas || !this._ctx) {
                    this._stopAllVisualEffects(); return;
                }

                this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);

                const baseRotation = (this._visualEffectFrame * 0.1) % (Math.PI * 2);

                for (let i = 0; i < numSquaresPerSpiral; i++) {
                    for (let j = 0; j < spiralTurns; j++) {
                        const squareIndex = i + (j * numSquaresPerSpiral);

                        const currentProgress = (this._visualEffectFrame - squareIndex) % totalSquarePath;
                        if (currentProgress < 0) continue;

                        const radius = maxRadius * (currentProgress / totalSquarePath);
                        const angle = baseRotation + (currentProgress / numSquaresPerSpiral) * (Math.PI * 2);

                        const x = centerX + radius * Math.cos(angle);
                        const y = centerY + radius * Math.sin(angle);

                        const opacity = 1 - (currentProgress / totalSquarePath);
                        if (opacity <= 0) continue;

                        const color = this._getRandomColor();

                        const halfSize = squareSize / 2;
                        const points = [
                            [x - halfSize, y - halfSize],
                            [x + halfSize, y - halfSize],
                            [x + halfSize, y + halfSize],
                            [x - halfSize, y + halfSize]
                        ];

                        for (let k = 0; k < 4; k++) {
                            const p1 = points[k];
                            const p2 = points[(k + 1) % 4];
                            this._sendAndRenderDrawCommand(socket, p1, p2, color, thickness);
                        }
                    }
                }

                this._visualEffectFrame++;
            };
            this._visualEffectInterval = setInterval(drawCircularLoadingFrame, delayPerFrame);
        }
    }


    _setCanvasExpanded(isExpanded, isCircular) {
        const leftbar = document.getElementById('leftbar');
        const rightbar = document.getElementById('rightbar');
        const mainDiv = document.getElementById('main');
        if (!this._canvas) this._canvas = document.getElementById('canvas');

        if (!leftbar || !rightbar || !mainDiv || !this._canvas) {
            console.warn("Elementos principales de Drawaria (leftbar, rightbar, main, canvas) no encontrados para expandir/colapsar. Reintentando cuando los elementos estén listos.");
            return false;
        }
        this._ctx = this._canvas.getContext('2d');

        if (this._originalCanvasWidth === 0 || this._originalCanvasHeight === 0) {
            this._originalCanvasWidth = this._canvas.width;
            this._originalCanvasHeight = this._canvas.height;
            if (this._originalCanvasHeight > 0) {
                this._originalCanvasAspectRatio = this._originalCanvasWidth / this._originalCanvasHeight;
            } else {
                this._originalCanvasAspectRatio = 1;
                console.warn("La altura original del canvas es cero, asumiendo una relación de aspecto 1:1 como alternativa.");
            }
            console.log(`Dimensiones originales del Canvas inicializadas: ${this._originalCanvasWidth}x${this._originalCanvasHeight}, Relación de Aspecto: ${this._originalCanvasAspectRatio}`);
        }


        if (this._ui['expand-canvas']) {
            if (isExpanded) this._ui['expand-canvas'].classList.add('active');
            else this._ui['expand-canvas'].classList.remove('active');
        }
        if (this._ui['circular-canvas']) {
            if (isCircular) this._ui['circular-canvas'].classList.add('active');
            else this._ui['circular-canvas'].classList.remove('active');
        }

        if (isExpanded) {
            leftbar.style.display = 'none';
            rightbar.style.display = 'none';

            this._canvas.style.flexGrow = '1';
            this._canvas.style.width = 'auto';
            this._canvas.style.height = 'auto';
            this._canvas.style.maxWidth = '100%';
            this._canvas.style.maxHeight = '100%';

            setTimeout(() => {
                const availableWidthForCanvas = mainDiv.clientWidth;
                const availableHeightForCanvas = mainDiv.clientHeight;

                let newCanvasWidth, newCanvasHeight;

                if (isCircular) {
                    const size = Math.min(availableWidthForCanvas, availableHeightForCanvas);
                    newCanvasWidth = size;
                    newCanvasHeight = size;
                    this._canvas.style.borderRadius = '50%';
                    this._canvas.style.overflow = 'hidden';
                    this._canvas.style.margin = 'auto';
                    this._canvas.style.display = 'block';
                    localStorage.setItem(LS_KEY_CIRCULAR, 'true');
                } else if (this._originalCanvasAspectRatio > 0 && availableWidthForCanvas > 0 && availableHeightForCanvas > 0) {
                    const potentialHeightByWidth = Math.round(availableWidthForCanvas / this._originalCanvasAspectRatio);
                    const potentialWidthByHeight = Math.round(availableHeightForCanvas * this._originalCanvasAspectRatio);

                    if (potentialHeightByWidth <= availableHeightForCanvas) {
                        newCanvasWidth = availableWidthForCanvas;
                        newCanvasHeight = potentialHeightByWidth;
                    } else {
                        newCanvasHeight = availableHeightForCanvas;
                        newCanvasWidth = potentialWidthByHeight;
                    }
                    this._canvas.style.borderRadius = '';
                    this._canvas.style.overflow = '';
                    this._canvas.style.margin = '';
                    this._canvas.style.display = '';
                    localStorage.setItem(LS_KEY_CIRCULAR, 'false');
                } else {
                    newCanvasWidth = availableWidthForCanvas;
                    newCanvasHeight = availableHeightForCanvas;
                    this._canvas.style.borderRadius = '';
                    this._canvas.style.overflow = '';
                    this._canvas.style.margin = '';
                    this._canvas.style.display = '';
                    localStorage.setItem(LS_KEY_CIRCULAR, 'false');
                    console.warn("Los cálculos para las nuevas dimensiones del canvas no pudieron mantener la relación de aspecto, o el espacio disponible es cero. Puede causar distorsión si no es circular.");
                }

                this._canvas.width = newCanvasWidth;
                this._canvas.height = newCanvasHeight;

                this._canvas.style.width = newCanvasWidth + 'px';
                this._canvas.style.height = newCanvasHeight + 'px';
                this._canvas.style.flexGrow = '0';
                this._canvas.style.maxWidth = '';
                this._canvas.style.maxHeight = '';


                console.log(`Canvas expandido. Es Circular: ${isCircular}. Espacio disponible: ${availableWidthForCanvas}x${availableHeightForCanvas}. Nuevos atributos del Canvas: ${newCanvasWidth}x${newCanvasHeight}`);

                window.dispatchEvent(new Event('resize'));

                localStorage.setItem(LS_KEY_EXPAND, 'true');
                this._updateResetButtonVisibility();

            }, 150);
            return true;

        } else {
            leftbar.style.display = '';
            rightbar.style.display = '';

            this._canvas.width = this._originalCanvasWidth;
            this._canvas.height = this._originalCanvasHeight;

            this._canvas.style.flexGrow = '';
            this._canvas.style.width = '';
            this._canvas.style.height = '';
            this._canvas.style.borderRadius = '';
            this._canvas.style.overflow = '';
            this._canvas.style.maxWidth = '';
            this._canvas.style.maxHeight = '';
            this._canvas.style.margin = '';
            this._canvas.style.display = '';

            console.log("Canvas colapsado y dimensiones/estilos restaurados.");

            window.dispatchEvent(new Event('resize'));

            localStorage.setItem(LS_KEY_EXPAND, 'false');
            localStorage.setItem(LS_KEY_CIRCULAR, 'false');
            this._updateResetButtonVisibility();

            return true;
        }
    }


    _toggleExpandCanvas(button) {
        const isCurrentlyExpanded = button.classList.contains('active');
        const newExpandedState = !isCurrentlyExpanded;

        const circularButton = this._ui['circular-canvas'];
        const isCurrentCircular = circularButton ? circularButton.classList.contains('active') : false;


        this._setCanvasExpanded(newExpandedState, isCurrentCircular);

    }

    _toggleCircularCanvas(button) {
        const isCurrentlyCircular = button.classList.contains('active');
        const newCircularState = !isCurrentlyCircular;

        const expandButton = this._ui['expand-canvas'];
        const isCurrentExpanded = expandButton ? expandButton.classList.contains('active') : false;

        if (newCircularState && !isCurrentExpanded) {

            if (expandButton) expandButton.classList.add('active');
            localStorage.setItem(LS_KEY_EXPAND, 'true');

            this._setCanvasExpanded(true, newCircularState);
        } else {

            this._setCanvasExpanded(isCurrentExpanded, newCircularState);
        }

    }


    _initializeCanvasStateObserver(initialExpanded, initialCircular) {
        const observer = new MutationObserver((mutationsList, observerInstance) => {
            const mainDiv = document.getElementById('main');
            const canvasElement = document.getElementById('canvas');
            if (mainDiv && mainDiv.style.display !== 'none' && canvasElement) {
                console.log("Observer: #main y canvas visibles. Aplicando estado inicial del canvas.");
                this._canvas = canvasElement;
                this._ctx = canvasElement.getContext('2d');


                if (this._setCanvasExpanded(initialExpanded, initialCircular)) {
                    observerInstance.disconnect();
                    console.log("Observer desconectado: estado inicial del canvas aplicado.");
                }
            }
        });

        setTimeout(() => {
            observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style'] });
            const mainDiv = document.getElementById('main');
            const canvasElement = document.getElementById('canvas');
            if (mainDiv && mainDiv.style.display !== 'none' && canvasElement) {
                console.log("Observer (verificación inmediata): #main y canvas ya visibles. Aplicando estado inicial del canvas.");
                this._canvas = canvasElement;
                this._ctx = canvasElement.getContext('2d');
                if (this._setCanvasExpanded(initialExpanded, initialCircular)) {
                     observer.disconnect();
                     console.log("Observer desconectado (verificación inmediata): estado inicial del canvas aplicado.");
                }
            }
        }, 500);

        window.addEventListener('load', () => {
            const mainDiv = document.getElementById('main');
            const canvasElement = document.getElementById('canvas');
            if (mainDiv && mainDiv.style.display !== 'none' && canvasElement) {
                console.log("Window loaded (fallback): #main/canvas visible. Aplicando estado inicial del canvas.");
                this._canvas = canvasElement;
                this._ctx = canvasElement.getContext('2d');
                this._setCanvasExpanded(initialExpanded, initialCircular);
            }
        });
    }
}






class PermanentRoomBotTool extends QBit {
    _joinButton;
    _leaveButton;
    _statusIndicator;
    _statusText;

    _controlledBot = null;

    _activityToggle;
    _activityIntervalId = null;

    _autoReconnectToggle;
    _autoReconnectInterval = null;

    _autoAfkToggle;
    _autoAfkInterval = null;

    _spamMessageInput;
    _spamSendButton;
    _spamRepeatedlyToggle;
    _spamRepeatedlyInterval = null;

    constructor() {
        super("💤Sleep Room Bot", '<i class="fas fa-crosshairs"></i>');
        this._onStartup();
    }

    _onStartup() {
        this._loadInterface();
        this._setupEventListeners();
        setInterval(() => this._updateStatus(), 1000);

    }

    _loadInterface() {
        const container = domMake.Tree("div", { class: "permanent-room-section" });
        this.htmlElements.section.appendChild(container);

        container.appendChild(domMake.Tree("div", { class: "permanent-room-section-title" }, ["Mantener Sala Activa con Bot"]));

        container.appendChild(domMake.Tree("p", { style: "font-size: 0.85em; text-align: center; margin-bottom: 10px; color: var(--CE-color);" }, [
            "Asegúrate de crear y nombrar tu bot en la sección ",
            domMake.Tree("strong", {}, ["'BotClientManager'"]),
            " antes de usarlo aquí."
        ]));

        const buttonGroup = domMake.Row({ class: "permanent-room-btn-group" });
        this._joinButton = domMake.Button('<i class="fas fa-sign-in-alt"></i> Unir Bot a Sala Actual');
        this._joinButton.addEventListener('click', () => this._joinRoom());
        this._leaveButton = domMake.Button('<i class="fas fa-sign-out-alt"></i> Desconectar Bot');
        this._leaveButton.disabled = true;
        this._leaveButton.addEventListener('click', () => this._leaveRoom());
        buttonGroup.appendAll(this._joinButton, this._leaveButton);
        container.appendChild(buttonGroup);

        container.appendChild(domMake.Tree("div", { style: "margin-top: 10px;" }, [
            this._statusIndicator = domMake.Tree("span", { id: `${this.identifier}-statusIndicator`, class: `permanent-room-status-indicator permanent-room-status-disconnected` }),
            this._statusText = domMake.Tree("span", { id: `${this.identifier}-statusText` }, ["Desconectado"])
        ]));

        container.appendChild(domMake.Tree("div", { class: "permanent-room-section-title", style: "margin-top: 20px;" }, ["Actividad del Bot"]));

        const createToggle = (id, labelText, defaultChecked = false) => {
            const wrapper = domMake.Tree("div", { class: "module-form-group" });
            const input = domMake.Tree("input", { type: "checkbox", id: `${this.identifier}-${id}`, checked: defaultChecked });
            wrapper.appendAll(input, domMake.Tree("label", { for: `${this.identifier}-${id}`, style: "display: inline-block; margin-left: 5px; font-size: 0.9em; color: var(--CE-color);" }, [labelText]));
            container.appendChild(wrapper);
            return input;
        };

        this._activityToggle = createToggle('activityToggle', 'Bot Activo (Mover/Chatear)');
        this._autoReconnectToggle = createToggle('autoReconnectToggle', 'Auto-Reconectar si se Desconecta');
        this._autoAfkToggle = createToggle('autoAfkToggle', 'Auto-AFK (Alternar)');

        container.appendChild(domMake.Tree("div", { class: "permanent-room-section-title", style: "margin-top: 20px;" }, ["Spam de Mensajes"]));
        const spamControlsGroup = domMake.Row({ class: "permanent-room-btn-group" });
        this._spamMessageInput = domMake.Tree("input", { type: "text", placeholder: "Mensaje de spam...", class: "module-form-control" });
        this._spamSendButton = domMake.Button('<i class="fas fa-paper-plane"></i> Enviar');
        spamControlsGroup.appendAll(this._spamMessageInput, this._spamSendButton);
        container.appendChild(spamControlsGroup);

        this._spamRepeatedlyToggle = createToggle('spamRepeatedlyToggle', 'Enviar Repetidamente (700ms Delay)');
    }

    _setupEventListeners() {
        this._joinButton.addEventListener('click', () => this._joinRoom());
        this._leaveButton.addEventListener('click', () => this._leaveRoom());
        this._activityToggle.addEventListener('change', () => this._toggleActivity());
        this._autoReconnectToggle.addEventListener('change', () => this._toggleAutoReconnect());
        this._autoAfkToggle.addEventListener('change', () => this._toggleAutoAfk());
        this._spamSendButton.addEventListener('click', () => this._sendSpamMessage());
        this._spamRepeatedlyToggle.addEventListener('change', () => this._toggleSpamRepeatedly());
    }


    _getAvailableBot() {

        const botManagerClass = QBit.findGlobal("BotClientManager");
        if (!botManagerClass || !botManagerClass.siblings || botManagerClass.siblings.length === 0) {

            return null;
        }
        const managerInstance = botManagerClass.siblings[0];
        if (!managerInstance || !managerInstance.children) {

             return null;
        }

        const availableBotInterfaces = managerInstance.children.filter(bci => bci.bot);

        if (availableBotInterfaces.length === 0) {

            return null;
        }

        let botToControl = null;

        if (this._controlledBot && this._controlledBot.getReadyState()) {
            botToControl = this._controlledBot;
        } else {
            botToControl = availableBotInterfaces.find(bci => bci.bot && bci.bot.getReadyState())?.bot;
        }

        if (botToControl) {

        } else {

        }
        return botToControl;
    }


    _updateStatus() {
        const isConnected = this._controlledBot && this._controlledBot.getReadyState();
        this._statusIndicator.className = `permanent-room-status-indicator permanent-room-status-${isConnected ? 'connected' : 'disconnected'}`;
        this._statusText.textContent = isConnected ? `Conectado como "${this._controlledBot.name}"` : "Desconectado";
        this._joinButton.disabled = isConnected;
        this._leaveButton.disabled = !isConnected;
        this._activityToggle.disabled = !isConnected;
        this._autoReconnectToggle.disabled = !isConnected && !this._autoReconnectInterval;
        this._autoAfkToggle.disabled = !isConnected;
        this._spamMessageInput.disabled = !isConnected;
        this._spamSendButton.disabled = !isConnected || this._spamRepeatedlyToggle.checked;
        this._spamRepeatedlyToggle.disabled = !isConnected;

        if (!isConnected) {
            this._stopActivity();
            if (this._activityToggle.checked) {
                this._activityToggle.checked = false;
            }
            this._stopAutoAfk();
            if (this._autoAfkToggle.checked) {
                this._autoAfkToggle.checked = false;
            }
            this._stopSpamRepeatedly();
            if (this._spamRepeatedlyToggle.checked) {
                this._spamRepeatedlyToggle.checked = false;
            }

            if (this._autoReconnectToggle.checked && !this._autoReconnectInterval) {
                 this._startAutoReconnect();
            }
        }
    }


    _joinRoom() {
        this._controlledBot = this._getAvailableBot();
        if (!this._controlledBot) {
            return;
        }

        const currentPath = window.location.pathname;
        const roomMatch = currentPath.match(/\/room\/([a-f0-9.-]+(?:[.]\d+)?)/i);

        if (!roomMatch || !roomMatch[1]) {

            return;
        }

        const roomIdToJoin = roomMatch[1];


        this._controlledBot.enterRoom(roomIdToJoin);

        setTimeout(() => {
            if (this._activityToggle.checked) {
                this._startActivity();
            }
            if (this._autoAfkToggle.checked) {
                this._startAutoAfk();
            }
            if (this._spamRepeatedlyToggle.checked) {
                this._startSpamRepeatedly();
            }
        }, 2000);
    }


    _leaveRoom() {
        if (this._controlledBot) {

            this._controlledBot.disconnect();
        }
        this._controlledBot = null;
    }


    _toggleActivity() {
        if (this._activityToggle.checked) {
            this._startActivity();
        } else {
            this._stopActivity();
        }
    }


    _startActivity() {
        if (this._activityIntervalId) clearInterval(this._activityIntervalId);
        if (!this._controlledBot || !this._controlledBot.getReadyState()) {

            this._activityToggle.checked = false;
            return;
        }

        const bot = this._controlledBot;

        this._activityIntervalId = setInterval(() => {
            if (!bot.getReadyState()) {

                this._stopActivity();
                return;
            }

            const action = Math.random();
            if (action < 0.6) {
                const x = Math.random() * 100;
                const y = Math.random() * 100;
                bot.emit("moveavatar", x, y);
            } else {
                const messages = [
                    "Manteniendo sala...",
                    "Hola a todos!",
                    "Continuando el dibujo más tarde!",
                    "Zzz...",
                    "Un saludo!",
                    "La sala está segura 😉"
                ];
                const msg = messages[Math.floor(Math.random() * messages.length)];
                bot.emit("chatmsg", msg);
            }
        }, 15000 + Math.random() * 10000);
    }


    _stopActivity() {
        if (this._activityIntervalId) {
            clearInterval(this._activityIntervalId);
            this._activityIntervalId = null;

        }
        if (this._activityToggle.checked) {
            this._activityToggle.checked = false;
        }
    }


    _toggleAutoReconnect() {
        if (this._autoReconnectToggle.checked) {
            this._startAutoReconnect();
        } else {
            this._stopAutoReconnect();
        }
    }

    _startAutoReconnect() {
        if (this._autoReconnectInterval) clearInterval(this._autoReconnectInterval);
        if (!this._controlledBot) {

            this._autoReconnectToggle.checked = false;
            return;
        }

        this._autoReconnectInterval = setInterval(() => {
            if (!this._controlledBot.getReadyState()) {

                this._controlledBot.reconnect();
            }
        }, 30000);
    }

    _stopAutoReconnect() {
        if (this._autoReconnectInterval) {
            clearInterval(this._autoReconnectInterval);
            this._autoReconnectInterval = null;

        }
        if (this._autoReconnectToggle.checked) {
            this._autoReconnectToggle.checked = false;
        }
    }


    _toggleAutoAfk() {
        if (this._autoAfkToggle.checked) {
            this._startAutoAfk();
        } else {
            this._stopAutoAfk();
        }
    }

    _startAutoAfk() {
        if (this._autoAfkInterval) clearInterval(this._autoAfkInterval);
        if (!this._controlledBot || !this._controlledBot.getReadyState()) {

            this._autoAfkToggle.checked = false;
            return;
        }

        this._autoAfkInterval = setInterval(() => {
            if (this._controlledBot.getReadyState()) {
                this._controlledBot.emit("playerafk");

            }
        }, 60000);
    }

    _stopAutoAfk() {
        if (this._autoAfkInterval) {
            clearInterval(this._autoAfkInterval);
            this._autoAfkInterval = null;

        }
        if (this._autoAfkToggle.checked) {
            this._autoAfkToggle.checked = false;
        }
    }


    _sendSpamMessage() {
        const message = this._spamMessageInput.value.trim();
        if (!message) {

            return;
        }
        if (!this._controlledBot || !this._controlledBot.getReadyState()) {

            return;
        }
        this._controlledBot.emit("chatmsg", message);

    }

    _toggleSpamRepeatedly() {
        if (this._spamRepeatedlyToggle.checked) {
            this._startSpamRepeatedly();
        } else {
            this._stopSpamRepeatedly();
        }
    }

    _startSpamRepeatedly() {
        if (this._spamRepeatedlyInterval) clearInterval(this._spamRepeatedlyInterval);
        const message = this._spamMessageInput.value.trim();
        if (!message) {

            this._spamRepeatedlyToggle.checked = false;
            return;
        }
        if (!this._controlledBot || !this._controlledBot.getReadyState()) {

            this._spamRepeatedlyToggle.checked = false;
            return;
        }

        this._spamSendButton.disabled = true;


        this._spamRepeatedlyInterval = setInterval(() => {
            if (this._controlledBot.getReadyState()) {
                this._controlledBot.emit("chatmsg", message);
            }
        }, 700);
    }

    _stopSpamRepeatedly() {
        if (this._spamRepeatedlyInterval) {
            clearInterval(this._spamRepeatedlyInterval);
            this._spamRepeatedlyInterval = null;

        }
        this._spamSendButton.disabled = false;
        if (this._spamRepeatedlyToggle.checked) {
            this._spamRepeatedlyToggle.checked = false;
        }
    }
}




class GeometryDashCubeOnlineDrawer extends QBit {
  _currentCategory = 'cubes';
  _currentIconId = 1;
  _canvasBrushImg = null;
  _currentRotation = 0;
  _ui = {};
  _mainCanvas = null;
  _canvasClickHandler = null;
  _canvasClickHandlerAttached = false;
  _isActive = false;
  _isDrawingIcon = false;


  _categories = {
    cubes: { prefix: 'cube_', maxId: 485, icon: 'fas fa-cube' },
    ships: { prefix: 'ship_', maxId: 169, icon: 'fas fa-plane' },
    balls: { prefix: 'ball_', maxId: 118, icon: 'fas fa-circle' },
    ufos: { prefix: 'ufo_', maxId: 149, icon: 'fas fa-space-shuttle' },
    waves: { prefix: 'wave_', maxId: 96, icon: 'fas fa-wave-square' },
    robots: { prefix: 'robot_', maxId: 68, icon: 'fas fa-robot' },
    spiders: { prefix: 'spider_', maxId: 69, icon: 'fas fa-spider' },
    swings: { prefix: 'swing_', maxId: 43, icon: 'fas fa-wind' },
    jetpacks: { prefix: 'jetpack_', maxId: 8, icon: 'fas fa-rocket' }
  };

  _difficulties = {
    easy: 'easy.png', normal: 'normal.png', hard: 'hard.png', harder: 'harder.png',
    insane: 'insane.png', 'demon-easy': 'demon-easy.png', 'demon-medium': 'demon-medium.png',
    'demon-hard': 'demon-hard.png', 'demon-insane': 'demon-insane.png', 'demon-extreme': 'demon-extreme.png',
    unrated: 'unrated.png', auto: 'auto.png'
  };
  _difficultyModifiers = ['-featured', '-epic', '-legendary', '-mythic'];

  _extras = {
    like: 'like.png', star: 'star.png', moon: 'moon.png', coin: 'coin.png',
    silvercoin: 'silvercoin.png', download: 'download.png', youtube: 'youtube.png',
    time: 'time.png', orbs: 'orbs.png', refresh: 'refresh.png', magnify: 'magnify.png'
  };

  _gauntlets = {
    fire: 'fire.png', ice: 'ice.png', poison: 'poison.png', shadow: 'shadow.png',
    lava: 'lava.png', bonus: 'bonus.png', chaos: 'chaos.png', demon: 'demon.png',
    time: 'time.png', crystal: 'crystal.png', magic: 'magic.png', spike: 'spike.png',
    monster: 'monster.png', doom: 'doom.png', death: 'death.png', forest: 'forest.png',
    force: 'force.png', water: 'water.png', haunted: 'haunted.png', power: 'power.png',
    halloween: 'halloween.png', treasure: 'treasure.png', inferno: 'inferno.png', portal: 'portal.png',
    strange: 'strange.png', fantasy: 'fantasy.png', christmas: 'christmas.png', mystery: 'mystery.png',
    cursed: 'cursed.png', cyborg: 'cyborg.png', castle: 'castle.png', world: 'world.png',
    galaxy: 'galaxy.png', universe: 'universe.png', discord: 'discord.png', ncs_i: 'ncs_i.png',
    ncs_ii: 'ncs_ii.png', space: 'space.png', cosmos: 'cosmos.png'
  };


  _editorBlocks = {
    spike: 'https://static.wikia.nocookie.net/geometry-dash-level-editor/images/c/cc/Retouch_2024112913572713.png',
    block: 'https://raw.githubusercontent.com/GDColon/GDBrowser/refs/heads/master/assets/objects/blocks/classic.png',
    portal: 'https://static.wikia.nocookie.net/geometry-dash-fan-ideas/images/d/de/CubePortal.png'
  };



  _editorBlockDefaultOptimizations = {
    delayPerSegment: 100,
    delayPerRow: 200,
    qualityFactor: 3,
    targetSize: 40
  };

  constructor() {
    super("🔳GD Tools", '<i class="fas fa-cube"></i>');
    this._onStartup();
  }

  _onStartup() {
    this._mainCanvas = document.getElementById("canvas");
    if (!this._mainCanvas) {

    }

    this._loadInterface();
    this._setupEventListeners();
    this._loadIconImage('cubes', 1);
    this._setModuleActive(false);

  }

  _loadInterface() {
    const container = domMake.Tree("div", { id: `${this.identifier}-container`, class: "module-section" });
    this.htmlElements.section.appendChild(container);


    const moduleToggleGroup = domMake.Tree("div", { class: "module-btn-group", style: "margin-bottom:10px;" });
    this._ui.moduleToggleButton = domMake.Button('<i class="fas fa-power-off"></i> Activar Módulo');
    this._ui.moduleToggleButton.classList.add('module-toggle-button');
    this._ui.moduleToggleButton.addEventListener('click', () => this._toggleModuleActive());
    moduleToggleGroup.appendChild(this._ui.moduleToggleButton);
    container.appendChild(moduleToggleGroup);

    container.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["GD Cubes & Objects Drawer"]));


    const categoryNav = domMake.Tree("div", { class: "module-btn-group", style: "flex-wrap: wrap; margin-bottom: 15px;" });
    ['cubes', 'ships', 'balls', 'ufos', 'waves', 'robots', 'spiders', 'swings', 'jetpacks', 'difficulties', 'extras', 'gauntlets', 'editor_blocks'].forEach(catKey => {
      let iconClass = this._categories[catKey]?.icon || 'fas fa-question';
      if (catKey === 'difficulties') iconClass = 'fas fa-star';
      if (catKey === 'extras') iconClass = 'fas fa-plus';
      if (catKey === 'gauntlets') iconClass = 'fas fa-dice';
      if (catKey === 'editor_blocks') iconClass = 'fas fa-th-large';

      const button = domMake.Button(`<i class="${iconClass}"></i> ${catKey.charAt(0).toUpperCase() + catKey.slice(1).replace('_', ' ')}`);
      button.classList.add('category-nav-button');
      button.addEventListener('click', () => this._changeCategory(catKey));
      categoryNav.appendChild(button);
      this._ui[`${catKey}NavButton`] = button;
    });
    container.appendChild(categoryNav);



    const previewContainer = domMake.Tree("div", { style: "display:flex;align-items:center;justify-content:center;gap:10px;margin-bottom:10px;" });
    this._ui.previewCanvas = domMake.Tree("canvas", { id: `${this.identifier}-previewCanvas`, width: "96", height: "96", style: "border:1px solid var(--CE-color);background:#222;" });
    previewContainer.appendChild(this._ui.previewCanvas);
    container.appendChild(previewContainer);


    this._ui.commonControlsSection = domMake.Tree('div', { id: `${this.identifier}-common-controls` });
    this._ui.commonControlsSection.innerHTML = `
        <div style="display:flex;align-items:center;justify-content:center;gap:10px;margin-bottom:10px;">
            <button id="${this.identifier}-prevBtn" class="artfx-button special">←</button>
            <button id="${this.identifier}-nextBtn" class="artfx-button special">→</button>
        </div>
        <div id="${this.identifier}-info" style="text-align:center;margin-top:5px;font-size:0.9em;color:var(--CE-color);"></div>
        <div class="module-btn-group" style="flex-wrap:wrap;margin-top:15px;">
            <input type="number" id="${this.identifier}-iconIdInput" min="1" value="1" placeholder="ID del Icono" class="module-form-control" style="flex:1 1 60%;">
            <button id="${this.identifier}-acceptIconBtn" class="artfx-button" style="flex:1 1 35%;">Aceptar ID</button>
            <button id="${this.identifier}-randomIconBtn" class="artfx-button special" style="width:100%;">Icono Random</button>
        </div>
    `;
    container.appendChild(this._ui.commonControlsSection);

    this._ui.prevBtn = this._ui.commonControlsSection.querySelector(`#${this.identifier}-prevBtn`);
    this._ui.nextBtn = this._ui.commonControlsSection.querySelector(`#${this.identifier}-nextBtn`);
    this._ui.info = this._ui.commonControlsSection.querySelector(`#${this.identifier}-info`);
    this._ui.iconIdInput = this._ui.commonControlsSection.querySelector(`#${this.identifier}-iconIdInput`);
    this._ui.acceptIconBtn = this._ui.commonControlsSection.querySelector(`#${this.identifier}-acceptIconBtn`);
    this._ui.randomIconBtn = this._ui.commonControlsSection.querySelector(`#${this.identifier}-randomIconBtn`);



    this._ui.difficultiesSection = domMake.Tree('div', { id: `${this.identifier}-difficulties-section`, style: "display:none;" });
    this._ui.difficultiesSection.innerHTML = `
        <div class="module-section-title" style="margin-top:15px;">Dificultades</div>
        <div class="module-btn-group" style="flex-wrap:wrap;">
            ${Object.keys(this._difficulties).map(dKey => `<button class="artfx-button difficulty-button" data-diff="${dKey}">${dKey.charAt(0).toUpperCase() + dKey.slice(1).replace('-', ' ')}</button>`).join('')}
        </div>
        <div class="module-section-title" style="margin-top:15px;">Modificadores</div>
        <div class="module-btn-group" style="flex-wrap:wrap;">
            ${this._difficultyModifiers.map(mod => `
                <input type="checkbox" id="${this.identifier}-mod${mod.replace('-', '')}Toggle" class="difficulty-modifier-toggle" data-mod="${mod}">
                <label for="${this.identifier}-mod${mod.replace('-', '')}Toggle" style="margin-right:10px;font-size:0.9em;color:var(--CE-color);">${mod.slice(1).charAt(0).toUpperCase() + mod.slice(2)}</label>
            `).join('')}
            <button id="${this.identifier}-clearModsBtn" class="artfx-button danger" style="flex:1 1 auto;">Limpiar Modificadores</button>
        </div>
    `;
    container.appendChild(this._ui.difficultiesSection);
    this._ui.difficultyButtons = this._ui.difficultiesSection.querySelectorAll('.difficulty-button');
    this._ui.difficultyModToggles = this._ui.difficultiesSection.querySelectorAll('.difficulty-modifier-toggle');
    this._ui.clearModsBtn = this._ui.difficultiesSection.querySelector(`#${this.identifier}-clearModsBtn`);



    this._ui.extrasSection = domMake.Tree('div', { id: `${this.identifier}-extras-section`, style: "display:none;" });
    this._ui.extrasSection.innerHTML = `
        <div class="module-section-title" style="margin-top:15px;">Extras</div>
        <div class="module-btn-group" style="flex-wrap:wrap;">
            ${Object.keys(this._extras).map(eKey => `<button class="artfx-button extra-button" data-extra="${eKey}">${eKey.charAt(0).toUpperCase() + eKey.slice(1)}</button>`).join('')}
        </div>
    `;
    container.appendChild(this._ui.extrasSection);
    this._ui.extraButtons = this._ui.extrasSection.querySelectorAll('.extra-button');



    this._ui.gauntletsSection = domMake.Tree('div', { id: `${this.identifier}-gauntlets-section`, style: "display:none;" });
    this._ui.gauntletsSection.innerHTML = `
        <div class="module-section-title" style="margin-top:15px;">Gauntlets</div>
        <div class="module-btn-group" style="flex-wrap:wrap;">
            ${Object.keys(this._gauntlets).map(gKey => `<button class="artfx-button gauntlet-button" data-gauntlet="${gKey}">${gKey.charAt(0).toUpperCase() + gKey.slice(1)}</button>`).join('')}
        </div>
    `;
    container.appendChild(this._ui.gauntletsSection);
    this._ui.gauntletButtons = this._ui.gauntletsSection.querySelectorAll('.gauntlet-button');


    this._ui.editorBlocksSection = domMake.Tree('div', { id: `${this.identifier}-editorblocks-section`, style: "display:none;" });
    this._ui.editorBlocksSection.innerHTML = `
        <div class="module-section-title" style="margin-top:15px;">Bloques del Editor</div>
        <div class="module-btn-group" style="flex-wrap:wrap;">
            ${Object.keys(this._editorBlocks).map(ebKey => `<button class="artfx-button editor-block-button" data-eblock="${ebKey}">${ebKey.charAt(0).toUpperCase() + ebKey.slice(1)}</button>`).join('')}
        </div>
        <div class="module-btn-group" style="margin-top:10px;">
            <button id="${this.identifier}-rotateBlockBtn" class="artfx-button special"><i class="fas fa-sync-alt"></i> Rotar Objeto</button>
        </div>
    `;
    container.appendChild(this._ui.editorBlocksSection);
    this._ui.editorBlockButtons = this._ui.editorBlocksSection.querySelectorAll('.editor-block-button');
    this._ui.rotateBlockBtn = this._ui.editorBlocksSection.querySelector(`#${this.identifier}-rotateBlockBtn`);


    this._ui.editorBlockOptimizationSection = domMake.Tree('div', { id: `${this.identifier}-eb-opt-section`, style: "display:none;" });
    this._ui.editorBlockOptimizationSection.innerHTML = `
        <div class="module-section-title" style="margin-top:15px;">Optimización (Bloques Editor)</div>
        <div class="module-btn-group" style="flex-wrap:wrap;">
            <input type="number" id="${this.identifier}-ebDelayPerSegmentInput" min="0" max="500" value="${this._editorBlockDefaultOptimizations.delayPerSegment}" title="Retraso por segmento de línea (ms)" class="module-form-control">
            <label for="${this.identifier}-ebDelayPerSegmentInput">Delay Seg. (ms):</label>

            <input type="number" id="${this.identifier}-ebDelayPerRowInput" min="0" max="1000" value="${this._editorBlockDefaultOptimizations.delayPerRow}" title="Retraso por fila de píxeles (ms)" class="module-form-control">
            <label for="${this.identifier}-ebDelayPerRowInput">Delay Fila (ms):</label>

            <input type="number" id="${this.identifier}-ebQualityFactorInput" min="1" max="10" value="${this._editorBlockDefaultOptimizations.qualityFactor}" title="Factor de calidad (1=mejor, 10=peor, más rápido)" class="module-form-control">
            <label for="${this.identifier}-ebQualityFactorInput">Calidad (1-10):</label>

            <input type="number" id="${this.identifier}-ebTargetSizeInput" min="10" max="100" value="${this._editorBlockDefaultOptimizations.targetSize}" title="Tamaño objetivo del objeto en píxeles (en el canvas)" class="module-form-control">
            <label for="${this.identifier}-ebTargetSizeInput">Tamaño Obj. (px):</label>
        </div>
    `;
    container.appendChild(this._ui.editorBlockOptimizationSection);

    this._ui.ebDelaySegmentInput = this._ui.editorBlockOptimizationSection.querySelector(`#${this.identifier}-ebDelayPerSegmentInput`);
    this._ui.ebDelayPerRowInput = this._ui.editorBlockOptimizationSection.querySelector(`#${this.identifier}-ebDelayPerRowInput`);
    this._ui.ebQualityFactorInput = this._ui.editorBlockOptimizationSection.querySelector(`#${this.identifier}-ebQualityFactorInput`);
    this._ui.ebTargetSizeInput = this._ui.editorBlockOptimizationSection.querySelector(`#${this.identifier}-ebTargetSizeInput`);



    container.appendChild(domMake.Tree("div", { class: "module-section-title", style: "margin-top:15px;" }, ["Optimización de Dibujo (General)"]));
    const optSettingsGrid = domMake.Tree("div", { class: "module-btn-group", style: "flex-wrap:wrap;" });

    this._ui.delayPerSegmentInput = domMake.Tree("input", { type: "number", min: "0", max: "50", value: "40", title: "Retraso por segmento de línea (ms)", class: "module-form-control" });
    this._ui.delayPerSegmentLabel = domMake.Tree("label", { for: `${this.identifier}-delayPerSegmentInput` }, ["Delay Seg. (ms):"]);
    optSettingsGrid.append(domMake.Tree("div", { style: "flex:1 1 48%;" }, [this._ui.delayPerSegmentLabel, this._ui.delayPerSegmentInput]));

    this._ui.delayPerRowInput = domMake.Tree("input", { type: "number", min: "0", max: "200", value: "50", title: "Retraso por fila de píxeles (ms)", class: "module-form-control" });
    this._ui.delayPerRowLabel = domMake.Tree("label", { for: `${this.identifier}-delayPerRowInput` }, ["Delay Fila (ms):"]);
    optSettingsGrid.append(domMake.Tree("div", { style: "flex:1 1 48%;" }, [this._ui.delayPerRowLabel, this._ui.delayPerRowInput]));

    this._ui.qualityFactorInput = domMake.Tree("input", { type: "number", min: "1", max: "5", value: "4", title: "Factor de calidad (1=mejor, 5=peor, más rápido)", class: "module-form-control" });
    this._ui.qualityFactorLabel = domMake.Tree("label", { for: `${this.identifier}-qualityFactorInput` }, ["Calidad (1-5):"]);
    optSettingsGrid.append(domMake.Tree("div", { style: "flex:1 1 48%;" }, [this._ui.qualityFactorLabel, this._ui.qualityFactorInput]));

    this._ui.autoClearBeforeDrawToggle = domMake.Tree("input", { type: "checkbox", id: `${this.identifier}-autoClearToggle`, checked: false, title: "Limpiar el canvas antes de dibujar cada cubo" });
    this._ui.autoClearBeforeDrawLabel = domMake.Tree("label", { for: `${this.identifier}-autoClearToggle` }, ["Auto-Limpiar antes de dibujar"]);
    optSettingsGrid.append(domMake.Tree("div", { style: "flex:1 1 48%; display:flex; align-items:center;" }, [this._ui.autoClearBeforeDrawToggle, this._ui.autoClearBeforeDrawLabel]));

    container.appendChild(optSettingsGrid);


    this._ui.status = domMake.Tree("div", { style: "text-align:center;margin-top:10px;font-size:0.85em;color:var(--info);" }, ["Haz clic en el canvas principal para dibujar el icono."]);
    container.appendChild(this._ui.status);
  }

  _setupEventListeners() {

    this._ui.prevBtn.addEventListener('click', () => this._changeIconId(-1));
    this._ui.nextBtn.addEventListener('click', () => this._changeIconId(1));
    this._ui.iconIdInput.addEventListener('change', () => this._loadIconImage(this._currentCategory, parseInt(this._ui.iconIdInput.value) || 1));
    this._ui.acceptIconBtn.addEventListener('click', () => this._loadIconImage(this._currentCategory, parseInt(this._ui.iconIdInput.value) || 1));
    this._ui.randomIconBtn.addEventListener('click', () => this._loadRandomIcon());


    ['cubes', 'ships', 'balls', 'ufos', 'waves', 'robots', 'spiders', 'swings', 'jetpacks'].forEach(catKey => {
      this._ui[`${catKey}NavButton`].addEventListener('click', () => this._changeCategory(catKey));
    });

    this._ui.difficultiesNavButton.addEventListener('click', () => this._changeCategory('difficulties'));
    this._ui.extrasNavButton.addEventListener('click', () => this._changeCategory('extras'));
    this._ui.gauntletsNavButton.addEventListener('click', () => this._changeCategory('gauntlets'));
    this._ui.editor_blocksNavButton.addEventListener('click', () => this._changeCategory('editor_blocks'));



    this._ui.difficultyButtons.forEach(button => {
      button.addEventListener('click', () => this._loadDifficultyIcon(button.dataset.diff));
    });
    this._ui.difficultyModToggles.forEach(checkbox => {
      checkbox.addEventListener('change', () => this._loadDifficultyIcon(this._ui.difficultiesSection.querySelector('.difficulty-button.active')?.dataset.diff));
    });
    this._ui.clearModsBtn.addEventListener('click', () => this._clearDifficultyModifiers());


    this._ui.extraButtons.forEach(button => {
      button.addEventListener('click', () => this._loadExtraIcon(button.dataset.extra));
    });


    this._ui.gauntletButtons.forEach(button => {
      button.addEventListener('click', () => this._loadGauntletIcon(button.dataset.gauntlet));
    });


    this._ui.editorBlockButtons.forEach(button => {
      button.addEventListener('click', () => this._loadEditorBlock(button.dataset.eblock));
    });

    this._ui.rotateBlockBtn.addEventListener('click', () => this._rotateBrushImage());


    this._canvasClickHandler = this._handleCanvasClick.bind(this);
  }

  _toggleModuleActive() {
    this._setModuleActive(!this._isActive);
  }

  _setModuleActive(active) {
    this._isActive = active;
    if (active) {
      this._hookCanvasClick();
      this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Desactivar Módulo';
      this._ui.moduleToggleButton.classList.add('active');


      this._changeCategory(this._currentCategory);
    } else {
      this._unhookCanvasClick();
      this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Activar Módulo';
      this._ui.moduleToggleButton.classList.remove('active');


      this._isDrawingIcon = false;
      this._ui.status.textContent = "Módulo inactivo. Actívalo para usarlo.";


      if (this._ui.autoClearBeforeDrawToggle) {
        this._ui.autoClearBeforeDrawToggle.checked = false;
      }
    }

    this._ui.delayPerSegmentInput.disabled = !active;
    this._ui.delayPerRowInput.disabled = !active;
    this._ui.qualityFactorInput.disabled = !active;
    this._ui.autoClearBeforeDrawToggle.disabled = !active;


    this._ui.ebDelaySegmentInput.disabled = !active;
    this._ui.ebDelayPerRowInput.disabled = !active;
    this._ui.ebQualityFactorInput.disabled = !active;
    this._ui.ebTargetSizeInput.disabled = !active;


    this._ui.previewCanvas.style.opacity = active ? '1' : '0.5';



    Object.values(this._ui).forEach(element => {
        if (element && element.classList && element.classList.contains('category-nav-button')) {
            element.disabled = !active;
        }
    });


    if (!active) {
        this._ui.commonControlsSection.style.display = 'none';
        this._ui.difficultiesSection.style.display = 'none';
        this._ui.extrasSection.style.display = 'none';
        this._ui.gauntletsSection.style.display = 'none';
        this._ui.editorBlocksSection.style.display = 'none';
        this._ui.editorBlockOptimizationSection.style.display = 'none';
    }
  }

  _changeCategory(newCategory) {

    this._ui.commonControlsSection.style.display = 'none';
    this._ui.difficultiesSection.style.display = 'none';
    this._ui.extrasSection.style.display = 'none';
    this._ui.gauntletsSection.style.display = 'none';
    this._ui.editorBlocksSection.style.display = 'none';
    this._ui.editorBlockOptimizationSection.style.display = 'none';


    Object.values(this._ui).forEach(element => {
        if (element && element.classList && element.classList.contains('category-nav-button')) {
            element.classList.remove('active');
        }
    });


    this._ui.difficultyButtons.forEach(btn => btn.classList.remove('active'));
    this._ui.extraButtons.forEach(btn => btn.classList.remove('active'));
    this._ui.gauntletButtons.forEach(btn => btn.classList.remove('active'));
    this._ui.editorBlockButtons.forEach(btn => btn.classList.remove('active'));


    this._currentCategory = newCategory;


    this._currentRotation = 0;

    if (newCategory === 'difficulties') {
        this._ui.difficultiesSection.style.display = 'block';
        const activeDiffButton = this._ui.difficultiesSection.querySelector('.difficulty-button.active');
        if (activeDiffButton) {
            this._loadDifficultyIcon(activeDiffButton.dataset.diff);
        } else {
            this._loadDifficultyIcon('easy');
            this._ui.difficultiesSection.querySelector('.difficulty-button[data-diff="easy"]')?.classList.add('active');
        }
    } else if (newCategory === 'extras') {
        this._ui.extrasSection.style.display = 'block';
    } else if (newCategory === 'gauntlets') {
        this._ui.gauntletsSection.style.display = 'block';
    } else if (newCategory === 'editor_blocks') {
        this._ui.editorBlocksSection.style.display = 'block';
        this._ui.editorBlockOptimizationSection.style.display = 'block';

        const defaultBlock = Object.keys(this._editorBlocks)[0];
        if (defaultBlock) {
            this._loadEditorBlock(defaultBlock);
            this._ui.editorBlocksSection.querySelector(`.editor-block-button[data-eblock="${defaultBlock}"]`)?.classList.add('active');
        }
    } else {
        this._ui.commonControlsSection.style.display = 'block';
        this._currentIconId = 1;
        this._loadIconImage(this._currentCategory, this._currentIconId);
    }

    const newCategoryButton = this._ui[`${newCategory}NavButton`];
    if (newCategoryButton) {
        newCategoryButton.classList.add('active');
    }
    this._ui.status.textContent = `Categoría: ${newCategory.charAt(0).toUpperCase() + newCategory.slice(1).replace('_', ' ')}.`;
  }

  _updateInfo() {
    if (this._categories[this._currentCategory]) {
      this._ui.info.textContent = `Icono #${this._currentIconId} / ${this._categories[this._currentCategory].maxId}`;
      this._ui.iconIdInput.max = this._categories[this._currentCategory].maxId;
    } else {
      this._ui.info.textContent = `Selecciona un icono`;
      this._ui.iconIdInput.max = 999;
    }
  }

  _changeIconId(delta) {
    if (!this._categories[this._currentCategory]) return;
    let n = this._currentIconId + delta;
    n = this._clamp(n, 1, this._categories[this._currentCategory].maxId);
    this._loadIconImage(this._currentCategory, n);
  }

  _loadRandomIcon() {
    if (!this._categories[this._currentCategory]) return;
    const maxId = this._categories[this._currentCategory].maxId;
    const randomId = Math.floor(Math.random() * maxId) + 1;
    this._loadIconImage(this._currentCategory, randomId);
  }

  async _loadIconImage(categoryKey, id) {
    if (!this._isActive) return;
    const category = this._categories[categoryKey];
    if (!category) return;

    this._currentCategory = categoryKey;
    this._currentIconId = this._clamp(id, 1, category.maxId);
    this._ui.iconIdInput.value = this._currentIconId;
    this._updateInfo();

    const imageUrl = `https://gdbrowser.com/iconkit/premade/${category.prefix}${this._currentIconId}.png`;
    this._ui.status.textContent = `Cargando ${categoryKey.slice(0, -1)} #${this._currentIconId}...`;

    const img = new window.Image();
    img.crossOrigin = "anonymous";
    img.onload = () => {
      this._canvasBrushImg = img;
      this._drawPreviewWithRotation(img, 0);
      this._ui.status.textContent = "Listo. Haz click en el canvas principal para dibujar.";
    };
    img.onerror = () => {
      this._canvasBrushImg = null;
      this._drawPreviewError();

      this._ui.status.textContent = "Error al cargar imagen.";
    };
    img.src = imageUrl;
  }

  async _loadDifficultyIcon(difficultyKey) {
    if (!this._isActive) return;
    this._currentCategory = 'difficulties';
    this._ui.difficultyButtons.forEach(btn => btn.classList.remove('active'));
    const clickedButton = this._ui.difficultiesSection.querySelector(`.difficulty-button[data-diff="${difficultyKey}"]`);
    if(clickedButton) clickedButton.classList.add('active');

    let imageUrl = `https://gdbrowser.com/assets/difficulties/${this._difficulties[difficultyKey]}`;


    let activeModifiers = [];
    this._ui.difficultyModToggles.forEach(checkbox => {
      if (checkbox.checked) {
        activeModifiers.push(checkbox.dataset.mod);
      }
    });

    this._canvasBrushImg = null;
    this._ui.status.textContent = `Cargando ${difficultyKey} ...`;
    this._drawPreviewWithRotation(null, 0);

    const tryLoad = async (url) => {
        return new Promise(resolve => {
            const img = new Image();
            img.crossOrigin = "anonymous";
            img.onload = () => {
                this._canvasBrushImg = img;
                this._drawPreviewWithRotation(img, 0);
                resolve(true);
            };
            img.onerror = () => {
                resolve(false);
            };
            img.src = url;
        });
    };

    let loaded = false;
    if (activeModifiers.length > 0) {
        const baseName = this._difficulties[difficultyKey].replace('.png', '');
        for (const mod of activeModifiers) {
            const modifiedUrl = `https://gdbrowser.com/assets/difficulties/${baseName}${mod}.png`;
            loaded = await tryLoad(modifiedUrl);
            if (loaded) {

                break;
            }
        }
    }
    if (!loaded) {
        loaded = await tryLoad(imageUrl);
    }

    if (loaded) {
        this._ui.status.textContent = "Listo. Haz click para dibujar la dificultad.";
    } else {
        this._canvasBrushImg = null;
        this._drawPreviewError();

        this._ui.status.textContent = "Error al cargar imagen de dificultad.";
    }
  }

  _clearDifficultyModifiers() {
    this._ui.difficultyModToggles.forEach(checkbox => checkbox.checked = false);

    const currentDiff = this._ui.difficultiesSection.querySelector('.difficulty-button.active')?.dataset.diff;
    if(currentDiff) this._loadDifficultyIcon(currentDiff);

  }

  async _loadExtraIcon(extraKey) {
    if (!this._isActive) return;
    this._currentCategory = 'extras';
    this._ui.extraButtons.forEach(btn => btn.classList.remove('active'));
    const clickedButton = this._ui.extrasSection.querySelector(`.extra-button[data-extra="${extraKey}"]`);
    if(clickedButton) clickedButton.classList.add('active');

    const imageUrl = `https://gdbrowser.com/assets/${this._extras[extraKey]}`;
    this._ui.status.textContent = `Cargando extra: ${extraKey}...`;
    this._drawPreviewWithRotation(null, 0);

    const img = new window.Image();
    img.crossOrigin = "anonymous";
    img.onload = () => {
      this._canvasBrushImg = img;
      this._drawPreviewWithRotation(img, 0);

      this._ui.status.textContent = "Listo. Haz click para dibujar el extra.";
    };
    img.onerror = () => {
      this._canvasBrushImg = null;
      this._drawPreviewError();

      this._ui.status.textContent = "Error al cargar extra.";
    };
    img.src = imageUrl;
  }

  async _loadGauntletIcon(gauntletKey) {
    if (!this._isActive) return;
    this._currentCategory = 'gauntlets';
    this._ui.gauntletButtons.forEach(btn => btn.classList.remove('active'));
    const clickedButton = this._ui.gauntletsSection.querySelector(`.gauntlet-button[data-gauntlet="${gauntletKey}"]`);
    if(clickedButton) clickedButton.classList.add('active');

    const imageUrl = `https://gdbrowser.com/assets/gauntlets/${this._gauntlets[gauntletKey]}`;
    this._ui.status.textContent = `Cargando gauntlet: ${gauntletKey}...`;
    this._drawPreviewWithRotation(null, 0);

    const img = new window.Image();
    img.crossOrigin = "anonymous";
    img.onload = () => {
      this._canvasBrushImg = img;
      this._drawPreviewWithRotation(img, 0);

      this._ui.status.textContent = "Listo. Haz click para dibujar el gauntlet.";
    };
    img.onerror = () => {
      this._canvasBrushImg = null;
      this._drawPreviewError();

      this._ui.status.textContent = "Error al cargar gauntlet.";
    };
    img.src = imageUrl;
  }


  async _loadEditorBlock(blockKey) {
    if (!this._isActive) return;
    this._currentCategory = 'editor_blocks';
    this._ui.editorBlockButtons.forEach(btn => btn.classList.remove('active'));
    const clickedButton = this._ui.editorBlocksSection.querySelector(`.editor-block-button[data-eblock="${blockKey}"]`);
    if(clickedButton) clickedButton.classList.add('active');

    const imageUrl = this._editorBlocks[blockKey];
    if (!imageUrl) {

        this._ui.status.textContent = "Error: bloque no encontrado.";
        this._drawPreviewError();
        return;
    }

    this._ui.status.textContent = `Cargando bloque: ${blockKey}...`;
    this._drawPreviewWithRotation(null, 0);

    const img = new window.Image();
    img.crossOrigin = "anonymous";
    img.onload = () => {
      this._canvasBrushImg = img;
      this._currentRotation = 0;
      this._drawPreviewWithRotation(img, this._currentRotation);

      this._ui.status.textContent = "Listo. Haz click para dibujar el bloque.";
    };
    img.onerror = () => {
      this._canvasBrushImg = null;
      this._drawPreviewError();

      this._ui.status.textContent = "Error al cargar bloque.";
    };
    img.src = imageUrl;
  }


  _drawPreviewWithRotation(img, angle) {
    const ctx = this._ui.previewCanvas.getContext("2d");
    const previewSize = 96;
    ctx.clearRect(0, 0, previewSize, previewSize);

    if (!img) {

      ctx.fillStyle = "#d00";
      ctx.font = "14px Arial";
      ctx.fillText("NO IMG", 18, 55);
      return;
    }

    ctx.save();
    ctx.translate(previewSize / 2, previewSize / 2);
    ctx.rotate(angle * Math.PI / 180); // Rotate


    let drawWidth, drawHeight;
    const maxDim = Math.max(img.width, img.height);
    const scale = previewSize / maxDim * 0.9; // Scale to fit, leave a small margin
    drawWidth = img.width * scale;
    drawHeight = img.height * scale;

    ctx.drawImage(img, -drawWidth / 2, -drawHeight / 2, drawWidth, drawHeight);
    ctx.restore();
  }


  _drawPreviewError() {
    const ctx = this._ui.previewCanvas.getContext("2d");
    ctx.clearRect(0, 0, 96, 96);
    ctx.fillStyle = "#d00";
    ctx.font = "14px Arial";
    ctx.fillText("NO IMG", 18, 55);
  }


  _rotateBrushImage() {
    if (!this._canvasBrushImg) {

      return;
    }
    if (this._currentCategory !== 'editor_blocks') {

        return;
    }

    this._currentRotation = (this._currentRotation + 90) % 360;
    this._drawPreviewWithRotation(this._canvasBrushImg, this._currentRotation);

    this._ui.status.textContent = `Objeto rotado a ${this._currentRotation}°. Listo para dibujar.`;
  }


  _hookCanvasClick() {
    if (!this._mainCanvas) {
        this._mainCanvas = document.getElementById("canvas");
        if (!this._mainCanvas) {

            return;
        }
    }
    if (!this._canvasClickHandlerAttached) {
      this._mainCanvas.addEventListener("click", this._canvasClickHandler);
      this._canvasClickHandlerAttached = true;

    }
  }

  _unhookCanvasClick() {
    if (this._mainCanvas && this._canvasClickHandlerAttached) {
      this._mainCanvas.removeEventListener("click", this._canvasClickHandler);
      this._canvasClickHandlerAttached = false;

    }
  }

  async _handleCanvasClick(ev) {
    if (this._isDrawingIcon) {

        return;
    }
    if (!this._isActive) {

        return;
    }
    if (!this._canvasBrushImg) {

        return;
    }
    const socket = this._getGameSocket();
    if (!socket) {

        this._ui.status.textContent = "¡Sin conexión!";
        return;
    }
    if (!this._mainCanvas) {

        return;
    }

    this._isDrawingIcon = true;

    const rect = this._mainCanvas.getBoundingClientRect();
    const clickX_display_px = ev.clientX - rect.left;
    const clickY_display_px = ev.clientY - rect.top;

    const scaleToInternalCanvas = this._mainCanvas.width / rect.width;
    const clickX_internal_px = clickX_display_px * scaleToInternalCanvas;
    const clickY_internal_px = clickY_display_px * scaleToInternalCanvas;


    let delayPerSegment, delayPerRow, qualityFactor, targetDrawingSize_px;
    const autoClear = this._ui.autoClearBeforeDrawToggle.checked;

    if (this._currentCategory === 'editor_blocks') {
        delayPerSegment = parseInt(this._ui.ebDelaySegmentInput.value) || this._editorBlockDefaultOptimizations.delayPerSegment;
        delayPerRow = parseInt(this._ui.ebDelayPerRowInput.value) || this._editorBlockDefaultOptimizations.delayPerRow;
        qualityFactor = parseInt(this._ui.ebQualityFactorInput.value) || this._editorBlockDefaultOptimizations.qualityFactor;
        targetDrawingSize_px = parseInt(this._ui.ebTargetSizeInput.value) || this._editorBlockDefaultOptimizations.targetSize;
    } else {
        delayPerSegment = parseInt(this._ui.delayPerSegmentInput.value) || 1;
        delayPerRow = parseInt(this._ui.delayPerRowInput.value) || 20;
        qualityFactor = parseInt(this._ui.qualityFactorInput.value) || 1;
        targetDrawingSize_px = 64;
    }


    this._ui.status.textContent = "Dibujando objeto GD...";


    if (autoClear) {
        await this._clearCanvas(socket);
        await this._delay(100);
    }



    const originalImgWidth = this._canvasBrushImg.width;
    const originalImgHeight = this._canvasBrushImg.height;

    let rotatedTempCanvas = document.createElement("canvas");
    let tempCtx = rotatedTempCanvas.getContext("2d");


    let rotatedWidth = originalImgWidth;
    let rotatedHeight = originalImgHeight;
    if (this._currentRotation === 90 || this._currentRotation === 270) {
        rotatedWidth = originalImgHeight;
        rotatedHeight = originalImgWidth;
    }
    rotatedTempCanvas.width = rotatedWidth;
    rotatedTempCanvas.height = rotatedHeight;


    tempCtx.save();
    tempCtx.translate(rotatedWidth / 2, rotatedHeight / 2);
    tempCtx.rotate(this._currentRotation * Math.PI / 180);

    tempCtx.drawImage(this._canvasBrushImg, -originalImgWidth / 2, -originalImgHeight / 2);
    tempCtx.restore();


    const imgData = tempCtx.getImageData(0, 0, rotatedWidth, rotatedHeight).data;


    const scaleFactor_drawing = targetDrawingSize_px / Math.max(rotatedWidth, rotatedHeight);

    let currentLineStart_asset_px = null;
    let currentLineColor = null;
    let totalLinesDrawn = 0;

    for (let py = 0; py < rotatedHeight; py += qualityFactor) {
        if (!this._isActive || !this._isDrawingIcon) {

            break;
        }

        currentLineStart_asset_px = null;
        currentLineColor = null;

        for (let px = 0; px < rotatedWidth; px += qualityFactor) {
            const idx = (py * rotatedWidth + px) * 4;
            const r = imgData[idx], g = imgData[idx+1], b = imgData[idx+2], a = imgData[idx+3];
            const hexColor = "#" + [r,g,b].map(v=>v.toString(16).padStart(2,'0')).join('');

            if (a > 10) {
                if (currentLineStart_asset_px === null) {
                    currentLineStart_asset_px = px;
                    currentLineColor = hexColor;
                } else if (hexColor !== currentLineColor) {
                    const startX_draw = clickX_internal_px + (currentLineStart_asset_px - rotatedWidth/2) * scaleFactor_drawing;
                    const startY_draw = clickY_internal_px + (py - rotatedHeight/2) * scaleFactor_drawing;
                    const endX_draw = clickX_internal_px + (px - qualityFactor - rotatedWidth/2) * scaleFactor_drawing;
                    const endY_draw = startY_draw;

                    const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
                    this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
                    totalLinesDrawn++;

                    currentLineStart_asset_px = px;
                    currentLineColor = hexColor;
                }
            } else {
                if (currentLineStart_asset_px !== null) {
                    const startX_draw = clickX_internal_px + (currentLineStart_asset_px - rotatedWidth/2) * scaleFactor_drawing;
                    const startY_draw = clickY_internal_px + (py - rotatedHeight/2) * scaleFactor_drawing;
                    const endX_draw = clickX_internal_px + (px - qualityFactor - rotatedWidth/2) * scaleFactor_drawing;
                    const endY_draw = startY_draw;

                    const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
                    this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
                    totalLinesDrawn++;

                    currentLineStart_asset_px = null;
                    currentLineColor = null;
                }
            }
        }

        if (currentLineStart_asset_px !== null) {
            const startX_draw = clickX_internal_px + (currentLineStart_asset_px - rotatedWidth/2) * scaleFactor_drawing;
            const startY_draw = clickY_internal_px + (py - rotatedHeight/2) * scaleFactor_drawing;
            const endX_draw = clickX_internal_px + (rotatedWidth - 1 - rotatedWidth/2) * scaleFactor_drawing;
            const endY_draw = startY_draw;

            const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
            this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
            totalLinesDrawn++;

            currentLineStart_asset_px = null;
            currentLineColor = null;
        }
        await this._delay(delayPerRow);
    }

    this._isDrawingIcon = false;
    this.notify("success", `Objeto GD dibujado en el canvas (${totalLinesDrawn} líneas). Visible para todos.`);
    this._ui.status.textContent = "Listo. Haz click de nuevo para otro objeto.";
  }


  _getGameSocket() {
    if (globalThis.sockets && globalThis.sockets.length > 0) {
      return globalThis.sockets.find(s =>
        s.url.includes("drawaria.online/socket.io") && s.readyState === WebSocket.OPEN
      );
    }
    return null;
  }

  _delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
  _clamp(value, min, max) { return Math.max(min, Math.min(max, value)); }

  _sendDrawCommand(socket, start_px, end_px, color, thickness) {
      if (!this._mainCanvas) {
          console.error("Error: _mainCanvas is null in _sendDrawCommand. Cannot send drawing command.");
          return;
      }
      const normX1 = (start_px[0] / this._mainCanvas.width);
      const normY1 = (start_px[1] / this._mainCanvas.height);
      const normX2 = (end_px[0] / this._mainCanvas.width);
      const normY2 = (end_px[1] / this._mainCanvas.height);

      const ctx = this._mainCanvas.getContext('2d');
      ctx.strokeStyle = color;
      ctx.lineWidth = thickness;
      ctx.lineCap = 'round';
      ctx.beginPath();
      ctx.moveTo(start_px[0], start_px[1]);
      ctx.lineTo(end_px[0], end_px[1]);
      ctx.stroke();



      socket.send(`42["drawcmd",0,[${normX1.toFixed(4)},${normY1.toFixed(4)},${normX2.toFixed(4)},${normY2.toFixed(4)},false,${0 - thickness},"${color}",0,0,{}]]`);
  }

  async _clearCanvas(socket) {
        if (!this._mainCanvas) {

            return;
        }
        if (!socket || socket.readyState !== WebSocket.OPEN) {

            return;
        }

        const ctx = this._mainCanvas.getContext('2d');
        ctx.clearRect(0, 0, this._mainCanvas.width, this._mainCanvas.height);


        const clearThickness = 2000;
        const clearColor = '#FFFFFF';
        const steps = 5;

        for (let i = 0; i <= steps; i++) {


            this._sendDrawCommand(socket, [0, (i / steps)], [this._mainCanvas.width, (i / steps)], clearColor, clearThickness);
            await this._delay(5);

            this._sendDrawCommand(socket, [(i / steps), 0], [(i / steps), this._mainCanvas.height], clearColor, clearThickness);
            await this._delay(5);
        }
        this.notify("success", "Lienzo limpiado para todos.");
  }


  onHideContent() {
    this._unhookCanvasClick();

  }


  onShowContent() {

    if (this._isActive) {
        this._hookCanvasClick();

    }
  }
}






class MinecraftDrawer extends QBit {



  _minecraftImages = {
    mobs: [
      "https://minecraft.wiki/images/Allay_JE1_BE1.png",
      "https://minecraft.wiki/images/Cod.png",
      "https://minecraft.wiki/images/Salmon.png",
      "https://minecraft.wiki/images/Skeleton_Horse.png",
      "https://minecraft.wiki/images/Strider.png",
      "https://minecraft.wiki/images/Wandering_Trader.png",
      "https://minecraft.wiki/images/Bee.png",
      "https://minecraft.wiki/images/Dolphin.png",
      "https://minecraft.wiki/images/Drowned.png",
      "https://minecraft.wiki/images/Iron_Golem_JE2_BE2.png",
      "https://minecraft.wiki/images/Llama.png",
      "https://minecraft.wiki/images/Piglin.png",
      "https://minecraft.wiki/images/Endermite.png",
      "https://minecraft.wiki/images/Ender_Dragon.png",


      "https://minecraft.wiki/images/Zombie_Villager.png",
      "https://minecraft.wiki/images/Skeleton_Horseman.png",
      "https://minecraft.wiki/images/Agent.png",

      "https://minecraft.wiki/images/Beast_Boy.png",

      "https://minecraft.wiki/images/Rana.png",
      "https://minecraft.wiki/images/Diamond_Chicken.png",
      "https://minecraft.wiki/images/Mars.png",
      "https://minecraft.wiki/images/Moobloom.png",
      "https://minecraft.wiki/images/Moon_Cow.png",
      "https://minecraft.wiki/images/Friendly_Wither.png",
      "https://minecraft.wiki/images/Redstone_Bug.png",
      "https://minecraft.wiki/images/Smiling_Creeper.png",
      "https://minecraft.wiki/images/Pigman.png",
      "https://minecraft.wiki/images/Chinese_Alligator.png",
      "https://minecraft.wiki/images/Golden_Monkey.png",
      "https://minecraft.wiki/images/White-Lipped_Deer.png",
      "https://minecraft.wiki/images/Iceologer.png",
      "https://minecraft.wiki/images/Glare.png"
    ],
    items: [
      "https://minecraft.wiki/images/Wheat.png",
      "https://minecraft.wiki/images/Map.png",
      "https://minecraft.wiki/images/Spawn_Egg.png"
    ],
    blocks: [
      "https://minecraft.wiki/images/Dirt.png"
    ]
  };

  _currentCategory = 'mobs';
  _currentIndex = 0;
  _canvasBrushImg = null;
  _ui = {};
  _mainCanvas = null;
  _canvasClickHandler = null;
  _canvasClickHandlerAttached = false;
  _isActive = false;
  _isDrawingCube = false;


  constructor() {
    super("💎Minecraft Toolkit", '<i class="fas fa-gem"></i>');
    this._onStartup();
  }

  _onStartup() {

    this._mainCanvas = document.getElementById("canvas");
    if (!this._mainCanvas) {


    }

    this._loadInterface();
    this._setupEventListeners();


    this._loadMinecraftImage(this._currentCategory, this._currentIndex);


    this._setModuleActive(false);


  }

  _loadInterface() {
    const container = domMake.Tree("div", {
      id: `${this.identifier}-container`,
      class: "module-section"
    });
    this.htmlElements.section.appendChild(container);


    const moduleToggleGroup = domMake.Tree("div", {
      class: "module-btn-group",
      style: "margin-bottom:10px;"
    });
    this._ui.moduleToggleButton = domMake.Button('<i class="fas fa-power-off"></i> Activar Módulo');
    this._ui.moduleToggleButton.classList.add('module-toggle-button');
    this._ui.moduleToggleButton.addEventListener('click', () => this._toggleModuleActive());
    moduleToggleGroup.appendChild(this._ui.moduleToggleButton);
    container.appendChild(moduleToggleGroup);

    container.appendChild(domMake.Tree("div", {
      class: "module-section-title"
    }, ["Minecraft Online Drawer"]));


    container.appendChild(domMake.Tree("div", {
      class: "module-section-title",
      style: "margin-top:15px;"
    }, ["Seleccionar Categoría"]));
    const categoryButtonGroup = domMake.Tree("div", {
      class: "module-btn-group",
      style: "margin-bottom:10px;"
    });
    this._ui.mobsCategoryBtn = domMake.Button('Mobs', {
      class: "artfx-button"
    });
    this._ui.itemsCategoryBtn = domMake.Button('Items', {
      class: "artfx-button"
    });
    this._ui.blocksCategoryBtn = domMake.Button('Bloques', {
      class: "artfx-button"
    });

    this._ui.mobsCategoryBtn.addEventListener('click', () => this._setCategory('mobs'));
    this._ui.itemsCategoryBtn.addEventListener('click', () => this._setCategory('items'));
    this._ui.blocksCategoryBtn.addEventListener('click', () => this._setCategory('blocks'));

    categoryButtonGroup.append(this._ui.mobsCategoryBtn, this._ui.itemsCategoryBtn, this._ui.blocksCategoryBtn);
    container.appendChild(categoryButtonGroup);



    const navRow = domMake.Tree("div", {
      style: "display:flex;align-items:center;justify-content:center;gap:10px;margin-bottom:10px;"
    });
    this._ui.prevBtn = domMake.Button('←', {
      class: "artfx-button special"
    });
    this._ui.canvas = domMake.Tree("canvas", {
      width: 96,
      height: 96,
      style: "border:1px solid var(--CE-color);background:#222;"
    });
    this._ui.nextBtn = domMake.Button('→', {
      class: "artfx-button special"
    });
    navRow.append(this._ui.prevBtn, this._ui.canvas, this._ui.nextBtn);
    container.appendChild(navRow);


    this._ui.info = domMake.Tree("div", {
      style: "text-align:center;margin-top:5px;font-size:0.9em;color:var(--CE-color);"
    });
    container.appendChild(this._ui.info);


    container.appendChild(domMake.Tree("div", {
      class: "module-section-title",
      style: "margin-top:15px;"
    }, ["Buscar por ID"]));
    const searchRow = domMake.Row({
      style: "display:flex;align-items:center;gap:5px;"
    });
    this._ui.iconIdInput = domMake.Tree("input", {
      type: "number",
      min: "1",
      value: "1",
      placeholder: "ID del Objeto",
      class: "module-form-control"
    });
    this._ui.acceptIconBtn = domMake.Button('Aceptar ID', {
      class: "artfx-button"
    });
    searchRow.appendAll(this._ui.iconIdInput, this._ui.acceptIconBtn);
    container.appendChild(searchRow);


    const randomRow = domMake.Row({
      style: "margin-top:10px;"
    });
    this._ui.randomIconBtn = domMake.Button('Objeto Random', {
      class: "artfx-button special",
      style: "width:100%;"
    });
    randomRow.appendChild(this._ui.randomIconBtn);
    container.appendChild(randomRow);


    container.appendChild(domMake.Tree("div", {
      class: "module-section-title",
      style: "margin-top:15px;"
    }, ["Optimización de Dibujo"]));
    const optSettingsGrid = domMake.Tree("div", {
      class: "module-btn-group",
      style: "flex-wrap:wrap;"
    });



    this._ui.delayPerSegmentInput = domMake.Tree("input", {
      type: "number",
      min: "0",
      max: "50",
      value: "120",
      title: "Retraso por segmento de línea (ms)",
      class: "module-form-control"
    });
    this._ui.delayPerSegmentLabel = domMake.Tree("label", {
      for: `${this.identifier}-delayPerSegmentInput`
    }, ["Delay Segmento (ms):"]);
    optSettingsGrid.append(domMake.Tree("div", {
      style: "flex:1 1 48%;"
    }, [this._ui.delayPerSegmentLabel, this._ui.delayPerSegmentInput]));

    this._ui.delayPerRowInput = domMake.Tree("input", {
      type: "number",
      min: "0",
      max: "200",
      value: "130",
      title: "Retraso por fila de píxeles (ms)",
      class: "module-form-control"
    });
    this._ui.delayPerRowLabel = domMake.Tree("label", {
      for: `${this.identifier}-delayPerRowInput`
    }, ["Delay Fila (ms):"]);
    optSettingsGrid.append(domMake.Tree("div", {
      style: "flex:1 1 48%;"
    }, [this._ui.delayPerRowLabel, this._ui.delayPerRowInput]));

    this._ui.qualityFactorInput = domMake.Tree("input", {
      type: "number",
      min: "1",
      max: "5",
      value: "6",
      title: "Factor de calidad (1=mejor, 5=peor, más rápido)",
      class: "module-form-control"
    });
    this._ui.qualityFactorLabel = domMake.Tree("label", {
      for: `${this.identifier}-qualityFactorInput`
    }, ["Calidad (1-5):"]);
    optSettingsGrid.append(domMake.Tree("div", {
      style: "flex:1 1 48%;"
    }, [this._ui.qualityFactorLabel, this._ui.qualityFactorInput]));

    this._ui.autoClearBeforeDrawToggle = domMake.Tree("input", {
      type: "checkbox",
      id: `${this.identifier}-autoClearToggle`,
      checked: false,
      title: "Limpiar el canvas antes de dibujar cada objeto"
    });

    this._ui.autoClearBeforeDrawLabel = domMake.Tree("label", {
      for: `${this.identifier}-autoClearToggle`
    }, ["Auto-Limpiar antes de dibujar"]);
    optSettingsGrid.append(domMake.Tree("div", {
      style: "flex:1 1 48%; display:flex; align-items:center;"
    }, [this._ui.autoClearBeforeDrawToggle, this._ui.autoClearBeforeDrawLabel]));

    container.appendChild(optSettingsGrid);


    this._ui.status = domMake.Tree("div", {
      style: "text-align:center;margin-top:10px;font-size:0.85em;color:var(--info);"
    }, ["Haz clic en el canvas principal para dibujar el objeto donde hiciste clic (será visible para todos)."]);
    container.appendChild(this._ui.status);


    this._updateCategoryButtons();
  }

  _setupEventListeners() {
    this._ui.prevBtn.addEventListener('click', () => this._changeImage(-1));
    this._ui.nextBtn.addEventListener('click', () => this._changeImage(1));


    this._ui.acceptIconBtn.addEventListener('click', () => this._loadMinecraftImage(this._currentCategory, parseInt(this._ui.iconIdInput.value) - 1 || 0));
    this._ui.randomIconBtn.addEventListener('click', () => {
      const imagesInCurrentCategory = this._minecraftImages[this._currentCategory];
      if (imagesInCurrentCategory.length > 0) {
        const randomIndex = Math.floor(Math.random() * imagesInCurrentCategory.length);
        this._loadMinecraftImage(this._currentCategory, randomIndex);
      } else {

      }
    });



    this._canvasClickHandler = this._handleCanvasClick.bind(this);
  }


  _toggleModuleActive() {
    this._setModuleActive(!this._isActive);
  }


  _setModuleActive(active) {
    this._isActive = active;
    if (active) {
      this._hookCanvasClick();
      this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Desactivar Módulo';
      this._ui.moduleToggleButton.classList.add('active');

    } else {
      this._unhookCanvasClick();
      this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Activar Módulo';
      this._ui.moduleToggleButton.classList.remove('active');


      this._ui.autoClearBeforeDrawToggle.checked = false;

    }

    this._ui.prevBtn.disabled = !active;
    this._ui.nextBtn.disabled = !active;
    this._ui.iconIdInput.disabled = !active;
    this._ui.acceptIconBtn.disabled = !active;
    this._ui.randomIconBtn.disabled = !active;

    this._ui.mobsCategoryBtn.disabled = !active;
    this._ui.itemsCategoryBtn.disabled = !active;
    this._ui.blocksCategoryBtn.disabled = !active;
    this._ui.canvas.style.opacity = active ? '1' : '0.5';
    this._ui.delayPerSegmentInput.disabled = !active;
    this._ui.delayPerRowInput.disabled = !active;
    this._ui.qualityFactorInput.disabled = !active;
    this._ui.autoClearBeforeDrawToggle.disabled = !active;

  }


  _setCategory(category) {
    if (this._currentCategory === category) return;

    this._currentCategory = category;
    this._currentIndex = 0;
    this._updateCategoryButtons();
    this._loadMinecraftImage(this._currentCategory, this._currentIndex);
  }


  _updateCategoryButtons() {
    this._ui.mobsCategoryBtn.classList.toggle('active', this._currentCategory === 'mobs');
    this._ui.itemsCategoryBtn.classList.toggle('active', this._currentCategory === 'items');
    this._ui.blocksCategoryBtn.classList.toggle('active', this._currentCategory === 'blocks');
  }

  _changeImage(delta) {
    const imagesInCurrentCategory = this._minecraftImages[this._currentCategory];
    if (imagesInCurrentCategory.length === 0) {

      return;
    }

    let newIndex = this._currentIndex + delta;

    if (newIndex < 0) newIndex = imagesInCurrentCategory.length - 1;
    if (newIndex >= imagesInCurrentCategory.length) newIndex = 0;

    this._loadMinecraftImage(this._currentCategory, newIndex);
  }

  _updateInfo() {
    const imagesInCurrentCategory = this._minecraftImages[this._currentCategory];
    const categoryNameMap = {
      mobs: "Mob",
      items: "Item",
      blocks: "Bloque"
    };
    const categoryDisplayName = categoryNameMap[this._currentCategory] || "Objeto";

    this._ui.info.textContent = `${categoryDisplayName} #${this._currentIndex + 1} / ${imagesInCurrentCategory.length}`;
    this._ui.iconIdInput.max = imagesInCurrentCategory.length;
  }

  _loadMinecraftImage(category, index) {
    const imagesInCurrentCategory = this._minecraftImages[category];
    if (!imagesInCurrentCategory || imagesInCurrentCategory.length === 0) {

      const ctx = this._ui.canvas.getContext("2d");
      ctx.clearRect(0, 0, 96, 96);
      ctx.fillStyle = "#d00";
      ctx.font = "14px Arial";
      ctx.fillText("NO IMG", 18, 55);
      this._canvasBrushImg = null;
      this._ui.status.textContent = `Error: No hay imágenes para ${category}.`;
      this._updateInfo();
      return;
    }


    index = this._clamp(index, 0, imagesInCurrentCategory.length - 1);
    this._currentCategory = category;
    this._currentIndex = index;
    this._updateInfo();
    this._ui.iconIdInput.value = index + 1;

    const ctx = this._ui.canvas.getContext("2d");
    ctx.clearRect(0, 0, 96, 96);
    const img = new window.Image();
    img.crossOrigin = "anonymous";
    img.onload = () => {
      ctx.clearRect(0, 0, 96, 96);
      ctx.drawImage(img, 0, 0, 96, 96);
      this._canvasBrushImg = img;
      this._ui.status.textContent = "Listo. Haz click en el canvas principal para dibujar.";
    };
    img.onerror = (e) => {
      ctx.clearRect(0, 0, 96, 96);
      ctx.fillStyle = "#d00";
      ctx.font = "14px Arial";
      ctx.fillText("NO IMG", 18, 55);
      this._canvasBrushImg = null;
      const failedUrl = imagesInCurrentCategory[index];
      console.error(`Error al cargar imagen de Minecraft (Categoría: ${category}, Índice: ${index}, URL: ${failedUrl}):`, e);

      this._ui.status.textContent = "Error al cargar imagen del objeto.";
    };
    img.src = imagesInCurrentCategory[index];
  }


  _getGameSocket() {
    if (globalThis.sockets && globalThis.sockets.length > 0) {
      return globalThis.sockets.find(s =>
        s.url.includes("drawaria.online/socket.io") && s.readyState === WebSocket.OPEN
      );
    }
    return null;
  }


  _delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }


  _clamp(value, min, max) {
    return Math.max(min, Math.min(max, value));
  }


  async _handleCanvasClick(ev) {
    if (this._isDrawingCube) {

      return;
    }
    if (!this._isActive) {

      return;
    }
    if (!this._canvasBrushImg) {

      return;
    }
    const socket = this._getGameSocket();
    if (!socket) {

      this._ui.status.textContent = "¡Sin conexión!";
      return;
    }

    if (!this._mainCanvas) {

      return;
    }

    this._isDrawingCube = true;

    const rect = this._mainCanvas.getBoundingClientRect();

    const clickX_display_px = ev.clientX - rect.left;
    const clickY_display_px = ev.clientY - rect.top;


    const scaleToInternalCanvas = this._mainCanvas.width / rect.width;
    const clickX_internal_px = clickX_display_px * scaleToInternalCanvas;
    const clickY_internal_px = clickY_display_px * scaleToInternalCanvas;


    const delayPerSegment = parseInt(this._ui.delayPerSegmentInput.value) || 80;
    const delayPerRow = parseInt(this._ui.delayPerRowInput.value) || 100;
    const qualityFactor = parseInt(this._ui.qualityFactorInput.value) || 6;
    const autoClear = this._ui.autoClearBeforeDrawToggle.checked;

    this._ui.status.textContent = "Pintando objeto Minecraft...";


    if (autoClear) {
      await this._clearCanvas(socket);
      await this._delay(100);
    }


    const tempCanvas = document.createElement("canvas");
    tempCanvas.width = this._canvasBrushImg.width;
    tempCanvas.height = this._canvasBrushImg.height;
    const tempCtx = tempCanvas.getContext("2d");
    tempCtx.drawImage(this._canvasBrushImg, 0, 0);

    const imgData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height).data;



    const targetImageWidth_px = 64;
    const scaleFactor_drawing = targetImageWidth_px / tempCanvas.width; // Escala del asset original al tamaño objetivo

    let currentLineStart_asset_px = null;
    let currentLineColor = null;
    let totalLinesDrawn = 0;

    for (let py = 0; py < tempCanvas.height; py += qualityFactor) {
      if (!this._isActive) {

        break;
      }

      currentLineStart_asset_px = null;
      currentLineColor = null;

      for (let px = 0; px < tempCanvas.width; px += qualityFactor) {
        const idx = (py * tempCanvas.width + px) * 4;
        const r = imgData[idx],
          g = imgData[idx + 1],
          b = imgData[idx + 2],
          a = imgData[idx + 3];
        const hexColor = "#" + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');

        if (a > 10) {
          if (currentLineStart_asset_px === null) {
            currentLineStart_asset_px = px;
            currentLineColor = hexColor;
          } else if (hexColor !== currentLineColor) {

            const startX_draw = clickX_internal_px + (currentLineStart_asset_px - tempCanvas.width / 2) * scaleFactor_drawing;
            const startY_draw = clickY_internal_px + (py - tempCanvas.height / 2) * scaleFactor_drawing;
            const endX_draw = clickX_internal_px + (px - qualityFactor - tempCanvas.width / 2) * scaleFactor_drawing; // 'px - qualityFactor' para terminar antes del pixel actual
            const endY_draw = startY_draw;

            const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
            this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
            totalLinesDrawn++;


            currentLineStart_asset_px = px;
            currentLineColor = hexColor;
          }
        } else {
          if (currentLineStart_asset_px !== null) {

            const startX_draw = clickX_internal_px + (currentLineStart_asset_px - tempCanvas.width / 2) * scaleFactor_drawing;
            const startY_draw = clickY_internal_px + (py - tempCanvas.height / 2) * scaleFactor_drawing;
            const endX_draw = clickX_internal_px + (px - qualityFactor - tempCanvas.width / 2) * scaleFactor_drawing;
            const endY_draw = startY_draw;

            const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
            this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
            totalLinesDrawn++;

            currentLineStart_asset_px = null;
            currentLineColor = null;
          }
        }
      }

      if (currentLineStart_asset_px !== null) {
        const startX_draw = clickX_internal_px + (currentLineStart_asset_px - tempCanvas.width / 2) * scaleFactor_drawing;
        const startY_draw = clickY_internal_px + (py - tempCanvas.height / 2) * scaleFactor_drawing;
        const endX_draw = clickX_internal_px + (tempCanvas.width - 1 - tempCanvas.width / 2) * scaleFactor_drawing; // Hasta el final de la fila del asset
        const endY_draw = startY_draw;

        const thickness = Math.max(1, Math.round(scaleFactor_drawing * qualityFactor));
        this._sendDrawCommand(socket, [startX_draw, startY_draw], [endX_draw, endY_draw], currentLineColor, thickness);
        totalLinesDrawn++;

        currentLineStart_asset_px = null;
        currentLineColor = null;
      }

      await this._delay(delayPerRow);
    }

    this._isDrawingCube = false;
    this.notify("success", `Objeto Minecraft dibujado en el canvas (${totalLinesDrawn} líneas). Visible para todos.`);
    this._ui.status.textContent = "Listo. Haz click de nuevo para otro objeto.";
  }



  _hookCanvasClick() {

    if (!this._mainCanvas) {
      this._mainCanvas = document.getElementById("canvas");
      if (!this._mainCanvas) {

        return;
      }
    }

    if (!this._canvasClickHandlerAttached) {
      this._mainCanvas.addEventListener("click", this._canvasClickHandler);
      this._canvasClickHandlerAttached = true;

    }
  }



  _unhookCanvasClick() {
    if (this._mainCanvas && this._canvasClickHandlerAttached) {
      this._mainCanvas.removeEventListener("click", this._canvasClickHandler);
      this._canvasClickHandlerAttached = false;

    }
  }




  _sendDrawCommand(socket, start_px, end_px, color, thickness, isEraser = false) {

    if (!this._mainCanvas) {
      console.error("Error: _mainCanvas is null in _sendDrawCommand. Cannot send drawing command.");
      return;
    }


    const normX1 = (start_px[0] / this._mainCanvas.width);
    const normY1 = (start_px[1] / this._mainCanvas.height);
    const normX2 = (end_px[0] / this._mainCanvas.width);
    const normY2 = (end_px[1] / this._mainCanvas.height);


    const ctx = this._mainCanvas.getContext('2d');
    ctx.strokeStyle = color;
    ctx.lineWidth = thickness;
    ctx.lineCap = 'round';
    ctx.beginPath();
    ctx.moveTo(start_px[0], start_px[1]);
    ctx.lineTo(end_px[0], end_px[1]);
    ctx.stroke();



    socket.send(`42["drawcmd",0,[${normX1.toFixed(4)},${normY1.toFixed(4)},${normX2.toFixed(4)},${normY2.toFixed(4)},${isEraser},${0 - thickness},"${color}",0,0,{}]]`);
  }




  async _clearCanvas(socket) {
    if (!this._mainCanvas || !this._mainCanvas.getContext('2d')) {

      return;
    }
    if (!socket || socket.readyState !== WebSocket.OPEN) {

      return;
    }

    const ctx = this._mainCanvas.getContext('2d');
    ctx.clearRect(0, 0, this._mainCanvas.width, this._mainCanvas.height);


    const clearThickness = 2000;
    const clearColor = '#FFFFFF';
    const steps = 5;

    for (let i = 0; i <= steps; i++) {
      this._sendDrawCommand(socket, [0.01, (i / steps)], [0.99, (i / steps)], clearColor, clearThickness, true); // true for isEraser
      await this._delay(5);
      this._sendDrawCommand(socket, [(i / steps), 0.01], [(i / steps), 0.99], clearColor, clearThickness, true); // true for isEraser
      await this._delay(5);
    }
    this.notify("success", "Lienzo limpiado para todos.");
  }



  onHideContent() {
    this._unhookCanvasClick();

  }




  onShowContent() {


    if (this._isActive) {
      this._hookCanvasClick();

    }
  }

}




if (typeof QBit === 'undefined' || !QBit.Styles) {
    console.error("QBit or QBit.Styles is not defined. Ensure Cube Engine is loaded first.");
}



class WordHelperTool extends QBit {



    _isActive = false;

    _isInstantWinActive = false;

    _ui = {};

    _wordTipObserver = null;
    _refreshWordObserver = null;
    _infotextObserver = null;


    constructor() {
        super("📚Word Helper", '<i class="fas fa-book"></i>');
        this._onStartup();
    }
    _onStartup() {




        this._loadInterface();

        this._setupGlobalInstantWinStopperListeners();

    }

    _loadInterface() {
        const container = domMake.Tree("div", { id: `${this.identifier}-container`, class: "module-section" });
        this.htmlElements.section.appendChild(container);

        const moduleToggleGroup = domMake.Row({ class: "module-btn-group", style: "margin-bottom:10px;" });
        this._ui.moduleToggleButton = domMake.Button('<i class="fas fa-power-off"></i> Activar Word Helper');
        this._ui.moduleToggleButton.classList.add('module-toggle-button');
        this._ui.moduleToggleButton.addEventListener('click', () => this._toggleActiveState());
        moduleToggleGroup.appendChild(this._ui.moduleToggleButton);
        container.appendChild(moduleToggleGroup);

        this._ui.hintsPanel = domMake.Tree('div', {
            id: 'hintsPanel',
            style: 'display:none; background: #eeeeee; overflow-wrap: anywhere; border: 4px solid #eeeeee; border-radius: 2px; overflow-y: scroll; height: 180px; width:100%; margin: 8px 0 0 0; color: #3675ce;'
        });
        this._ui.hintsBox = domMake.Tree('span', { id: 'hintsBox' });
        this._ui.hintsPanel.appendChild(this._ui.hintsBox);
        container.appendChild(this._ui.hintsPanel);

        this._ui.instantWinButton = domMake.Button('Adivinar Rapido', {
            id: 'instantWinButton',
            style: 'width:100%; padding: 5px; margin-top: 5px; background: #28a745; color: white; border: none; border-radius: 3px; cursor: pointer;'
        });
        this._ui.instantWinButton.disabled = true;
        container.appendChild(this._ui.instantWinButton);

        this._setupEventListeners();
    }
    _setupEventListeners() {

        this._ui.instantWinButton.addEventListener('click', this._handleInstantWinClick.bind(this));


        this._ui.hintsPanel.addEventListener('click', (event) => {

            if (event.target.tagName === 'A' && event.target.classList.contains('hintClick')) {
                if (!this._isActive) return;
                const $inputChat = window.jQuery('#chatbox_textinput');
                const $sendButton = window.jQuery('#chatattop-sendbutton');
                $inputChat.val(event.target.textContent);
                $sendButton.click();

            }
        });
    }
    _toggleActiveState() {
        this._isActive = !this._isActive;
        if (this._isActive) {
            this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Desactivar Word Helper';
            this._ui.moduleToggleButton.classList.add('active');

            this._activateWordHelperLogic();
        } else {
            this._ui.moduleToggleButton.innerHTML = '<i class="fas fa-power-off"></i> Activar Word Helper';
            this._ui.moduleToggleButton.classList.remove('active');

            this._deactivateWordHelperLogic();
        }

        this._ui.instantWinButton.disabled = !this._isActive;
    }
    _activateWordHelperLogic() {

        const $targetWord = window.jQuery('#targetword_tip');
        const $inputChat = window.jQuery('#chatbox_textinput');
        const $refreshListElem = window.jQuery('#wordchooser-refreshlist');


        this._ui.hintsPanel.style.display = 'block';
        this._ui.instantWinButton.style.display = 'block';

        $inputChat.on('input.wordhelper', this._assist.bind(this));

        if ($targetWord.length && !this._wordTipObserver) {
            this._wordTipObserver = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {

                    if (!this._isActive) return;
                    const isTargetWordHiddenCompletely = mutation.target.style.display === 'none';
                    if (!isTargetWordHiddenCompletely) {
                        this._ui.hintsPanel.style.display = 'block';
                        this._ui.instantWinButton.style.display = 'block';
                        this._assist();
                    } else {



                    }
                });
            });
            this._wordTipObserver.observe($targetWord[0], { attributes: true, attributeFilter: ['style'], childList: true, subtree: true });

        }
        if ($refreshListElem.length && !this._refreshWordObserver) {
            this._refreshWordObserver = new MutationObserver((mutations) => {
                if (!this._isActive) return;
                if (mutations[0].target.disabled === false) {
                    setTimeout(() => this._assist(), 50);
                }
            });
            this._refreshWordObserver.observe($refreshListElem[0], { attributes: true });

        }

        const infotextElement = window.jQuery('#infotext')[0];
        const roomKeywords = /\слов|Palabras|Word/;
        if (infotextElement && !this._infotextObserver) {
            this._infotextObserver = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    if (this._isActive && roomKeywords.test(mutation.target.textContent)) {
                        this.notify("debug", "Infotext indicates word room. Running assist().");
                        this._ui.hintsPanel.style.display = 'block';
                        this._ui.instantWinButton.style.display = 'block';
                        this._assist();
                    } else if (this._isActive && !roomKeywords.test(mutation.target.textContent)) {
                        window.jQuery(this._ui.hintsBox).empty();
                        window.jQuery(this._ui.hintsBox).append('<span style="color: gray;">No es una sala de palabras.</span>');
                    }
                });
            });
            this._infotextObserver.observe(infotextElement, { childList: true, subtree: true });


            if (roomKeywords.test(infotextElement.textContent)) {

                this._ui.hintsPanel.style.display = 'block';
                this._ui.instantWinButton.style.display = 'block';
                this._assist();
            }
        }

    }
    _deactivateWordHelperLogic() {
        const $inputChat = window.jQuery('#chatbox_textinput');

        this._ui.hintsPanel.style.display = 'none';
        this._ui.instantWinButton.style.display = 'none';

        $inputChat.off('input.wordhelper');

        if (this._wordTipObserver) {
            this._wordTipObserver.disconnect();
            this._wordTipObserver = null;

        }
        if (this._refreshWordObserver) {
            this._refreshWordObserver.disconnect();
            this._refreshWordObserver = null;

        }
        if (this._infotextObserver) {
             this._infotextObserver.disconnect();
             this._infotextObserver = null;

        }

        this._stopInstantWinBatches();

    }




    _setupGlobalInstantWinStopperListeners() {
        if (this._globalStopperListenersSetup) return;


        if (window._io && window._io.events) {
            const eventsToStop = [
                'mc_turn_wordguessed',
                'uc_turn_wordguessedself',
                'bc_turn_results',
                'bc_turn_abort'
            ];
            eventsToStop.forEach(eventName => {

                const originalEventHandler = window._io.events[eventName];

                if (typeof window.jQuery !== 'undefined') {
                    window.jQuery(window._io.events).on(eventName, (...args) => {
                        this._stopInstantWinBatches();
                        if (originalEventHandler) {
                            originalEventHandler(...args);
                        }
                    });
                } else {


                    if (typeof window._io.events.addEventListener === 'function') {
                        window._io.events.addEventListener(eventName, (...args) => {
                            this._stopInstantWinBatches();
                            if (originalEventHandler) {
                                originalEventHandler(...args);
                            }
                        });
                    } else {



                    }
                }
            });
            this._globalStopperListenersSetup = true;
            this.notify("debug", "Global Instant Win Stopper listeners hooked.");
        } else {

        }
    }

    _stopInstantWinBatches() {
        if (this._isInstantWinActive) {
            this._isInstantWinActive = false;

            if (typeof window.jQuery !== 'undefined' && window.jQuery(this._ui.instantWinButton).length) {
                window.jQuery(this._ui.instantWinButton).prop('disabled', false).text('Adivinar Rapido');
            }
            if (typeof window.jQuery !== 'undefined' && window.jQuery(this._ui.hintsBox).length) {
                window.jQuery(this._ui.hintsBox).prepend('<span style="color: green; font-weight: bold;">Envío de batches detenido.</span><br>');
            }

        }
    }

    _generateNinetyNineCharBatches(wordList) {
        const batches = [];
        let currentBatch = "";
        const maxChars = 99;
        if (!wordList || wordList.length === 0) {

            return [];
        }

        const shuffledWordList = [...wordList].sort(() => 0.5 - Math.random());
        for (let i = 0; i < shuffledWordList.length; i++) {
            const word = shuffledWordList[i];

            if (currentBatch.length === 0 || (currentBatch.length + 1 + word.length <= maxChars)) {
                currentBatch += (currentBatch.length === 0 ? "" : " ") + word;
            } else {

                batches.push(currentBatch);
                currentBatch = word;
            }
        }

        if (currentBatch.length > 0) {
            batches.push(currentBatch);
        }

        return batches;
    }

    async _handleInstantWinClick() {
        if (!this._isActive) return;
        if (this._isInstantWinActive) {

            this._isInstantWinActive = false;
            if (typeof window.jQuery !== 'undefined') {
                window.jQuery(this._ui.instantWinButton).prop('disabled', false).text('Adivinar Rapido');
            }

            if (typeof window.jQuery !== 'undefined') {
                window.jQuery(this._ui.hintsBox).prepend('<span style="color: green; font-weight: bold;">Envío detenido manualmente.</span><br>');
            }
            return;
        }


        const wordsData = window.DATA_ARRAY;
        if (!wordsData) {

            alert('Error: La lista de palabras no está cargada. Asegúrate de que la biblioteca externa se cargó correctamente.');
            return;
        }
        const $inputChat = window.jQuery('#chatbox_textinput');
        const $sendButton = window.jQuery('#chatattop-sendbutton');
        const gameLang = window.jQuery('#langselector').val();

        const wordsForLang = wordsData[gameLang] || wordsData['en'];
        if (!wordsForLang || wordsForLang.length === 0) {

            alert(`No se pudo encontrar la lista de palabras para el idioma '${gameLang}'.`);
            return;
        }
        const batchesToSend = this._generateNinetyNineCharBatches(wordsForLang);
        if (batchesToSend.length === 0) {

            alert('No se pudieron generar batches para enviar. Intenta de nuevo.');
            return;
        }

        if (typeof window.jQuery !== 'undefined') {
            window.jQuery(this._ui.instantWinButton).text('Detener Envío');
        }
        this._isInstantWinActive = true;
        if (typeof window.jQuery !== 'undefined') {
            window.jQuery(this._ui.instantWinButton).prop('disabled', false);
        }
        const delayBetweenBatches = 700;
        for (let i = 0; i < batchesToSend.length; i++) {
            if (!this._isInstantWinActive) {

                break;
            }
            const batch = batchesToSend[i];

            if ($inputChat.is(':hidden') || $inputChat.prop('disabled')) {

                this._isInstantWinActive = false;
                break;
            }
            $inputChat.val(batch);
            $sendButton.click();

            await new Promise(resolve => setTimeout(resolve, delayBetweenBatches));
        }

        if (this._isInstantWinActive) {
            this.notify("success", 'Proceso de envío de batches completado. Revisa el chat para el resultado.');
            if (typeof window.jQuery !== 'undefined') {
                window.jQuery(this._ui.hintsBox).prepend('<span style="color: green; font-weight: bold;">Envío de batches completado. Revisa el chat.</span><br>');
            }
        }
        if (typeof window.jQuery !== 'undefined') {
            window.jQuery(this._ui.instantWinButton).prop('disabled', false);
            window.jQuery(this._ui.instantWinButton).text('Adivinar Rapido');
        }
        this._isInstantWinActive = false;
    }

    _assist() {
        if (!this._isActive) return;
        if (typeof window.jQuery === 'undefined') {

            return;
        }
        window.jQuery(this._ui.hintsBox).empty();

        const wordsData = window.DATA_ARRAY;
        if (!wordsData) {
            window.jQuery(this._ui.hintsBox).append('<span style="color: gray;">Cargando lista de palabras...</span>');
            return;
        }
        const gameLang = window.jQuery('#langselector').val();

        const currentLangWordList = wordsData[gameLang] || wordsData['en'];
        if (!currentLangWordList || currentLangWordList.length === 0) {
            window.jQuery(this._ui.hintsBox).append(`<span style="color:red; font-weight:bold;">Lista de palabras no disponible para '${gameLang}'.</span>`);
            return;
        }
        const $targetWord = window.jQuery('#targetword_tip');
        const targetWordText = $targetWord.text();


        const isGenericPattern = !targetWordText ||
                                 targetWordText.replace(/\s/g, '').replace(/_/g, '').trim() === '' ||
                                 targetWordText.includes('?');
        let hints;
        if (isGenericPattern) {

            hints = currentLangWordList;
        } else {

            const regexPattern = targetWordText.replace(/\s/g, '').replace(/_/g, '.');
            const wordRegex = new RegExp(`^${regexPattern}$`, 'i');
            hints = currentLangWordList.filter(word => wordRegex.test(word));
        }

        if (hints.length === 0) {
            window.jQuery(this._ui.hintsBox).append('<span style="color:red; font-weight:bold">Lo siento, no se encontró ninguna palabra!</span>');
        } else {
            window.jQuery(this._ui.hintsBox).append('<span style="color:green; font-weight:bold">Haz clic en cualquier palabra para enviarla: </span><br>');
            const inputVal = window.jQuery('#chatbox_textinput').val().toLowerCase();
            let matchingHints = hints.filter(hint => inputVal === '' || hint.toLowerCase().includes(inputVal));
            let nonMatchingHints = hints.filter(hint => inputVal !== '' && !hint.toLowerCase().includes(inputVal));

            matchingHints.sort((a, b) => {
                const aExact = a.toLowerCase() === inputVal;
                const bExact = b.toLowerCase() === inputVal;
                if (aExact && !bExact) return -1;
                if (!aExact && bExact) return 1;
                return a.length - b.length;
            });

            let html = matchingHints.map(hint => `<a href="javascript:void(0);" class="hintClick">${hint}</a>`).join(', ');
            if (nonMatchingHints.length > 0) {

                html += (html ? ', ' : '') + nonMatchingHints.map(hint => `<a href="javascript:void(0);" class="hintClick">${hint}</a>`).join(', ');
            }
            window.jQuery(this._ui.hintsBox).append(html);
        }
    }

    onHideContent() {
        this._deactivateWordHelperLogic();
        this.notify("debug", "Word Helper logic deactivated due to module hide.");
    }

    onShowContent() {

        if (this._isActive) {
            this._activateWordHelperLogic();
            this.notify("debug", "Word Helper logic reactivated due to module show.");
        }
    }
}









function getGameSocket() {
    if (window._io && window._io.socket && window._io.socket.readyState === WebSocket.OPEN) {
        return window._io.socket;
    }

    if (globalThis.sockets && globalThis.sockets.length > 0) {
        const primarySocket = globalThis.sockets.find(s =>
            s.url.includes("drawaria.online/socket.io") && s.readyState === WebSocket.OPEN
        );
        if (primarySocket) {
            return primarySocket;
        }
    }
    return null;
}




function createToggleForPlayerSentinel(labelText, callback, initialChecked = false) {
    const row = domMake.Row();
    row.style.alignItems = 'center';
    row.style.justifyContent = 'space-between';

    const labelSpan = domMake.Tree("span", {}, [labelText]);

    const checkboxId = "player-sentinel-toggle-" + (Math.random() * 1e9 | 0);
    const checkbox = domMake.Tree("input", { type: "checkbox", id: checkboxId, hidden: true });
    if (initialChecked) {
        checkbox.checked = true;
    }

    const indicatorLabel = domMake.Tree("label", {
        for: checkboxId,
        class: "icon",
        style: `
            width: 24px;
            height: 24px;
            min-width: unset;
            min-height: unset;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: background-color 0.2s ease, border-color 0.2s ease;
            background-color: ${initialChecked ? 'var(--info)' : 'var(--secondary)'};
        `
    });

    indicatorLabel.innerHTML = initialChecked
        ? '<i class="fas fa-check-square" style="font-size: 1.2em; color: var(--success);"></i>'
        : '<i class="fas fa-square" style="font-size: 1.2em;"></i>';

    checkbox.addEventListener('change', (event) => {
        const checked = event.target.checked;
        if (checked) {
            indicatorLabel.innerHTML = '<i class="fas fa-check-square" style="font-size: 1.2em; color: var(--success);"></i>';
            indicatorLabel.style.backgroundColor = 'var(--info)';
        } else {
            indicatorLabel.innerHTML = '<i class="fas fa-square" style="font-size: 1.2em;"></i>';
            indicatorLabel.style.backgroundColor = 'var(--secondary)';
        }
        if (callback && typeof callback === 'function') {
            callback(checked);
        }
    });

    row.append(labelSpan, domMake.Tree("div", {style: "flex-shrink: 0;"}, [checkbox, indicatorLabel]));
    return row;
}


if (typeof QBit !== 'undefined' && QBit.Styles) {
    QBit.Styles.addRules([
        `#${QBit.identifier} .module-section {
            display: flex;
            flex-direction: column;
            gap: 10px;
            padding: 5px;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
            margin-bottom: 10px;
        }`,
        `#${QBit.identifier} .module-section-title {
            font-weight: bold;
            margin-bottom: 5px;
            color: var(--dark-blue-title);
            text-align: center;
        }`,
        `#${QBit.identifier} .module-status-indicator {
            display: inline-block;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            margin-right: 5px;
            vertical-align: middle;
        }`,
        `#${QBit.identifier} .module-status-connected {
            background-color: var(--success);
        }`,
        `#${QBit.identifier} .module-status-disconnected {
            background-color: var(--danger);
        }`,
        `#${QBit.identifier} .artfx-button-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
            gap: 8px;
            margin-top: 10px;
        }`,
        `#${QBit.identifier} .artfx-button {
            padding: 8px 10px;
            background-color: var(--primary);
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            text-align: center;
            font-size: 0.85em;
            transition: background-color 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
        }`,
        `#${QBit.identifier} .artfx-button:hover {
            background-color: var(--dark-primary);
        }`,
        `#${QBit.identifier} .artfx-button.special {
            background-color: var(--info);
        }`,
        `#${QBit.identifier} .artfx-button.special:hover {
            background-color: var(--dark-info);
        }`,
        `#${QBit.identifier} .artfx-button.active {
            background-color: var(--success); /* O otro color distinto de activo */
        }`,
        `#${QBit.identifier} .artfx-button.active i {
            color: white; /* Asegura que el color del icono sea visible en estado activo */
        }`,
        `#${QBit.identifier} .sentinel-control-group { /* Re-used from original BotSentinel for clarity */
            display: flex;
            gap: 10px;
            margin-top: 8px;
            align-items: center;
        }`,
        `#${QBit.identifier} .sentinel-control-group > select,
         #${QBit.identifier} .sentinel-control-group > input[type="number"],
         #${QBit.identifier} .sentinel-control-group > button {
            flex-grow: 1;
            padding: 5px;
            box-sizing: border-box;
            border: 1px solid var(--CE-color);
            border-radius: .25rem;
            background-color: var(--CE-bg_color);
            color: var(--CE-color);
        }`,
        `#${QBit.identifier} .sentinel-control-group label {
             flex-shrink: 0;
             margin-right: 5px;
        }`,
        `#${QBit.identifier} .sentinel-follow-button.active {
             background-color: var(--warning);
             color: white;
        }`,
        `#${QBit.identifier} .bad-girl-section-title {
            color: var(--danger);
        }`,
        `#${QBit.identifier} .bad-girl-toggle-button.active {
            background-color: var(--danger);
            color: white;
        }`,
        `#${QBit.identifier} .bad-girl-textarea {
            width: 100%;
            min-height: 80px;
            margin-top: 5px;
            background-color: var(--input-bg);
            color: var(--dark-text);
            border: 1px solid var(--input-border-blue);
            padding: 5px;
            box-sizing: border-box;
        }`,
         `#${QBit.identifier} .bad-girl-controls {
            display: flex;
            gap: 10px;
            align-items: center;
            margin-top: 5px;
        }`,
        `#${QBit.identifier} .bad-girl-controls label {
             margin-right: 5px;
        }`
    ]);
} else {
    console.error("QBit o QBit.Styles no está definido. Los estilos de Player Sentinel pueden no cargarse.");
}







class PlayerSentinelTool extends QBit {




    #fastDelayActive = {
        naturalMovement: false,
        followPlayer: false
    };



    #bugExperienceInterval = null;
    #playerChaosInterval = null;
    #glitchVisualInterval = null;


    #naturalMovementInterval = null;
    #followTarget = { id: null, interval: null, name: null };
    #activeToggles = {
        naturalMovement: false,
        reactiveChat: false,
        smartGestures: false,
        badGirlSpam: false
    };
    #chatCooldown = new Map();
    #gestureCooldown = new Map();


    #ui = {};


    #spamInterval = null;
    #intervalTime = 700;
    #messageList = [
        "Eres muy lento", "Novato", "Jaja, qué mal", "Inténtalo de nuevo",
        "¿Eso es todo lo que tienes?", "Aburrido...", "Me duermo", "Puedes hacerlo mejor",
        "...", "Casi, pero no"
    ];


    #personalities = {
        Amigable: {
            spanish_greetings: ["¡Hola a todos!", "¡Buenas!", "¿Qué tal?", "Hey! Un gusto estar aquí 😊"],
            spanish_acknowledgements: ["Si!", "Claro!", "Entendido!", "Asi es!"],
            spanish_questions: ["Como estás?", "Y tu?", "¿Que tal?"],
            spanish_laughter: ["XD", "Jaja", "LOL"],
            spanish_general: ["Que?", "Bueno...", "Pero..."],
            spanish_congrats: ["¡Bien hecho!", "¡Excelente!", "¡Esa era!", "Felicidades!"],
            spanish_farewell: ["¡Adiós!", "Nos vemos", "¡Hasta la próxima!", "Chao 👋"],
            spanish_playerJoin: ["¡Bienvenido, {player}!", "Hola {player}!", "Mira quién llegó, {player} 👋"],
            spanish_playerLeave: ["Adiós, {player}!", "{player} se fue 😔", "Chao {player}"],

            english_greetings: ["Hi!", "Hello!", "Hey there!", "Nice to see you 😊"],
            english_acknowledgements: ["Yes!", "Got it!", "Right!"],
            english_questions: ["How are you?", "And you?", "What's up?"],
            english_laughter: ["LOL", "Haha", "XD", "Omg!"],
            english_general: ["What?", "Well...", "But..."],
            english_congrats: ["Good job!", "Excellent!", "That was it!", "Congrats!"],
            english_farewell: ["Bye!", "See ya!", "Later!", "So long 👋"],
            english_playerJoin: ["Welcome, {player}!", "Hi {player}!", "Look who's here, {player} 👋"],
            english_playerLeave: ["Bye, {player}!", "{player} left 😔", "See ya {player}"],

            gestures: {
                greeting: 5,
                acknowledgement: 11,
                question: 10,
                laughter: 7,
                general: 17,
                congrats: 19,
                playerJoin: 5,
                playerLeave: 3,
                drawing: 4,
                goodjob_drawing: 0
            }
        },
        Competitivo: {
            spanish_greetings: ["He llegado.", "Prepárense para dibujar.", "A ver quién gana."],
            spanish_acknowledgements: ["Si.", "Ok.", "Correcto."],
            spanish_questions: ["¿Estás listo?", "Quién sigue?", "¿Qué dibujas?"],
            spanish_laughter: ["Jaja.", "Easy."],
            spanish_general: ["..."],
            spanish_congrats: ["Nada mal.", "Correcto.", "Uno menos.", "Ok, adivinaste."],
            spanish_farewell: ["Me retiro.", "Suficiente por hoy.", "GG."],
            spanish_playerJoin: ["Otro rival...", "Llegó {player}...", "Hola {player}."],
            spanish_playerLeave: ["Uno menos.", "{player} se fue.", "OK, {player}."],

            english_greetings: ["I'm here.", "Get ready to draw.", "Who's next?"],
            english_acknowledgements: ["Yes.", "Ok.", "Correct."],
            english_questions: ["You ready?", "Who's drawing?", "What is it?"],
            english_laughter: ["Haha.", "Easy."],
            english_general: ["..."],
            english_congrats: ["Not bad.", "Correct.", "One less.", "Okay, you got it."],
            english_farewell: ["I'm out.", "Enough for today.", "GG."],
            english_playerJoin: ["Another rival...", "{player} arrived...", "Hi {player}."],
            english_playerLeave: ["One less.", "{player} left.", "Okay {player}."],

            gestures: {
                greeting: 1,
                acknowledgement: 12,
                question: 10,
                laughter: 6,
                general: 16,
                congrats: 0,
                playerJoin: 13,
                playerLeave: 3,
                drawing: 12,
                goodjob_drawing: 0
            }
        },
        Neutral: {
            spanish_greetings: ["Hola.", "Saludos."],
            spanish_acknowledgements: ["Si.", "Ok."],
            spanish_questions: ["?", "Cómo?"],
            spanish_laughter: ["Jeje."],
            spanish_general: ["..."],
            spanish_congrats: ["Bien.", "Correcto."],
            spanish_farewell: ["Adiós."],
            spanish_playerJoin: ["{player} se unió."],
            spanish_playerLeave: ["{player} se fue."],

            english_greetings: ["Hi.", "Greetings."],
            english_acknowledgements: ["Yes.", "Ok."],
            english_questions: ["?", "How?"],
            english_laughter: ["Hehe."],
            english_general: ["..."],
            english_congrats: ["Good.", "Correct."],
            english_farewell: ["Bye."],
            english_playerJoin: ["{player} joined."],
            english_playerLeave: ["{player} left."],

            gestures: {
                greeting: 11,
                acknowledgement: 11,
                question: 10,
                laughter: 5,
                general: 17,
                congrats: 11,
                playerJoin: 11,
                playerLeave: 11,
                drawing: 8,
                goodjob_drawing: 11
            }
        }
    };
    #currentPersonality = "Amigable";



    constructor() {

        super("🛡️Player Controller", '<i class="fas fa-user-shield"></i>');

        this.#onStartup();
    }


    #onStartup() {

        this.#loadInterface();

        this.#ui.messageTextarea.value = this.#messageList.join("\n");
        this.#ui.intervalInput.value = this.#intervalTime;


        const playerListElement = document.getElementById("playerlist");
        if (playerListElement) {
            new MutationObserver(() => this.#updatePlayerDropdown()).observe(playerListElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-playerid', 'data-loggedin', 'style'] });
            this.#updatePlayerDropdown();
        }


        const chatboxMessages = document.getElementById("chatbox_messages");
        if (chatboxMessages) {
            new MutationObserver((mutations) => {
                mutations.forEach(record => record.addedNodes.forEach(node => {
                    if (node.nodeType === 1 && node.classList.contains('chatmessage')) {
                        this.#handleReactiveChat(node);
                    }
                }));
            }).observe(chatboxMessages, { childList: true, subtree: false });
        }


        if (window._io && window._io.events) {
            const originalTurnWordGuessed = window._io.events.uc_turn_wordguessedlocalThis;
            window._io.events.uc_turn_wordguessedlocalThis = (...args) => {
                this.#handleCorrectGuess(args);
                if (originalTurnWordGuessed) originalTurnWordGuessed(...args);
            };

            const originalPlayerNew = window._io.events.bc_playernew;
            window._io.events.bc_playernew = (...args) => {
                this.#handlePlayerJoin(args);
                if (originalPlayerNew) originalPlayerNew(...args);
            };

            const originalPlayerLeft = window._io.events.bc_playerleft;
            window._io.events.bc_playerleft = (...args) => {
                this.#handlePlayerLeave(args);
                if (originalPlayerLeft) originalPlayerLeft(...args);
            };

            const originalTurnBeginDraw = window._io.events.uc_turn_begindraw;
            window._io.events.uc_turn_begindraw = (...args) => {
                this.#handleTurnBeginDraw(args);
                if (originalTurnBeginDraw) originalTurnBeginDraw(...args);
            };

            const originalTurnSelectWord = window._io.events.uc_turn_selectword;
            window._io.events.uc_turn_selectword = (...args) => {
                this.#handleWordSelected(args);
                if (originalTurnSelectWord) originalTurnSelectWord(...args);
            };
        }



        setInterval(() => this.#updateConnectionStatus(), 1000);


    }


    #loadInterface() {

        const container = domMake.Tree("div", { id: `${this.identifier}-container`, class: "module-section" });

        this.htmlElements.section.appendChild(container);



        const connectionStatusDiv = domMake.Tree("div", {}, [

            domMake.Tree("span", { id: `${this.identifier}-connectionStatus`, class: `module-status-indicator module-status-disconnected` }),

            domMake.Tree("span", { id: `${this.identifier}-statusText` }, ["Desconectado"])
        ]);



        const movementSection = domMake.Tree("div", { class: "module-section" });
        movementSection.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Movimiento Avanzado"]));


        const naturalMovementToggleRow = domMake.Tree("div", { class: "sentinel-control-row" });
        const naturalMovementToggle = createToggleForPlayerSentinel("Movimiento Natural", (checked) => this.#toggleNaturalMovement(checked));
        this.#ui.naturalMovementToggleCheckbox = naturalMovementToggle.querySelector('input[type="checkbox"]');
        naturalMovementToggleRow.appendChild(naturalMovementToggle);


        const naturalMovementFastDelayToggle = createToggleForPlayerSentinel("Fast Movements", (checked) => {
            this.#fastDelayActive.naturalMovement = checked;

            if (this.#activeToggles.naturalMovement) {
                this.#toggleNaturalMovement(false);
                this.#toggleNaturalMovement(true);
            }
        });
        this.#ui.naturalMovementFastDelayCheckbox = naturalMovementFastDelayToggle.querySelector('input[type="checkbox"]');
        naturalMovementFastDelayToggle.classList.add("sentinel-compact-toggle");
        naturalMovementToggleRow.appendChild(naturalMovementFastDelayToggle);
        movementSection.appendChild(naturalMovementToggleRow);


        const followGroup = domMake.Tree("div", { class: "sentinel-control-group" });
        this.#ui.playerDropdown = domMake.Tree("select", {style: "flex-grow: 1;"});
        followGroup.appendChild(this.#ui.playerDropdown);

        this.#ui.followButton = domMake.Button("Seguir");
        this.#ui.followButton.classList.add("sentinel-follow-button");
        this.#ui.followButton.addEventListener("click", () => this.#toggleFollowPlayer());
        followGroup.appendChild(this.#ui.followButton);
        movementSection.appendChild(followGroup);


        const followPlayerFastDelayToggle = createToggleForPlayerSentinel("Fast Follow", (checked) => {
            this.#fastDelayActive.followPlayer = checked;

            if (this.#followTarget.id !== null) {
                this.#toggleFollowPlayer();
                this.#toggleFollowPlayer();
            }
        });
        this.#ui.followPlayerFastDelayCheckbox = followPlayerFastDelayToggle.querySelector('input[type="checkbox"]');
        followPlayerFastDelayToggle.classList.add("sentinel-compact-toggle");
        movementSection.appendChild(followPlayerFastDelayToggle);

        container.appendChild(movementSection);


        const interactionSection = domMake.Tree("div", { class: "module-section" });
        interactionSection.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Interacción Inteligente"]));

        const reactiveChatToggle = createToggleForPlayerSentinel("Chat Reactivo", (checked) => this.#activeToggles.reactiveChat = checked);
        this.#ui.reactiveChatToggleCheckbox = reactiveChatToggle.querySelector('input[type="checkbox"]');
        interactionSection.appendChild(reactiveChatToggle);

        const smartGesturesToggle = createToggleForPlayerSentinel("Gestos Inteligentes", (checked) => this.#activeToggles.smartGestures = checked);
        this.#ui.smartGesturesToggleCheckbox = smartGesturesToggle.querySelector('input[type="checkbox"]');
        interactionSection.appendChild(smartGesturesToggle);

        const personalityGroup = domMake.Tree("div", { class: "sentinel-control-group" });
        personalityGroup.appendChild(domMake.Tree("label", {}, ["Personalidad:"]));
        this.#ui.personalitySelect = domMake.Tree("select", {style: "flex-grow: 1;"});
        Object.keys(this.#personalities).forEach(p => {
            this.#ui.personalitySelect.appendChild(domMake.Tree("option", { value: p }, [p]));
        });
        this.#ui.personalitySelect.addEventListener("change", (e) => this.#currentPersonality = e.target.value);
        personalityGroup.appendChild(this.#ui.personalitySelect);
        interactionSection.appendChild(personalityGroup);
        container.appendChild(interactionSection);


        const badGirlSection = domMake.Tree("div", { class: "module-section" });
        badGirlSection.appendChild(domMake.Tree("div", { class: "module-section-title bad-girl-section-title" }, ["Modo 'Chica Mala' (Spam)"]));

        this.#ui.badGirlToggleButton = domMake.Button('<i class="fas fa-play-circle"></i> Iniciar Spam');
        this.#ui.badGirlToggleButton.classList.add("bad-girl-toggle-button");
        this.#ui.badGirlToggleButton.addEventListener("click", () => this.#toggleBadGirlSpam());
        badGirlSection.appendChild(this.#ui.badGirlToggleButton);

        badGirlSection.appendChild(domMake.Tree("label", { style: "margin-top: 10px; display: block;" }, ["Lista de mensajes (uno por línea):"]));
        this.#ui.messageTextarea = domMake.Tree("textarea", {
            class: "bad-girl-textarea",
            placeholder: "Escribe aquí tus mensajes, uno por línea."
        });
        badGirlSection.appendChild(this.#ui.messageTextarea);

        const controlsDiv = domMake.Tree("div", { class: "bad-girl-controls" });
        const intervalLabel = domMake.Tree("label", { for: "bad-girl-interval" }, ["Intervalo (ms):"]);
        this.#ui.intervalInput = domMake.Tree("input", {
            type: "number",
            id: "bad-girl-interval",
            value: this.#intervalTime,
            min: "100",
            step: "50",
            style: "width: 80px;"
        });
        this.#ui.intervalInput.addEventListener("change", (e) => {
            const newTime = parseInt(e.target.value);
            if (newTime >= 100) {

                this.#intervalTime = newTime;
                if (this.#spamInterval) {
                    this.#toggleBadGirlSpam();
                    this.#toggleBadGirlSpam();
                }
            }
        });

        const saveMessagesButton = domMake.Button("Guardar Lista");
        saveMessagesButton.addEventListener("click", () => this.#updateMessageList());
        controlsDiv.append(intervalLabel, this.#ui.intervalInput, saveMessagesButton);
        badGirlSection.appendChild(controlsDiv);
        container.appendChild(badGirlSection);



        container.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Herramientas de Caos (Player)"]));


        const createToggleButtonForChaos = (id, labelText, iconClass, toggleFunction) => {
            const button = domMake.Button(`<i class="${iconClass}"></i> ${labelText}`);
            button.id = `${this.identifier}-${id}`;
            button.classList.add('artfx-button', 'special');
            button.addEventListener('click', () => toggleFunction.call(this, button));
            this.#ui[id] = button;
            return button;
        };

        const chaosButtonsGrid = domMake.Tree("div", { class: "artfx-button-grid" });
        chaosButtonsGrid.append(
            createToggleButtonForChaos('bug-experience', 'Bugear Experiencia', 'fas fa-gamepad', this.#toggleBugExperience),
            createToggleButtonForChaos('player-chaos', 'Caos de Jugador', 'fas fa-running', this.#togglePlayerChaos),
            createToggleButtonForChaos('glitch-visual', 'Glitch Visual', 'fas fa-ghost', this.#toggleGlitchVisual)
        );
        container.appendChild(chaosButtonsGrid);
    }


    #updateConnectionStatus() {
        const socket = getGameSocket();
        const isConnected = socket && socket.readyState === WebSocket.OPEN;

        const statusIndicator = document.getElementById(`${this.identifier}-connectionStatus`);
        const statusText = document.getElementById(`${this.identifier}-statusText`);

        if (statusIndicator && statusText) {
            statusIndicator.className = `module-status-indicator module-status-${isConnected ? 'connected' : 'disconnected'}`;
            statusText.textContent = isConnected ? "Conectado" : "Desconectado";
        }

        const buttonsToControl = [
            this.#ui.naturalMovementToggleCheckbox, this.#ui.naturalMovementFastDelayCheckbox,
            this.#ui.followButton, this.#ui.followPlayerFastDelayCheckbox,
            this.#ui.reactiveChatToggleCheckbox, this.#ui.smartGesturesToggleCheckbox,
            this.#ui.personalitySelect, this.#ui.badGirlToggleButton,
            this.#ui.messageTextarea, this.#ui.intervalInput, this.#ui.saveMessagesButton,
            this.#ui['bug-experience'], this.#ui['player-chaos'], this.#ui['glitch-visual']
        ];

        buttonsToControl.forEach(element => {
            if (element) element.disabled = !isConnected;
        });


        if (this.#ui.naturalMovementToggleCheckbox) this.#ui.naturalMovementToggleCheckbox.disabled = !isConnected;
        if (this.#ui.reactiveChatToggleCheckbox) this.#ui.reactiveChatToggleCheckbox.disabled = !isConnected;
        if (this.#ui.smartGesturesToggleCheckbox) this.#ui.smartGesturesToggleCheckbox.disabled = !isConnected;
        if (this.#ui.personalitySelect) this.#ui.personalitySelect.disabled = !isConnected;
    }


    #toggleNaturalMovement(isActive) {
        this.#activeToggles.naturalMovement = isActive;
        if (isActive) {
            if (this.#followTarget.id !== null) {

                this.#toggleFollowPlayer();
            }
            const socket = getGameSocket();
            if (!socket) {

                this.#ui.naturalMovementToggleCheckbox.checked = false;
                this.#activeToggles.naturalMovement = false;
                return;
            }

            clearInterval(this.#naturalMovementInterval);
            if (this.#fastDelayActive.naturalMovement) {
                this.#naturalMovementInterval = setInterval(this.#executeNaturalMovement.bind(this), 100 + Math.random() * 100);
            } else {
                this.#naturalMovementInterval = setInterval(this.#executeNaturalMovement.bind(this), 3000 + Math.random() * 2000);
            }
            this.#executeNaturalMovement();
        } else {
            clearInterval(this.#naturalMovementInterval);
            this.#naturalMovementInterval = null;

        }
    }

    #executeNaturalMovement() {
        if (!this.#activeToggles.naturalMovement || this.#followTarget.id !== null) return;
        const socket = getGameSocket();
        if (!socket || socket.readyState !== WebSocket.OPEN) {

            this.#toggleNaturalMovement(false);
            return;
        }

        const centerX = 50;
        const centerY = 50;
        const driftAmount = 15;

        let targetX = centerX + (Math.random() - 0.5) * driftAmount;
        let targetY = centerY + (Math.random() - 0.5) * driftAmount;

        targetX = Math.max(5, Math.min(95, targetX));
        targetY = Math.max(5, Math.min(95, targetY));

        this.#movePlayerSmoothly(targetX, targetY);
    }

    #updatePlayerDropdown() {
        const playerlistElement = document.getElementById("playerlist");
        if (!playerlistElement || !this.#ui.playerDropdown) return;

        const currentSelection = this.#ui.playerDropdown.value;
        this.#ui.playerDropdown.innerHTML = "";

        const myPlayerIdElement = document.querySelector(".playerlist-row[data-self='true']")?.dataset.playerid;
        const myPlayerId = myPlayerIdElement ? myPlayerIdElement : null;

        const playerRows = playerlistElement.querySelectorAll(".playerlist-row");
        let humanPlayers = [];

        playerRows.forEach(row => {
            const playerId = row.dataset.playerid;
            const playerName = row.querySelector(".playerlist-name a")?.textContent || `Jugador ${playerId}`;
            if (playerId !== myPlayerId && playerId !== "0") {
                humanPlayers.push({ id: playerId, name: playerName });
            }
        });

        if (humanPlayers.length === 0) {
            this.#ui.playerDropdown.appendChild(domMake.Tree("option", { value: "" }, ["No hay jugadores"]));
            this.#ui.playerDropdown.disabled = true;
            this.#ui.followButton.disabled = true;
            this.#toggleFollowPlayer(false);
        } else {
            this.#ui.playerDropdown.disabled = false;
            this.#ui.followButton.disabled = false;
            humanPlayers.forEach(p => {
                this.#ui.playerDropdown.appendChild(domMake.Tree("option", { value: p.id }, [p.name]));
            });

            if (humanPlayers.some(p => p.id === currentSelection)) {
                this.#ui.playerDropdown.value = currentSelection;
            } else {
                this.#ui.playerDropdown.selectedIndex = 0;
            }
        }
    }

    #toggleFollowPlayer() {
        const targetId = this.#ui.playerDropdown.value;
        const targetName = this.#ui.playerDropdown.querySelector(`option[value="${targetId}"]`)?.textContent;

        const socket = getGameSocket();
        if (!socket) {

            return;
        }

        if (this.#followTarget.id === targetId) {
            clearInterval(this.#followTarget.interval);
            this.#followTarget = { id: null, interval: null, name: null };
            this.#ui.followButton.textContent = "Seguir";
            this.#ui.followButton.classList.remove("active");


            if (this.#activeToggles.naturalMovement) {
                this.#toggleNaturalMovement(true);
            }
        } else {
            if (!targetId) {

                return;
            }
            if (this.#naturalMovementInterval) {
                this.#toggleNaturalMovement(false);
            }
            if (this.#followTarget.interval) {
                clearInterval(this.#followTarget.interval);
            }

            this.#followTarget = { id: targetId, name: targetName, interval: null };
            this.#ui.followButton.textContent = `Siguiendo: ${targetName}`;
            this.#ui.followButton.classList.add("active");


            if (this.#fastDelayActive.followPlayer) {
                this.#followTarget.interval = setInterval(this.#followLogic.bind(this), 100);
            } else {
                this.#followTarget.interval = setInterval(this.#followLogic.bind(this), 500);
            }
            this.#followLogic();
        }
    }

    #followLogic() {
        if (!this.#followTarget.id || !getGameSocket()) {

            this.#toggleFollowPlayer();
            return;
        }

        const targetPlayerElement = document.querySelector(`.spawnedavatar[data-playerid="${this.#followTarget.id}"]`);

        if (!targetPlayerElement) {

            this.#toggleFollowPlayer();
            return;
        }

        const mainCanvas = document.getElementById('canvas');
        if (!mainCanvas) return;

        const canvasRect = mainCanvas.getBoundingClientRect();
        const avatarRect = targetPlayerElement.getBoundingClientRect();


        let targetX_norm = ((avatarRect.left + avatarRect.width / 2 - canvasRect.left) / canvasRect.width);
        let targetY_norm = ((avatarRect.top + avatarRect.height / 2 - canvasRect.top) / canvasRect.height);


        let targetX_game = targetX_norm * 100;
        let targetY_game = targetY_norm * 100;



        const offsetX_game = 5;
        const offsetY_game = 5;

        let moveX = targetX_game + offsetX_game;
        let moveY = targetY_game + offsetY_game;


        moveX = Math.max(5, Math.min(95, moveX));
        moveY = Math.max(5, Math.min(95, moveY));

        this.#movePlayerSmoothly(moveX, moveY);
    }

    #movePlayerSmoothly(targetX, targetY) {


        if (window.game && typeof window.game.sendMove === "function") {
            window.game.sendMove(targetX, targetY);

        } else {

            const socket = getGameSocket();
            if (!socket || socket.readyState !== WebSocket.OPEN) {

                return;
            }


            socket.send(`42["clientcmd",103,[${1e4 * Math.floor((targetX / 100) * 1e4) + Math.floor((targetY / 100) * 1e4)},false]]`);

        }


        const canvas = document.getElementById('canvas');
        const brushCursor = document.querySelector('.brushcursor');
        if (canvas && brushCursor) {

            brushCursor.classList.remove('brushcursor-hidden');

            const canvasRect = canvas.getBoundingClientRect();


            const xPx = canvasRect.width * (targetX / 100);
            const yPx = canvasRect.height * (targetY / 100);







            brushCursor.style.left = `${xPx}px`;
            brushCursor.style.top = `${yPx}px`;
        }

    }

    _delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }


    #canPlayerChat() {
        const now = Date.now();
        const lastChat = this.#chatCooldown.get('player') || 0;

        const chatCooldownMs = 2000;
        const canChat = this.#activeToggles.reactiveChat && (now - lastChat > chatCooldownMs);
        if (canChat) {
            this.#chatCooldown.set('player', now);
        }
        return canChat;
    }

    #sendPlayerChat(message) {
        const socket = getGameSocket();
        if (socket && socket.readyState === WebSocket.OPEN && this.#canPlayerChat()) {

            const chatInput = document.getElementById('chatbox_textinput');
            const sendButton = document.getElementById('chatattop-sendbutton');
            if (chatInput && sendButton) {
                chatInput.value = message;

                chatInput.dispatchEvent(new Event('input', { bubbles: true }));
                sendButton.click();

            } else {

            }
        }
    }

    #canPlayerGesture() {
        const now = Date.now();
        const lastGesture = this.#gestureCooldown.get('player') || 0;

        const gestureCooldownMs = 500;
        const canGesture = this.#activeToggles.smartGestures && (now - lastGesture > gestureCooldownMs);
        if (canGesture) {
            this.#gestureCooldown.set('player', now);
        }
        return canGesture;
    }

    #sendPlayerGesture(gestureId) {
        const socket = getGameSocket();
        if (socket && socket.readyState === WebSocket.OPEN && this.#canPlayerGesture()) {
            socket.send(`42["sendgesture",${gestureId}]`);

        }
    }

    #getMessageLanguage(message) {
        const lowerCaseMsg = message.toLowerCase();
        const spanishKeywords = ['hola', 'qué tal', 'gracias', 'adiós', 'jaja', 'dibujo'];
        const englishKeywords = ['hi', 'hello', 'thanks', 'bye', 'lol', 'draw'];
        let spanishMatches = spanishKeywords.filter(k => lowerCaseMsg.includes(k)).length;
        let englishMatches = englishKeywords.filter(k => lowerCaseMsg.includes(k)).length;

        if (spanishMatches > englishMatches && spanishMatches > 0) return 'spanish';
        if (englishMatches > spanishMatches && englishMatches > 0) return 'english';
        return 'neutral';
    }

    #handleReactiveChat(messageNode) {
        const playerNameElement = messageNode.querySelector(".playerchatmessage-name a");
        const playerRow = messageNode.closest(".playerlist-row");
        const playerId = playerRow ? playerRow.dataset.playerid : null;

        const myPlayerIdElement = document.querySelector(".playerlist-row[data-self='true']")?.dataset.playerid;
        const myPlayerId = myPlayerIdElement ? myPlayerIdElement : null;

        if (playerId === myPlayerId || playerId === "0") return;

        const messageTextElement = messageNode.querySelector(".playerchatmessage-text");
        if (!messageTextElement || !this.#activeToggles.reactiveChat) return;

        const msg = {
            id: playerId,
            name: playerNameElement ? playerNameElement.textContent.trim() : 'Jugador',
            message: messageTextElement.textContent.trim()
        };

        const personality = this.#personalities[this.#currentPersonality];
        const lowerCaseMsg = msg.message.toLowerCase().trim();
        const lang = this.#getMessageLanguage(msg.message);

        let responseType = null;
        let responseMessages = null;

        if (/\b(hola|buenas|hey|hi|hello)\b/.test(lowerCaseMsg)) responseType = 'greeting';
        else if (/\b(si|yes|ok|claro|yeah)\b/.test(lowerCaseMsg)) responseType = 'acknowledgement';
        else if (/\b(como\sestas|y\stu|how\sare\syou|what's\sup|what)\b/.test(lowerCaseMsg)) responseType = 'question';
        else if (/\b(xd|lol|jaja|haha|omg)\b/.test(lowerCaseMsg)) responseType = 'laughter';
        else if (/\b(que|but|well|pero|bueno)\b/.test(lowerCaseMsg)) responseType = 'general';
        else if (/\b(lindo|hermoso|dibujas\sbien|buen\sdibujo|buen\strabajo|good\sjob|nice\sdraw)\b/.test(lowerCaseMsg)) responseType = 'goodjob_drawing';

        if (responseType) {
            responseMessages = personality[`${lang}_${responseType}`] || personality[`${lang}_general`] || personality.spanish_general;
            if (this.#canPlayerChat() && responseMessages && Math.random() < 0.7) {
                const selectedResponse = responseMessages[Math.floor(Math.random() * responseMessages.length)];
                this.#sendPlayerChat(selectedResponse.replace("{player}", msg.name));
            }
            if (this.#activeToggles.smartGestures && personality.gestures[responseType] !== undefined) {
                this.#sendPlayerGesture(personality.gestures[responseType]);
            }
        }
    }

    #handleCorrectGuess(data) {

        const playerId = data[0];
        const myPlayerIdElement = document.querySelector(".playerlist-row[data-self='true']")?.dataset.playerid;
        const myPlayerId = myPlayerIdElement ? myPlayerIdElement : null;

        if (playerId === myPlayerId || playerId === "0") return;

        if (!this.#activeToggles.reactiveChat && !this.#activeToggles.smartGestures) return;

        const personality = this.#personalities[this.#currentPersonality];
        const playerName = data[1] || 'alguien';

        if (this.#activeToggles.reactiveChat && Math.random() < 0.7 && this.#canPlayerChat()) {
            const response = personality.spanish_congrats[Math.floor(Math.random() * personality.spanish_congrats.length)];
            this.#sendPlayerChat(response.replace("{player}", playerName));
        }
        if (this.#activeToggles.smartGestures && personality.gestures.congrats !== undefined && this.#canPlayerGesture()) {
            this.#sendPlayerGesture(personality.gestures.congrats);
        }
    }

    #handlePlayerJoin(data) {

        const playerId = data[0];
        const myPlayerIdElement = document.querySelector(".playerlist-row[data-self='true']")?.dataset.playerid;
        const myPlayerId = myPlayerIdElement ? myPlayerIdElement : null;

        if (playerId === myPlayerId || playerId === "0") return;

        if (!this.#activeToggles.reactiveChat && !this.#activeToggles.smartGestures) return;

        const personality = this.#personalities[this.#currentPersonality];
        const playerName = data[1] || 'alguien';

        if (this.#activeToggles.reactiveChat && Math.random() < 0.6 && this.#canPlayerChat()) {
            const response = personality.spanish_playerJoin[Math.floor(Math.random() * personality.spanish_playerJoin.length)];
            this.#sendPlayerChat(response.replace("{player}", playerName));
        }
        if (this.#activeToggles.smartGestures && personality.gestures.playerJoin !== undefined && this.#canPlayerGesture()) {
            this.#sendPlayerGesture(personality.gestures.playerJoin);
        }
    }

    #handlePlayerLeave(data) {

        const playerId = data[0];
        const myPlayerIdElement = document.querySelector(".playerlist-row[data-self='true']")?.dataset.playerid;
        const myPlayerId = myPlayerIdElement ? myPlayerIdElement : null;

        if (playerId === myPlayerId || playerId === "0") return;


        if (this.#followTarget.id === playerId) {

            this.#toggleFollowPlayer();
        }

        if (!this.#activeToggles.reactiveChat && !this.#activeToggles.smartGestures) return;

        const personality = this.#personalities[this.#currentPersonality];
        const playerName = data[1] || 'alguien';

        if (this.#activeToggles.reactiveChat && Math.random() < 0.5 && this.#canPlayerChat()) {
            const response = personality.spanish_playerLeave[Math.floor(Math.random() * personality.spanish_playerLeave.length)];
            this.#sendPlayerChat(response.replace("{player}", playerName));
        }
        if (this.#activeToggles.smartGestures && personality.gestures.playerLeave !== undefined && this.#canPlayerGesture()) {
            this.#sendPlayerGesture(personality.gestures.playerLeave);
        }
    }

    #handleTurnBeginDraw(data) {

        const drawingPlayerId = data[0];
        const myPlayerIdElement = document.querySelector(".playerlist-row[data-self='true']")?.dataset.playerid;
        const myPlayerId = myPlayerIdElement ? myPlayerIdElement : null;


        if (drawingPlayerId === myPlayerId || (!this.#activeToggles.reactiveChat && !this.#activeToggles.smartGestures)) return;

        const personality = this.#personalities[this.#currentPersonality];

        const drawingPlayerName = document.querySelector(`.playerlist-row[data-playerid="${drawingPlayerId}"] .playerlist-name a`)?.textContent || 'alguien';


        if (this.#activeToggles.smartGestures && personality.gestures.drawing !== undefined && this.#canPlayerGesture()) {
            this.#sendPlayerGesture(personality.gestures.drawing);
        }
        if (this.#activeToggles.reactiveChat && Math.random() < 0.4 && this.#canPlayerChat()) {
            const chatMessage = `¡Buena suerte, ${drawingPlayerName}!`;
            this.#sendPlayerChat(chatMessage);
        }
    }

    #handleWordSelected(data) {




        if (!this.#activeToggles.smartGestures || !this.#canPlayerGesture()) return;

        const personality = this.#personalities[this.#currentPersonality];
        if (personality.gestures.acknowledgement !== undefined) {
            this.#sendPlayerGesture(personality.gestures.acknowledgement);
        }
    }


    #updateMessageList() {
        const newMessages = this.#ui.messageTextarea.value.split('\n').filter(msg => msg.trim() !== '');
        if (newMessages.length > 0) {
            this.#messageList = newMessages;

        } else {

        }
    }

    #toggleBadGirlSpam() {
        this.#activeToggles.badGirlSpam = !this.#activeToggles.badGirlSpam;

        if (this.#spamInterval) {

            clearInterval(this.#spamInterval);
            this.#spamInterval = null;
            this.#ui.badGirlToggleButton.classList.remove("active");
            this.#ui.badGirlToggleButton.innerHTML = '<i class="fas fa-play-circle"></i> Iniciar Spam';

        } else {

            const socket = getGameSocket();
            if (!socket || socket.readyState !== WebSocket.OPEN) {

                this.#activeToggles.badGirlSpam = false;
                return;
            }

            if (this.#messageList.length === 0) {

                this.#activeToggles.badGirlSpam = false;
                return;
            }

            this.#ui.badGirlToggleButton.classList.add("active");
            this.#ui.badGirlToggleButton.innerHTML = '<i class="fas fa-stop-circle"></i> Detener Spam';


            this.#spamInterval = setInterval(() => {
                if (!socket || socket.readyState !== WebSocket.OPEN) {

                    this.#toggleBadGirlSpam();
                    return;
                }
                const randomMessage = this.#messageList[Math.floor(Math.random() * this.#messageList.length)];
                this.#sendPlayerChat(randomMessage);
            }, this.#intervalTime);
        }
    }



    #toggleBugExperience(button) {
        if (this.#bugExperienceInterval) {
            clearInterval(this.#bugExperienceInterval);
            this.#bugExperienceInterval = null;
            button.classList.remove('active');

        } else {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            button.classList.add('active');



            this.#bugExperienceInterval = setInterval(() => {
                if (!socket || socket.readyState !== WebSocket.OPEN) {

                    this.#toggleBugExperience(button);
                    return;
                }
                const x = Math.random() * 100;
                const y = Math.random() * 100;
                this.#movePlayerSmoothly(x, y);
                socket.send(`42["sendgesture",${Math.floor(Math.random() * 32)}]`);
            }, 100);
        }
    }

    #togglePlayerChaos(button) {
        if (this.#playerChaosInterval) {
            clearInterval(this.#playerChaosInterval);
            this.#playerChaosInterval = null;
            button.classList.remove('active');

        } else {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            button.classList.add('active');



            this.#playerChaosInterval = setInterval(() => {
                if (!socket || socket.readyState !== WebSocket.OPEN) {

                    this.#togglePlayerChaos(button);
                    return;
                }
                const x = Math.random() * 100;
                const y = Math.random() * 100;
                this.#movePlayerSmoothly(x, y);
                socket.send(`42["sendgesture",${Math.floor(Math.random() * 32)}]`);
                socket.send(`42["playerafk"]`);
                socket.send(`42["clientcmd",3,[${Math.floor(Math.random() * 5)},${Math.random() < 0.5}]]`);
            }, 100);
        }
    }

    #toggleGlitchVisual(button) {
        if (this.#glitchVisualInterval) {
            clearInterval(this.#glitchVisualInterval);
            this.#glitchVisualInterval = null;
            button.classList.remove('active');

        } else {
            const socket = getGameSocket();
            if (!socket) {

                return;
            }
            button.classList.add('active');


            const chatMessages = ["!! GLITCH DETECTED !!", "ERROR CODE 404: REALITY NOT FOUND", "SYSTEM OVERLOAD", "// VISUAL ANOMALY //", "PACKET CORRUPTION", "DISCONNECTING...", "RECALIBRATING... X_X"];


            this.#glitchVisualInterval = setInterval(async () => {
                if (!socket || socket.readyState !== WebSocket.OPEN) {

                    this.#toggleGlitchVisual(button);
                    return;
                }


                socket.send(`42["clientcmd",101]`);
                await this._delay(50);
                socket.send(`42["clientcmd",115]`);
                await this._delay(50);
                this.#sendPlayerChat(chatMessages[Math.floor(Math.random() * chatMessages.length)]);
                await this._delay(50);


                const playerlistElement = document.getElementById('playerlist');
                if (playerlistElement) {
                    const playerRows = playerlistElement.querySelectorAll('.playerlist-row');
                    const myIdElement = document.querySelector(".playerlist-row[data-self='true']")?.dataset.playerid;
                    const myPlayerId = myIdElement ? myIdElement : null;
                    const otherPlayerIds = Array.from(playerRows)
                        .map(row => row.dataset.playerid)
                        .filter(id => id && id !== myPlayerId && id !== "0");

                    if (otherPlayerIds.length > 0) {
                        const randomTargetId = otherPlayerIds[Math.floor(Math.random() * otherPlayerIds.length)];
                        socket.send(`42["sendvotekick",${randomTargetId}]`);
                        await this._delay(50);
                        socket.send(`42["clientcmd",2,[${randomTargetId},${Math.floor(Math.random() * 9)}]]`);
                        await this._delay(50);
                    }
                }

                socket.send(`42["sendvote"]`);
                await this._delay(50);

            }, 200);
        }
    }
}



class AnimatorTool extends QBit {
    static dummy1 = QBit.register(this);


_JSON_SOURCES = {
    'Ninguno❌': '',
    'Ataque👊': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/ataque.json',
    'Pistola🔫': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/pistola.json',
    'Espada🗡️': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/espada.json',
    'Escudo🛡️': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/escudo.json',
    'Defensa🔵': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/defensa.json',
    'Cohete🚀': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/cohete.json',
    'Explosion💥': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/explosion.json',
    'Rayo⚡': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/rayo.json',
    'Gorra🧢': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/gorra.json',
    'Fuego🔥': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/fire.json',
    'Fuego blue🧊': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/bluefire.json',
    'Moneda︎🪙': 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/moneda.json',
};
_DEFAULT_JSON_NAME = 'Ninguno❌';

_JSON_EFFECTS = {
    'Ninguno❌': '',
    'Arco y Flecha🏹': 'effect:arrow_chaser',
    'Aura de Fuego🔥': 'effect:fire_aura_circular',
    'Bomba💣': 'effect:bomb',
    'Búmeran🪃': 'effect:boomerang_guided',
    'Cohete Espacial🚀': 'effect:space_rocket',
    'Pistola🔫': 'effect:pistol_shoot',
    'Dron Seguidor🤖': 'effect:drone_follower_ray',
    'Escopeta Mágica✨': 'effect:shotgun_blast',
    'Espadazo⚔️': 'effect:sword_slash_arc',
    'Flashlight Supernova⭐': 'effect:flashlight_star',
    'Granada Pegajosa🧨': 'effect:sticky_grenade_proj',
    'Lanzagranadas💥': 'effect:grenade_launcher',
    'Látigo Eléctrico🦯〽': 'effect:electric_whip_snap',
    'Martillazo Sísmico🔨': 'effect:seismic_smash_wave',
    'Mina de Defensa🔵': 'effect:proximity_mine_trap',
    'Muro de Tierra🧱': 'effect:earth_wall_shield',
    'Láser Rifle⚡': 'effect:laser_rifle_beam',
    'Rayo Zigzag🌀': 'effect:lightning_zigzag',
    'Tormenta de Hielo❄️': 'effect:ice_storm_area',
    'Tornado de Viento🌪️': 'effect:wind_tornado_spin',
};
_DEFAULT_EFFECT_NAME = 'Ninguno❌';


    _BOMBA_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/bomba.json';
    _PISTOLA_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/pistola.json';
    _ARCO_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/arco.json';
    _LANZAGRANADAS_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/lanzagranadas.json';
    _RIFLE_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/rifle.json';
    _BOOMERANG_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/boomerang.json';
    _ESPADA_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/espada.json';
    _MARTILLO_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/martillo.json';
    _LATIGO_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/latigo.json';
    _GRANADA_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/granada.json';
    _MINA_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/mina.json';
    _ESCOPETA_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/escopeta.json';
    _DRON_JSON_URL = 'https://raw.githubusercontent.com/DrawariaDeveloper/Json-to-Drawaria/main/dron.json';

    _DRAW_PADDING = 10;
    _DRAW_PADDING_HAND = 3;
    _HAND_GRIP_OFFSET_Y = 2;
    _REPEAT_ACTION_DELAY = 15;
    _WAIT_ACTION_DELAY = 500;


    _socket = null;
    _canvas = null;
    _ctx = null;
    _stopSignal = false;
    _isDrawing = false;
    _repeatIntervalId = null;
    _ui = {};
    _lastPlayerList = new Set();
    _isUpdatingList = false;


    _soundEffectsEnabled = false;
    _externalPlaySoundFn = null;


    _animationSoundMap = {
        '_drawBombWithExplosion': [
            { delay: 400, sound: 'nani' },
            { delay: 7400, sound: 'mega-explosion' }
        ],
        '_pistolShootEffect': [
            { delay: 500, sound: 'reload' },
            { delay: 7000, sound: 'shoot' }

        ],
        '_lightningZigzagChaser': [
            { delay: 0, sound: 'laser-reload' }

        ],
        '_circularFireAura': [
            { delay: 700, sound: 'fire' }
        ],
        '_spaceRocketChaser': [
            { delay: 200, sound: 'explosion' },
            { delay: 2500, sound: 'cannon' }
        ],
        '_drawArrowChaser': [
            { delay: 1600, sound: 'whoosh' },
            { delay: 3500, sound: 'explosion' }
        ],
        '_drawShotgunBlast': [
            { delay: 0, sound: 'reload' },
            { delay: 2200, sound: 'laser-reload' }

        ],
        '_drawGrenadeLauncher': [
            { delay: 0, sound: 'reload' },
            { delay: 1000, sound: 'whoosh' },
            { delay: 4800, sound: 'mega-explosion' }
        ],
        '_drawLaserRifleBeam': [
            { delay: 0, sound: 'reload' },
            { delay: 2400, sound: 'laser-shot' }
        ],
        '_drawSwordSlashArc': [
            { delay: 0, sound: 'sword-mistery' },
            { delay: 11000, sound: 'wow' },
        ],
        '_flashlightStarChaser': [
            { delay: 200, sound: 'laser-reload' },
            { delay: 2500, sound: 'explosion' }
        ],
        '_drawBoomerangGuided': [
            { delay: 2200, sound: 'whoosh' }
        ],
        '_drawSeismicSmashWave': [
            { delay: 2400, sound: 'whoosh' },
            { delay: 5800, sound: 'sword-slash' }
        ],
        '_drawElectricWhipSnap': [
            { delay: 600, sound: 'laser-reload' },
            { delay: 5000, sound: 'fire' }
        ],
        '_drawStickyGrenadeProj': [
            { delay: 400, sound: 'nani' },
            { delay: 11000, sound: 'mega-explosion' }
        ],
        '_drawProximityMineTrap': [
            { delay: 200, sound: 'laser-reload' },
            { delay: 4500, sound: 'laser-shot' }
        ],
        '_drawIceStormArea': [
            { delay: 200, sound: 'fire' }
        ],
        '_drawWindTornadoSpin': [
            { delay: 200, sound: 'fire' }
        ],
        '_drawEarthWallShield': [
            { delay: 400, sound: 'build' }
        ],
        '_coinCollect': [
            { delay: 0, sound: 'coin-collect' }
        ],
        '_drawDroneFollowerRay': [
            { delay: 500, sound: 'laser-reload' },
            { delay: 4000, sound: 'laser-shot' },
            { delay: 6000, sound: 'laser-shot' },
            { delay: 8000, sound: 'laser-shot' }
        ]
    };
    _originalAnimationMethods = new Map();
    _activeAnimationSoundTimeouts = new Set();

    constructor() {
        super("🔥The Animator Tool", '<i class="fas fa-mouse-pointer"></i>');
        this._scheduleInitialization();

        window.animatorToolInstance = this;
    }


    _scheduleInitialization() {
        const checkGameReady = () => {
            const canvasElement = document.getElementById('canvas');
            const playerlistElement = document.getElementById('playerlist');


            const gameSocketReady = (window._io && window._io.socket && window._io.socket.readyState === WebSocket.OPEN) ||
                                    (globalThis.sockets && globalThis.sockets.length > 0 && globalThis.sockets.some(s => s.readyState === WebSocket.OPEN && s.url.includes("drawaria.online/socket.io")));

            if (canvasElement && playerlistElement && gameSocketReady) {
                this._onStartup();
            } else {
                setTimeout(checkGameReady, 500);
            }
        };
        checkGameReady();
    }


    _onStartup() {

        this._canvas = document.getElementById('canvas');
        this._ctx = this._canvas.getContext('2d');


        this._findGameSocketSafely();

        this._loadInterface();
        this._setupEventListeners();


        const playerListElement = document.getElementById('playerlist');
        if (playerListElement) {
            let refreshTimeout;
            new MutationObserver(() => {
                clearTimeout(refreshTimeout);
                refreshTimeout = setTimeout(() => this._refreshPlayerList(), 100);
            }).observe(playerListElement, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ['data-playerid']
            });
            this._refreshPlayerList();
        }


    }


    _findGameSocketSafely() {

        if (window._io && window._io.socket && window._io.socket.readyState === WebSocket.OPEN) {
            this._socket = window._io.socket;
            return;
        }


        if (globalThis.sockets && globalThis.sockets.length > 0) {
            const primarySocket = globalThis.sockets.find(s =>
                s.url.includes("drawaria.online/socket.io") && s.readyState === WebSocket.OPEN
            );
            if (primarySocket) {
                this._socket = primarySocket;
                return;
            }
        }
    }

    _loadInterface() {
        const container = document.createElement('div');
        container.style.cssText = `
            display:flex; flex-direction:column; gap:10px;
        `;
        this.htmlElements.section.appendChild(container);


        const baseInputStyle = `
            flex-grow: 1;
            padding: 7px 10px; border-radius: 5px; border: 1px solid #555;
            background: #fff; color: #000;
            font-size: 13px;
        `;
        const selectBaseStyle = baseInputStyle + `
            appearance: none;
            background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23ffffff%22%20d%3D%22M287%2C197.3L159.2%2C69.5c-3.6-3.6-8.2-5.4-12.8-5.4s-9.2%2C1.8-12.8%2C5.4L5.4%2C197.3c-7.2%2C7.2-7.2%2C18.8%2C0%2C26c3.6%2C3.6%2C8.2%2C5.4%2C12.8%2C5.4s9.2%2C1.8%2C12.8%2C5.4l117%2C117c3.6%2C3.6%2C8.2%2C5.4%2C12.8%2C5.4s9.2%2C1.8%2C12.8%2C5.4l117-117c7.2-7.2%2C7.2-18.8%2C0-26C294.2%2C204.5%2C294.2%2C200.9%2C287%2C197.3z%22%2F%3E%3C%2Fsvg%3E');
            background-repeat: no-repeat;
            background-position: right 8px center;
            background-size: 10px;
            cursor: pointer;
        `;

        const createLabeledRow = (parent, labelText, inputElement) => {
            const wrapper = document.createElement('div');
            wrapper.style.cssText = `display:flex; align-items:center; gap:10px;`;
            const label = document.createElement('span');
            label.textContent = labelText;
            wrapper.appendChild(label);
            wrapper.appendChild(inputElement);
            parent.appendChild(wrapper);
            return { wrapper, label, inputElement };
        };

        this._ui.playerSelect = document.createElement('select');
        this._ui.playerSelect.style.cssText = selectBaseStyle;
        createLabeledRow(container, 'Jugador:', this._ui.playerSelect);

        this._ui.jsonUrlSelect = document.createElement('select');
        this._ui.jsonUrlSelect.style.cssText = selectBaseStyle;
        for (const name in this._JSON_SOURCES) {
            const opt = document.createElement('option');
            opt.value = this._JSON_SOURCES[name];
            opt.textContent = name;
            this._ui.jsonUrlSelect.appendChild(opt);
        }
        this._ui.jsonUrlSelect.value = this._JSON_SOURCES[this._DEFAULT_JSON_NAME];
        createLabeledRow(container, 'Dibujo:', this._ui.jsonUrlSelect);

        this._ui.effectSelect = document.createElement('select');
        this._ui.effectSelect.style.cssText = selectBaseStyle;
        for (const name in this._JSON_EFFECTS) {
            const opt = document.createElement('option');
            opt.value = this._JSON_EFFECTS[name];
            opt.textContent = name;
            this._ui.effectSelect.appendChild(opt);
        }
        this._ui.effectSelect.value = this._JSON_EFFECTS[this._DEFAULT_EFFECT_NAME];
        createLabeledRow(container, 'Efectos:', this._ui.effectSelect);

        this._ui.positionSelect = document.createElement('select');
        this._ui.positionSelect.style.cssText = selectBaseStyle;
        const positions = {
            'Cabeza👤': 'head',
            'Agarre Derecha🤜': 'grip_right',
            'Agarre Izquierda🤛': 'grip_left',
            'Derecha➡️': 'right',
            'Izquierda⬅️': 'left',
            'Arriba⬆️': 'top',
            'Abajo⬇️': 'bottom',
            'Centrado🔘': 'centered'
        };
        for (const name in positions) {
            const opt = document.createElement('option');
            opt.value = positions[name];
            opt.textContent = name;
            this._ui.positionSelect.appendChild(opt);
        }
        this._ui.positionSelect.value = 'head';
        createLabeledRow(container, 'Posición:', this._ui.positionSelect);

        this._ui.orientationSelect = document.createElement('select');
        this._ui.orientationSelect.style.cssText = selectBaseStyle;
        const orientations = {
            'Actual': 'none', 'Derecha (90°)': 'right', 'Izquierda (-90°)': 'left',
            'Abajo (180°)': 'down', 'Arriba (0°)': 'up'
        };
        for (const name in orientations) {
            const opt = document.createElement('option');
            opt.value = orientations[name];
            opt.textContent = name;
            this._ui.orientationSelect.appendChild(opt);
        }
        this._ui.orientationSelect.value = 'none';


        this._ui.sizeInput = document.createElement('input');
        this._ui.sizeInput.type = 'number';
        this._ui.sizeInput.min = '0.1';
        this._ui.sizeInput.max = '2.0';
        this._ui.sizeInput.step = '0.1';
        this._ui.sizeInput.value = '1.0';
        this._ui.sizeInput.style.cssText = baseInputStyle + `width: 60px; text-align: center;`;
        createLabeledRow(container, 'Tamaño (Escala):', this._ui.sizeInput);

        this._ui.repeatActionToggle = document.createElement('input');
        this._ui.repeatActionToggle.type = 'checkbox';
        this._ui.repeatActionToggle.id = `${this.identifier}-repeatActionToggle`;
        this._ui.repeatActionToggle.style.cssText = `margin-right: 5px; cursor: pointer; transform: scale(1.2);`;
        const repeatActionLabel = document.createElement('label');
        repeatActionLabel.htmlFor = this._ui.repeatActionToggle.id;
        repeatActionLabel.textContent = ` Repetir Acción (cada ${this._WAIT_ACTION_DELAY / 1000}s)`;
        repeatActionLabel.style.cssText = `display: flex; align-items: center; cursor: pointer;`;
        const repeatActionWrapper = document.createElement('div');
        repeatActionWrapper.style.cssText = `display:flex; align-items:center; gap:0;`;


        container.appendChild(repeatActionWrapper);

        this._ui.drawBtn = document.createElement('button');
        this._ui.drawBtn.textContent = 'Dibujar en avatar✏️';
        this._ui.drawBtn.disabled = true;
        this._ui.drawBtn.style.cssText = `
    width: 100%;
    height: 32px;
    padding: 0 12px;
    border: 1px solid #4a4a4a;
    border-radius: 4px;
    background: #fff;
    color: #333;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    &:disabled { background: #1a1a1a; border-color: #333; color: #666; cursor: not-allowed; }
`;
        container.appendChild(this._ui.drawBtn);

        this._ui.stopBtn = document.createElement('button');
        this._ui.stopBtn.textContent = 'Detener Animación🛑';
        this._ui.stopBtn.disabled = true;
        this._ui.stopBtn.style.cssText = `
    width: 100%;
    height: 28px;
    margin-top: 4px;
    padding: 0 10px;
    border: 1px solid #4a4a4a;
    border-radius: 4px;
    background: #fff;
    color: #333;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
    &:hover { background: #3a3a3a; border-color: #5a5a5a; }
    &:active { background: #1a1a1a; }
    &:disabled { background: #1a1a1a; border-color: #333; color: #666; cursor: not-allowed; }
`;
        container.appendChild(this._ui.stopBtn);
 }

    _setupEventListeners() {
        this._ui.jsonUrlSelect.addEventListener('change', () => {
            if (this._ui.jsonUrlSelect.value !== '') {
                this._ui.effectSelect.value = this._JSON_EFFECTS['Ninguno'];
            }
        });

        this._ui.effectSelect.addEventListener('change', () => {
            if (this._ui.effectSelect.value !== '') {
                this._ui.jsonUrlSelect.value = this._JSON_SOURCES['Ninguno'];

                if (this._ui.effectSelect.value === 'effect:pistol_shoot') {
                    this._ui.positionSelect.value = 'grip_right';

                }
            }
        });

        this._ui.drawBtn.addEventListener('click', this._handleDrawButtonClick.bind(this));
        this._ui.stopBtn.addEventListener('click', this._handleStopButtonClick.bind(this));
        this._ui.repeatActionToggle.addEventListener('change', () => {
            if (!this._ui.repeatActionToggle.checked) {
                this._handleStopButtonClick();
            }
        });
    }


    _getGameSocket() {
        if (this._socket && this._socket.readyState === WebSocket.OPEN) {
            return this._socket;
        }

        return null;
    }


    async _fetchJson(url) {
        return new Promise(resolve => {
            if (typeof GM_xmlhttpRequest === 'undefined') {

                resolve(null);
                return;
            }
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                onload: r => {
                    try { resolve(JSON.parse(r.responseText)); }
                    catch (e) {

                        console.error('Error al analizar JSON:', url, r.responseText, e);
                        resolve(null);
                    }
                },
                onerror: (error) => {

                    console.error('Error al obtener JSON:', url, error);
                    resolve(null);
                }
            });
        });
    }


    _sendDrawCommand(x1, y1, x2, y2, color, thickness) {
        x1 = Math.round(x1); y1 = Math.round(y1);
        x2 = Math.round(x2); y2 = Math.round(y2);

        if (this._ctx && this._canvas) {
            this._ctx.strokeStyle = color;
            this._ctx.lineWidth = thickness;
            this._ctx.lineCap = 'round';
            this._ctx.lineJoin = 'round';

            this._ctx.beginPath();
            this._ctx.moveTo(x1, y1);
            this._ctx.lineTo(x2, y2);
            this._ctx.stroke();
        }

        const currentSocket = this._getGameSocket();
        if (!currentSocket) {

            return false;
        }

        const normX1 = (x1 / this._canvas.width).toFixed(4);
        const normY1 = (y1 / this._canvas.height).toFixed(4);
        const normX2 = (x2 / this._canvas.width).toFixed(4);
        const normY2 = (y2 / this._canvas.height).toFixed(4);
        const cmd = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - thickness},"${color}",0,0,{}]]`;
        currentSocket.send(cmd);
        return true;
    }


    _handleStopButtonClick() {

        this._stopSignal = true;

        if (this._repeatIntervalId) {
            clearInterval(this._repeatIntervalId);
            this._repeatIntervalId = null;
        }


        this._ui.drawBtn.textContent = 'Dibujar en avatar';
        this._ui.drawBtn.style.background = 'linear-gradient(145deg, #4CAF50, #45a049)';
        this._ui.drawBtn.disabled = false;
        this._ui.stopBtn.disabled = true;
        this._isDrawing = false;

    }


    async _handleDrawButtonClick() {
        const pid = this._ui.playerSelect.value;
        if (!pid) {

            return;
        }

        const selectedDrawingUrl = this._ui.jsonUrlSelect.value;
        const selectedEffectValue = this._ui.effectSelect.value;


        if (this._repeatIntervalId) {
            this._handleStopButtonClick();
            return;
        }


        let actionToExecute = null;
        let effectiveWaitDelay = this._WAIT_ACTION_DELAY;


        this._stopSignal = false;
        this._isDrawing = true;
        this._ui.drawBtn.disabled = true;
        this._ui.stopBtn.disabled = false;

        try {
            if (selectedEffectValue && selectedEffectValue.startsWith('effect:')) {

                switch (selectedEffectValue) {
                    case 'effect:bomb': actionToExecute = () => this._drawBombWithExplosion(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 2500; break;
                    case 'effect:arrow_chaser': actionToExecute = () => this._drawArrowChaser(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 2000; break;
                    case 'effect:fire_aura_circular': actionToExecute = () => this._circularFireAura(pid, 500); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 500; break;
                    case 'effect:space_rocket': actionToExecute = () => this._spaceRocketChaser(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 4500; break;
                    case 'effect:pistol_shoot': actionToExecute = () => this._pistolShootEffect(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 1500; break;
                    case 'effect:flashlight_star': actionToExecute = () => this._flashlightStarChaser(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 2500; break;
                    case 'effect:lightning_zigzag': actionToExecute = () => this._lightningZigzagChaser(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 2500; break;
                    case 'effect:shotgun_blast': actionToExecute = () => this._drawShotgunBlast(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 1000; break;
                    case 'effect:grenade_launcher': actionToExecute = () => this._drawGrenadeLauncher(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 3000; break;
                    case 'effect:laser_rifle_beam': actionToExecute = () => this._drawLaserRifleBeam(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 1000; break;
                    case 'effect:boomerang_guided': actionToExecute = () => this._drawBoomerangGuided(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 4000; break;
                    case 'effect:sword_slash_arc': actionToExecute = () => this._drawSwordSlashArc(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 1000; break;
                    case 'effect:seismic_smash_wave': actionToExecute = () => this._drawSeismicSmashWave(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 2000; break;
                    case 'effect:electric_whip_snap': actionToExecute = () => this._drawElectricWhipSnap(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 1500; break;
                    case 'effect:sticky_grenade_proj': actionToExecute = () => this._drawStickyGrenadeProj(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 3500; break;
                    case 'effect:proximity_mine_trap': actionToExecute = () => this._drawProximityMineTrap(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 1000; break;
                    case 'effect:ice_storm_area': actionToExecute = () => this._drawIceStormArea(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 5000; break;
                    case 'effect:wind_tornado_spin': actionToExecute = () => this._drawWindTornadoSpin(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 5000; break;
                    case 'effect:earth_wall_shield': actionToExecute = () => this._drawEarthWallShield(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 3000; break;
                    case 'effect:drone_follower_ray': actionToExecute = () => this._drawDroneFollowerRay(pid); effectiveWaitDelay = this._WAIT_ACTION_DELAY + 8000; break;
                    default:

                        alert('Efecto procedural no reconocido o no implementado.');
                        return;
                }
            } else if (selectedDrawingUrl && selectedDrawingUrl !== this._JSON_SOURCES['Ninguno']) {
                actionToExecute = () => this._drawJsonCommands(pid);
            } else {

                return;
            }

            if (this._ui.repeatActionToggle.checked) {
                this._ui.drawBtn.textContent = 'Detener Repetición';
                this._ui.drawBtn.style.background = 'linear-gradient(145deg, #f44336, #d32f2f)';
                this._ui.drawBtn.disabled = false;


                const repeatedAction = async () => {

                    if (this._stopSignal || !this._getGameSocket() || !this._ui.repeatActionToggle.checked) {
                        this._handleStopButtonClick();
                        return;
                    }
                    if (this._isDrawing) {

                        return;
                    }
                    this._isDrawing = true;
                    try {
                        await actionToExecute();
                    } finally {
                        this._isDrawing = false;
                    }

                };

                await repeatedAction();
                if (!this._stopSignal) {
                    this._repeatIntervalId = setInterval(repeatedAction, effectiveWaitDelay);
                }
            } else {


                await actionToExecute();

            }
        } finally {


            if (!this._repeatIntervalId) {
                this._ui.drawBtn.disabled = false;
                this._ui.stopBtn.disabled = true;
                this._isDrawing = false;
            }
        }
    }

    _refreshPlayerList() {
        if (this._isUpdatingList) return;

        const currentPlayers = new Set();
        const playerRows = document.querySelectorAll('.playerlist-row[data-playerid]');

        playerRows.forEach(row => {
            if (row.dataset.self !== 'true' && row.dataset.playerid !== '0') {
                const name = row.querySelector('.playerlist-name a')?.textContent || `Jugador ${row.dataset.playerid}`;
                currentPlayers.add(`${row.dataset.playerid}:${name}`);
            }
        });

        const playersChanged = currentPlayers.size !== this._lastPlayerList.size ||
              ![...currentPlayers].every(player => this._lastPlayerList.has(player));

        if (!playersChanged) return;

        this._isUpdatingList = true;

        const previousSelection = this._ui.playerSelect.value;
        const previousSelectedText = this._ui.playerSelect.selectedOptions?.[0]?.textContent || '';

        this._ui.playerSelect.innerHTML = '';

        playerRows.forEach(row => {
            if (row.dataset.self === 'true') return;
            if (row.dataset.playerid === '0') return;
            const name = row.querySelector('.playerlist-name a')?.textContent || `Jugador ${row.dataset.playerid}`;
            const opt = document.createElement('option');
            opt.value = row.dataset.playerid;
            opt.textContent = name;
            this._ui.playerSelect.appendChild(opt);
        });

        if (previousSelection) {
            let restored = false;
            for (let option of this._ui.playerSelect.options) {
                if (option.value === previousSelection) {
                    this._ui.playerSelect.value = previousSelection;
                    restored = true;
                    break;
                }
            }

            if (!restored && previousSelectedText) {
                for (let option of this._ui.playerSelect.options) {
                    if (option.textContent === previousSelectedText) {
                        this._ui.playerSelect.value = option.value;
                        restored = true;
                        break;
                    }
                }
            }
        }

        this._lastPlayerList = new Set(currentPlayers);

        this._ui.drawBtn.disabled = this._ui.playerSelect.children.length === 0;
        this._isUpdatingList = false;
    }

    _analyzeJsonBounds(jsonCommands) {
        let min_nx = Infinity, max_nx = -Infinity;
        let min_ny = Infinity, max_ny = -Infinity;

        if (!Array.isArray(jsonCommands) || jsonCommands.length === 0) {
            return { min_nx: 0, max_nx: 0, min_ny: 0, max_ny: 0 };
        }

        for (const cmdArr of jsonCommands) {
            if (cmdArr.length > 2 && Array.isArray(cmdArr[2]) && cmdArr[2].length >= 4) {
                const [nx1, ny1, nx2, ny2] = cmdArr[2];
                min_nx = Math.min(min_nx, nx1, nx2);
                max_nx = Math.max(max_nx, nx1, nx2);
                min_ny = Math.min(min_ny, ny1, ny2);
                max_ny = Math.max(max_ny, ny1, ny2);
            }
        }
        if (min_nx === Infinity || max_nx === -Infinity || min_ny === Infinity || max_ny === -Infinity) {
            return { min_nx: 0, max_nx: 0, min_ny: 0, max_ny: 0 };
        }
        return { min_nx, max_nx, min_ny, max_ny };
    }

    async _drawJsonCommands(targetPlayerId, jsonUrlOverride = null, positionOverride = null, orientationOverride = null, sizeFactorOverride = null) {
        if (this._stopSignal) {  return; }
        const currentSocket = this._getGameSocket();
        if (!currentSocket) {

        }
        const avatar = document.querySelector(`.spawnedavatar[data-playerid="${targetPlayerId}"]`);
        if (!avatar) {

            return;
        }

        const cRect = this._canvas.getBoundingClientRect();
        const aRect = avatar.getBoundingClientRect();

        const avatarX = aRect.left - cRect.left;
        const avatarY = aRect.top - cRect.top;
        const avatarWidth = aRect.width;
        const avatarHeight = aRect.height;
        const avatarCenterX = avatarX + avatarWidth / 2;
        const avatarCenterY = avatarY + avatarHeight / 2;

        const url = jsonUrlOverride || this._ui.jsonUrlSelect.value;
        const currentPosition = positionOverride || this._ui.positionSelect.value;
        const orientation = orientationOverride || this._ui.orientationSelect.value;
        const sizeFactor = sizeFactorOverride !== null ? sizeFactorOverride : parseFloat(this._ui.sizeInput.value) || 1.0;

        if (!url || url === '' || url.startsWith('effect:')) {

            return;
        }

        const json = await this._fetchJson(url);
        if (this._stopSignal) return;
        if (!json || !Array.isArray(json.commands)) {

            alert('JSON inválido o no se pudo cargar el dibujo. Asegúrate de que el formato sea correcto y la URL accesible.');
            return;
        }

        const { min_nx, max_nx, min_ny, max_ny } = this._analyzeJsonBounds(json.commands);

        const scaledDrawWidth = (max_nx - min_nx) * this._canvas.width * sizeFactor;
        const scaledDrawHeight = (max_ny - min_ny) * this._canvas.height * sizeFactor;

        const scaledOriginalOriginX = min_nx * this._canvas.width * sizeFactor;
        const scaledOriginalOriginY = min_ny * this._canvas.height * sizeFactor;

        const pivotX = scaledOriginalOriginX + scaledDrawWidth / 2;
        const pivotY = scaledOriginalOriginY + scaledDrawHeight / 2;

        let drawingOriginX;
        let drawingOriginY;

        switch (currentPosition) {
            case 'centered':
                drawingOriginX = avatarCenterX - pivotX;
                drawingOriginY = avatarCenterY - pivotY;
                break;
            case 'top':
                drawingOriginX = avatarCenterX - pivotX;
                drawingOriginY = (avatarY - this._DRAW_PADDING) - scaledDrawHeight - scaledOriginalOriginY;
                break;
            case 'bottom':
                drawingOriginX = avatarCenterX - pivotX;
                drawingOriginY = (avatarY + avatarHeight + this._DRAW_PADDING) - scaledOriginalOriginY;
                break;
            case 'left':
                drawingOriginY = avatarCenterY - pivotY;
                drawingOriginX = (avatarX - this._DRAW_PADDING) - scaledDrawWidth - scaledOriginalOriginX;
                break;
            case 'right':
                drawingOriginY = avatarCenterY - pivotY;
                drawingOriginX = (avatarX + avatarWidth + this._DRAW_PADDING) - scaledOriginalOriginX;
                break;
            case 'head':
                drawingOriginX = avatarCenterX - pivotX;
                drawingOriginY = avatarY - scaledDrawHeight - scaledOriginalOriginY + (avatarHeight * 0.1);
                break;
            case 'grip_right':
                drawingOriginX = (avatarX + avatarWidth + this._DRAW_PADDING_HAND) - scaledOriginalOriginX;
                drawingOriginY = avatarCenterY - pivotY + this._HAND_GRIP_OFFSET_Y;
                break;
            case 'grip_left':
                drawingOriginX = (avatarX - this._DRAW_PADDING_HAND) - scaledDrawWidth - scaledOriginalOriginX;
                drawingOriginY = avatarCenterY - pivotY + this._HAND_GRIP_OFFSET_Y;
                break;
            default:
                drawingOriginX = avatarCenterX - pivotX;
                drawingOriginY = avatarCenterY - pivotY;
                break;
        }

        for (const cmdArr of json.commands) {
            if (this._stopSignal) {  return; }
            if (this._repeatIntervalId && !this._ui.repeatActionToggle.checked) {

                return;
            }

            const [, , [nx1, ny1, nx2, ny2, , thickNeg, color]] = cmdArr;

            let currentX1 = (nx1 * this._canvas.width * sizeFactor) - scaledOriginalOriginX;
            let currentY1 = (ny1 * this._canvas.height * sizeFactor) - scaledOriginalOriginY;
            let currentX2 = (nx2 * this._canvas.width * sizeFactor) - scaledOriginalOriginX;
            let currentY2 = (ny2 * this._canvas.height * sizeFactor) - scaledOriginalOriginY;

            const finalX1 = currentX1 + drawingOriginX;
            const finalY1 = currentY1 + drawingOriginY;
            const finalX2 = currentX2 + drawingOriginX;
            const finalY2 = currentY2 + drawingOriginY;

            this._sendDrawCommand(finalX1, finalY1, finalX2, finalY2, color, -thickNeg);
            await new Promise(r => setTimeout(r, this._REPEAT_ACTION_DELAY));
        }
    }

    _getAttachmentPoint(playerId, attachmentPointName = 'centered') {
        const avatar = document.querySelector(`.spawnedavatar[data-playerid="${playerId}"]`);
        if (!avatar) {

            return null;
        }

        const cRect = this._canvas.getBoundingClientRect();
        const aRect = avatar.getBoundingClientRect();

        const avatarX = aRect.left - cRect.left;
        const avatarY = aRect.top - cRect.top;
        const avatarWidth = aRect.width;
        const avatarHeight = aRect.height;
        const avatarCenterX = avatarX + avatarWidth / 2;
        const avatarCenterY = avatarY + avatarHeight / 2;

        let attachX, attachY;

        switch (attachmentPointName) {
            case 'grip_right':
                attachX = avatarX + avatarWidth + this._DRAW_PADDING_HAND;
                attachY = avatarCenterY + this._HAND_GRIP_OFFSET_Y;
                break;
            case 'grip_left':
                attachX = avatarX - this._DRAW_PADDING_HAND;
                attachY = avatarCenterY + this._HAND_GRIP_OFFSET_Y;
                break;
            case 'head':
                attachX = avatarCenterX;
                attachY = avatarY + (avatarHeight * 0.1);
                break;
            case 'bottom':
                attachX = avatarCenterX;
                attachY = avatarY + avatarHeight + this._DRAW_PADDING;
                break;
            case 'centered':
            default:
                attachX = avatarCenterX;
                attachY = avatarCenterY;
                break;
        }
        return { x: attachX, y: attachY };
    }

    _getTargetCoords(targetPlayerId) {
        return this._getAttachmentPoint(targetPlayerId, 'centered');
    }

    _distance(x1, y1, x2, y2) {
        return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
    }

    _getOwnPlayerId() {
        const ownPlayerName = document.querySelector('.playerlist-row .playerlist-name-self');
        if (ownPlayerName) {
            const ownPlayerRow = ownPlayerName.closest('.playerlist-row');
            if (ownPlayerRow) {
                return ownPlayerRow.dataset.playerid;
            }
        }
        const ownAvatar = document.querySelector('.spawnedavatar-self');
        if (ownAvatar) {
            return ownAvatar.dataset.playerid;
        }
        return null;
    }




    async _explosionBlast(centerX, centerY, size = 1.0) {
        if (this._stopSignal) {  return; }
        const steps = 80;
        const maxRadius = 100 * size;

        const explosionColors = [
            'hsl(0, 100%, 60%)', 'hsl(15, 100%, 65%)', 'hsl(30, 100%, 60%)',
            'hsl(45, 100%, 65%)', 'hsl(60, 100%, 70%)', 'hsl(25, 100%, 55%)', 'hsl(10, 100%, 50%)',
        ];

        for (let i = 0; i < steps; i++) {
            if (this._stopSignal) {  return; }
            if (!this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

                return;
            }

            const progress = i / steps;
            const particlesThisStep = 2 + Math.floor(progress * 5);

            for (let p = 0; p < particlesThisStep; p++) {
                const angle = Math.random() * Math.PI * 2;
                const distance = progress * maxRadius * (0.8 + Math.random() * 0.4);

                const endX = centerX + distance * Math.cos(angle);
                const endY = centerY + distance * Math.sin(angle);

                const colorIndex = Math.floor(Math.random() * explosionColors.length);
                const color = explosionColors[colorIndex];
                const thickness = Math.max(1, 8 - progress * 7 + Math.random() * 2);

                this._sendDrawCommand(centerX, centerY, endX, endY, color, thickness);
            }
            await new Promise(resolve => setTimeout(resolve, 25 + progress * 15));
        }
    }


    async _drawBombWithExplosion(playerId) {
        if (this._stopSignal) {  return; }



        this._playAnimationSound('_drawBombWithExplosion', 'place');

        const avatar = document.querySelector(`.spawnedavatar[data-playerid="${playerId}"]`);
        if (!avatar) {

            return;
        }

        const bombPlacement = this._getAttachmentPoint(playerId, 'bottom');
        if (!bombPlacement) {
        }

        const explosionPointX = bombPlacement.x;
        const explosionPointY = bombPlacement.y;


        await this._drawJsonCommands(playerId, this._BOMBA_JSON_URL, 'bottom', 'none', 1.0);
        if (this._stopSignal) return;

        if (!this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

            return;
        }


        await new Promise(resolve => setTimeout(resolve, 2000));
        if (this._stopSignal) return;


        this._playAnimationSound('_drawBombWithExplosion', 'explode');

        await this._explosionBlast(explosionPointX, explosionPointY, 1.2);
    }


    async _lightningZigzagChaser(targetPlayerId) {
        if (this._stopSignal) {  return; }



        this._playAnimationSound('_lightningZigzagChaser', 'charge');

        const currentSocket = this._getGameSocket();
        if (!currentSocket) {

            return;
        }

        const cRect = this._canvas.getBoundingClientRect();

        const getTargetCoordsDynamic = () => {
            const currentAvatar = document.querySelector(`.spawnedavatar[data-playerid="${targetPlayerId}"]`);
            if (!currentAvatar) return null;
            const currentARect = currentAvatar.getBoundingClientRect();
            return {
                x: Math.round((currentARect.left - cRect.left) + (currentARect.width / 2)),
                y: Math.round((currentARect.top - cRect.top) + (currentARect.height / 2))
            };
        };

        const corners = [
            { x: 20, y: 20 },
            { x: Math.round(this._canvas.width - 20), y: 20 },
            { x: 20, y: Math.round(this._canvas.height - 20) },
            { x: Math.round(this._canvas.width - 20), y: Math.round(this._canvas.height - 20) }
        ];
        const startCorner = corners[Math.floor(Math.random() * corners.length)];

        let currentX = startCorner.x;
        let currentY = startCorner.y;

        const totalSegments = 25;
        const zigzagIntensity = 28;
        const lightningColors = ['#FFFFFF', '#E0E6FF', '#6495ED', '#4169E1'];

        let previousAngle = 0;
        const smoothingFactor = 0.3;

        for (let segment = 0; segment < totalSegments; segment++) {
            if (this._stopSignal) {  return; }
            if (!currentSocket || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

                break;
            }

            const progress = segment / totalSegments;
            const targetCoords = getTargetCoordsDynamic();

            if (!targetCoords) {

                break;
            }

            const targetX = targetCoords.x;
            const targetY = targetCoords.y;

            const stepSize = 0.13 + (progress * 0.05);
            const directX = Math.round(currentX + (targetX - currentX) * stepSize);
            const directY = Math.round(currentY + (targetY - currentY) * stepSize);

            const directionX = targetX - currentX;
            const directionY = targetY - currentY;
            const distance = Math.sqrt(directionX * directionX + directionY * directionY);

            if (distance > 8) {
                const perpX = -directionY / distance;
                const perpY = directionX / distance;

                const baseZigzag = Math.sin(segment * 0.8) * zigzagIntensity * (1 - progress * 0.6);
                const noiseZigzag = (Math.random() - 0.5) * 15 * (1 - progress * 0.3);
                const smoothedZigzag = baseZigzag + noiseZigzag;

                const currentAngle = Math.atan2(directionY, directionX);
                const angleDiff = currentAngle - previousAngle;
                const smoothedAngle = previousAngle + angleDiff * smoothingFactor;
                previousAngle = smoothedAngle;

                const finalZigzag = smoothedZigzag * Math.sin(progress * Math.PI);

                const nextX = Math.round(directX + perpX * finalZigzag);
                const nextY = Math.round(directY + perpY * finalZigzag);

                const segmentLayers = [];

                for (let layer = 0; layer < 3; layer++) {
                    const colorIndex = (segment + layer) % lightningColors.length;
                    const color = lightningColors[colorIndex];
                    const thickness = Math.max(1, 7 - layer * 2);

                    const offsetX = Math.round((Math.random() - 0.5) * (4 - layer));
                    const offsetY = Math.round((Math.random() - 0.5) * (4 - layer));

                    segmentLayers.push({
                        startX: currentX + offsetX,
                        startY: currentY + offsetY,
                        endX: nextX + offsetX,
                        endY: nextY + offsetY,
                        color: color,
                        thickness: thickness
                    });
                }

                segmentLayers.forEach(layer => {
                    this._sendDrawCommand(
                        layer.startX,
                        layer.startY,
                        layer.endX,
                        layer.endY,
                        layer.color,
                        layer.thickness
                    );
                });

                await new Promise(resolve => setTimeout(resolve, 12));
                if (this._stopSignal) return;

                if (segment % 4 === 0 && progress < 0.8) {
                    const sparkAngle = currentAngle + (Math.random() - 0.5) * Math.PI * 0.5;
                    const sparkDistance = 15 + Math.random() * 10;
                    const sparkX = Math.round(nextX + Math.cos(sparkAngle) * sparkDistance);
                    const sparkY = Math.round(nextY + Math.sin(sparkAngle) * sparkDistance);

                    this._sendDrawCommand(nextX, nextY, sparkX, sparkY, '#E0E6FF', 1);

                    await new Promise(resolve => setTimeout(resolve, 8));
                    if (this._stopSignal) return;
                }

                currentX = directX;
                currentY = directY;
            } else {
                const finalStepX = Math.round(currentX + (targetX - currentX) * 0.3);
                const finalStepY = Math.round(currentY + (targetY - currentY) * 0.3);

                this._sendDrawCommand(currentX, currentY, finalStepX, finalStepY, '#FFFFFF', 5);
                await new Promise(resolve => setTimeout(resolve, 8));
                if (this._stopSignal) return;
                this._sendDrawCommand(currentX, currentY, finalStepX, finalStepY, '#E0E6FF', 3);

                currentX = targetX;
                currentY = targetY;
                break;
            }

            await new Promise(resolve => setTimeout(resolve, 85));
        }

        if (currentSocket && !this._stopSignal && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            const targetCoords = getTargetCoordsDynamic();
            if (targetCoords) {
                this._playAnimationSound('_lightningZigzagChaser', 'impact');
                await this._lightningImpact(targetCoords.x, targetCoords.y);
            } else {

            }
        }
    }

    async _lightningImpact(centerX, centerY) {
        if (this._stopSignal) {  return; }
        const impactSteps = 15;
        const maxRadius = 50;



        for (let step = 0; step < impactSteps; step++) {
            if (this._stopSignal) {  return; }
            if (!this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

                return;
            }

            const progress = step / impactSteps;
            const currentRadius = Math.round(maxRadius * progress);
            const raysThisStep = 8;

            for (let ray = 0; ray < raysThisStep; ray++) {
                const angle = (ray / raysThisStep) * 2 * Math.PI + Math.random() * 0.3;
                const rayLength = Math.round(currentRadius + Math.random() * 18);

                const endX = centerX + rayLength * Math.cos(angle);
                const endY = centerY + rayLength * Math.sin(angle);

                const colors = ['#FFFFFF', '#E0E6FF', '#6495ED'];
                const color = colors[Math.floor(Math.random() * colors.length)];
                const thickness = Math.max(1, 6 - progress * 4);

                this._sendDrawCommand(centerX, centerY, midX, midY, color, thickness);
                this._sendDrawCommand(midX, midY, endX, endY, color, thickness * 0.7);
            }
            await new Promise(resolve => setTimeout(resolve, 85));
        }
    }


    async _circularFireAura(targetPlayerId, duration = 500) {
        if (this._stopSignal) {  return; }

        this._playAnimationSound('_circularFireAura', 'ignite');

        const currentSocket = this._getGameSocket();
        if (!currentSocket) {

            return;
        }

        const cRect = this._canvas.getBoundingClientRect();

        const getCenterCoords = () => {
            const currentAvatar = document.querySelector(`.spawnedavatar[data-playerid="${targetPlayerId}"]`);
            if (!currentAvatar) return null;
            const currentARect = currentAvatar.getBoundingClientRect();
            return {
                x: Math.floor((currentARect.left - cRect.left) + (currentARect.width / 2)),
                y: Math.floor((currentARect.top - cRect.top) + (currentARect.height / 2))
            };
        };

        const minRadius = 30;
        const maxRadius = 90;
        const ringCount = 5;
        const flamesPerRing = 20;

        const fireGradient = [
            '#FFFF99', '#FFCC00', '#FF9900', '#FF6600', '#FF3300', '#CC0000'
        ];

        const startTime = Date.now();
        let frame = 0;



        while (Date.now() - startTime < duration) {
            if (this._stopSignal) {  return; }
            if (!currentSocket || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

                break;
            }

            frame++;

            const currentCenter = getCenterCoords();
            if (!currentCenter) {

                return;
            }
            const centerX = currentCenter.x;
            const centerY = currentCenter.y;

            for (let ring = 0; ring < ringCount; ring++) {
                if (this._stopSignal) return;
                const ringProgress = ring / ringCount;
                const ringRadius = minRadius + (maxRadius - minRadius) * ringProgress;

                const colorIndex = Math.min(ring, fireGradient.length - 1);
                const ringColor = fireGradient[colorIndex];

                for (let flameBatch = 0; flameBatch < flamesPerRing; flameBatch += 4) {
                    if (this._stopSignal) return;
                    for (let flame = flameBatch; flame < Math.min(flameBatch + 4, flamesPerRing); flame++) {
                        if (this._stopSignal) return;
                        const baseAngle = (flame / flamesPerRing) * 2 * Math.PI;

                        const timeOffset = frame * 0.08 + ring * 0.4;
                        const flameVariation =
                            Math.sin(baseAngle * 4 + timeOffset) * 8 +
                            Math.sin(baseAngle * 7 + timeOffset * 1.3) * 5 +
                            Math.cos(baseAngle * 3 + timeOffset * 0.7) * 6;

                        const actualRadius = ringRadius + flameVariation;

                        const flameX = Math.floor(centerX + actualRadius * Math.cos(baseAngle));
                        const flameY = Math.floor(centerY + actualRadius * Math.sin(baseAngle));

                        const innerRadius = ringRadius * 0.65;
                        const innerX = Math.floor(centerX + innerRadius * Math.cos(baseAngle));
                        const innerY = Math.floor(centerY + innerRadius * Math.sin(baseAngle));

                        const flickerIntensity = 0.6 + 0.4 * Math.sin(frame * 0.12 + flame * 0.6);

                        if (flickerIntensity > 0.7) {
                            const thickness = Math.max(1, 5 - ringProgress * 3 + Math.random() * 2);

                            this._sendDrawCommand(innerX, innerY, flameX, flameY, ringColor, thickness);

                            await new Promise(resolve => setTimeout(resolve, 8));
                            if (this._stopSignal) return;

                            if (ring === ringCount - 1 && Math.random() < 0.15) {
                                const sparkDistance = actualRadius + Math.random() * 15;
                                const sparkX = Math.floor(centerX + sparkDistance * Math.cos(baseAngle));
                                const sparkY = Math.floor(centerY + sparkDistance * Math.sin(baseAngle));

                                this._sendDrawCommand(flameX, flameY, sparkX, sparkY, '#FFCC00', 1);

                                await new Promise(resolve => setTimeout(resolve, 12));
                                if (this._stopSignal) return;
                            }
                        }
                    }
                    await new Promise(resolve => setTimeout(resolve, 25));
                    if (this._stopSignal) return;
                }

                if (frame % 3 === 0) {
                    const connectionBatches = Math.ceil((flamesPerRing / 2) / 2);

                    for (let connBatch = 0; connBatch < connectionBatches; connBatch++) {
                        if (this._stopSignal) return;
                        const startConn = connBatch * 2;
                        const endConn = Math.min(startConn + 2, flamesPerRing / 2);

                        for (let connection = startConn; connection < endConn; connection++) {
                            if (this._stopSignal) return;
                            const angle1 = (connection * 2 / flamesPerRing) * 2 * Math.PI;
                            const angle2 = ((connection * 2 + 1) / flamesPerRing) * 2 * Math.PI;

                            const x1 = Math.floor(centerX + ringRadius * Math.cos(angle1));
                            const y1 = Math.floor(centerY + ringRadius * Math.sin(angle1));
                            const x2 = Math.floor(centerX + ringRadius * Math.cos(angle2));
                            const y2 = Math.floor(centerY + ringRadius * Math.sin(angle2));

                            this._sendDrawCommand(x1, y1, x2, y2, ringColor, Math.max(1, 4 - ringProgress * 2));

                            await new Promise(resolve => setTimeout(resolve, 15));
                            if (this._stopSignal) return;
                        }

                        if (connBatch < connectionBatches - 1) {
                            await new Promise(resolve => setTimeout(resolve, 30));
                            if (this._stopSignal) return;
                        }
                    }
                }

                await new Promise(resolve => setTimeout(resolve, 80));
                if (this._stopSignal) return;
            }

            await new Promise(resolve => setTimeout(resolve, 150));
        }

        if (currentSocket && !this._stopSignal && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            const currentCenter = getCenterCoords();
            if(currentCenter) {
                await this._fireAuraFadeOutUltraOptimized(currentCenter.x, currentCenter.y, maxRadius);
            } else {

            }
        }
    }

    async _fireAuraFadeOutUltraOptimized(centerX, centerY, radius) {
        if (this._stopSignal) {  return; }
        const fadeSteps = 15;

        centerX = Math.floor(centerX);
        centerY = Math.floor(centerY);



        for (let step = fadeSteps; step > 0; step--) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

                break;
            }

            const fadeIntensity = step / fadeSteps;
            const currentRadius = radius * fadeIntensity;
            const rings = Math.max(1, Math.floor(4 * fadeIntensity));

            for (let ring = 0; ring < rings; ring++) {
                if (this._stopSignal) return;
                const ringRadius = currentRadius * (0.4 + ring * 0.2);
                const segments = Math.max(8, Math.floor(16 * fadeIntensity));

                for (let segBatch = 0; segBatch < segments; segBatch += 3) {
                    if (this._stopSignal) return;
                    for (let segment = segBatch; segment < Math.min(segBatch + 3, segments); segment++) {
                        const angle1 = (segment / segments) * 2 * Math.PI;
                        const angle2 = ((segment + 1) / segments) * 2 * Math.PI;

                        const x1 = Math.floor(centerX + ringRadius * Math.cos(angle1));
                        const y1 = Math.floor(centerY + ringRadius * Math.sin(angle1));
                        const x2 = Math.floor(centerX + ringRadius * Math.cos(angle2));
                        const y2 = Math.floor(centerY + ringRadius * Math.sin(angle2));

                        const color = ring < 2 ? '#FF6600' : '#CC0000';
                        const thickness = Math.max(1, fadeIntensity * 4);

                        const r = parseInt(color.substr(1, 2), 16);
                        const g = parseInt(color.substr(3, 2), 16);
                        const b = parseInt(color.substr(5, 2), 16);
                        const fadedColor = `rgba(${r}, ${g}, ${b}, ${fadeIntensity})`;

                        this._sendDrawCommand(x1, y1, x2, y2, fadedColor, thickness);

                        await new Promise(resolve => setTimeout(resolve, 20));
                        if (this._stopSignal) return;
                    }

                    await new Promise(resolve => setTimeout(resolve, 35));
                    if (this._stopSignal) return;
                }

                await new Promise(resolve => setTimeout(resolve, 50));
                if (this._stopSignal) return;
            }

            await new Promise(resolve => setTimeout(resolve, 120));
        }

        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 500));
        if (this._stopSignal) return;
        this._sendDrawCommand(centerX - 20, centerY, centerX + 20, centerY, '#FFFFFF', 8);
        await new Promise(resolve => setTimeout(resolve, 300));
        if (this._stopSignal) return;
        this._sendDrawCommand(centerX, centerY - 20, centerX, centerY + 20, '#FFFFFF', 8);
    }

    async _pistolShootEffect(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_pistolShootEffect', 'reload');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        const ownAvatar = document.querySelector(`.spawnedavatar[data-playerid="${ownPlayerId}"]`);
        if (!ownAvatar) {

            return;
        }

        const targetAvatar = document.querySelector(`.spawnedavatar[data-playerid="${targetPlayerId}"]`);
        if (!targetAvatar) {

            return;
        }

        const pistolAttachPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        if (!pistolAttachPoint) {
        }

        const muzzleOffsetX = 47;
        const muzzleOffsetY = -18;

        const muzzleX = pistolAttachPoint.x + muzzleOffsetX;
        const muzzleY = pistolAttachPoint.y + muzzleOffsetY;


        await this._drawJsonCommands(ownPlayerId, this._PISTOLA_JSON_URL, 'grip_right', 'right', 1.0);
        if (this._stopSignal) return;

        if (!this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

            return;
        }


        await new Promise(r => setTimeout(r, 800));
        if (this._stopSignal) return;

        const targetCoords = this._getTargetCoords(targetPlayerId);
        if (!targetCoords) {

            return;
        }

        this._playAnimationSound('_pistolShootEffect', 'shoot');

        await this._fireBullet(muzzleX, muzzleY, targetCoords.x, targetCoords.y);
    }

    async _fireBullet(startX, startY, targetX, targetY) {
        if (this._stopSignal) {  return; }


        const bulletSteps = 25;
        const bulletSpeed = 1 / bulletSteps;

        const bulletColor = '#FFD700';
        const trailColor = '#FFA500';

        for (let step = 0; step <= bulletSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

                break;
            }

            const progress = step * bulletSpeed;

            const bulletX = startX + (targetX - startX) * progress;
            const bulletY = startY + (targetY - startY) * progress;

            const bulletSize = 3;
            this._sendDrawCommand(
                bulletX - bulletSize, bulletY - bulletSize,
                bulletX + bulletSize, bulletY + bulletSize,
                bulletColor, 4
            );

            if (step > 0) {
                const prevProgress = (step - 1) * bulletSpeed;
                const prevBulletX = startX + (targetX - startX) * prevProgress;
                const prevBulletY = startY + (targetY - startY) * prevProgress;

                this._sendDrawCommand(prevBulletX, prevBulletY, bulletX, bulletY, trailColor, 2);
            }

            await new Promise(resolve => setTimeout(resolve, 30));
        }

        if (this._getGameSocket() && !this._stopSignal && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            this._playAnimationSound('_pistolShootEffect', 'impact');
            await this._bulletImpact(targetX, targetY);
        }
    }

    async _bulletImpact(x, y) {
        if (this._stopSignal) {  return; }
        const impactSteps = 15;
        const impactRadius = 25;
        const impactColors = ['#FF4500', '#FFD700', '#FF6347', '#FFA500'];



        for (let step = 0; step < impactSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;

            const progress = step / impactSteps;
            const currentRadius = impactRadius * progress;
            const sparkCount = 8;

            for (let spark = 0; spark < sparkCount; spark++) {
                const angle = (spark / sparkCount) * 2 * Math.PI + Math.random() * 0.3;
                const sparkDistance = currentRadius + Math.random() * 15;

                const endX = x + sparkDistance * Math.cos(angle);
                const endY = y + sparkDistance * Math.sin(angle);

                const colorIndex = Math.floor(Math.random() * impactColors.length);
                const color = impactColors[colorIndex];
                const thickness = Math.max(1, 4 - progress * 3);

                this._sendDrawCommand(x, y, endX, endY, color, thickness);
            }
            await new Promise(resolve => setTimeout(resolve, 60));
        }
    }

    async _spaceRocketChaser(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_spaceRocketChaser', 'launch');

        const currentSocket = this._getGameSocket();
        if (!currentSocket) {

            return;
        }

        const cRect = this._canvas.getBoundingClientRect();

        const getTargetCoordsDynamic = () => {
            const currentAvatar = document.querySelector(`.spawnedavatar[data-playerid="${targetPlayerId}"]`);
            if (!currentAvatar) return null;
            const currentARect = currentAvatar.getBoundingClientRect();
            return {
                x: Math.round((currentARect.left - cRect.left) + (currentARect.width / 2)),
                y: Math.round((currentARect.top - cRect.top) + (currentARect.height / 2))
            };
        };

        const spawnSides = [
            { x: 20, y: Math.round(Math.random() * this._canvas.height) },
            { x: Math.round(this._canvas.width - 20), y: Math.round(Math.random() * this._canvas.height) },
            { x: Math.round(Math.random() * this._canvas.width), y: 20 },
            { x: Math.round(this._canvas.width - 20), y: Math.round(this._canvas.height - 20) }
        ];
        const spawnPoint = spawnSides[Math.floor(Math.random() * spawnSides.length)];

        let rocketX = spawnPoint.x;
        let rocketY = spawnPoint.y;

        const totalSteps = 80;
        const rocketSpeed = 0.08;

        for (let step = 0; step < totalSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

            }

            const targetCoords = getTargetCoordsDynamic();
            if (!targetCoords) {

                break;
            }

            const targetX = targetCoords.x;
            const targetY = targetCoords.y;

            const directionX = targetX - rocketX;
            const directionY = targetY - rocketY;
            const distance = Math.sqrt(directionX * directionX + directionY * directionY);

            if (distance < 15) {

                this._playAnimationSound('_spaceRocketChaser', 'explode');
                await this._rocketExplosion(rocketX, rocketY);
                return;
            }

            const normalizedX = directionX / distance;
            const normalizedY = directionY / distance;

            const nextX = rocketX + normalizedX * distance * rocketSpeed;
            const nextY = rocketY + normalizedY * distance * rocketSpeed;

            const angle = Math.atan2(directionY, directionX);

            await this._drawSpaceRocket(rocketX, rocketY, nextX, nextY, angle, step);
            if (this._stopSignal) return;

            rocketX = nextX;
            rocketY = nextY;

            const baseDelay = 45;
            const progress = step / totalSteps;
            const speedFactor = 1 + progress;
            await new Promise(resolve => setTimeout(resolve, baseDelay / speedFactor));
        }

        if (this._getGameSocket() && !this._stopSignal && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            const finalTarget = getTargetCoordsDynamic();
            if (finalTarget) {

                this._playAnimationSound('_spaceRocketChaser', 'explode');
                await this._rocketExplosion(finalTarget.x, finalTarget.y);
            } else {

            }
        }
    }

    async _drawSpaceRocket(currentX, currentY, nextX, nextY, angle, step) {
        if (this._stopSignal) return;
        const rocketSize = 12;
        const thrusterLength = 15;

        const rocketColors = {
            body: '#C0C0C0',
            nose: '#FF6B6B',
            thruster: '#FF4500',
            flame: '#FFD700'
        };

        const cosA = Math.cos(angle);
        const sinA = Math.sin(angle);

        const noseX = nextX + cosA * rocketSize;
        const noseY = nextY + sinA * rocketSize;

        const bodyStartX = nextX - cosA * (rocketSize * 0.3);
        const bodyStartY = nextY - sinA * (rocketSize * 0.3);

        const perpX = -sinA * (rocketSize * 0.4);
        const perpY = cosA * (rocketSize * 0.4);

        const finLeft1X = bodyStartX + perpX;
        const finLeft1Y = bodyStartY + perpY;
        const finRight1X = bodyStartX - perpX;
        const finRight1Y = bodyStartY - perpY;

        const tailX = nextX - cosA * rocketSize;
        const tailY = nextY - sinA * rocketSize;

        this._sendDrawCommand(bodyStartX, bodyStartY, noseX, noseY, rocketColors.body, 4);
        this._sendDrawCommand(bodyStartX, bodyStartY, noseX, noseY, rocketColors.nose, 2);

        this._sendDrawCommand(bodyStartX, bodyStartY, finLeft1X, finLeft1Y, rocketColors.body, 2);
        this._sendDrawCommand(bodyStartX, bodyStartY, finRight1X, finRight1Y, rocketColors.body, 2);

        const flameIntensity = 0.7 + 0.3 * Math.sin(step * 0.3);
        if (flameIntensity > 0.8) {
            const flameLength = thrusterLength * flameIntensity;
            const flameEndX = tailX - cosA * flameLength;
            const flameEndY = tailY - sinA * flameLength;

            this._sendDrawCommand(tailX, tailY, flameEndX, flameEndY, rocketColors.flame, 3);

            const flame2X = flameEndX - cosA * 5 + perpX * 0.3;
            const flame2Y = flameEndY - sinA * 5 + perpY * 0.3;
            const flame3X = flameEndX - cosA * 5 - perpX * 0.3;
            const flame3Y = flameEndY - sinA * 5 - perpY * 0.3;

            this._sendDrawCommand(tailX, tailY, flame2X, flame2Y, rocketColors.thruster, 2);
            this._sendDrawCommand(tailX, tailY, flame3X, flame3Y, rocketColors.thruster, 2);
        }

        this._sendDrawCommand(currentX, currentY, nextX, nextY, '#87CEEB', 1);
    }

    async _rocketExplosion(centerX, centerY) {
        if (this._stopSignal) {  return; }
        const explosionSteps = 20;
        const maxRadius = 70;

        centerX = Math.floor(centerX);
        centerY = Math.floor(centerY);



        const fragmentsPerStep = 12;
        const explosionColors = ['#FF4500', '#FFD700', '#FF6B6B'];
        const preCalculatedAngles = [];

        for (let i = 0; i < fragmentsPerStep; i++) {
            preCalculatedAngles.push((i / fragmentsPerStep) * 2 * Math.PI);
        }

        for (let step = 0; step < explosionSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

                break;
            }

            const progress = step / explosionSteps;
            const currentRadius = Math.floor(maxRadius * progress);

            for (let colorIdx = 0; colorIdx < explosionColors.length; colorIdx++) {
                if (this._stopSignal) return;
                const color = explosionColors[colorIdx];
                const commandBatch = [];

                for (let fragment = 0; fragment < fragmentsPerStep; fragment++) {
                    if (fragment % explosionColors.length !== colorIdx) continue;

                    const angle = preCalculatedAngles[fragment] + Math.random() * 0.3;
                    const fragmentDistance = Math.floor(currentRadius + Math.random() * 20);

                    const endX = Math.floor(centerX + fragmentDistance * Math.cos(angle));
                    const endY = Math.floor(centerY + fragmentDistance * Math.sin(angle));

                    const thickness = Math.max(1, Math.floor(6 - progress * 4));

                    commandBatch.push({
                        startX: centerX,
                        startY: centerY,
                        endX,
                        endY,
                        thickness
                    });
                }

                commandBatch.forEach(cmd => {
                    this._sendDrawCommand(cmd.startX, cmd.startY, cmd.endX, cmd.endY, color, cmd.thickness);
                });

                if (colorIdx < explosionColors.length - 1) {
                    await new Promise(resolve => setTimeout(resolve, 25));
                    if (this._stopSignal) return;
                }
            }

            if (step % 4 === 0 && progress < 0.6) {
                const sparkCount = 3;
                for (let spark = 0; spark < sparkCount; spark++) {
                    const sparkAngle = (spark / sparkCount) * 2 * Math.PI;
                    const sparkRadius = Math.floor(currentRadius * 1.1);
                    const sparkX = Math.floor(centerX + sparkRadius * Math.cos(sparkAngle));
                    const sparkY = Math.floor(centerY + sparkRadius * Math.sin(sparkAngle));

                    const sparkEndX = Math.floor(sparkX + (Math.random() - 0.5) * 8);
                    const sparkEndY = Math.floor(sparkY + (Math.random() - 0.5) * 8);

                    this._sendDrawCommand(sparkX, sparkY, sparkEndX, sparkEndY, '#FFFF00', 1);
                }

                await new Promise(resolve => setTimeout(resolve, 30));
                if (this._stopSignal) return;
            }

            const baseDelay = 80 + progress * 40;
            await new Promise(resolve => setTimeout(resolve, Math.max(baseDelay, 100)));
        }

        if (!this._stopSignal) {
            await this._ultraSimplifiedFlash(centerX, centerY);
        }
    }

    async _ultraSimplifiedFlash(centerX, centerY) {
        if (this._stopSignal) return;
        const flashSteps = 6;
        const flashColors = ['#FFFFFF', '#FFD700'];

        for (let step = 0; step < flashSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;

            const progress = step / flashSteps;
            const intensity = 1 - progress;
            const flashRadius = Math.floor(50 * intensity);

            const color = flashColors[step % flashColors.length];
            const rayCount = 8;

            const rayBatch = [];
            for (let ray = 0; ray < rayCount; ray++) {
                const rayAngle = (ray / rayCount) * 2 * Math.PI;
                const rayEndX = Math.floor(centerX + flashRadius * Math.cos(rayAngle));
                const rayEndY = Math.floor(centerY + flashRadius * Math.sin(rayAngle));

                rayBatch.push({ endX: rayEndX, endY: rayEndY });
            }

            rayBatch.forEach(ray => {
                this._sendDrawCommand(centerX, centerY, ray.endX, ray.endY, color, Math.max(1, 4 * intensity));
            });

            await new Promise(resolve => setTimeout(resolve, 120));
        }
    }

    async _flashlightStarChaser(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_flashlightStarChaser', 'launch');

        const currentSocket = this._getGameSocket();
        if (!currentSocket) {

            return;
        }

        const cRect = this._canvas.getBoundingClientRect();

        const getTargetCoordsDynamic = () => {
            const currentAvatar = document.querySelector(`.spawnedavatar[data-playerid="${targetPlayerId}"]`);
            if (!currentAvatar) return null;
            const currentARect = currentAvatar.getBoundingClientRect();
            return {
                x: Math.round((currentARect.left - cRect.left) + (currentARect.width / 2)),
                y: Math.round((currentARect.top - cRect.top) + (currentARect.height / 2))
            };
        };

        const spawnCorners = [
            { x: 30, y: 30 },
            { x: Math.round(this._canvas.width - 30), y: 30 },
            { x: 30, y: Math.round(this._canvas.height - 30) },
            { x: Math.round(this._canvas.width - 30), y: Math.round(this._canvas.height - 30) }
        ];

        const spawnPoint = spawnCorners[Math.floor(Math.random() * spawnCorners.length)];
        let starX = spawnPoint.x;
        let starY = spawnPoint.y;

        const totalSteps = 25;
        const starSpeed = 0.2;
        const baseDelay = 110;

        for (let step = 0; step < totalSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

            }

            const targetCoords = getTargetCoordsDynamic();
            if (!targetCoords) {

                break;
            }

            const directionX = targetCoords.x - starX;
            const directionY = targetCoords.y - starY;
            const distance = Math.sqrt(directionX * directionX + directionY * directionY);

            if (distance < 25) {

                this._playAnimationSound('_flashlightStarChaser', 'explode');
                await this._veryOptimizedExplosion(starX, starY);
                return;
            }

            const normalizedX = directionX / distance;
            const normalizedY = directionY / distance;

            starX = starX + normalizedX * distance * starSpeed;
            starY = starY + normalizedY * distance * starSpeed;

            await this._drawVeryOptimizedStar(starX, starY, step);
            if (this._stopSignal) return;

            const progress = step / totalSteps;
            const adaptiveDelay = baseDelay + (progress * 30);
            await new Promise(resolve => setTimeout(resolve, adaptiveDelay));
        }

        if (this._getGameSocket() && !this._stopSignal && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            const finalTarget = getTargetCoordsDynamic();
            if (finalTarget) {

                this._playAnimationSound('_flashlightStarChaser', 'explode');
                await this._veryOptimizedExplosion(finalTarget.x, finalTarget.y);
            } else {

            }
        }
    }

    async _drawVeryOptimizedStar(x, y, step) {
        if (this._stopSignal) return;
        const colors = ['#FFFFFF', '#9370DB', '#4169E1'];

        const coreSize = 6;
        this._sendDrawCommand(x - coreSize, y, x + coreSize, y, colors[0], 4);
        this._sendDrawCommand(x, y - coreSize, x, y + coreSize, colors[0], 4);

        const rayLength = 12;
        for (let ray = 0; ray < 3; ray++) {
            const angle = (ray / 3) * Math.PI * 2 + step * 0.15;
            const endX = x + rayLength * Math.cos(angle);
            const endY = y + rayLength * Math.sin(angle);
            this._sendDrawCommand(x, y, endX, endY, colors[1], 2);
        }

        const auraSize = 8;
        const auraAngle = step * 0.1;
        const auraX = x + auraSize * Math.cos(auraAngle);
        const auraY = y + auraSize * Math.sin(auraAngle);
        this._sendDrawCommand(x, y, auraX, auraY, colors[2], 1);
    }

    async _veryOptimizedExplosion(centerX, centerY) {
        if (this._stopSignal) {  return; }


        await this._veryOptimizedFlash(centerX, centerY);
        if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) return;

        await this._veryOptimizedWave(centerX, centerY);
        if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) return;
    }

    async _veryOptimizedFlash(centerX, centerY) {
        if (this._stopSignal) return;
        const flashSteps = 5;
        const maxRadius = 35;
        const colors = ['#FFFFFF', '#E0E6FF'];

        for (let step = 0; step < flashSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;

            const progress = step / flashSteps;
            const radius = maxRadius * (1 - progress * 0.6);
            const intensity = 1 - progress;

            const rayCount = 6;
            for (let ray = 0; ray < rayCount; ray++) {
                const angle = (ray / rayCount) * 2 * Math.PI;
                const rayLength = radius * intensity;

                const endX = centerX + rayLength * Math.cos(angle);
                const endY = centerY + rayLength * Math.sin(angle);

                const color = colors[step % colors.length];
                const thickness = Math.max(1, intensity * 4);

                this._sendDrawCommand(centerX, centerY, endX, endY, color, thickness);
            }
            await new Promise(resolve => setTimeout(resolve, 100));
        }
    }

    async _veryOptimizedWave(centerX, centerY) {
        if (this._stopSignal) return;
        const waveSteps = 10;
        const maxRadius = 70;
        const color = '#4169E1';

        for (let step = 0; step < waveSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;

            const progress = step / waveSteps;
            const waveRadius = maxRadius * progress;
            const intensity = 1 - progress;

            const segments = 8;
            for (let seg = 0; seg < segments; seg++) {
                const angle1 = (seg / segments) * 2 * Math.PI;
                const angle2 = ((seg + 1) / segments) * 2 * Math.PI;

                const x1 = centerX + waveRadius * Math.cos(angle1);
                const y1 = centerY + waveRadius * Math.sin(angle1);
                const x2 = centerX + waveRadius * Math.cos(angle2);
                const y2 = centerY + waveRadius * Math.sin(angle2);

                const thickness = Math.max(1, intensity * 3);
                this._sendDrawCommand(x1, y1, x2, y2, color, thickness);
            }
            await new Promise(resolve => setTimeout(resolve, 120));
        }
    }

    async _drawArrowChaser(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawArrowChaser', 'draw');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._ARCO_JSON_URL, 'grip_right', 'right', 1.0);
        if (this._stopSignal) return;

        const bowAttachPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        if (!bowAttachPoint) {
        }

        const arrowLaunchOffsetX = 50;
        const arrowLaunchOffsetY = 0;

        const arrowOrigin = {
            x: bowAttachPoint.x + arrowLaunchOffsetX,
            y: bowAttachPoint.y + arrowLaunchOffsetY
        };

        const totalSteps = 40;
        const arrowSpeedFactor = 0.1;
        const wobbleIntensity = 15;
        const arrowColor = '#A52A2A';
        const featherColor = '#FFFFFF';

        let currentX = arrowOrigin.x;
        let currentY = arrowOrigin.y;


        this._playAnimationSound('_drawArrowChaser', 'shoot');

        for (let step = 0; step < totalSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {

            }

            const targetCoords = this._getTargetCoords(targetPlayerId);
            if (!targetCoords) {
            }

            const directionX = targetCoords.x - currentX;
            const directionY = targetCoords.y - currentY;
            const dist = this._distance(currentX, currentY, targetCoords.x, targetCoords.y);

            if (dist < 15) {
                this._playAnimationSound('_drawArrowChaser', 'impact');
                await this._bulletImpact(currentX, currentY);
                return;
            }

            const normalizedX = directionX / dist;
            const normalizedY = directionY / dist;

            const wobbleOffset = Math.sin(step * 0.8) * wobbleIntensity * (1 - step / totalSteps);
            const perpX = -normalizedY;
            const perpY = normalizedX;

            const nextX = currentX + normalizedX * dist * arrowSpeedFactor + perpX * wobbleOffset;
            const nextY = currentY + normalizedY * dist * arrowSpeedFactor + perpY * wobbleOffset;

            const angle = Math.atan2(directionY, directionX);

            await this._drawArrow(currentX, currentY, nextX, nextY, angle, arrowColor, featherColor);
            if (this._stopSignal) return;

            currentX = nextX;
            currentY = nextY;

            await new Promise(resolve => setTimeout(resolve, 50));
        }

        const finalTarget = this._getTargetCoords(targetPlayerId);
        if (finalTarget && this._getGameSocket() && !this._stopSignal && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            this._playAnimationSound('_drawArrowChaser', 'impact');
            await this._bulletImpact(finalTarget.x, finalTarget.y);
        }
    }

    async _drawArrow(x1, y1, x2, y2, angle, color, featherColor) {
        if (this._stopSignal) return;
        const arrowHeadLength = 10;
        const featherLength = 8;
        const featherAngleOffset = Math.PI / 6;

        this._sendDrawCommand(x1, y1, x2, y2, color, 2);

        const tipX1 = x2 - arrowHeadLength * Math.cos(angle - Math.PI / 6);
        const tipY1 = y2 - arrowHeadLength * Math.sin(angle - Math.PI / 6);
        const tipX2 = x2 - arrowHeadLength * Math.cos(angle + Math.PI / 6);
        const tipY2 = y2 - arrowHeadLength * Math.sin(angle + Math.PI / 6);

        this._sendDrawCommand(x2, y2, tipX1, tipY1, color, 2);
        this._sendDrawCommand(x2, y2, tipX2, tipY2, color, 2);

        const tailX = x1 - (Math.cos(angle) * 5);
        const tailY = y1 - (Math.sin(angle) * 5);

        const feather1X = tailX - featherLength * Math.cos(angle + featherAngleOffset);
        const feather1Y = tailY - featherLength * Math.sin(angle + featherAngleOffset);
        const feather2X = tailX - featherLength * Math.cos(angle - featherAngleOffset);
        const feather2Y = tailY - featherLength * Math.sin(angle - featherAngleOffset);

        this._sendDrawCommand(tailX, tailY, feather1X, feather1Y, featherColor, 1);
        this._sendDrawCommand(tailX, tailY, feather2X, feather2Y, featherColor, 1);
    }

    async _drawShotgunBlast(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawShotgunBlast', 'reload');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._ESCOPETA_JSON_URL, 'grip_right', 'right', 1.0);
        if (this._stopSignal) return;

        await new Promise(resolve => setTimeout(resolve, 300));
        if (this._stopSignal) return;

        const shotgunAttachPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        if (!shotgunAttachPoint) {
        }

        const portalCenter = {
            x: shotgunAttachPoint.x + 80,
            y: shotgunAttachPoint.y + -20
        };

        const targetCoords = this._getTargetCoords(targetPlayerId);
        if (!targetCoords) {
        }


        this._playAnimationSound('_drawShotgunBlast', 'whoosh');

        await this._openMagicPortalUltraDelayed(portalCenter.x, portalCenter.y);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 500));
        if (this._stopSignal) return;

        this._playAnimationSound('_drawShotgunBlast', 'shoot');
        await this._launchMagicProjectilesUltraDelayed(portalCenter, targetCoords);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 500));
        if (this._stopSignal) return;
        await this._closeMagicPortalUltraDelayed(portalCenter.x, portalCenter.y);
    }

    async _openMagicPortalUltraDelayed(centerX, centerY) {
        if (this._stopSignal) return;
        const openingSteps = 20;
        const maxRadius = 50;
        const portalColors = ['#9400D3', '#4B0082', '#8A2BE2', '#9932CC'];
        const starColors = ['#FFD700', '#FFFFFF', '#00FFFF'];

        centerX = Math.floor(centerX);
        centerY = Math.floor(centerY);

        for (let step = 0; step < openingSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;

            const progress = step / openingSteps;
            const currentRadius = maxRadius * Math.sin((progress * Math.PI) / 2);

            for (let colorIdx = 0; colorIdx < portalColors.length; colorIdx += 2) {
                if (this._stopSignal) return;
                const ringSegments = 16;
                for (let segBatch = 0; segBatch < ringSegments; segBatch += 4) {
                    if (this._stopSignal) return;
                    for (let seg = segBatch; seg < Math.min(segBatch + 4, ringSegments); seg++) {
                        if (Math.floor((seg + step) % portalColors.length) !== colorIdx) continue;
                        const angle1 = (seg / ringSegments) * 2 * Math.PI + step * 0.1;
                        const angle2 = ((seg + 1) / ringSegments) * 2 * Math.PI + step * 0.1;
                        const x1 = Math.floor(centerX + currentRadius * Math.cos(angle1));
                        const y1 = Math.floor(centerY + currentRadius * Math.sin(angle1) * 0.7);
                        const x2 = Math.floor(centerX + currentRadius * Math.cos(angle2));
                        const y2 = Math.floor(centerY + currentRadius * Math.sin(angle2) * 0.7);
                        const thickness = Math.max(2, 6 - progress * 2);
                        this._sendDrawCommand(x1, y1, x2, y2, portalColors[colorIdx], thickness);
                    }
                    await new Promise(resolve => setTimeout(resolve, 15));
                    if (this._stopSignal) return;
                }
                await new Promise(resolve => setTimeout(resolve, 25));
                if (this._stopSignal) return;
            }

            if (step > 5) {
                const energyLines = 8;
                for (let lineBatch = 0; lineBatch < energyLines; lineBatch += 2) {
                    if (this._stopSignal) return;
                    for (let line = lineBatch; line < Math.min(lineBatch + 2, energyLines); line++) {
                        const angle = (line / energyLines) * 2 * Math.PI + Math.random() * 0.3;
                        const startRadius = currentRadius * 1.2;
                        const endRadius = currentRadius * 0.3;
                        const startX = Math.floor(centerX + startRadius * Math.cos(angle));
                        const startY = Math.floor(centerY + startRadius * Math.sin(angle) * 0.7);
                        const endX = Math.floor(centerX + endRadius * Math.cos(angle));
                        const endY = Math.floor(centerY + endRadius * Math.sin(angle) * 0.7);
                        const color = starColors[Math.floor(Math.random() * starColors.length)];
                        this._sendDrawCommand(startX, startY, endX, endY, color, 2);
                    }
                    await new Promise(resolve => setTimeout(resolve, 20));
                    if (this._stopSignal) return;
                }
            }

            for (let particle = 0; particle < 3; particle++) {
                if (this._stopSignal) return;
                const particleAngle = Math.random() * 2 * Math.PI;
                const particleRadius = currentRadius * (0.8 + Math.random() * 0.4);
                const px = Math.floor(centerX + particleRadius * Math.cos(particleAngle));
                const py = Math.floor(centerY + particleRadius * Math.sin(particleAngle) * 0.7);
                this._sendDrawCommand(px - 2, py - 2, px + 2, py + 2, '#FFD700', 2);
                if (particle < 2) {
                    await new Promise(resolve => setTimeout(resolve, 10));
                    if (this._stopSignal) return;
                }
            }
            await new Promise(resolve => setTimeout(resolve, 150));
        }
    }

    async _launchMagicProjectilesUltraDelayed(portalCenter, targetCoords) {
        if (this._stopSignal) return;
        const numProjectiles = 5;
        const projectileColors = ['#FF1493', '#00CED1', '#32CD32', '#FFD700', '#FF69B4'];

        for (let i = 0; i < numProjectiles; i++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            await this._launchSingleMagicProjectileUltraDelayed(portalCenter, targetCoords, projectileColors[i], i);
            if (this._stopSignal) return;
            await new Promise(resolve => setTimeout(resolve, 400));
        }
    }

    async _launchSingleMagicProjectileUltraDelayed(startPoint, targetCoords, color, index) {
        if (this._stopSignal) return;
        const totalSteps = 25;
        const sparkTrail = [];
        const offsetAngle = (index - 2) * 0.3;
        const curveIntensity = 30;

        let currentX = Math.floor(startPoint.x);
        let currentY = Math.floor(startPoint.y);

        for (let step = 0; step < totalSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;

            const progress = step / totalSteps;
            const baseX = startPoint.x + (targetCoords.x - startPoint.x) * progress;
            const baseY = startPoint.y + (targetCoords.y - startPoint.y) * progress;
            const curve = Math.sin(progress * Math.PI) * curveIntensity;
            const nextX = Math.floor(baseX + Math.cos(offsetAngle) * curve);
            const nextY = Math.floor(baseY + Math.sin(offsetAngle) * curve - curve * 0.5);

            this._sendDrawCommand(currentX, currentY, nextX, nextY, color, 4);
            await new Promise(resolve => setTimeout(resolve, 15));
            if (this._stopSignal) return;

            const auraRadius = 8;
            const auraSpokes = 6;
            for (let spokeBatch = 0; spokeBatch < auraSpokes; spokeBatch += 2) {
                if (this._stopSignal) return;
                for (let spoke = spokeBatch; spoke < Math.min(spokeBatch + 2, auraSpokes); spoke++) {
                    const spokeAngle = (spoke / auraSpokes) * 2 * Math.PI + step * 0.2;
                    const auraX = Math.floor(nextX + auraRadius * Math.cos(spokeAngle));
                    const auraY = Math.floor(nextY + auraRadius * Math.sin(spokeAngle));
                    this._sendDrawCommand(nextX, nextY, auraX, auraY, color, 1);
                }
                await new Promise(resolve => setTimeout(resolve, 8));
                if (this._stopSignal) return;
            }

            sparkTrail.push({ x: nextX, y: nextY, life: 1.0 });
            if (sparkTrail.length > 8) sparkTrail.shift();
            const trailBatch = 4;
            for (let t = 0; t < sparkTrail.length; t += trailBatch) {
                if (this._stopSignal) return;
                for (let idx = t; idx < Math.min(t + trailBatch, sparkTrail.length); idx++) {
                    const spark = sparkTrail[idx];
                    const trailIntensity = spark.life * (idx / sparkTrail.length);
                    if (trailIntensity > 0.3) {
                        this._sendDrawCommand(spark.x - 1, spark.y - 1, spark.x + 1, spark.y + 1, color, Math.max(1, 3 * trailIntensity));
                    }
                    spark.life -= 0.1;
                }
                if (t + trailBatch < sparkTrail.length) {
                    await new Promise(resolve => setTimeout(resolve, 5));
                    if (this._stopSignal) return;
                }
            }
            currentX = nextX;
            currentY = nextY;
            await new Promise(resolve => setTimeout(resolve, 85));
        }
        if (!this._stopSignal) await this._magicImpactBurstUltraDelayed(currentX, currentY, color);
    }

    async _magicImpactBurstUltraDelayed(x, y, color) {
        if (this._stopSignal) return;
        const burstSteps = 10;
        const burstRadius = 25;
        x = Math.floor(x);
        y = Math.floor(y);

        for (let step = 0; step < burstSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / burstSteps;
            const currentRadius = burstRadius * progress;
            const intensity = 1 - progress;
            const sparkCount = 8;
            for (let spark = 0; spark < sparkCount; spark++) {
                if (this._stopSignal) return;
                const angle = (spark / sparkCount) * 2 * Math.PI + Math.random() * 0.5;
                const sparkDistance = currentRadius + Math.random() * 10;
                const endX = Math.floor(x + sparkDistance * Math.cos(angle));
                const endY = Math.floor(y + sparkDistance * Math.sin(angle));
                this._sendDrawCommand(x, y, endX, endY, color, Math.max(1, 3 * intensity));
                await new Promise(resolve => setTimeout(resolve, 12));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 100));
        }
    }

    async _closeMagicPortalUltraDelayed(centerX, centerY) {
        if (this._stopSignal) return;
        const closingSteps = 15;
        const startRadius = 50;
        centerX = Math.floor(centerX);
        centerY = Math.floor(centerY);

        for (let step = 0; step < closingSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / closingSteps;
            const currentRadius = startRadius * (1 - progress);
            const intensity = 1 - progress;
            const implosionLines = 12;
            for (let lineBatch = 0; lineBatch < implosionLines; lineBatch += 3) {
                if (this._stopSignal) return;
                for (let line = lineBatch; line < Math.min(lineBatch + 3, implosionLines); line++) {
                    const angle = (line / implosionLines) * 2 * Math.PI;
                    const startX = Math.floor(centerX + currentRadius * Math.cos(angle));
                    const startY = Math.floor(centerY + currentRadius * Math.sin(angle) * 0.7);
                    const endRadius = currentRadius * 0.3;
                    const endX = Math.floor(centerX + endRadius * Math.cos(angle));
                    const endY = Math.floor(centerY + endRadius * Math.sin(angle) * 0.7);
                    this._sendDrawCommand(startX, startY, endX, endY, '#9400D3', Math.max(1, 4 * intensity));
                }
                await new Promise(resolve => setTimeout(resolve, 30));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 180));
        }
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 500));
        if (this._stopSignal) return;
        this._sendDrawCommand(centerX - 15, centerY, centerX + 15, centerY, '#FFFFFF', 6);
        await new Promise(resolve => setTimeout(resolve, 100));
        if (this._stopSignal) return;
        this._sendDrawCommand(centerX, centerY - 15, centerX, centerY + 15, '#FFFFFF', 6);
    }

    async _drawGrenadeLauncher(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawGrenadeLauncher', 'reload');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._LANZAGRANADAS_JSON_URL, 'grip_right', 'right', 1.0);
        if (this._stopSignal) return;

        const launcherAttachPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        if (!launcherAttachPoint) {
        }

        const launchPoint = {
            x: launcherAttachPoint.x + 40,
            y: launcherAttachPoint.y - 20
        };

        const targetCoords = this._getTargetCoords(targetPlayerId);
        if (!launchPoint || !targetCoords) {
        }

        const grenadeColor = '#6A5ACD';
        const arcHeight = 80;
        const totalFrames = 40;
        const fuseTimeMs = 2000;

        let grenadeX = launchPoint.x;
        let grenadeY = launchPoint.y;


        this._playAnimationSound('_drawGrenadeLauncher', 'whoosh');

        for (let frame = 0; frame < totalFrames; frame++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = frame / totalFrames;
            const nextX = launchPoint.x + (targetCoords.x - launchPoint.x) * progress;
            const nextY = launchPoint.y + (targetCoords.y - launchPoint.y) * progress - arcHeight * Math.sin(Math.PI * progress);
            this._sendDrawCommand(grenadeX, grenadeY, nextX, nextY, grenadeColor, 3);
            grenadeX = nextX;
            grenadeY = nextY;
            await new Promise(resolve => setTimeout(resolve, 40));
        }
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, fuseTimeMs));
        if (this._stopSignal) return;

        if (this._getGameSocket() && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            this._playAnimationSound('_drawGrenadeLauncher', 'mega-explosion');
            await this._explosionBlast(grenadeX, grenadeY, 1.5);
        }
    }

    async _drawLaserRifleBeam(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawLaserRifleBeam', 'laser-reload');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._RIFLE_JSON_URL, 'grip_right', 'right', 1.0);
        if (this._stopSignal) return;

        const rifleAttachPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        if (!rifleAttachPoint) {
        }

        const barrelTip = {
            x: rifleAttachPoint.x + 60,
            y: rifleAttachPoint.y - 16
        };

        const targetCoords = this._getTargetCoords(targetPlayerId);
        if (!barrelTip || !targetCoords) {
        }


        await this._blueMuzzleBall(barrelTip.x, barrelTip.y);
        if (this._stopSignal) return;

        await new Promise(resolve => setTimeout(resolve, 100));
        if (this._stopSignal) return;

        const laserColorCore = '#FFFFFF';
        const laserColorFringe = '#00FFFF';
        const laserThickness = 6;
        const laserDurationFrames = 15;


        this._playAnimationSound('_drawLaserRifleBeam', 'laser-shot');

        for (let frame = 0; frame < laserDurationFrames; frame++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            this._sendDrawCommand(barrelTip.x, barrelTip.y, targetCoords.x, targetCoords.y, laserColorCore, laserThickness);
            this._sendDrawCommand(barrelTip.x, barrelTip.y, targetCoords.x, targetCoords.y, laserColorFringe, laserThickness * 1.5);
            for (let i = 0; i < 3; i++) {
                const progress = Math.random();
                const sparkX = barrelTip.x + (targetCoords.x - barrelTip.x) * progress + (Math.random() - 0.5) * 5;
                const sparkY = barrelTip.y + (targetCoords.y - barrelTip.y) * progress + (Math.random() - 0.5) * 5;
                this._sendDrawCommand(sparkX, sparkY, sparkX + 1, sparkY + 1, '#FFD700', 1);
            }
            await new Promise(resolve => setTimeout(resolve, 50));
        }
    }

    async _blueMuzzleBall(x, y) {
        if (this._stopSignal) return;
        const steps = 8;
        const maxRadius = 20;
        const colors = ['#87CEEB', '#ADD8E6', '#00BFFF', '#1E90FF'];

        for (let step = 0; step < steps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / steps;
            const currentRadius = maxRadius * Math.sin(Math.PI * progress);
            const intensity = 1 - progress;
            const coreRays = 8;
            for (let ray = 0; ray < coreRays; ray++) {
                const angle = (ray / coreRays) * 2 * Math.PI;
                const rayLength = currentRadius * intensity;
                const endX = x + rayLength * Math.cos(angle);
                const endY = y + rayLength * Math.sin(angle);
                const color = colors[Math.min(step, colors.length - 1)];
                const thickness = Math.max(1, 16 * intensity);
                this._sendDrawCommand(x, y, endX, endY, color, thickness);
            }
            const crossSize = currentRadius * 0.8;
            this._sendDrawCommand(x - crossSize, y, x + crossSize, y, '#FFFFFF', Math.max(1, 4 * intensity));
            this._sendDrawCommand(x, y - crossSize, x, y + crossSize, '#FFFFFF', Math.max(1, 4 * intensity));
            await new Promise(resolve => setTimeout(resolve, 40));
        }
    }

    async _drawBoomerangGuided(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawBoomerangGuided', 'whoosh');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._BOOMERANG_JSON_URL, 'grip_right', 'none', 1.0);
        if (this._stopSignal) return;

        const boomerangAttachPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        if (!boomerangAttachPoint) {
        }

        const startPoint = {
            x: boomerangAttachPoint.x + 40,
            y: boomerangAttachPoint.y - 5
        };

        const targetCoords = this._getTargetCoords(targetPlayerId);
        if (!startPoint || !targetCoords) {
        }

        const controlPointOffset = 100;
        const totalFrames = 60;
        const spinSpeed = 0.2;
        const boomerangColor = '#8B4513';
        const trailColor = '#D2B48C';
        let boomerangAngle = 0;


        for (let frame = 0; frame < totalFrames; frame++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;

            const progress = frame / totalFrames;
            const curveFactor = Math.sin(Math.PI * progress);

            let currentTargetX = (progress < 0.5) ? targetCoords.x : startPoint.x;
            let currentTargetY = (progress < 0.5) ? targetCoords.y : startPoint.y;

            const t = progress;
            const mt = 1 - t;
            const controlX = startPoint.x + (targetCoords.x - startPoint.x) / 2 + controlPointOffset * curveFactor * Math.cos(boomerangAngle * 2);
            const controlY = startPoint.y + (targetCoords.y - startPoint.y) / 2 + controlPointOffset * curveFactor * Math.sin(boomerangAngle * 2);
            const boomerangX = mt * mt * startPoint.x + 2 * mt * t * controlX + t * t * currentTargetX;
            const boomerangY = mt * mt * startPoint.y + 2 * mt * t * controlY + t * t * currentTargetY;

            boomerangAngle += spinSpeed;
            await this._drawBoomerangShape(boomerangX, boomerangY, boomerangAngle, boomerangColor);
            if (this._stopSignal) return;

            if (frame > 0) {
                const prevProgress = (frame - 1) / totalFrames;
                const prevControlX = startPoint.x + (targetCoords.x - startPoint.x) / 2 + controlPointOffset * Math.sin(Math.PI * prevProgress) * Math.cos((boomerangAngle - spinSpeed) * 2);
                const prevControlY = startPoint.y + (targetCoords.y - startPoint.y) / 2 + controlPointOffset * Math.sin(Math.PI * prevProgress) * Math.sin((boomerangAngle - spinSpeed) * 2);
                const prevBoomerangX = (1 - prevProgress) * ((1 - prevProgress) * startPoint.x + prevProgress * prevControlX) + prevProgress * ((1 - prevProgress) * prevControlX + prevProgress * (prevProgress < 0.5 ? targetCoords.x : startPoint.x));
                const prevBoomerangY = (1 - prevProgress) * ((1 - prevProgress) * startPoint.y + prevProgress * prevControlY) + prevProgress * ((1 - prevProgress) * prevControlY + prevProgress * (prevProgress < 0.5 ? targetCoords.y : startPoint.y));
                this._sendDrawCommand(prevBoomerangX, prevBoomerangY, boomerangX, boomerangY, trailColor, 1);
            }

            if (progress < 0.5 && this._distance(boomerangX, boomerangY, targetCoords.x, targetCoords.y) < 20) {

                this._playAnimationSound('_drawBoomerangGuided', 'impact');
                await this._bulletImpact(targetCoords.x, targetCoords.y);
                if (this._stopSignal) return;
                await new Promise(resolve => setTimeout(resolve, 500));
                if (this._stopSignal) return;
            }
            if (progress >= 0.5 && this._distance(boomerangX, boomerangY, startPoint.x, startPoint.y) < 20) {

                this._playAnimationSound('_drawBoomerangGuided', 'return');
                return;
            }
            await new Promise(resolve => setTimeout(resolve, 60));
        }
    }

    async _drawBoomerangShape(x, y, angle, color) {
        if (this._stopSignal) return;
        const armLength = 20;
        const armAngle = Math.PI / 4;
        const cx = x;
        const cy = y;
        const p1x = cx + armLength * Math.cos(angle);
        const p1y = cy + armLength * Math.sin(angle);
        const p2x = cx + armLength * Math.cos(angle + armAngle);
        const p2y = cy + armLength * Math.sin(angle + armAngle);
        const p3x = cx + armLength * Math.cos(angle - armAngle);
        const p3y = cy + armLength * Math.sin(angle - armAngle);
        this._sendDrawCommand(cx, cy, p1x, p1y, color, 4);
        this._sendDrawCommand(cx, cy, p2x, p2y, color, 4);
        this._sendDrawCommand(cx, cy, p3x, p3y, color, 4);
    }

    async _drawSwordSlashArc(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawSwordSlashArc', 'draw');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._ESPADA_JSON_URL, 'grip_right', 'right', 1.0);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 500));
        if (this._stopSignal) return;

        const swordAttachPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        if (!swordAttachPoint) {
        }

        const targetCoords = this._getTargetCoords(targetPlayerId);
        if (!targetCoords) {
        }

        const absorptionPoint = {
            x: Math.floor(swordAttachPoint.x + 60),
            y: Math.floor(swordAttachPoint.y - 15)
        };


        this._playAnimationSound('_drawSwordSlashArc', 'slash');

        await this._createEnergyConnectionUltra(targetCoords, absorptionPoint);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 800));
        if (this._stopSignal) return;
        await this._drainEnergyFlowUltra(targetCoords, absorptionPoint, targetPlayerId);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 800));
        if (this._stopSignal) return;
        await this._finalizeEnergyAbsorptionUltra(absorptionPoint);
    }

    async _createEnergyConnectionUltra(sourceCoords, absorptionPoint) {
        if (this._stopSignal) return;
        const connectionSteps = 12;
        const energyColors = ['#9400D3', '#FF1493', '#00FFFF', '#FFD700'];
        sourceCoords.x = Math.floor(sourceCoords.x);
        sourceCoords.y = Math.floor(sourceCoords.y);

        for (let step = 0; step < connectionSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / connectionSteps;
            const tentacles = 4;
            for (let tentacle = 0; tentacle < tentacles; tentacle++) {
                if (this._stopSignal) return;
                const tentacleAngle = (tentacle / tentacles) * 2 * Math.PI;
                const tentacleRadius = 30 * progress;
                const tentacleStartX = Math.floor(sourceCoords.x + tentacleRadius * Math.cos(tentacleAngle));
                const tentacleStartY = Math.floor(sourceCoords.y + tentacleRadius * Math.sin(tentacleAngle));
                const midProgress = progress * 0.7;
                const tentacleEndX = Math.floor(tentacleStartX + (absorptionPoint.x - tentacleStartX) * midProgress);
                const tentacleEndY = Math.floor(tentacleStartY + (absorptionPoint.y - tentacleStartY) * midProgress);
                const color = energyColors[tentacle % energyColors.length];
                const thickness = Math.max(2, 5 - progress * 2);
                this._sendDrawCommand(tentacleStartX, tentacleStartY, tentacleEndX, tentacleEndY, color, thickness);
                await new Promise(resolve => setTimeout(resolve, 60));
                if (this._stopSignal) return;

                if (step % 3 === 0) {
                    const sparkX = Math.floor(tentacleEndX + (Math.random() - 0.5) * 10);
                    const sparkY = Math.floor(tentacleEndY + (Math.random() - 0.5) * 10);
                    this._sendDrawCommand(tentacleEndX, tentacleEndY, sparkX, sparkY, '#FFFFFF', 1);
                    await new Promise(resolve => setTimeout(resolve, 20));
                    if (this._stopSignal) return;
                }
            }
            const pulseRadius = 25 + Math.sin(step * 0.8) * 10;
            const pulseSegments = 8;
            for (let seg = 0; seg < pulseSegments; seg++) {
                if (this._stopSignal) return;
                const angle = (seg / pulseSegments) * 2 * Math.PI;
                const pulseX = Math.floor(sourceCoords.x + pulseRadius * Math.cos(angle));
                const pulseY = Math.floor(sourceCoords.y + pulseRadius * Math.sin(angle));
                const pulseIntensity = 1 - progress;
                const pulseColor = `rgba(255, 0, 100, ${pulseIntensity * 0.6})`;
                this._sendDrawCommand(sourceCoords.x, sourceCoords.y, pulseX, pulseY, pulseColor, Math.max(1, 3 * pulseIntensity));
                await new Promise(resolve => setTimeout(resolve, 25));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 200));
        }
    }

    async _drainEnergyFlowUltra(sourceCoords, absorptionPoint, targetPlayerId) {
        if (this._stopSignal) return;
        const drainDuration = 4000;
        const startTime = Date.now();
        let frame = 0;
        const flowColors = ['#9400D3', '#8A2BE2', '#FF1493', '#00FFFF', '#FFD700'];
        const streamCount = 3;

        while (Date.now() - startTime < drainDuration) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            frame++;
            const currentTargetCoords = this._getTargetCoords(targetPlayerId) || sourceCoords;
            currentTargetCoords.x = Math.floor(currentTargetCoords.x);
            currentTargetCoords.y = Math.floor(currentTargetCoords.y);

            for (let stream = 0; stream < streamCount; stream++) {
                if (this._stopSignal) return;
                const streamOffset = (stream / streamCount) * 2 * Math.PI;
                const streamPhase = frame * 0.1 + streamOffset;
                const particleCount = 4;
                for (let particle = 0; particle < particleCount; particle++) {
                    if (this._stopSignal) return;
                    const particleProgress = (particle / particleCount) + (frame * 0.05) % 1;
                    const baseX = currentTargetCoords.x + (absorptionPoint.x - currentTargetCoords.x) * particleProgress;
                    const baseY = currentTargetCoords.y + (absorptionPoint.y - currentTargetCoords.y) * particleProgress;
                    const waveIntensity = 15 * Math.sin(particleProgress * Math.PI);
                    const waveX = Math.floor(baseX + waveIntensity * Math.cos(streamPhase + particleProgress * 4));
                    const waveY = Math.floor(baseY + waveIntensity * Math.sin(streamPhase + particleProgress * 4) * 0.5);
                    const color = flowColors[stream % flowColors.length];
                    const intensity = 1 - particleProgress;
                    const thickness = Math.max(1, 4 * intensity);
                    this._sendDrawCommand(waveX - 2, waveY - 2, waveX + 2, waveY + 2, color, thickness);
                    await new Promise(resolve => setTimeout(resolve, 30));
                    if (this._stopSignal) return;

                    if (particleProgress > 0.1) {
                        const trailX = Math.floor(waveX - 8 * Math.cos(streamPhase));
                        const trailY = Math.floor(waveY - 8 * Math.sin(streamPhase) * 0.5);
                        this._sendDrawCommand(waveX, waveY, trailX, trailY, color, Math.max(1, thickness * 0.6));
                        await new Promise(resolve => setTimeout(resolve, 15));
                        if (this._stopSignal) return;
                    }
                }
                await new Promise(resolve => setTimeout(resolve, 100));
                if (this._stopSignal) return;
            }
            if (frame % 6 === 0) {
                const drainPulse = Math.sin(frame * 0.3) * 20 + 30;
                const drainSegments = 8;
                for (let seg = 0; seg < drainSegments; seg++) {
                    if (this._stopSignal) return;
                    const angle = (seg / drainSegments) * 2 * Math.PI + frame * 0.1;
                    const drainX = Math.floor(currentTargetCoords.x + drainPulse * Math.cos(angle));
                    const drainY = Math.floor(currentTargetCoords.y + drainPulse * Math.sin(angle));
                    const drainColor = `rgba(255, ${100 - frame % 100}, 0, 0.7)`;
                    this._sendDrawCommand(currentTargetCoords.x, currentTargetCoords.y, drainX, drainY, drainColor, 2);
                    await new Promise(resolve => setTimeout(resolve, 40));
                    if (this._stopSignal) return;
                }
            }
            if (frame % 8 === 0) {
                const accumulation = Math.sin(frame * 0.2) * 15 + 20;
                const accumulationSpokes = 6;
                for (let spoke = 0; spoke < accumulationSpokes; spoke++) {
                    if (this._stopSignal) return;
                    const spokeAngle = (spoke / accumulationSpokes) * 2 * Math.PI + frame * 0.15;
                    const accX = Math.floor(absorptionPoint.x + accumulation * Math.cos(spokeAngle));
                    const accY = Math.floor(absorptionPoint.y + accumulation * Math.sin(spokeAngle));
                    this._sendDrawCommand(absorptionPoint.x, absorptionPoint.y, accX, accY, '#FFD700', 3);
                    await new Promise(resolve => setTimeout(resolve, 35));
                    if (this._stopSignal) return;
                }
            }
            await new Promise(resolve => setTimeout(resolve, 150));
        }
    }

    async _finalizeEnergyAbsorptionUltra(absorptionPoint) {
        if (this._stopSignal) return;
        const finalizationSteps = 15;
        const maxRadius = 40;
        const finalColors = ['#FFFFFF', '#FFD700', '#00FFFF'];

        for (let step = 0; step < finalizationSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / finalizationSteps;
            const currentRadius = maxRadius * Math.sin(progress * Math.PI);
            const intensity = 1 - progress;
            const burstRays = 12;
            for (let colorIdx = 0; colorIdx < finalColors.length; colorIdx++) {
                if (this._stopSignal) return;
                for (let ray = 0; ray < burstRays; ray += 6) {
                    if (this._stopSignal) return;
                    for (let r = ray; r < Math.min(ray + 2, burstRays); r++) {
                        if (r % finalColors.length !== colorIdx) continue;
                        const rayAngle = (r / burstRays) * 2 * Math.PI + step * 0.2;
                        const rayLength = Math.floor(currentRadius + Math.random() * 15);
                        const rayX = Math.floor(absorptionPoint.x + rayLength * Math.cos(rayAngle));
                        const rayY = Math.floor(absorptionPoint.y + rayLength * Math.sin(rayAngle));
                        const thickness = Math.max(1, 5 * intensity);
                        this._sendDrawCommand(absorptionPoint.x, absorptionPoint.y, rayX, rayY, finalColors[colorIdx], thickness);
                        await new Promise(resolve => setTimeout(resolve, 45));
                        if (this._stopSignal) return;
                    }
                    await new Promise(resolve => setTimeout(resolve, 80));
                    if (this._stopSignal) return;
                }
                await new Promise(resolve => setTimeout(resolve, 120));
                if (this._stopSignal) return;
            }
            const coreSize = Math.floor(12 * intensity);
            const coreSegments = 4;
            for (let seg = 0; seg < coreSegments; seg++) {
                if (this._stopSignal) return;
                const coreAngle = (seg / coreSegments) * 2 * Math.PI;
                const coreX = Math.floor(absorptionPoint.x + coreSize * Math.cos(coreAngle));
                const coreY = Math.floor(absorptionPoint.y + coreSize * Math.sin(coreAngle));
                this._sendDrawCommand(absorptionPoint.x, absorptionPoint.y, coreX, coreY, '#FFFFFF', Math.max(2, 6 * intensity));
                await new Promise(resolve => setTimeout(resolve, 50));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 250));
        }
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 600));
        if (this._stopSignal) return;
        this._sendDrawCommand(absorptionPoint.x - 20, absorptionPoint.y, absorptionPoint.x + 20, absorptionPoint.y, '#FFFFFF', 8);
        await new Promise(resolve => setTimeout(resolve, 300));
        if (this._stopSignal) return;
        this._sendDrawCommand(absorptionPoint.x, absorptionPoint.y - 20, absorptionPoint.x, absorptionPoint.y + 20, '#FFFFFF', 8);
    }

    async _drawSeismicSmashWave(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawSeismicSmashWave', 'cannon');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._MARTILLO_JSON_URL, 'grip_right', 'down', 1.0);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 300));
        if (this._stopSignal) return;

        const hammerPoint = this._getAttachmentPoint(ownPlayerId, 'grip_right');
        const targetPoint = this._getTargetCoords(targetPlayerId);
        if (!hammerPoint || !targetPoint) {
        }

        const hammerX = Math.floor(hammerPoint.x);
        const hammerY = Math.floor(hammerPoint.y);
        const targetX = Math.floor(targetPoint.x);
        const targetY = Math.floor(targetPoint.y);



        await this._launchNetProjectiles(hammerX, hammerY, targetX, targetY);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 400));
        if (this._stopSignal) return;
        this._playAnimationSound('_drawSeismicSmashWave', 'explosion');
        await this._expandTrapNet(targetX, targetY);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 400));
        if (this._stopSignal) return;
        await this._closeTrapNet(targetX, targetY);
    }

    async _launchNetProjectiles(startX, startY, targetX, targetY) {
        if (this._stopSignal) return;
        const projectileSteps = 15;
        const netColors = ['#8B4513', '#A0522D', '#CD853F'];

        for (let step = 0; step < projectileSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / projectileSteps;
            for (let colorIdx = 0; colorIdx < netColors.length; colorIdx++) {
                if (this._stopSignal) return;
                const color = netColors[colorIdx];
                const projectilesThisColor = [];
                const projectileCount = 3;
                for (let proj = 0; proj < projectileCount; proj++) {
                    if (proj % netColors.length !== colorIdx) continue;
                    const angle = (proj / projectileCount) * 0.6 - 0.3;
                    const currentX = Math.floor(startX + (targetX - startX) * progress);
                    const currentY = Math.floor(startY + (targetY - startY) * progress - 20 * Math.sin(Math.PI * progress));
                    const offsetX = Math.floor(Math.cos(angle) * 15);
                    const offsetY = Math.floor(Math.sin(angle) * 15);
                    projectilesThisColor.push({ x: currentX + offsetX, y: currentY + offsetY });
                }
                projectilesThisColor.forEach(proj => {
                    const prevX = Math.floor(startX + (targetX - startX) * Math.max(0, progress - 0.1));
                    const prevY = Math.floor(startY + (targetY - startY) * Math.max(0, progress - 0.1));
                    this._sendDrawCommand(prevX, prevY, proj.x, proj.y, color, 3);
                    this._sendDrawCommand(proj.x - 3, proj.y - 3, proj.x + 3, proj.y + 3, color, 2);
                });
                await new Promise(resolve => setTimeout(resolve, 15));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 60));
        }
    }

    async _expandTrapNet(centerX, centerY) {
        if (this._stopSignal) return;
        const expansionSteps = 18;
        const maxRadius = 80;
        const netColor = '#8B4513';
        const accentColor = '#CD853F';

        for (let step = 0; step < expansionSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / expansionSteps;
            const currentRadius = Math.floor(maxRadius * progress);
            const netCommands = [];
            const rings = Math.min(4, Math.floor(progress * 4) + 1);
            for (let ring = 0; ring < rings; ring++) {
                const ringRadius = Math.floor((currentRadius / rings) * (ring + 1));
                const segments = 12;
                for (let seg = 0; seg < segments; seg++) {
                    const angle1 = (seg / segments) * 2 * Math.PI;
                    const angle2 = ((seg + 1) / segments) * 2 * Math.PI;
                    const x1 = Math.floor(centerX + ringRadius * Math.cos(angle1));
                    const y1 = Math.floor(centerY + ringRadius * Math.sin(angle1));
                    const x2 = Math.floor(centerX + ringRadius * Math.cos(angle2));
                    const y2 = Math.floor(centerY + ringRadius * Math.sin(angle2));
                    netCommands.push({ x1, y1, x2, y2, color: netColor, thickness: 2 });
                }
            }
            const radialLines = 8;
            for (let line = 0; line < radialLines; line++) {
                const angle = (line / radialLines) * 2 * Math.PI;
                const endX = Math.floor(centerX + currentRadius * Math.cos(angle));
                const endY = Math.floor(centerY + currentRadius * Math.sin(angle));
                netCommands.push({ x1: centerX, y1: centerY, x2: endX, y2: endY, color: netColor, thickness: 2 });
            }
            netCommands.forEach(cmd => this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.color, cmd.thickness));
            await new Promise(resolve => setTimeout(resolve, 25));
            if (this._stopSignal) return;

            if (step % 3 === 0) {
                const nodeCommands = [];
                for (let ring = 1; ring <= rings; ring++) {
                    const ringRadius = Math.floor((currentRadius / rings) * ring);
                    const nodes = 6;
                    for (let node = 0; node < nodes; node++) {
                        const angle = (node / nodes) * 2 * Math.PI;
                        const nodeX = Math.floor(centerX + ringRadius * Math.cos(angle));
                        const nodeY = Math.floor(centerY + ringRadius * Math.sin(angle));
                        nodeCommands.push({ x1: nodeX - 2, y1: nodeY - 2, x2: nodeX + 2, y2: nodeY + 2, color: accentColor, thickness: 3 });
                    }
                }
                nodeCommands.forEach(cmd => this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.color, cmd.thickness));
                await new Promise(resolve => setTimeout(resolve, 20));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 180));
        }
    }

    async _closeTrapNet(centerX, centerY) {
        if (this._stopSignal) return;
        const closingSteps = 12;
        const initialRadius = 80;
        const finalRadius = 25;
        const trapColor = '#654321';
        const sparkColor = '#DAA520';

        for (let step = 0; step < closingSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / closingSteps;
            const currentRadius = Math.floor(initialRadius - (initialRadius - finalRadius) * progress);
            const intensity = 1 - progress;
            const contractionCommands = [];
            const contractionLines = 10;
            for (let line = 0; line < contractionLines; line++) {
                const angle = (line / contractionLines) * 2 * Math.PI;
                const outerX = Math.floor(centerX + currentRadius * Math.cos(angle));
                const outerY = Math.floor(centerY + currentRadius * Math.sin(angle));
                const innerX = Math.floor(centerX + (currentRadius * 0.3) * Math.cos(angle));
                const innerY = Math.floor(centerY + (currentRadius * 0.3) * Math.sin(angle));
                contractionCommands.push({ x1: outerX, y1: outerY, x2: innerX, y2: innerY, color: trapColor, thickness: Math.max(1, 4 * intensity) });
            }
            contractionCommands.forEach(cmd => this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.color, cmd.thickness));
            await new Promise(resolve => setTimeout(resolve, 30));
            if (this._stopSignal) return;

            if (step % 2 === 0) {
                const sparkCommands = [];
                for (let spark = 0; spark < 6; spark++) {
                    const sparkAngle = (spark / 6) * 2 * Math.PI + Math.random() * 0.5;
                    const sparkRadius = currentRadius + Math.random() * 10;
                    const sparkX = Math.floor(centerX + sparkRadius * Math.cos(sparkAngle));
                    const sparkY = Math.floor(centerY + sparkRadius * Math.sin(sparkAngle));
                    const sparkEndX = Math.floor(sparkX + (Math.random() - 0.5) * 15);
                    const sparkEndY = Math.floor(sparkY + (Math.random() - 0.5) * 15);
                    sparkCommands.push({ x1: sparkX, y1: sparkY, x2: sparkEndX, y2: sparkEndY, color: sparkColor, thickness: 1 });
                }
                sparkCommands.forEach(cmd => this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.color, cmd.thickness));
                await new Promise(resolve => setTimeout(resolve, 25));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 100));
        }
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 200));
        if (this._stopSignal) return;
        const pulseRadius = finalRadius;
        for (let pulse = 0; pulse < 3; pulse++) {
            if (this._stopSignal) return;
            const pulseSegments = 8;
            for (let seg = 0; seg < pulseSegments; seg++) {
                const angle = (seg / pulseSegments) * 2 * Math.PI;
                const pulseX = Math.floor(centerX + pulseRadius * Math.cos(angle));
                const pulseY = Math.floor(centerY + pulseRadius * Math.sin(angle));
                this._sendDrawCommand(centerX, centerY, pulseX, pulseY, sparkColor, 3);
            }
            await new Promise(resolve => setTimeout(resolve, 150));
        }
    }

    async _drawElectricWhipSnap(targetPlayerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawElectricWhipSnap', 'charge');

        const ownPlayerId = this._getOwnPlayerId();
        if (!ownPlayerId) {
        }

        await this._drawJsonCommands(ownPlayerId, this._LATIGO_JSON_URL, 'grip_right', 'right', 1.0);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 600));
        if (this._stopSignal) return;

        const targetCoords = this._getTargetCoords(targetPlayerId);
        if (!targetCoords) {
        }

        const centerX = Math.floor(targetCoords.x);
        const centerY = Math.floor(targetCoords.y);


        await this._createClonesUltraOptimized(centerX, centerY);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 800));
        if (this._stopSignal) return;
        await this._emergingSunUltraOptimized(centerX, centerY);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 800));
        if (this._stopSignal) return;
        this._playAnimationSound('_drawElectricWhipSnap', 'burn');
        await this._burnPlayerUltraOptimized(centerX, centerY);
    }

    async _createClonesUltraOptimized(centerX, centerY) {
        if (this._stopSignal) return;
        const cloneSteps = 12;
        const maxRadius = 90;
        const cloneCount = 5;
        const cloneColors = ['#FFD700', '#FFA500'];

        for (let step = 0; step < cloneSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / cloneSteps;
            const currentRadius = Math.floor(maxRadius * progress);
            for (let colorIdx = 0; colorIdx < cloneColors.length; colorIdx++) {
                if (this._stopSignal) return;
                const color = cloneColors[colorIdx];
                const allCloneCommands = [];
                for (let clone = 0; clone < cloneCount; clone++) {
                    if (clone % cloneColors.length !== colorIdx) continue;
                    const angle = (clone / cloneCount) * 2 * Math.PI + step * 0.1;
                    const cloneX = Math.floor(centerX + currentRadius * Math.cos(angle));
                    const cloneY = Math.floor(centerY + currentRadius * Math.sin(angle));
                    const size = Math.floor(12 + Math.sin(step * 0.4) * 4);
                    allCloneCommands.push(
                        { x1: cloneX - size, y1: cloneY, x2: cloneX + size, y2: cloneY },
                        { x1: cloneX, y1: cloneY - size, x2: cloneX, y2: cloneY + size },
                        { x1: cloneX - size, y1: cloneY - size, x2: cloneX + size, y2: cloneY + size }
                    );
                }
                allCloneCommands.forEach(cmd => this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, color, colorIdx === 0 ? 3 : 1));
                await new Promise(resolve => setTimeout(resolve, 80));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 180));
        }
    }

    async _emergingSunUltraOptimized(centerX, centerY) {
        if (this._stopSignal) return;
        const sunSteps = 15;
        const maxSunRadius = 70;
        const sunColors = ['#FFFF00', '#FFA500'];

        for (let step = 0; step < sunSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / sunSteps;
            const currentRadius = Math.floor(maxSunRadius * Math.sin((progress * Math.PI) / 2));
            for (let colorIdx = 0; colorIdx < sunColors.length; colorIdx++) {
                if (this._stopSignal) return;
                const color = sunColors[colorIdx];
                const layerRadius = Math.floor(currentRadius * (1 - colorIdx * 0.3));
                const rayCount = 10 - colorIdx * 2;
                const allSunCommands = [];
                for (let ray = 0; ray < rayCount; ray++) {
                    const angle = (ray / rayCount) * 2 * Math.PI + step * 0.1;
                    const rayLength = Math.floor(layerRadius + Math.sin(step * 0.3 + ray) * 8);
                    const rayEndX = Math.floor(centerX + rayLength * Math.cos(angle));
                    const rayEndY = Math.floor(centerY + rayLength * Math.sin(angle));
                    allSunCommands.push({ x1: centerX, y1: centerY, x2: rayEndX, y2: rayEndY, thickness: Math.max(1, (3 - colorIdx) * (1 - progress * 0.2)) });
                }
                const coronaSegments = 8;
                for (let seg = 0; seg < coronaSegments; seg++) {
                    const segAngle = (seg / coronaSegments) * 2 * Math.PI;
                    const coronaX = Math.floor(centerX + layerRadius * 0.7 * Math.cos(segAngle));
                    const coronaY = Math.floor(centerY + layerRadius * 0.7 * Math.sin(segAngle));
                    allSunCommands.push({ x1: centerX, y1: centerY, x2: coronaX, y2: coronaY, thickness: Math.max(1, 2 - colorIdx) });
                }
                allSunCommands.forEach(cmd => this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, color, cmd.thickness));
                await new Promise(resolve => setTimeout(resolve, 100));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 200));
        }
    }

    async _burnPlayerUltraOptimized(centerX, centerY) {
        if (this._stopSignal) return;
        const burnSteps = 12;
        const fireColors = ['#FF4500', '#FFD700'];

        for (let step = 0; step < burnSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / burnSteps;
            const intensity = 1 - progress;
            const burnRadius = Math.floor(50 * progress);
            for (let colorIdx = 0; colorIdx < fireColors.length; colorIdx++) {
                if (this._stopSignal) return;
                const color = fireColors[colorIdx];
                const allFireCommands = [];
                const flameCount = 8;
                for (let flame = 0; flame < flameCount; flame++) {
                    if (flame % fireColors.length !== colorIdx) continue;
                    const flameAngle = (flame / flameCount) * 2 * Math.PI + step * 0.15;
                    const flameDistance = Math.floor(burnRadius + Math.random() * 15);
                    const flameX = Math.floor(centerX + flameDistance * Math.cos(flameAngle));
                    const flameY = Math.floor(centerY + flameDistance * Math.sin(flameAngle) - Math.random() * 10);
                    allFireCommands.push({ x1: centerX, y1: centerY, x2: flameX, y2: flameY, thickness: Math.max(1, Math.floor(4 * intensity)) });
                    if (Math.random() < 0.3) {
                        const sparkX = Math.floor(flameX + (Math.random() - 0.5) * 8);
                        const sparkY = Math.floor(flameY + (Math.random() - 0.5) * 8);
                        allFireCommands.push({ x1: flameX, y1: flameY, x2: sparkX, y2: sparkY, thickness: 1 });
                    }
                }
                allFireCommands.forEach(cmd => {
                    const fireColor = cmd.thickness === 1 ? '#FFFF00' : color;
                    this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, fireColor, cmd.thickness);
                });
                await new Promise(resolve => setTimeout(resolve, 90));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 220));
        }
    }

    async _drawStickyGrenadeProj(playerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawStickyGrenadeProj', 'whoosh');

        const avatar = document.querySelector(`.spawnedavatar[data-playerid="${playerId}"]`);
        if (!avatar) {
        }

        await this._drawJsonCommands(playerId, this._GRANADA_JSON_URL, 'grip_right', 'none', 0.8);
        if (this._stopSignal) return;

        const grenadeAttachPoint = this._getAttachmentPoint(playerId, 'grip_right');
        if (!grenadeAttachPoint) {
        }

        const throwOrigin = {
            x: grenadeAttachPoint.x + 20,
            y: grenadeAttachPoint.y + 0
        };

        const targetCoords = this._getTargetCoords(playerId);
        if (!throwOrigin || !targetCoords) {
        }

        const fuseTimeMs = 2500;
        const flightTimeMs = 800;
        const flightSteps = 20;
        let grenadeCurrentX = throwOrigin.x;
        let grenadeCurrentY = throwOrigin.y;


        for (let step = 0; step < flightSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / flightSteps;
            const nextX = throwOrigin.x + (targetCoords.x - throwOrigin.x) * progress;
            const nextY = throwOrigin.y + (targetCoords.y - throwOrigin.y) * progress - 50 * Math.sin(Math.PI * progress);
            this._sendDrawCommand(grenadeCurrentX, grenadeCurrentY, nextX, nextY, '#808080', 3);
            grenadeCurrentX = nextX;
            grenadeCurrentY = nextY;
            await new Promise(resolve => setTimeout(resolve, flightTimeMs / flightSteps));
        }
        if (this._stopSignal) return;

        const finalGrenadeX = targetCoords.x;
        const finalGrenadeY = targetCoords.y - 15;


        const blinkInterval = setInterval(() => {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) { clearInterval(blinkInterval); return; }
            const blinkColor = Math.random() > 0.5 ? '#FF0000' : '#FFFF00';
            this._sendDrawCommand(finalGrenadeX - 5, finalGrenadeY, finalGrenadeX + 5, finalGrenadeY, blinkColor, 2);
            this._sendDrawCommand(finalGrenadeX, finalGrenadeY - 5, finalGrenadeX, finalGrenadeY + 5, blinkColor, 2);
        }, 100);

        await new Promise(resolve => setTimeout(resolve, fuseTimeMs));
        clearInterval(blinkInterval);
        if (this._stopSignal) return;

        if (this._getGameSocket() && !(this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) {
            this._playAnimationSound('_drawStickyGrenadeProj', 'mega-explosion');
            await this._explosionBlast(finalGrenadeX, finalGrenadeY, 1.0);
        }
    }

    async _drawProximityMineTrap(playerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawProximityMineTrap', 'laser-reload');

        const avatar = document.querySelector(`.spawnedavatar[data-playerid="${playerId}"]`);
        if (!avatar) {
        }

        const mineGroundPosition = this._getAttachmentPoint(playerId, 'bottom');
        if (!mineGroundPosition) {
        }

        await this._drawJsonCommands(playerId, this._MINA_JSON_URL, 'bottom', 'none', 1.0);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 800));
        if (this._stopSignal) return;

        const centerX = Math.floor(mineGroundPosition.x);
        const centerY = Math.floor(mineGroundPosition.y);



        await this._initializeForceFieldUltra(centerX, centerY);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 1000));
        if (this._stopSignal) return;
        this._playAnimationSound('_drawProximityMineTrap', 'laser-shot');
        await this._activeForceFieldUltra(centerX, centerY);
        if (this._stopSignal) return;
        await new Promise(resolve => setTimeout(resolve, 1000));
        if (this._stopSignal) return;
    }

    async _initializeForceFieldUltra(centerX, centerY) {
        if (this._stopSignal) return;
        const initSteps = 10;
        const maxRadius = 80;
        const fieldColors = ['#00BFFF', '#4169E1'];
        const preCalculatedAngles = [];
        const segments = 12;
        for (let seg = 0; seg < segments; seg++) {
            preCalculatedAngles.push((seg / segments) * 2 * Math.PI);
        }

        for (let step = 0; step < initSteps; step++) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            const progress = step / initSteps;
            const currentRadius = Math.floor(maxRadius * Math.sin((progress * Math.PI) / 2));
            for (let colorIdx = 0; colorIdx < fieldColors.length; colorIdx++) {
                if (this._stopSignal) return;
                const color = fieldColors[colorIdx];
                const allCommands = [];
                const ringRadius = Math.floor(currentRadius * (1 - colorIdx * 0.3));
                preCalculatedAngles.forEach((baseAngle, segIdx) => {
                    const angle1 = baseAngle + step * 0.1;
                    const angle2 = preCalculatedAngles[(segIdx + 1) % preCalculatedAngles.length] + step * 0.1;
                    const x1 = Math.floor(centerX + ringRadius * Math.cos(angle1));
                    const y1 = Math.floor(centerY + ringRadius * Math.sin(angle1));
                    const x2 = Math.floor(centerX + ringRadius * Math.cos(angle2));
                    const y2 = Math.floor(centerY + ringRadius * Math.sin(angle2));
                    allCommands.push({ x1, y1, x2, y2 });
                });
                const energyRays = 4;
                for (let ray = 0; ray < energyRays; ray++) {
                    const rayAngle = (ray / energyRays) * 2 * Math.PI + step * 0.15;
                    const rayStartX = Math.floor(centerX + ringRadius * Math.cos(rayAngle));
                    const rayStartY = Math.floor(centerY + ringRadius * Math.sin(rayAngle));
                    const rayEndX = Math.floor(centerX + (ringRadius * 0.4) * Math.cos(rayAngle));
                    const rayEndY = Math.floor(centerY + (ringRadius * 0.4) * Math.sin(rayAngle));
                    allCommands.push({ x1: rayStartX, y1: rayStartY, x2: rayEndX, y2: rayEndY });
                }
                const thickness = Math.max(1, (3 - colorIdx) * (0.5 + progress * 0.5));
                allCommands.forEach(cmd => this._sendDrawCommand(cmd.x1, cmd.y1, cmd.x2, cmd.y2, color, thickness));
                await new Promise(resolve => setTimeout(resolve, 120));
                if (this._stopSignal) return;
            }
            await new Promise(resolve => setTimeout(resolve, 250));
        }
    }
    async _activeForceFieldUltra(centerX, centerY) {
        if (this._stopSignal) return;

        await new Promise(resolve => setTimeout(resolve, 1000));
    }

    async _drawIceStormArea(playerId) {


        this._playAnimationSound('_drawIceStormArea', 'whoosh');

        const avatarCenter = this._getTargetCoords(playerId);
        if (!avatarCenter) {
        }

        const stormDurationMs = 5000;
        const startTime = Date.now();
        let frame = 0;
        const stormColors = ['#ADD8E6', '#E0FFFF', '#FFFFFF', '#B0E0E6'];


        while (Date.now() - startTime < stormDurationMs) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) { break; }

            frame++;
            const currentAvatarCenter = this._getTargetCoords(playerId);
            if (!currentAvatarCenter) {
            }
            const centerX = currentAvatarCenter.x;
            const centerY = currentAvatarCenter.y;

            for (let i = 0; i < 5; i++) {
                const x = centerX + (Math.random() - 0.5) * 150;
                const y = centerY - 80 + Math.random() * 160;
                const size = Math.random() * 3 + 1;
                this._sendDrawCommand(x, y, x + size, y + size, '#FFFFFF', 1);
            }

            if (Math.random() < 0.2) {
                const x = centerX + (Math.random() - 0.5) * 100;
                const y1 = centerY - 50 + Math.random() * 20;
                const y2 = y1 + 10 + Math.random() * 20;
                const color = stormColors[Math.floor(Math.random() * stormColors.length)];
                this._sendDrawCommand(x, y1, x, y2, color, Math.max(1, Math.random() * 3));
            }

            const pulseRadius = 60 + 10 * Math.sin(frame * 0.1);
            const pulseThickness = 2 + 1 * Math.sin(frame * 0.1);
            const segments = 12;
            for(let i=0; i<segments; i++) {
                const angle1 = (i / segments) * 2 * Math.PI;
                const angle2 = ((i + 1) / segments) * 2 * Math.PI;
                const x1 = centerX + pulseRadius * Math.cos(angle1);
                const y1 = centerY + pulseRadius * Math.sin(angle1);
                const x2 = centerX + pulseRadius * Math.cos(angle2);
                const y2 = centerY + pulseRadius * Math.sin(angle2);
                this._sendDrawCommand(x1, y1, x2, y2, '#B0E0E6', pulseThickness);
            }

            await new Promise(resolve => setTimeout(resolve, 100));
        }
        this._playAnimationSound('_drawIceStormArea', 'explosion');
    }

    async _drawWindTornadoSpin(playerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawWindTornadoSpin', 'whoosh');

        const avatarCenter = this._getTargetCoords(playerId);
        if (!avatarCenter) {
        }

        const tornadoDurationMs = 5000;
        const startTime = Date.now();
        let frame = 0;
        const tornadoHeight = 150;
        const tornadoRadius = 50;
        const rotationSpeed = 0.15;
        const spiralCount = 3;
        const windColors = ['#D3D3D3', '#A9A9A9', '#778899'];


        while (Date.now() - startTime < tornadoDurationMs) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            frame++;
            const currentAvatarCenter = this._getTargetCoords(playerId);
            if (!currentAvatarCenter) {
            }

            const centerX = currentAvatarCenter.x;
            const centerY = currentAvatarCenter.y;

            for (let i = 0; i < spiralCount; i++) {
                if (this._stopSignal) return;
                const spiralOffset = (2 * Math.PI / spiralCount) * i;
                for (let seg = 0; seg < 20; seg++) {
                    const progress = seg / 20;
                    const currentAngle = frame * rotationSpeed + spiralOffset + progress * Math.PI * 4;
                    const currentHeight = tornadoHeight * progress;
                    const currentRadius = tornadoRadius * (1 - progress * 0.5);
                    const x1 = Math.floor(centerX + currentRadius * Math.cos(currentAngle));
                    const y1 = Math.floor(centerY - tornadoHeight / 2 + currentHeight + currentRadius * Math.sin(currentAngle));
                    const nextAngle = frame * rotationSpeed + spiralOffset + (seg + 1) / 20 * Math.PI * 4;
                    const nextHeight = tornadoHeight * ((seg + 1) / 20);
                    const nextRadius = tornadoRadius * (1 - ((seg + 1) / 20) * 0.5);
                    const x2 = Math.floor(centerX + nextRadius * Math.cos(nextAngle));
                    const y2 = Math.floor(centerY - tornadoHeight / 2 + nextHeight + nextRadius * Math.sin(nextAngle));
                    const color = windColors[i % windColors.length];
                    this._sendDrawCommand(x1, y1, x2, y2, color, Math.max(1, 3 * (1 - progress)));
                    if (seg > 0 && seg % 10 === 0) {
                        await new Promise(resolve => setTimeout(resolve, 2));
                        if (this._stopSignal) return;
                    }
                }
                if (i < spiralCount - 1) {
                    await new Promise(resolve => setTimeout(resolve, 8));
                    if (this._stopSignal) return;
                }
            }
            await new Promise(resolve => setTimeout(resolve, 140));
        }
        this._playAnimationSound('_drawWindTornadoSpin', 'sword-slash');
    }

    async _drawEarthWallShield(playerId) {
        if (this._stopSignal) {  return; }


        this._playAnimationSound('_drawEarthWallShield', 'cannon');

        const avatar = document.querySelector(`.spawnedavatar[data-playerid="${playerId}"]`);
        if (!avatar) {
        }

        const avatarCenter = this._getTargetCoords(playerId);
        if (!avatarCenter) {
        }

        const wallDurationMs = 3000;
        const startTime = Date.now();
        let frame = 0;
        const wallWidth = 100;
        const wallHeight = 80;
        const earthColors = ['#8B4513', '#A0522D', '#D2B48C'];
        const initialWallX = avatarCenter.x;
        const initialWallY = avatarCenter.y + avatar.getBoundingClientRect().height / 2 + 10;


        while (Date.now() - startTime < wallDurationMs) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            frame++;
            const progress = (Date.now() - startTime) / wallDurationMs;
            const opacity = 1 - progress;
            const currentHeight = Math.min(wallHeight, frame * 5);
            const currentWallX = initialWallX;
            const currentWallY = initialWallY - currentHeight;

            for (let i = 0; i < 5; i++) {
                const startX = currentWallX - wallWidth / 2 + (Math.random() - 0.5) * 10;
                const endX = currentWallX + wallWidth / 2 + (Math.random() - 0.5) * 10;
                const y = currentWallY + (Math.random() * currentHeight);
                const thickness = Math.max(1, 8 * opacity * Math.random());
                const color = earthColors[Math.floor(Math.random() * earthColors.length)];
                this._sendDrawCommand(startX, y, endX, y, color, thickness);
            }
            await new Promise(resolve => setTimeout(resolve, 100));
        }
    }

    async _drawDroneFollowerRay(playerId) {
        if (this._stopSignal) {  return; }



        await this._drawJsonCommands(playerId, this._DRON_JSON_URL, 'head', 'none', 1.0);
        if (this._stopSignal) return;

        await new Promise(resolve => setTimeout(resolve, 1000));
        if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) return;

        const avatarCenter = this._getTargetCoords(playerId);
        if (!avatarCenter) {
        }

        const droneDurationMs = 8000;
        const startTime = Date.now();
        let frame = 0;
        const orbitRadius = 60;
        const droneSize = 10;
        const droneColor = '#800080';
        const laserColor = '#FF00FF';


        this._playAnimationSound('_drawDroneFollowerRay', 'laser-reload');

        while (Date.now() - startTime < droneDurationMs) {
            if (this._stopSignal || !this._getGameSocket() || (this._repeatIntervalId && !this._ui.repeatActionToggle.checked)) break;
            frame++;
            const currentAvatarCenter = this._getTargetCoords(playerId);
            if (!currentAvatarCenter) {
            }
            const centerX = currentAvatarCenter.x;
            const centerY = currentAvatarCenter.y;
            const droneAngle = frame * 0.1;
            const droneX = centerX + orbitRadius * Math.cos(droneAngle);
            const droneY = centerY + orbitRadius * Math.sin(droneAngle) * 0.5;

            this._sendDrawCommand(droneX - droneSize / 2, droneY - droneSize / 2, droneX + droneSize / 2, droneY + droneSize / 2, droneColor, 3);

            if (frame % 10 === 0) {

                this._playAnimationSound('_drawDroneFollowerRay', 'laser-shot');
                const rayTargetX = centerX + (Math.random() - 0.5) * 20;
                const rayTargetY = centerY + (Math.random() - 0.5) * 20;
                this._sendDrawCommand(droneX, droneY, rayTargetX, rayTargetY, laserColor, 2);
            }
            await new Promise(resolve => setTimeout(resolve, 80));
        }
    }




    setSoundEffectsState(enable, playSoundFn = null) {
        this._soundEffectsEnabled = enable;
        if (enable && playSoundFn) {
            this._externalPlaySoundFn = playSoundFn;
            this._hookAnimationMethods();

        } else {
            this._unhookAnimationMethods();
            this._externalPlaySoundFn = null;

        }
    }


    _playAnimationSound(methodName, soundPhase) {
        if (this._soundEffectsEnabled && this._externalPlaySoundFn && !this._stopSignal) {

            const soundEntry = this._animationSoundMap[methodName]?.find(e => e.soundPhase === soundPhase);
            if (soundEntry) {
                this._externalPlaySoundFn(soundEntry.sound);

            }
        }
    }


    _hookAnimationMethods() {

        Object.keys(this._animationSoundMap).forEach(methodName => {
            if (typeof this[methodName] === 'function' && !this._originalAnimationMethods.has(methodName)) {


                const originalMethod = this[methodName].bind(this);
                this._originalAnimationMethods.set(methodName, originalMethod);

                this[methodName] = (async (...args) => {


                    const soundEvents = this._animationSoundMap[methodName];
                    const soundEffectsEnabled = this._soundEffectsEnabled;
                    const externalPlaySoundFn = this._externalPlaySoundFn;

                    const timeoutIds = [];
                    soundEvents.forEach(event => {
                        const timeoutId = setTimeout(() => {
                            if (soundEffectsEnabled && externalPlaySoundFn && !this._stopSignal) {

                                externalPlaySoundFn(event.sound);
                            } else {

                            }
                        }, event.delay);

                        timeoutIds.push(timeoutId);
                        this._activeAnimationSoundTimeouts.add(timeoutId);
                    });

                    try {
                        const result = await originalMethod(...args);
                        return result;
                    } catch (error) {
                        this._cleanupAnimationTimeouts(timeoutIds);
                        throw error;
                    } finally {

                        setTimeout(() => {
                            this._cleanupAnimationTimeouts(timeoutIds);
                        }, Math.max(...soundEvents.map(e => e.delay)) + 1000);
                    }
                }).bind(this);


            } else if (!this[methodName]) {

            }
        });

    }


    _unhookAnimationMethods() {

        if (this._originalAnimationMethods) {
            this._originalAnimationMethods.forEach((originalMethod, methodName) => {

                this[methodName] = originalMethod;
            });
            this._originalAnimationMethods.clear();
        }
        if (this._activeAnimationSoundTimeouts) {
            this._activeAnimationSoundTimeouts.forEach(timeoutId => clearTimeout(timeoutId));
            this._activeAnimationSoundTimeouts.clear();

        }

    }


    _cleanupAnimationTimeouts(timeoutIds) {
        timeoutIds.forEach(id => {
            clearTimeout(id);
            if (this._activeAnimationSoundTimeouts) {
                this._activeAnimationSoundTimeouts.delete(id);
            }
        });
    }
}



class DraggableActionMenuTool extends QBit {
    _reportSpamInterval = null;
    _rulesSpamInterval = null;
    _afkSpamInterval = null;
    _allSpamCombinedInterval = null;




    _ui = {};


    _soundEffectsActive = false;
    _soundButtonsData = [
        { name: 'laugh', url: 'https://www.myinstants.com/media/sounds/sonic-exe-laugh.mp3', shortcut: 'Alt+1' },
        { name: 'troll', url: 'https://www.myinstants.com/media/sounds/funny_82hiegE.mp3', shortcut: 'Alt+2' },
        { name: 'punch', url: 'https://www.myinstants.com/media/sounds/punch_u4LmMsr.mp3', shortcut: 'Alt+3' },
        { name: 'boom', url: 'https://www.myinstants.com/media/sounds/vine-boom.mp3', shortcut: 'Alt+4' },
        { name: 'nope', url: 'https://www.myinstants.com/media/sounds/engineer_no01_1.mp3', shortcut: 'Alt+5' },
        { name: 'yeay', url: 'https://www.myinstants.com/media/sounds/yeay-childrens.mp3', shortcut: 'Alt+6' },
        { name: 'wow', url: 'https://www.myinstants.com/media/sounds/anime-wow-sound-effect.mp3', shortcut: 'Alt+7' },
        { name: 'nani', url: 'https://www.myinstants.com/media/sounds/nani_-meme-sound-effect-su0k4q3yrfw-mp3cut.mp3', shortcut: 'Alt+8' },
        { name: 'lose', url: 'https://www.myinstants.com/media/sounds/mario-bros-lose-life.mp3', shortcut: 'Alt+9' }
    ];

    _WeaponsButtonsData = [
        { name: 'whoosh', url: 'https://www.myinstants.com/media/sounds/whoosh-wind.mp3', shortcut: 'Alt+0' },
        { name: 'reload', url: 'https://www.myinstants.com/media/sounds/reloading.mp3', shortcut: 'Alt+Q' },
        { name: 'shoot', url: 'https://www.myinstants.com/media/sounds/shooooot.mp3', shortcut: 'Alt+W' },
        { name: 'explosion', url: 'https://www.myinstants.com/media/sounds/explosion_inwsgmG.mp3', shortcut: 'Alt+E' },
        { name: 'cannon', url: 'https://www.myinstants.com/media/sounds/cannon.mp3', shortcut: 'Alt+R' },
        { name: 'mega-explosion', url: 'https://www.myinstants.com/media/sounds/trexp.mp3', shortcut: 'Alt+T' },
        { name: 'laser-reload', url: 'https://www.myinstants.com/media/sounds/laserr.mp3', shortcut: 'Alt+Y' },
        { name: 'laser-shot', url: 'https://www.myinstants.com/media/sounds/pixel-gun-3d-ultimatum_WMig2NH.mp3', shortcut: 'Alt+U' },
        { name: 'fire', url: 'https://www.myinstants.com/media/sounds/fuego-brotando.mp3', shortcut: 'Alt+I' },
        { name: 'sword-slash', url: 'https://www.myinstants.com/media/sounds/sword-slash_NkdUSLZ.mp3', shortcut: 'Alt+O' },
        { name: 'sword-mistery', url: 'https://www.myinstants.com/media/sounds/zelda-chest-opening-and-item-catch.mp3', shortcut: 'Alt+P' },
        { name: 'build', url: 'https://www.myinstants.com/media/sounds/tower-built-2_o6EWKdO.mp3', shortcut: 'Alt+A' },
        { name: 'coin-collect', url: 'https://www.myinstants.com/media/sounds/mario-coin-sound.mp3', shortcut: 'Alt+L' }
    ];

    _soundPlayers = new Map();
    _currentPlayingSound = null;

    constructor() {
        super("🎵Epic Sounds", '<i class="fas fa-music"></i>');
        this._onStartup();
    }

    _onStartup() {
        this._loadInterface();
        this._setupEventListeners();



    this._ui.soundEffectorToggleButton.click();

    }

    _loadInterface() {
        const container = domMake.Tree("div", { id: `${this.identifier}-container`, class: "module-section" });
        this.htmlElements.section.appendChild(container);


        container.appendChild(domMake.Tree("div", { class: "module-section-title", style: "margin-top: 15px;" }, ["Activate Sounds"]));


        const soundEffectorToggleGroup = domMake.Row({ class: "module-btn-group", style: "margin-bottom: 10px;" });
        this._ui.soundEffectorToggleButton = domMake.Button('<i class="fas fa-volume-up"></i> Activar Efectos de Sonido');
        this._ui.soundEffectorToggleButton.id = `${this.identifier}-soundEffectorToggle`;
        this._ui.soundEffectorToggleButton.classList.add('module-toggle-button');
        this._ui.soundEffectorToggleButton.addEventListener('click', () => this._toggleSoundEffector(this._ui.soundEffectorToggleButton));
        soundEffectorToggleGroup.appendChild(this._ui.soundEffectorToggleButton);
        container.appendChild(soundEffectorToggleGroup);


        const soundSectionsContainer = domMake.Tree("div", { class: "sound-sections-container", style: "margin-top: 10px;" });


        const createCollapsibleSection = (title, soundsData, isExpanded = false) => {
            const sectionWrapper = domMake.Tree("div", { class: "sound-section-wrapper", style: "margin-bottom: 8px; border: 1px solid var(--CE-color); border-radius: 6px; overflow: hidden;" });


            const sectionHeader = domMake.Tree("div", {
                class: "sound-section-header",
                style: "background: var(--CE-bg_color); padding: 8px 12px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; user-select: none;"
            });

            const headerTitle = domMake.Tree("span", { style: "font-weight: bold; font-size: 0.9em;" }, [title]);
            const headerIcon = domMake.Tree("i", { class: isExpanded ? "fas fa-chevron-up" : "fas fa-chevron-down", style: "font-size: 0.8em;" });

            sectionHeader.appendChild(headerTitle);
            sectionHeader.appendChild(headerIcon);


            const sectionContent = domMake.Tree("div", {
                class: "sound-section-content",
                style: `display: ${isExpanded ? 'block' : 'none'}; padding: 10px; background: rgba(var(--CE-color-rgb), 0.05);`
            });


            const buttonsGrid = domMake.Tree("div", {
                class: "sound-buttons-grid",
                style: "display: grid; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr)); gap: 6px; margin-bottom: 8px;"
            });

            soundsData.forEach(sound => {
                const soundBtn = domMake.Button(sound.name.charAt(0).toUpperCase() + sound.name.slice(1));
                soundBtn.id = `${this.identifier}-sound-${sound.name}`;
                soundBtn.classList.add('artfx-button');
                soundBtn.setAttribute('data-sound-name', sound.name);
                soundBtn.style.cssText = "font-size: 0.75em; padding: 4px 6px; min-height: 28px;";
                soundBtn.addEventListener('click', () => this._playSound(sound.name));
                buttonsGrid.appendChild(soundBtn);


                if (!this._ui.soundButtons) this._ui.soundButtons = {};
                this._ui.soundButtons[sound.name] = soundBtn;
            });

            sectionContent.appendChild(buttonsGrid);


            const shortcutsInfo = domMake.Tree("div", {
                style: "font-size: 0.7em; color: var(--CE-color); opacity: 0.8; line-height: 1.3;"
            });
            const shortcutsText = soundsData.map(s => `<span style="white-space: nowrap;"><b>${s.name}:</b> ${s.shortcut}</span>`).join(' • ');
            shortcutsInfo.innerHTML = shortcutsText;
            sectionContent.appendChild(shortcutsInfo);


            sectionHeader.addEventListener('click', () => {
                const isCurrentlyExpanded = sectionContent.style.display !== 'none';
                sectionContent.style.display = isCurrentlyExpanded ? 'none' : 'block';
                headerIcon.className = isCurrentlyExpanded ? "fas fa-chevron-down" : "fas fa-chevron-up";
            });

            sectionWrapper.appendChild(sectionHeader);
            sectionWrapper.appendChild(sectionContent);

            return sectionWrapper;
        };


        const basicEffectsSection = createCollapsibleSection("🎵 Efectos Básicos", this._soundButtonsData, false);
        const weaponsEffectsSection = createCollapsibleSection("⚔️ Efectos de Armas", this._WeaponsButtonsData, false);

        soundSectionsContainer.appendChild(basicEffectsSection);
        soundSectionsContainer.appendChild(weaponsEffectsSection);
        container.appendChild(soundSectionsContainer);


        const globalShortcutsWrapper = domMake.Tree("div", {
            class: "shortcuts-summary-wrapper",
            style: "margin-top: 10px; border: 1px solid rgba(var(--CE-color-rgb), 0.3); border-radius: 4px; overflow: hidden;"
        });

        const shortcutsHeader = domMake.Tree("div", {
            class: "shortcuts-header",
            style: "background: rgba(var(--CE-color-rgb), 0.1); padding: 6px 10px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; user-select: none;"
        });

        const shortcutsTitle = domMake.Tree("span", { style: "font-size: 0.8em; font-weight: bold;" }, ["📋 Resumen de Atajos"]);
        const shortcutsIcon = domMake.Tree("i", { class: "fas fa-chevron-down", style: "font-size: 0.7em;" });

        shortcutsHeader.appendChild(shortcutsTitle);
        shortcutsHeader.appendChild(shortcutsIcon);

        const shortcutsContent = domMake.Tree("div", {
            class: "shortcuts-content",
            style: "display: none; padding: 8px; font-size: 0.7em; line-height: 1.4; color: var(--CE-color); background: rgba(var(--CE-color-rgb), 0.02);"
        });


        const basicShortcuts = this._soundButtonsData.map(s => `<span style="white-space: nowrap;"><b>${s.name}:</b> ${s.shortcut}</span>`).join(' • ');
        const weaponsShortcuts = this._WeaponsButtonsData.map(s => `<span style="white-space: nowrap;"><b>${s.name}:</b> ${s.shortcut}</span>`).join(' • ');

        shortcutsContent.innerHTML = `
            <div style="margin-bottom: 6px;"><b>🎵 Básicos:</b><br>${basicShortcuts}</div>
            <div><b>⚔️ Armas:</b><br>${weaponsShortcuts}</div>
        `;


        shortcutsHeader.addEventListener('click', () => {
            const isCurrentlyExpanded = shortcutsContent.style.display !== 'none';
            shortcutsContent.style.display = isCurrentlyExpanded ? 'none' : 'block';
            shortcutsIcon.className = isCurrentlyExpanded ? "fas fa-chevron-down" : "fas fa-chevron-up";
        });

        globalShortcutsWrapper.appendChild(shortcutsHeader);
        globalShortcutsWrapper.appendChild(shortcutsContent);
        container.appendChild(globalShortcutsWrapper);


        container.appendChild(domMake.Tree("div", { class: "module-section-title" }, ["Acciones Directas"]));

        const createActionButton = (id, text, actionFn) => {
            const button = domMake.Button(text);
            button.id = `${this.identifier}-${id}`;
            button.addEventListener('click', actionFn);
            return button;
        };


        const createSimpleSpamToggle = (id, labelText) => {
            const wrapper = domMake.Tree("div", { class: "module-form-group" });
            const inputToggle = domMake.Tree("input", { type: "checkbox", id: `${this.identifier}-${id}SpamToggle` });
            const labelToggle = domMake.Tree("label", { for: `${this.identifier}-${id}SpamToggle`, style: "font-size: 0.9em; color: var(--CE-color); flex-shrink: 0;" }, [labelText]);

            wrapper.appendAll(inputToggle, labelToggle);
            this._ui[`${id}SpamToggle`] = inputToggle;
            return wrapper;
        };



        const reportGroup = domMake.Row({class: "module-btn-group"});
        this._ui.reportButton = createActionButton('report-button', 'Reportar', this._handleReport.bind(this));
        reportGroup.appendChild(this._ui.reportButton);
        container.appendChild(reportGroup);
        container.appendChild(createSimpleSpamToggle('report', 'Spam Reportar (700ms)'));


        const rulesGroup = domMake.Row({class: "module-btn-group"});
        this._ui.rulesButton = createActionButton('rules-button', 'Reglas', this._handleRules.bind(this));
        rulesGroup.appendChild(this._ui.rulesButton);
        container.appendChild(rulesGroup);
        container.appendChild(createSimpleSpamToggle('rules', 'Spam Reglas (700ms)'));


        const afkGroup = domMake.Row({class: "module-btn-group"});
        this._ui.afkButton = createActionButton('afk-button', 'Estados AFK', this._handleToggleAFK.bind(this));
        afkGroup.appendChild(this._ui.afkButton);
        container.appendChild(afkGroup);
        container.appendChild(createSimpleSpamToggle('afk', 'Spam Estados AFK (700ms)'));


        const allSpamGroup = domMake.Row({class: "module-btn-group", style: "margin-top: 15px;"});
        this._ui.allSpamToggle = domMake.Button('Spam TODO');
        this._ui.allSpamToggle.id = `${this.identifier}-allSpamToggle`;
        this._ui.allSpamToggle.classList.add('module-toggle-button');
        allSpamGroup.appendChild(this._ui.allSpamToggle);
        container.appendChild(allSpamGroup);


        this._setSoundEffectorUIEnabled(false);
    }

    _setupEventListeners() {





        this._ui.allSpamToggle.addEventListener('click', () => {
            const isCurrentlyActive = this._ui.allSpamToggle.classList.contains('active');
            this._toggleAllSpam(!isCurrentlyActive, this._ui.allSpamToggle);
        });


        document.addEventListener('keydown', this._handleSoundShortcut.bind(this));
    }


    _handleReport() {
        const socket = getGameSocket();
        if (socket && socket.readyState === WebSocket.OPEN) {
            socket.send(`42["clientnotify",-1,2,["", "Generic Report"]]`);
            return true;
        }
        return false;
    }

    _handleRules() {
        const socket = getGameSocket();
        if (socket && socket.readyState === WebSocket.OPEN) {
            socket.send(`42["clientnotify",-1,100,[2]]`);
            return true;
        }
        return false;
    }

    _handleToggleAFK() {
        const socket = getGameSocket();
        if (socket && socket.readyState === WebSocket.OPEN) {
            socket.send(`42["playerafk"]`);
            return true;
        }
        return false;
    }



    _startIndividualSpamInterval(type) {
        let intervalIdField;
        let actionFunction;
        const delay = 700;

        switch(type) {
            case 'report':
                intervalIdField = '_reportSpamInterval';
                actionFunction = this._handleReport.bind(this);
                break;
            case 'rules':
                intervalIdField = '_rulesSpamInterval';
                actionFunction = this._handleRules.bind(this);
                break;
            case 'afk':
                intervalIdField = '_afkSpamInterval';
                actionFunction = this._handleToggleAFK.bind(this);
                break;
            default:
                console.error(`DraggableActionMenuTool: Tipo de spam individual desconocido: ${type}`);
                return;
        }

        if (this[intervalIdField]) clearInterval(this[intervalIdField]);

        this[intervalIdField] = setInterval(() => {
            if (getGameSocket() && getGameSocket().readyState === WebSocket.OPEN) {
                actionFunction();
            } else {
                console.error(`DraggableActionMenuTool: Conexión perdida, deteniendo spam de ${type}.`);
                this._stopIndividualSpamInterval(type);

                if (this._ui[`${type}SpamToggle`]) {
                    this._ui[`${type}SpamToggle`].checked = false;
                }
            }
        }, delay);

    }


    _stopIndividualSpamInterval(type) {
        let intervalIdField;
        switch(type) {
            case 'report': intervalIdField = '_reportSpamInterval'; break;
            case 'rules': intervalIdField = '_rulesSpamInterval'; break;
            case 'afk': intervalIdField = '_afkSpamInterval'; break;
            default: return;
        }

        if (this[intervalIdField]) {
            clearInterval(this[intervalIdField]);
            this[intervalIdField] = null;

        }
    }





    _toggleAllSpam(enable, button) {
        const types = ['report', 'rules', 'afk'];

        if (enable) {
            const socket = getGameSocket();
            if (!socket || socket.readyState !== WebSocket.OPEN) {

                button.classList.remove('active');
                button.textContent = 'Spam TODO';
                return;
            }

            button.classList.add('active');
            button.textContent = 'Detener Spam TODO';


            types.forEach(type => {
                this._startIndividualSpamInterval(type);
                if (this._ui[`${type}SpamToggle`]) {
                    this._ui[`${type}SpamToggle`].checked = true;
                }
            });

        } else {
            button.classList.remove('active');
            button.textContent = 'Spam TODO';


            types.forEach(type => {
                this._stopIndividualSpamInterval(type);
                if (this._ui[`${type}SpamToggle`]) {
                    this._ui[`${type}SpamToggle`].checked = false;
                }
            });
        }
    }


    _toggleSoundEffector(button) {
        this._soundEffectsActive = !this._soundEffectsActive;
        if (this._soundEffectsActive) {
            button.innerHTML = '<i class="fas fa-volume-up"></i> Desactivar Efectos de Sonido';
            button.classList.add('active');

            this._preloadSounds();
            this._setSoundEffectorUIEnabled(true);


            if (window.animatorToolInstance && typeof window.animatorToolInstance.setSoundEffectsState === 'function') {
                window.animatorToolInstance.setSoundEffectsState(true, this._playSound.bind(this));
            } else {
                console.warn("DraggableActionMenuTool: Instancia de AnimatorTool no encontrada o no lista para integrar sonidos.");
            }

        } else {
            button.innerHTML = '<i class="fas fa-volume-up"></i> Activar Efectos de Sonido';
            button.classList.remove('active');

            this._unloadSounds();
            this._setSoundEffectorUIEnabled(false);


            if (window.animatorToolInstance && typeof window.animatorToolInstance.setSoundEffectsState === 'function') {
                window.animatorToolInstance.setSoundEffectsState(false);
            }
        }
    }

    _preloadSounds() {
        this._soundPlayers.clear();


        const allSounds = [...this._soundButtonsData, ...this._WeaponsButtonsData];

        allSounds.forEach(sound => {
            const audio = new Audio(sound.url);
            audio.volume = 0.5;
            audio.preload = 'auto';

            this._soundPlayers.set(sound.name, audio);
        });

    }

    _unloadSounds() {
        if (this._currentPlayingSound) {
            this._currentPlayingSound.pause();
            this._currentPlayingSound.currentTime = 0;
            this._currentPlayingSound = null;
        }
        this._soundPlayers.forEach(audio => {
            audio.pause();
            audio.currentTime = 0;



        });
        this._soundPlayers.clear();

    }

    _playSound(soundName) {
        if (!this._soundEffectsActive) {

            return;
        }
        const audio = this._soundPlayers.get(soundName);
        if (audio) {
            if (this._currentPlayingSound && this._currentPlayingSound !== audio) {
                this._currentPlayingSound.pause();
                this._currentPlayingSound.currentTime = 0;
            }
            audio.currentTime = 0;
            audio.play().then(() => {

                this._currentPlayingSound = audio;
            }).catch(e => {

                console.error(`Error playing sound ${soundName}:`, e);
            });
        } else {

            console.warn(`Sound "${soundName}" not found or not preloaded.`);
        }
    }

    _handleSoundShortcut(event) {
        if (!this._soundEffectsActive) return;

        if (event.altKey) {
            event.preventDefault();


            const shortcutMap = new Map();


            this._soundButtonsData.forEach(sound => {
                const key = sound.shortcut.split('+')[1];
                shortcutMap.set(key, sound.name);
            });


            this._WeaponsButtonsData.forEach(sound => {
                const key = sound.shortcut.split('+')[1];
                shortcutMap.set(key, sound.name);
            });


            const pressedKey = event.key.toUpperCase();
            if (shortcutMap.has(pressedKey)) {
                const soundName = shortcutMap.get(pressedKey);
                this._playSound(soundName);
            }
        }
    }

    _setSoundEffectorUIEnabled(enabled) {

        const soundButtons = this.htmlElements.section.querySelectorAll('.sound-buttons-grid button');
        soundButtons.forEach(button => {
            button.disabled = !enabled;
        });
    }
}



class LayerAnimationTool extends QBit {
    constructor() {
        super("🧾Layer Animator", '<i class="fas fa-layer-group"></i>');


        this.layers = [{ commands: [], visible: true, name: 'Layer 1' }];
        this.currentLayer = 0;
        this.isDrawing = false;
        this.lastX = 0;
        this.lastY = 0;
        this.isAnimating = false;
        this.animationSpeed = 50;
        this.isLoopEnabled = false;
        this.highQualityImport = false;
        this.pixelProcessingStep = 2;
        this.targetImageResolution = 100;
        this.onionOpacity = 0.3;


        this.gameCanvas = null;
        this.gameCtx = null;
        this.previewCanvas = null;
        this.previewCtx = null;


        this._ui = {
            previewContainer: null,
            layerInfo: null,
            prevLayer: null,
            nextLayer: null,
            addLayer: null,
            removeLayer: null,
            clearLayer: null,
            duplicateLayer: null,
            toggleLoopBtn: null,
            toggleQualityBtn: null,
            importImageBtn: null,
            loadPokemonBtn: null,
            imageFileInput: null,
            onionToggle: null,
            onionRange: null,
            playBtn: null,
            stopBtn: null,
            speedRange: null,
            speedValue: null,
            layerList: null,
            socketIndicator: null
        };

        this._onStartup();
    }

    _onStartup() {
        this._findGameCanvas();
        this._setupPreviewCanvas();
        this._loadInterface();
        this._setupEventListeners();
        this._updateUI();


        setInterval(() => this._updateSocketStatus(), 3000);

        this.notify("info", "Layer Animation System cargado correctamente. ¡Comienza a animar!");
    }

    _findGameCanvas() {
        this.gameCanvas = document.getElementById('canvas');
        if (this.gameCanvas) {
            this.gameCtx = this.gameCanvas.getContext('2d');
        } else {
            this.notify("warning", "Canvas del juego no encontrado.");
        }
    }

    _setupPreviewCanvas() {
        this.previewCanvas = document.createElement('canvas');
        this.previewCtx = this.previewCanvas.getContext('2d');
        this.previewCanvas.width = 220;
        this.previewCanvas.height = 160;
        this.previewCanvas.style.cssText = `
            width: 100%;
            height: 120px;
            border: 2px solid #555;
            border-radius: 5px;
            background: white;
            cursor: crosshair;
            display: block;
        `;

        this.previewCtx.lineCap = 'round';
        this.previewCtx.lineJoin = 'round';
    }

    _loadInterface() {

        this._addStyles();

        const container = domMake.Tree("div", { class: "layer-animation-section" });
        this.htmlElements.section.appendChild(container);


        const header = domMake.Tree("div", { class: "layer-header" });
        header.appendChild(domMake.Tree("span", {}, ["Layer Animator "]));
        this._ui.socketIndicator = domMake.Tree("span", { class: "socket-indicator" }, ["⚫"]);
        header.appendChild(this._ui.socketIndicator);



        this._ui.previewContainer = domMake.Tree("div", { class: "preview-container" });
        this._ui.previewContainer.appendChild(this.previewCanvas);
        container.appendChild(this._ui.previewContainer);


        const layerNav = domMake.Tree("div", { class: "layer-nav" });
        this._ui.prevLayer = domMake.Button("◀");
        this._ui.layerInfo = domMake.Tree("span", { class: "layer-info" }, ["1/1"]);
        this._ui.nextLayer = domMake.Button("▶");
        layerNav.appendAll(this._ui.prevLayer, this._ui.layerInfo, this._ui.nextLayer);
        container.appendChild(layerNav);


        const actions = domMake.Tree("div", { class: "layer-actions" });
        this._ui.addLayer = domMake.Button("➕");
        this._ui.addLayer.title = "Add Layer";
        this._ui.removeLayer = domMake.Button("➖");
        this._ui.removeLayer.title = "Remove Layer";
        this._ui.clearLayer = domMake.Button("🗑️");
        this._ui.clearLayer.title = "Clear Layer";
        this._ui.duplicateLayer = domMake.Button("📋");
        this._ui.duplicateLayer.title = "Duplicate Layer";
        this._ui.toggleLoopBtn = domMake.Button("🔄");
        this._ui.toggleLoopBtn.title = "Toggle Loop Animation";
        this._ui.toggleQualityBtn = domMake.Button("⭐");
        this._ui.toggleQualityBtn.title = "Toggle High Quality Import";
        this._ui.importImageBtn = domMake.Button("🏞️");
        this._ui.importImageBtn.title = "Import Image(s)";
        this._ui.loadPokemonBtn = domMake.Button("🎮");
        this._ui.loadPokemonBtn.title = "Load Random Pokemon";
        this._ui.loadPokemonBtn.className = "pokemon-btn";

        this._ui.imageFileInput = domMake.Tree("input", {
            type: "file",
            multiple: true,
            accept: "image/*",
            style: "display: none;"
        });

        actions.appendAll(
            this._ui.addLayer, this._ui.removeLayer, this._ui.clearLayer,
            this._ui.duplicateLayer, this._ui.toggleLoopBtn, this._ui.toggleQualityBtn,
            this._ui.importImageBtn, this._ui.loadPokemonBtn, this._ui.imageFileInput
        );
        container.appendChild(actions);


        const onionControls = domMake.Tree("div", { class: "onion-controls" });
        const onionLabel = domMake.Tree("label");
        this._ui.onionToggle = domMake.Tree("input", { type: "checkbox", checked: true });
        onionLabel.appendChild(this._ui.onionToggle);
        onionLabel.appendChild(domMake.Tree("span", {}, [" Onion Skin"]));
        this._ui.onionRange = domMake.Tree("input", { type: "range", min: "10", max: "100", value: "30" });
        onionControls.appendAll(onionLabel, this._ui.onionRange);
        container.appendChild(onionControls);


        const animControls = domMake.Tree("div", { class: "anim-controls" });
        this._ui.playBtn = domMake.Button("▶️ PLAY");
        this._ui.playBtn.className = "play-btn";
        this._ui.stopBtn = domMake.Button("⏹️ STOP");
        this._ui.stopBtn.className = "stop-btn";
        this._ui.stopBtn.disabled = true;

        const speedContainer = domMake.Tree("div", { class: "speed-control" });
        const speedLabel = domMake.Tree("label", { class: "speed-label" });
        speedLabel.appendChild(domMake.Tree("span", {}, ["Speed: "]));
        this._ui.speedValue = domMake.Tree("span", {}, ["18"]);
        speedLabel.appendChild(this._ui.speedValue);
        speedLabel.appendChild(domMake.Tree("span", {}, ["ms"]));
        this._ui.speedRange = domMake.Tree("input", {
            type: "range",
            min: "1",
            max: "500",
            value: "18",
            class: "speed-slider"
        });
        speedContainer.appendAll(speedLabel, this._ui.speedRange);

        animControls.appendAll(this._ui.playBtn, this._ui.stopBtn, speedContainer);
        container.appendChild(animControls);


        this._ui.layerList = domMake.Tree("div", { class: "layer-list" });
        container.appendChild(this._ui.layerList);
    }

    _addStyles() {
        const css = `
.layer-animation-section {
    background: linear-gradient(145deg, #ffffff, #f8f9fa);
    border: 2px solid #ffffff;
    border-radius: 10px;
    padding: 12px;
    color: #495057;
    font-size: 12px;
    font-family: Arial, sans-serif;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.layer-header {
    text-align: center;
    font-weight: bold;
    margin-bottom: 10px;
    color: #007bff;
    border-bottom: 1px solid #dee2e6;
    padding-bottom: 5px;
}

.socket-indicator.connected { color: #28a745; }
.socket-indicator.disconnected { color: #dc3545; }

.preview-container {
    margin-bottom: 10px;
}

.layer-nav {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 12px;
    margin-bottom: 10px;
}

.layer-nav button {
    background: #007bff;
    border: none;
    color: white;
    padding: 6px 10px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    transition: background 0.3s;
    box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}

.layer-nav button:hover:not(:disabled) {
    background: #0056b3;
    box-shadow: 0 2px 6px rgba(0,0,0,0.15);
}
.layer-nav button:disabled {
    background: #adb5bd;
    cursor: not-allowed;
    box-shadow: none;
}

.layer-info {
    background: #e9ecef;
    padding: 6px 12px;
    border-radius: 4px;
    min-width: 50px;
    text-align: center;
    font-size: 11px;
    font-weight: bold;
    color: #495057;
    border: 1px solid #ced4da;
}

.layer-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    margin-bottom: 10px;
}

.layer-actions button {
    background: #f8f9fa;
    border: 1px solid #ced4da;
    color: #495057;
    padding: 6px 8px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    transition: all 0.2s;
    min-width: 30px;
}

.layer-actions button:hover:not(:disabled) {
    background: #e9ecef;
    border-color: #adb5bd;
    transform: scale(1.05);
    box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}

.layer-actions button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.layer-actions button.active {
    background: #007bff;
    color: white;
    border-color: #0056b3;
    box-shadow: 0 2px 4px rgba(0,123,255,0.25);
}

.pokemon-btn {
    background: linear-gradient(45deg, #fd7e14, #e66100) !important;
    color: white !important;
    border: none !important;
    animation: pokemonGlow 2s infinite alternate;
}

.pokemon-btn:hover:not(:disabled) {
    background: linear-gradient(45deg, #e66100, #fd7e14) !important;
    transform: scale(1.1) !important;
}

.pokemon-btn.loading {
    animation: pokemonSpin 1s linear infinite;
}

@keyframes pokemonGlow {
    0% { box-shadow: 0 0 5px rgba(253,126,20,0.5); }
    100% { box-shadow: 0 0 15px rgba(253,126,20,0.8); }
}

@keyframes pokemonSpin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

.onion-controls {
    background: #f8f9fa;
    border: 1px solid #dee2e6;
    padding: 8px;
    border-radius: 5px;
    margin-bottom: 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.onion-controls label {
    font-size: 11px;
    color: #495057;
}
.onion-controls input[type="range"] {
    width: 70px;
    accent-color: #007bff;
}

.anim-controls {
    margin-bottom: 10px;
}

.play-btn, .stop-btn {
    width: 100%;
    padding: 10px;
    margin-bottom: 8px;
    border: none;
    border-radius: 6px;
    font-size: 12px;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.3s;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.play-btn {
    background: linear-gradient(45deg, #28a745, #218838);
    color: white;
}

.stop-btn {
    background: linear-gradient(45deg, #dc3545, #c82333);
    color: white;
}

.play-btn:hover:not(:disabled), .stop-btn:hover:not(:disabled) {
    transform: scale(1.02);
    box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}

.play-btn:disabled, .stop-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

.speed-control {
    background: #f8f9fa;
    border: 1px solid #dee2e6;
    padding: 8px;
    border-radius: 5px;
    text-align: center;
}

.speed-label {
    display: block;
    font-size: 11px;
    margin-bottom: 5px;
    color: #6c757d;
}

.speed-slider {
    width: 100%;
    height: 4px;
    background: #dee2e6;
    outline: none;
    border-radius: 2px;
    cursor: pointer;
    accent-color: #007bff;
}

.layer-list {
    max-height: 120px;
    overflow-y: auto;
    background: #ffffff;
    border: 1px solid #dee2e6;
    border-radius: 5px;
    padding: 4px;
}

.layer-item {
    padding: 5px 8px;
    border-bottom: 1px solid #f1f3f4;
    cursor: pointer;
    font-size: 10px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-radius: 3px;
    transition: background 0.2s;
    color: #495057;
}

.layer-item:hover {
    background: #f8f9fa;
}
.layer-item.active {
    background: #007bff;
    color: white;
    font-weight: bold;
}
.layer-item.has-content {
    border-left: 3px solid #28a745;
}

.layer-visibility {
    cursor: pointer;
    padding: 2px 4px;
    border-radius: 2px;
    background: #e9ecef;
    border: 1px solid #ced4da;
    font-size: 10px;
    color: #495057;
    transition: all 0.2s;
}

.layer-visibility:hover {
    background: #dee2e6;
}
        `;

        const style = document.createElement('style');
        style.textContent = css;
        document.head.appendChild(style);
    }

    _setupEventListeners() {

        this.previewCanvas.addEventListener('mousedown', (e) => this._startDrawing(e));
        this.previewCanvas.addEventListener('mousemove', (e) => this._draw(e));
        this.previewCanvas.addEventListener('mouseup', () => this._stopDrawing());
        this.previewCanvas.addEventListener('mouseout', () => this._stopDrawing());


        this._ui.prevLayer.addEventListener('click', () => this._previousLayer());
        this._ui.nextLayer.addEventListener('click', () => this._nextLayer());


        this._ui.addLayer.addEventListener('click', () => this._addLayer());
        this._ui.removeLayer.addEventListener('click', () => this._removeLayer());
        this._ui.clearLayer.addEventListener('click', () => this._clearCurrentLayer());
        this._ui.duplicateLayer.addEventListener('click', () => this._duplicateCurrentLayer());
        this._ui.toggleLoopBtn.addEventListener('click', () => this._toggleLoop());
        this._ui.toggleQualityBtn.addEventListener('click', () => this._toggleQuality());
        this._ui.importImageBtn.addEventListener('click', () => this._importImages());
        this._ui.loadPokemonBtn.addEventListener('click', () => this._loadRandomPokemon());
        this._ui.imageFileInput.addEventListener('change', (e) => this._handleImageFiles(e.target.files));


        this._ui.onionToggle.addEventListener('change', () => this._updateOnionSkin());
        this._ui.onionRange.addEventListener('input', (e) => {
            this.onionOpacity = e.target.value / 100;
            this._updateOnionSkin();
        });


        this._ui.playBtn.addEventListener('click', () => this._playRealAnimation());
        this._ui.stopBtn.addEventListener('click', () => this._stopAnimation());
        this._ui.speedRange.addEventListener('input', (e) => {
            this.animationSpeed = parseInt(e.target.value);
            this._ui.speedValue.textContent = this.animationSpeed;
        });
    }


    _getMousePos(e) {
        const rect = this.previewCanvas.getBoundingClientRect();
        const scaleX = this.previewCanvas.width / rect.width;
        const scaleY = this.previewCanvas.height / rect.height;
        return {
            x: (e.clientX - rect.left) * scaleX,
            y: (e.clientY - rect.top) * scaleY
        };
    }

    _startDrawing(e) {
        this.isDrawing = true;
        const pos = this._getMousePos(e);
        this.lastX = pos.x;
        this.lastY = pos.y;
    }

    _draw(e) {
        if (!this.isDrawing) return;

        const pos = this._getMousePos(e);
        const command = {
            type: 'line',
            x1: this.lastX,
            y1: this.lastY,
            x2: pos.x,
            y2: pos.y,
            color: this._getCurrentColor(),
            thickness: this._getCurrentThickness()
        };

        this.layers[this.currentLayer].commands.push(command);
        this._drawCommand(this.previewCtx, command);

        this.lastX = pos.x;
        this.lastY = pos.y;
        this._updateLayerList();
    }

    _stopDrawing() {
        this.isDrawing = false;
    }

    _getCurrentColor() {
        const colorPicker = document.querySelector('input[type="color"]');
        return colorPicker ? colorPicker.value : '#000000';
    }

    _getCurrentThickness() {
        const thicknessPicker = document.querySelector('input[type="range"]');
        return thicknessPicker ? parseInt(thicknessPicker.value) : 5;
    }

    _drawCommand(ctx, command) {
        if (command.type === 'line') {
            ctx.strokeStyle = command.color;
            ctx.lineWidth = command.thickness;
            ctx.lineCap = 'round';
            ctx.beginPath();
            ctx.moveTo(command.x1, command.y1);
            ctx.lineTo(command.x2, command.y2);
            ctx.stroke();
        }
    }


    _addLayer() {
        this.layers.push({ commands: [], visible: true, name: `Layer ${this.layers.length + 1}` });
        this.currentLayer = this.layers.length - 1;
        this._updateUI();
        this.notify("success", `Capa ${this.currentLayer + 1} agregada.`);
    }

    _removeLayer() {
        if (this.layers.length <= 1) {
            this.notify("warning", "No se puede eliminar la última capa.");
            return;
        }
        if (this.currentLayer === this.layers.length - 1) {
            this.currentLayer = Math.max(0, this.currentLayer - 1);
        }
        this.layers.splice(this.currentLayer, 1);
        this._updateUI();
        this.notify("info", `Capa eliminada. Capas restantes: ${this.layers.length}`);
    }

    _previousLayer() {
        if (this.currentLayer > 0) {
            this.currentLayer--;
            this._updateUI();
        }
    }

    _nextLayer() {
        if (this.currentLayer < this.layers.length - 1) {
            this.currentLayer++;
            this._updateUI();
        }
    }

    _clearCurrentLayer() {
        this.layers[this.currentLayer].commands = [];
        this._updateOnionSkin();
        this._updateLayerList();
        this.notify("info", `Capa ${this.currentLayer + 1} limpiada.`);
    }

    _duplicateCurrentLayer() {
        const duplicated = {
            commands: [...this.layers[this.currentLayer].commands],
            visible: true,
            name: `${this.layers[this.currentLayer].name} Copy`
        };
        this.layers.push(duplicated);
        this.currentLayer = this.layers.length - 1;
        this._updateUI();
        this.notify("success", `Capa ${this.currentLayer + 1} duplicada.`);
    }

    _toggleLoop() {
        this.isLoopEnabled = !this.isLoopEnabled;
        this._updateLoopButtonState();
        this.notify("info", `Modo bucle: ${this.isLoopEnabled ? 'ACTIVADO' : 'DESACTIVADO'}`);
    }

    _toggleQuality() {
        this.highQualityImport = !this.highQualityImport;
        this.pixelProcessingStep = this.highQualityImport ? 1 : 2;
        this.targetImageResolution = this.highQualityImport ? 150 : 100;
        this._updateQualityButtonState();
        this.notify("info", `Calidad de importación: ${this.highQualityImport ? 'ALTA' : 'NORMAL'}`);
    }


    async _loadRandomPokemon() {
        if (this.isAnimating) {
            this.notify("warning", "No se puede cargar Pokémon durante la animación.");
            return;
        }

        this._ui.loadPokemonBtn.classList.add('loading');
        this._ui.loadPokemonBtn.disabled = true;

        try {
            const randomId = Math.floor(Math.random() * 1010) + 1;
            const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${randomId}`);

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }

            const pokemonData = await response.json();
            const spriteUrl = pokemonData.sprites.front_default;

            if (!spriteUrl) {
                throw new Error('No sprite disponible para este Pokémon');
            }

            this.notify("info", `🎮 Cargando ${pokemonData.name.toUpperCase()} (ID: ${pokemonData.id})`);

            const img = new Image();
            img.crossOrigin = 'anonymous';

            img.onload = async () => {
                try {
                    const newCommands = await this._processImageToCommands(img);
                    if (newCommands.length > 0) {
                        this._addLayer();
                        this.layers[this.currentLayer].name = `🎮 ${pokemonData.name.charAt(0).toUpperCase() + pokemonData.name.slice(1)}`;
                        this.layers[this.currentLayer].commands = newCommands;

                        this.notify("success", `✅ ${pokemonData.name.toUpperCase()} importado exitosamente!`);
                        this._updateUI();
                    } else {
                        this.notify("error", `❌ No se pudieron generar comandos para ${pokemonData.name}`);
                    }
                } catch (error) {
                    this.notify("error", "Error al procesar el sprite del Pokémon");
                    console.error(error);
                } finally {
                    this._ui.loadPokemonBtn.classList.remove('loading');
                    this._ui.loadPokemonBtn.disabled = false;
                }
            };

            img.onerror = () => {
                this.notify("error", `❌ No se pudo cargar el sprite de ${pokemonData.name}`);
                this._ui.loadPokemonBtn.classList.remove('loading');
                this._ui.loadPokemonBtn.disabled = false;
            };

            img.src = spriteUrl;

        } catch (error) {
            this.notify("error", "Error al cargar Pokémon desde PokeAPI");
            console.error(error);
            this._ui.loadPokemonBtn.classList.remove('loading');
            this._ui.loadPokemonBtn.disabled = false;
        }
    }


    _importImages() {
        this._ui.imageFileInput.click();
    }

    async _handleImageFiles(files) {
        if (files.length === 0) return;

        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            if (!file.type.startsWith('image/')) {
                this.notify("warning", `${file.name} no es una imagen válida.`);
                continue;
            }

            const reader = new FileReader();
            reader.onload = async (e) => {
                const img = new Image();
                img.onload = async () => {
                    try {
                        const newCommands = await this._processImageToCommands(img);
                        if (newCommands.length > 0) {
                            this._addLayer();
                            this.layers[this.currentLayer].name = `📷 ${file.name.substring(0, 15)}...`;
                            this.layers[this.currentLayer].commands = newCommands;
                            this.notify("success", `Imagen "${file.name}" importada correctamente.`);
                            this._updateUI();
                        } else {
                            this.notify("error", `No se pudieron generar comandos para "${file.name}".`);
                        }
                    } catch (error) {
                        this.notify("error", "Error al procesar la imagen");
                        console.error(error);
                    }
                };
                img.onerror = () => {
                    this.notify("error", `No se pudo cargar la imagen: ${file.name}`);
                };
                img.src = e.target.result;
            };
            reader.readAsDataURL(file);
        }
    }

    async _processImageToCommands(img) {
        const tempCanvas = document.createElement('canvas');
        const tempCtx = tempCanvas.getContext('2d');

        const maxWidth = this.targetImageResolution;
        const maxHeight = this.targetImageResolution;
        let width = img.width;
        let height = img.height;

        if (width > height) {
            if (width > maxWidth) {
                height *= maxWidth / width;
                width = maxWidth;
            }
        } else {
            if (height > maxHeight) {
                width *= maxHeight / height;
                height = maxHeight;
            }
        }

        tempCanvas.width = width;
        tempCanvas.height = height;
        tempCtx.imageSmoothingEnabled = this.highQualityImport;
        tempCtx.imageSmoothingQuality = this.highQualityImport ? 'high' : 'medium';
        tempCtx.drawImage(img, 0, 0, width, height);

        const imageData = tempCtx.getImageData(0, 0, width, height);
        const data = imageData.data;
        const commands = [];
        const previewWidth = this.previewCanvas.width;
        const previewHeight = this.previewCanvas.height;
        const currentPixelStep = this.pixelProcessingStep;
        const baseThickness = currentPixelStep;

        for (let y = 0; y < height; y += currentPixelStep) {
            let currentSegment = null;

            for (let x = 0; x < width; x += currentPixelStep) {
                const i = (y * width + x) * 4;
                const r = data[i];
                const g = data[i + 1];
                const b = data[i + 2];
                const a = data[i + 3];

                if (a < 50) {
                    if (currentSegment) {
                        commands.push({
                            type: 'line',
                            x1: (currentSegment.startX / width) * previewWidth,
                            y1: (y / height) * previewHeight,
                            x2: (x / width) * previewWidth,
                            y2: (y / height) * previewHeight,
                            color: currentSegment.color,
                            thickness: baseThickness
                        });
                        currentSegment = null;
                    }
                    continue;
                }

                const currentColor = `rgb(${r},${g},${b})`;

                if (currentSegment === null) {
                    currentSegment = { startX: x, color: currentColor };
                } else if (currentSegment.color !== currentColor) {
                    commands.push({
                        type: 'line',
                        x1: (currentSegment.startX / width) * previewWidth,
                        y1: (y / height) * previewHeight,
                        x2: (x / width) * previewWidth,
                        y2: (y / height) * previewHeight,
                        color: currentSegment.color,
                        thickness: baseThickness
                    });
                    currentSegment = { startX: x, color: currentColor };
                }
            }

            if (currentSegment) {
                commands.push({
                    type: 'line',
                    x1: (currentSegment.startX / width) * previewWidth,
                    y1: (y / height) * previewHeight,
                    x2: (width / width) * previewWidth,
                    y2: (y / height) * previewHeight,
                    color: currentSegment.color,
                    thickness: baseThickness
                });
            }
        }

        return commands;
    }


    async _playRealAnimation() {
        const gameSocket = getGameSocket();
        if (!this.gameCanvas || !gameSocket || this.isAnimating) {
            this.notify("warning", "Canvas o WebSocket no disponible o animación ya en curso.");
            return;
        }

        this.isAnimating = true;
        this._ui.playBtn.disabled = true;
        this._ui.stopBtn.disabled = false;
        this._disableLayerControls(true);

        this.notify("info", "Iniciando animación en tiempo real...");

        do {
            await this._clearRealCanvas();
            await this._delay(300);

            for (let layerIndex = 0; layerIndex < this.layers.length && this.isAnimating; layerIndex++) {
                const layer = this.layers[layerIndex];
                if (!layer.visible || layer.commands.length === 0) continue;

                if (layerIndex > 0) {
                    await this._clearRealCanvas();
                    await this._delay(200);
                }

                for (let i = 0; i < layer.commands.length && this.isAnimating; i++) {
                    const command = layer.commands[i];
                    await this._executeRealCommand(command);
                    await this._delay(this.animationSpeed);
                }

                if (this.isAnimating && layerIndex < this.layers.length - 1) {
                    await this._delay(300);
                }
            }

            if (this.isAnimating && this.isLoopEnabled) {
                await this._delay(500);
            }

        } while (this.isAnimating && this.isLoopEnabled);

        this._stopAnimation();
    }

    async _executeRealCommand(command) {
        const gameSocket = getGameSocket();
        if (!this.gameCanvas || !gameSocket) return;

        const scaleX = this.gameCanvas.width / this.previewCanvas.width;
        const scaleY = this.gameCanvas.height / this.previewCanvas.height;

        const gameX1 = command.x1 * scaleX;
        const gameY1 = command.y1 * scaleY;
        const gameX2 = command.x2 * scaleX;
        const gameY2 = command.y2 * scaleY;


        this.gameCtx.strokeStyle = command.color;
        this.gameCtx.lineWidth = command.thickness + 5;
        this.gameCtx.lineCap = 'round';
        this.gameCtx.beginPath();
        this.gameCtx.moveTo(gameX1, gameY1);
        this.gameCtx.lineTo(gameX2, gameY2);
        this.gameCtx.stroke();


        const normX1 = (gameX1 / this.gameCanvas.width).toFixed(4);
        const normY1 = (gameY1 / this.gameCanvas.height).toFixed(4);
        const normX2 = (gameX2 / this.gameCanvas.width).toFixed(4);
        const normY2 = (gameY2 / this.gameCanvas.height).toFixed(4);

        const payload = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - command.thickness},"${command.color}",0,0,{}]]`;

        try {
            gameSocket.send(payload);
        } catch (error) {
            console.error('Error enviando comando:', error);
        }
    }

    async _clearRealCanvas() {
        const gameSocket = getGameSocket();
        if (!gameSocket || !this.gameCtx || !this.gameCanvas) return;

        this.gameCtx.clearRect(0, 0, this.gameCanvas.width, this.gameCanvas.height);

        try {
            const clearThickness = Math.max(this.gameCanvas.width, this.gameCanvas.height) * 0.8;
            const clearColor = '#ffffff';
            const steps = 3;

            for (let i = 0; i < steps; i++) {
                const y = (i / (steps - 1)) * this.gameCanvas.height;
                const normX1 = 0;
                const normY1 = (y / this.gameCanvas.height).toFixed(4);
                const normX2 = 1;
                const normY2 = normY1;

                const payload = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - clearThickness},"${clearColor}",0,0,{}]]`;
                gameSocket.send(payload);
                await this._delay(10);
            }
        } catch (error) {
            console.error('Error limpiando canvas:', error);
        }
    }

    _stopAnimation() {
        this.isAnimating = false;
        this._ui.playBtn.disabled = false;
        this._ui.stopBtn.disabled = true;
        this._disableLayerControls(false);
        this.notify("info", "Animación detenida.");
    }

    _disableLayerControls(disable) {
        const controls = [
            'prevLayer', 'nextLayer', 'addLayer', 'removeLayer',
            'clearLayer', 'duplicateLayer', 'toggleLoopBtn',
            'toggleQualityBtn', 'importImageBtn', 'loadPokemonBtn',
            'onionToggle', 'onionRange', 'speedRange'
        ];

        controls.forEach(control => {
            if (this._ui[control]) {
                this._ui[control].disabled = disable;
            }
        });
    }


    _updateUI() {
        this._updateLayerInfo();
        this._updateOnionSkin();
        this._updateLayerList();
        this._updateButtonStates();
        this._updateLoopButtonState();
        this._updateQualityButtonState();
    }

    _updateLayerInfo() {
        if (this._ui.layerInfo) {
            this._ui.layerInfo.textContent = `${this.currentLayer + 1}/${this.layers.length}`;
        }
    }

    _updateButtonStates() {
        if (this._ui.prevLayer) this._ui.prevLayer.disabled = this.currentLayer === 0 || this.isAnimating;
        if (this._ui.nextLayer) this._ui.nextLayer.disabled = this.currentLayer === this.layers.length - 1 || this.isAnimating;
        if (this._ui.removeLayer) this._ui.removeLayer.disabled = this.layers.length <= 1 || this.isAnimating;
        if (this._ui.addLayer) this._ui.addLayer.disabled = this.isAnimating;
    }

    _updateLoopButtonState() {
        if (this._ui.toggleLoopBtn) {
            if (this.isLoopEnabled) {
                this._ui.toggleLoopBtn.classList.add('active');
            } else {
                this._ui.toggleLoopBtn.classList.remove('active');
            }
        }
    }

    _updateQualityButtonState() {
        if (this._ui.toggleQualityBtn) {
            if (this.highQualityImport) {
                this._ui.toggleQualityBtn.classList.add('active');
            } else {
                this._ui.toggleQualityBtn.classList.remove('active');
            }
        }
    }

    _updateOnionSkin() {
        if (!this.previewCtx) return;

        this.previewCtx.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);
        const showOnion = this._ui.onionToggle?.checked;

        if (showOnion) {
            if (this.currentLayer > 0) {
                this.previewCtx.globalAlpha = this.onionOpacity;
                this.layers[this.currentLayer - 1].commands.forEach(command => {
                    this._drawCommand(this.previewCtx, {...command, color: '#ff6b6b'});
                });
            }

            if (this.currentLayer < this.layers.length - 1) {
                this.previewCtx.globalAlpha = this.onionOpacity;
                this.layers[this.currentLayer + 1].commands.forEach(command => {
                    this._drawCommand(this.previewCtx, {...command, color: '#6bb6ff'});
                });
            }
        }

        this.previewCtx.globalAlpha = 1;
        this.layers[this.currentLayer].commands.forEach(command => {
            this._drawCommand(this.previewCtx, command);
        });
    }

    _updateLayerList() {
        if (!this._ui.layerList) return;

        this._ui.layerList.innerHTML = '';

        this.layers.forEach((layer, index) => {
            const item = domMake.Tree("div", {
                class: `layer-item ${index === this.currentLayer ? 'active' : ''} ${layer.commands.length > 0 ? 'has-content' : ''}`
            });

            const nameSpan = domMake.Tree("span", {}, [`${layer.name || `L${index + 1}`} (${layer.commands.length})`]);
            const visibilitySpan = domMake.Tree("span", {
                class: "layer-visibility"
            }, [layer.visible ? '👁️' : '🚫']);

            item.appendAll(nameSpan, visibilitySpan);

            item.addEventListener('click', (e) => {
                if (this.isAnimating) return;
                if (e.target.classList.contains('layer-visibility')) {
                    this.layers[index].visible = !this.layers[index].visible;
                    this._updateLayerList();
                } else {
                    this.currentLayer = index;
                    this._updateUI();
                }
            });

            this._ui.layerList.appendChild(item);
        });
    }

    _updateSocketStatus() {
        if (!this._ui.socketIndicator) return;

        const gameSocket = getGameSocket();
        if (gameSocket && gameSocket.readyState === 1) {
            this._ui.socketIndicator.textContent = '🟢';
            this._ui.socketIndicator.className = 'socket-indicator connected';
        } else {
            this._ui.socketIndicator.textContent = '🔴';
            this._ui.socketIndicator.className = 'socket-indicator disconnected';
        }
    }

    _delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}





class SolarSmashModTool extends QBit {

    _SOLAR_WEAPONS = {
        'Ninguno': '',
        '⚡ Rayo Láser': 'weapon:laser_beam',
        '🔮 Rayo de Plasma': 'weapon:plasma_beam',
        '🌠 Lluvia de Meteoritos': 'weapon:meteor_rain',
        '☄️ Asteroide Gigante': 'weapon:giant_asteroid',
        '🔥 Dragón Espacial': 'weapon:space_dragon',
        '🛸 Invasión OVNI': 'weapon:ufo_invasion',
        '🗡️ Sable Láser Galáctico': 'weapon:galactic_lightsaber',
        '🚀 Misil de Antimateria': 'weapon:antimatter_missile',
        '🌪️ Vórtice Gravitacional': 'weapon:gravity_vortex',
        '🌑 Agujero Negro': 'weapon:black_hole'
    };

    _SOLAR_EFFECTS = {
        'Ninguno': '',
        '⚡ Rayo Láser (v1.0)': 'effect:laser_beam_v1_0',
        '🛡️ Escudo Planetario': 'effect:planetary_shield',
        '🎆 Fuegos Artificiales Solares': 'effect:solar_fireworks',
        '❄️ Era de Hielo Instantánea': 'effect:instant_ice_age',
        '🌋 Erupción Volcánica': 'effect:volcanic_eruption',
        '🌀 Anomalía Espacial': 'effect:space_anomaly',
        '🛸 Invasión OVNI (v1.0)': 'effect:ufo_invasion_v1_0',
        '🌪️ Vórtice Gravitacional (v1.0)': 'effect:gravity_vortex_v1_0',
        '🌑 Agujero Negro (v1.0)': 'effect:black_hole_v1_0'
    };


    _gameCanvas = null;
    _gameCtx = null;
    _stopSignal = false;
    _isExecuting = false;
    _activeEffectInterval = null;


    _ui = {
        container: null,
        titleBar: null,
        playerSelect: null,
        weaponSelect: null,
        effectSelect: null,
        intensitySlider: null,
        repeatToggle: null,
        executeBtn: null,
        stopBtn: null,
        statusLabel: null
    };

    constructor() {
        super("🚀Solar Smash Tool", '<i class="fas fa-meteor"></i>');
        this._onStartup();
    }

    _onStartup() {
        this._findGameCanvas();
        this._loadInterface();
        this._setupEventListeners();
        this._updatePlayerOptions();
        setInterval(() => this._updatePlayerOptions(), 2000);
        setInterval(() => this._updateButtonStates(), 1000);
        this.notify("info", "🌌 THE SOLAR SMASH MOD cargado exitosamente! 🌌");
        this.notify("info", "🚀 Versión 1.3 - Actualizaciones de armas y efectos completadas");
        this.notify("info", "⚡ Cada arma ahora tiene su propio comportamiento distintivo");
    }

    _findGameCanvas() {
        this._gameCanvas = document.getElementById('canvas');
        if (this._gameCanvas) {
            this._gameCtx = this._gameCanvas.getContext('2d');
        } else {
            this.notify("warning", "Canvas del juego no encontrado. Las funciones de dibujo no estarán disponibles.");
        }
    }


    _sendAndRenderDrawCommand(x1_px, y1_px, x2_px, y2_px, thickness, color) {
        const gameSocket = getGameSocket();
        if (!gameSocket) {
            this.notify("warning", "No hay conexión WebSocket activa. No se pueden enviar comandos de dibujo.");
            this._stopExecution();
            return false;
        }

        if (this._gameCtx && this._gameCanvas) {
            this._gameCtx.strokeStyle = color;
            this._gameCtx.lineWidth = thickness;
            this._gameCtx.lineCap = 'round';
            this._gameCtx.lineJoin = 'round';
            this._gameCtx.beginPath();
            this._gameCtx.moveTo(x1_px, y1_px);
            this._gameCtx.lineTo(x2_px, y2_px);
            this._gameCtx.stroke();
        }


        const x1_norm = (x1_px / this._gameCanvas.width).toFixed(4);
        const y1_norm = (y1_px / this._gameCanvas.height).toFixed(4);
        const x2_norm = (x2_px / this._gameCanvas.width).toFixed(4);
        const y2_norm = (y2_px / this._gameCanvas.height).toFixed(4);

        const payload = `42["drawcmd",0,[${x1_norm},${y1_norm},${x2_norm},${y2_norm},false,${0 - thickness},"${color}",0,0,{}]]`;
        gameSocket.send(payload);
        return true;
    }

    _loadInterface() {
const container = domMake.Tree("div", {
    class: "solar-smash-container",
    style: `
        background: linear-gradient(145deg, #ffffff, #f8f9fa);
        border: 2px solid #ffffff;
        border-radius: 10px;
        padding: 12px;
        color: #495057;
        font-size: 12px;
        font-family: Arial, sans-serif;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        display: flex;
        flex-direction: column;
        gap: 12px;
        margin-bottom: 10px;
        max-width: 100%;
        overflow: hidden;
    `
});
        this.htmlElements.section.appendChild(container);
        this._ui.container = container;

        const titleBar = domMake.Tree("div", {
            class: "solar-smash-title-bar",
            style: `
                font-weight: bold;
                font-size: 16px;
                text-align: center;
                color: #007bff; /* Color de texto azul primario */
                padding-bottom: 5px;
                border-bottom: 1px solid #ced4da;
                margin-bottom: 10px;
            `
        }, ["🌌 SOLAR SMASH TOOL 🌌"]);

        this._ui.titleBar = titleBar;

        const contentDiv = domMake.Tree("div", {
            style: `display:flex; flex-direction:column; gap:12px;`
        });
        container.appendChild(contentDiv);

const solarInputStyle = `
    flex-grow: 1;
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid #ced4da;
    background: #ffffff;
    color: #212529;
    font-size: 12px;
    font-family: Arial, sans-serif;
    transition: all 0.3s ease;
    appearance: none;
    background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007bff%22%20d%3D%22M287%2C197.3L159.2%2C69.5c-3.6-3.6-8.2-5.4-12.8-5.4s-9.2%2C1.8-12.8%2C5.4L5.4%2C197.3c-7.2%2C7.2-7.2%2C18.8%2C0%2C26c3.6%2C3.6%2C8.2%2C5.4%2C12.8%2C5.4s9.2%2C1.8%2C12.8%2C5.4l117%2C117c3.6%2C3.6%2C8.2%2C5.4%2C12.8%2C5.4s9.2%2C1.8%2C12.8%2C5.4l117-117c7.2-7.2%2C7.2-18.8%2C0-26C294.2%2C204.5%2C294.2%2C200.9%2C287%2C197.3z%22%2F%3E%3C%2Fsvg%3E');
    background-repeat: no-repeat;
    background-position: right 8px center;
    background-size: 12px;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
`;

        const createSolarRow = (parent, labelText, inputElement) => {
            const wrapper = domMake.Tree("div", {
                style: `display:flex; align-items:center; gap:12px;`
            });
            const label = domMake.Tree("span", {
                style: `color: #495057; font-weight: bold; min-width: 90px;`
            }, [labelText]);
            wrapper.appendChild(label);
            wrapper.appendChild(inputElement);
            parent.appendChild(wrapper);
            return {
                wrapper,
                label,
                inputElement
            };
        };

        this._ui.playerSelect = domMake.Tree("select", {
            style: solarInputStyle
        });
        createSolarRow(contentDiv, '🎯 Target:', this._ui.playerSelect);

        this._ui.weaponSelect = domMake.Tree("select", {
            style: solarInputStyle
        });
        for (const name in this._SOLAR_WEAPONS) {
            const opt = domMake.Tree("option", {
                value: this._SOLAR_WEAPONS[name]
            }, [name]);
            this._ui.weaponSelect.appendChild(opt);
        }
        this._ui.weaponSelect.value = this._SOLAR_WEAPONS['Ninguno'];
        createSolarRow(contentDiv, '⚔️ Weapon:', this._ui.weaponSelect);

        this._ui.effectSelect = domMake.Tree("select", {
            style: solarInputStyle
        });
        for (const name in this._SOLAR_EFFECTS) {
            const opt = domMake.Tree("option", {
                value: this._SOLAR_EFFECTS[name]
            }, [name]);
            this._ui.effectSelect.appendChild(opt);
        }
        this._ui.effectSelect.value = this._SOLAR_EFFECTS['Ninguno'];
        createSolarRow(contentDiv, '✨ Effect:', this._ui.effectSelect);

        this._ui.intensitySlider = domMake.Tree("input", {
            type: "range",
            min: "1",
            max: "5",
            value: "3",
            style: `
                flex-grow: 1; -webkit-appearance: none; height: 6px; border-radius: 5px;
                background: #dee2e6; /* Fondo de la barra gris claro */
                outline: none;
                accent-color: #007bff; /* Color del "thumb" (indicador) */
            `
        });
        createSolarRow(contentDiv, '⚡ Power:', this._ui.intensitySlider);

        this._ui.repeatToggle = domMake.Tree("input", {
            type: "checkbox",
            id: 'solarRepeatToggle',
            style: `margin-right: 8px; cursor: pointer; transform: scale(1.2); accent-color: #007bff;`
        });
        const repeatLabel = domMake.Tree("label", {
            htmlFor: 'solarRepeatToggle',
            style: `display: flex; align-items: center; cursor: pointer; color: #495057;`
        }, [' 🔄 Repeat Action']);
        const repeatWrapper = domMake.Tree("div", {
            style: `display:flex; align-items:center; gap:0;`
        });
        repeatWrapper.append(this._ui.repeatToggle, repeatLabel);
        contentDiv.appendChild(repeatWrapper);

        this._ui.executeBtn = domMake.Button('🚀 EXECUTE SOLAR WEAPON', {
            disabled: true,
            style: `
                padding: 12px 20px; border-radius: 10px; border: none;
                background: linear-gradient(45deg, #28a745, #218838); /* Verde para ejecutar */
                color: white; font-weight: bold; font-size: 14px;
                cursor: pointer; transition: all 0.3s ease;
                box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                text-transform: uppercase; letter-spacing: 1px;
            `
        });

        this._ui.stopBtn = domMake.Button('🛑 STOP DESTRUCTION', {
            disabled: true,
            style: `
                margin-top: 8px; padding: 10px 18px; border-radius: 8px; border: none;
                background: linear-gradient(45deg, #dc3545, #c82333); /* Rojo para detener */
                color: white; font-weight: bold; font-size: 13px;
                cursor: pointer; transition: all 0.3s ease;
                box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                text-transform: uppercase;
            `
        });

        contentDiv.append(this._ui.executeBtn, this._ui.stopBtn);

        this._ui.statusLabel = domMake.Tree("div", {
            class: "solar-smash-status",
            style: `color: #495057; margin-top: 10px; text-align: center;`
        }, ["Estado: Listo."]);
        contentDiv.appendChild(this._ui.statusLabel);

    }

    _setupEventListeners() {
        this._ui.weaponSelect.addEventListener('change', () => {
            if (this._ui.weaponSelect.value !== '') {
                this._ui.effectSelect.value = this._SOLAR_EFFECTS['Ninguno'];
            }
        });

        this._ui.effectSelect.addEventListener('change', () => {
            if (this._ui.effectSelect.value !== '') {
                this._ui.weaponSelect.value = this._SOLAR_WEAPONS['Ninguno'];
            }
        });

        this._ui.executeBtn.addEventListener('click', () => this._executeSolarAction());
        this._ui.stopBtn.addEventListener('click', () => this._stopExecution());


        [this._ui.executeBtn, this._ui.stopBtn, this._ui.playerSelect, this._ui.weaponSelect, this._ui.effectSelect, this._ui.intensitySlider, this._ui.repeatToggle].forEach(element => {
            element.addEventListener('mouseenter', () => {
                element.style.transform = 'translateY(-2px)';

                if (element === this._ui.executeBtn) {
                    element.style.boxShadow = '0 4px 8px rgba(40, 167, 69, 0.25)';
                } else if (element === this._ui.stopBtn) {
                    element.style.boxShadow = '0 4px 8px rgba(220, 53, 69, 0.25)';
                } else if (element === this._ui.playerSelect || element === this._ui.weaponSelect || element === this._ui.effectSelect) {
                    element.style.boxShadow = '0 2px 5px rgba(0, 123, 255, 0.15)';
                    element.style.borderColor = '#007bff';
                } else if (element === this._ui.intensitySlider) {
                     element.style.boxShadow = '0 2px 5px rgba(0, 123, 255, 0.15)';
                } else if (element === this._ui.repeatToggle) {

                }
            });

            element.addEventListener('mouseleave', () => {
                element.style.transform = 'translateY(0)';

                if (element === this._ui.executeBtn || element === this._ui.stopBtn) {
                    element.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
                } else if (element === this._ui.playerSelect || element === this._ui.weaponSelect || element === this._ui.effectSelect) {
                    element.style.boxShadow = 'none';
                    element.style.borderColor = '#ced4da';
                } else if (element === this._ui.intensitySlider) {
                     element.style.boxShadow = 'none';
                }
            });
        });
    }

    _updateButtonStates() {
        const isConnected = getGameSocket() !== null;
        this._ui.executeBtn.disabled = !isConnected || this._isExecuting;
        this._ui.stopBtn.disabled = !this._isGenerating;
        this._ui.stopBtn.disabled = !this._isExecuting;

        if (!isConnected) {
            this._ui.statusLabel.textContent = "Estado: Desconectado del juego.";
            this._ui.statusLabel.style.color = '#dc3545';
        } else if (this._isExecuting) {
            this._ui.statusLabel.textContent = "Estado: Ejecutando acción...";
            this._ui.statusLabel.style.color = '#28a745';
        } else {
            this._ui.statusLabel.textContent = "Estado: Listo.";
            this._ui.statusLabel.style.color = '#495057';
        }
    }

    _getPlayerCoords(playerId) {
        if (!this._gameCanvas) return null;

        const avatar = document.querySelector(`.spawnedavatar[data-playerid="${playerId}"]`);
        if (!avatar) return null;

        const cRect = this._gameCanvas.getBoundingClientRect();
        const aRect = avatar.getBoundingClientRect();

        return {
            x: Math.round((aRect.left - cRect.left) + (aRect.width / 2)),
            y: Math.round((aRect.top - cRect.top) + (aRect.height / 2)),
            width: aRect.width,
            height: aRect.height
        };
    }

    _updatePlayerOptions() {

        const currentSelection = this._ui.playerSelect.value;

        this._ui.playerSelect.innerHTML = '';

        const playerElements = document.querySelectorAll('.spawnedavatar[data-playerid], .playerlist-row[data-playerid]');
        const validPlayers = [];

        playerElements.forEach(el => {
            const playerId = el.dataset.playerid;
            if (!playerId || playerId === '0' || el.dataset.self === 'true') {
                return;
            }

            let playerName = '';
            const nicknameEl = el.querySelector('.nickname, .playerlist-name a, .player-name');
            if (nicknameEl) {
                playerName = nicknameEl.textContent.trim();
            }
            if (!playerName) {
                const parentRow = el.closest('.playerlist-row');
                if (parentRow) {
                    const nameEl = parentRow.querySelector('.playerlist-name a, .player-name');
                    if (nameEl) {
                        playerName = nameEl.textContent.trim();
                    }
                }
            }
            if (!playerName) {
                playerName = `Player ${playerId}`;
            }

            if (!validPlayers.some(p => p.id === playerId)) {
                validPlayers.push({
                    id: playerId,
                    name: playerName
                });
            }
        });

        if (validPlayers.length === 0) {
            const opt = domMake.Tree("option", {
                value: ''
            }, ['❌ No players available']);
            this._ui.playerSelect.appendChild(opt);
            this._ui.executeBtn.disabled = true;
        } else {
            validPlayers.forEach(player => {
                const opt = domMake.Tree("option", {
                    value: player.id
                }, [`🎯 ${player.name}`]);
                this._ui.playerSelect.appendChild(opt);
            });

            const stillExists = validPlayers.some(p => p.id === currentSelection);
            if (currentSelection && stillExists) {
                this._ui.playerSelect.value = currentSelection;
            } else {
                this._ui.playerSelect.selectedIndex = 0;
            }
            this._ui.executeBtn.disabled = false;
        }
    }


    async _executeSolarAction() {
        if (this._isExecuting) return;

        const playerId = this._ui.playerSelect.value;
        const weaponType = this._ui.weaponSelect.value;
        const effectType = this._ui.effectSelect.value;
        const intensity = parseInt(this._ui.intensitySlider.value);
        const shouldRepeat = this._ui.repeatToggle.checked;

        if (!playerId) {
            this.notify('error', '🚫 Por favor selecciona un objetivo válido');
            return;
        }
        if (!weaponType && !effectType) {
            this.notify('error', '🚫 Por favor selecciona un arma o efecto');
            return;
        }

        this._isExecuting = true;
        this._stopSignal = false;
        this._updateButtonStates();

        do {
            try {
                if (weaponType) {
                    switch (weaponType) {
                        case 'weapon:meteor_rain':
                            await this._meteorRain(playerId, intensity);
                            break;
                        case 'weapon:giant_asteroid':
                            await this._giantAsteroid(playerId, intensity);
                            break;
                        case 'weapon:laser_beam':
                            await this._laserBeam(playerId, intensity);
                            break;
                        case 'weapon:plasma_beam':
                            await this._plasmaBeam(playerId, intensity);
                            break;
                        case 'weapon:space_dragon':
                            await this._spaceDragon(playerId, intensity);
                            break;
                        case 'weapon:black_hole':
                            await this._blackHole(playerId, intensity);
                            break;
                        case 'weapon:ufo_invasion':
                            await this._ufoInvasion(playerId, intensity);
                            break;
                        case 'weapon:galactic_lightsaber':
                            await this._galacticLightsaber(playerId, intensity);
                            break;
                        case 'weapon:antimatter_missile':
                            await this._antimatterMissile(playerId, intensity);
                            break;
                        case 'weapon:gravity_vortex':
                            await this._gravityVortex(playerId, intensity);
                            break;
                    }
                }

                if (effectType) {
                    switch (effectType) {
                        case 'effect:planetary_shield':
                            await this._planetaryShield(playerId, intensity);
                            break;
                        case 'effect:solar_fireworks':
                            await this._solarFireworks(playerId, intensity);
                            break;
                        case 'effect:instant_ice_age':
                            await this._instantIceAge(playerId, intensity);
                            break;
                        case 'effect:volcanic_eruption':
                            await this._volcanicEruption(playerId, intensity);
                            break;
                        case 'effect:space_anomaly':
                            await this._spaceAnomaly(playerId, intensity);
                            break;
                        case 'effect:laser_beam_v1_0':
                            await this._laserBeam_v1_0(playerId, intensity);
                            break;
                        case 'effect:black_hole_v1_0':
                            await this._blackHole_v1_0(playerId, intensity);
                            break;
                        case 'effect:ufo_invasion_v1_0':
                            await this._ufoInvasion_v1_0(playerId, intensity);
                            break;
                        case 'effect:gravity_vortex_v1_0':
                            await this._gravityVortex_v1_0(playerId, intensity);
                            break;
                    }
                }

                if (shouldRepeat && !this._stopSignal) {
                    await new Promise(r => setTimeout(r, 1000));
                }

            } catch (error) {
                this.notify('error', `🔥 Error ejecutando acción: ${error.message}`);
                console.error('🔥 Error ejecutando Solar Smash:', error);
                break;
            }
        } while (shouldRepeat && !this._stopSignal);

        this._isExecuting = false;
        this._updateButtonStates();
        this.notify('success', '🌌 Secuencia Solar Smash completada');
    }

    _stopExecution() {
        this._stopSignal = true;
        this._isExecuting = false;
        if (this._activeEffectInterval) {
            clearInterval(this._activeEffectInterval);
            this._activeEffectInterval = null;
        }
        this._updateButtonStates();
        this.notify('info', '🛑 Destrucción detenida por el usuario');
    }



    async _meteorRain(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🌠 Lluvia de meteoritos iniciada`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const meteorCount = 20 + (intensity * 8);
        const colors = ['#ff4500', '#ff6347', '#ffa500'];

        for (let i = 0; i < meteorCount; i++) {
            if (this._stopSignal) break;

            const startX = target.x;
            const startY = target.y;

            const flowerAngle = (i / meteorCount) * Math.PI * 2;
            const flowerDistance = 50 + (i % 4) * 25;
            const endX = target.x + flowerDistance * Math.cos(flowerAngle);
            const endY = target.y + flowerDistance * Math.sin(flowerAngle) * 0.7;

            const color = colors[Math.floor(Math.random() * colors.length)];

            this._sendAndRenderDrawCommand(startX, startY, endX, endY, 2 + Math.random() * 3, '#ffe99c');

            for (let spark = 0; spark < 3; spark++) {
                const sparkAngle = (spark / 3) * Math.PI * 2;
                const sparkDist = 8 + Math.random() * 12;
                const sparkX = endX + sparkDist * Math.cos(sparkAngle + flowerAngle);
                const sparkY = endY + sparkDist * Math.sin(sparkAngle + flowerAngle);

                this._sendAndRenderDrawCommand(endX, endY, sparkX, sparkY, 1, color);

                const centerX = endX + 3 * Math.cos(sparkAngle + Math.PI);
                const centerY = endY + 3 * Math.sin(sparkAngle + Math.PI);
                this._sendAndRenderDrawCommand(endX, endY, centerX, centerY, 2, '#FFD700');
            }

            if (i % 2 === 0) {
                const leafAngle1 = flowerAngle + Math.PI * 0.3;
                const leafAngle2 = flowerAngle - Math.PI * 0.3;
                const leafDist = flowerDistance * 0.6;

                const leaf1X = target.x + leafDist * Math.cos(leafAngle1);
                const leaf1Y = target.y + leafDist * Math.sin(leafAngle1) * 0.7;
                const leaf2X = target.x + leafDist * Math.cos(leafAngle2);
                const leaf2Y = target.y + leafDist * Math.sin(leafAngle2) * 0.7;

                this._sendAndRenderDrawCommand(startX, startY, leaf1X, leaf1Y, 1, '#ffa159');
                this._sendAndRenderDrawCommand(startX, startY, leaf2X, leaf2Y, 1, '#ffcd59');
            }

            await new Promise(r => setTimeout(r, 80 + Math.random() * 60));
        }
    }

    async _giantAsteroid(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `☄️ Asteroide gigante aproximándose`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const size = 30 + intensity * 10;
        const startX = -100;
        const startY = -100;
        const endX = target.x;
        const endY = target.y;

        const steps = 25;
        for (let step = 0; step < steps; step++) {
            if (this._stopSignal) break;

            const progress = step / steps;
            const currentX = startX + (endX - startX) * progress;
            const currentY = startY + (endY - startY) * progress;
            const currentSize = size * (0.2 + progress * 0.8);

            this._sendAndRenderDrawCommand(currentX - currentSize / 2, currentY - currentSize / 2,
                currentX + currentSize / 2, currentY + currentSize / 2, currentSize / 3, '#8B4513');

            for (let trail = 0; trail < 8; trail++) {
                const trailX = currentX - (progress * 50) - trail * 15;
                const trailY = currentY - (progress * 50) - trail * 15;
                this._sendAndRenderDrawCommand(currentX, currentY, trailX, trailY, 8 - trail, '#ff4500');
            }
            await new Promise(r => setTimeout(r, 120));
        }

        for (let explosion = 0; explosion < 30; explosion++) {
            if (this._stopSignal) break;
            const angle = Math.random() * Math.PI * 2;
            const distance = Math.random() * (80 + intensity * 20);
            const impactX = endX + distance * Math.cos(angle);
            const impactY = endY + distance * Math.sin(angle);
            const color = ['#ff0000', '#ff4500', '#ffa500'][Math.floor(Math.random() * 3)];
            this._sendAndRenderDrawCommand(endX, endY, impactX, impactY, 8 + Math.random() * 6, color);
        }
    }

    async _laserBeam(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `⚡ Disparando rayo láser`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const startX = Math.random() * this._gameCanvas.width;
        const startY = 0;
        const endX = target.x;
        const endY = target.y;

        for (let charge = 0; charge < 10; charge++) {
            if (this._stopSignal) break;
            const chargeIntensity = charge * 3;
            this._sendAndRenderDrawCommand(startX - chargeIntensity, startY, startX + chargeIntensity, startY, chargeIntensity, '#ff0000');
            await new Promise(r => setTimeout(r, 100));
        }

        const beamDuration = 2000 + intensity * 500;
        const beamStart = Date.now();

        while (Date.now() - beamStart < beamDuration) {
            if (this._stopSignal) break;

            this._sendAndRenderDrawCommand(startX, startY, endX, endY, 15, '#ff0000');
            this._sendAndRenderDrawCommand(startX, startY, endX, endY, 8, '#ffffff');

            for (let scatter = 0; scatter < 3; scatter++) {
                const scatterX = endX + (Math.random() - 0.5) * 30;
                const scatterY = endY + (Math.random() - 0.5) * 30;
                this._sendAndRenderDrawCommand(endX, endY, scatterX, scatterY, 4, '#ff6600');
            }
            await new Promise(r => setTimeout(r, 50));
        }
    }

    async _plasmaBeam(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🔮 Disparando rayo de plasma`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const startX = Math.random() * this._gameCanvas.width;
        const startY = 0;
        const endX = target.x;
        const endY = target.y;

        for (let charge = 0; charge < 8; charge++) {
            if (this._stopSignal) break;
            const chargeRadius = charge * 5;
            for (let spark = 0; spark < 12; spark++) {
                const angle = (spark / 12) * Math.PI * 2 + charge * 0.5;
                const sparkX = startX + chargeRadius * Math.cos(angle);
                const sparkY = startY + chargeRadius * Math.sin(angle);
                this._sendAndRenderDrawCommand(startX, startY, sparkX, sparkY, 4 - charge * 0.3, '#ff00ff');
            }
            await new Promise(r => setTimeout(r, 150));
        }

        const beamDuration = 2000;
        const beamStartTime = Date.now();

        while (Date.now() - beamStartTime < beamDuration) {
            if (this._stopSignal) break;

            const waveOffset = Math.sin((Date.now() - beamStartTime) * 0.01) * 15;

            this._sendAndRenderDrawCommand(startX, startY, endX + waveOffset, endY, 12, '#ff00ff');
            this._sendAndRenderDrawCommand(startX, startY, endX + waveOffset, endY, 6, '#ffffff');

            for (let discharge = 0; discharge < 5; discharge++) {
                const progress = discharge / 5;
                const dischargeX = startX + (endX - startX) * progress + waveOffset * progress;
                const dischargeY = startY + (endY - startY) * progress;
                const sideX = dischargeX + (Math.random() - 0.5) * 40;
                const sideY = dischargeY + (Math.random() - 0.5) * 40;

                this._sendAndRenderDrawCommand(dischargeX, dischargeY, sideX, sideY, 3, '#ff00ff');
            }
            await new Promise(r => setTimeout(r, 80));
        }

        for (let impact = 0; impact < 15; impact++) {
            if (this._stopSignal) break;
            const burstAngle = Math.random() * Math.PI * 2;
            const burstDist = Math.random() * 60;
            const burstX = endX + burstDist * Math.cos(burstAngle);
            const burstY = endY + burstDist * Math.sin(burstAngle);
            this._sendAndRenderDrawCommand(endX, endY, burstX, burstY, 6 - impact * 0.3, '#ff00ff');
        }
    }

    async _spaceDragon(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🔥 Invocando dragón espacial`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        let dragonX = -100;
        let dragonY = target.y - 50;
        const dragonSize = 20 + intensity * 8;

        const flightSteps = 60;
        for (let step = 0; step < flightSteps; step++) {
            if (this._stopSignal) break;

            const nextX = dragonX + (this._gameCanvas.width + 200) / flightSteps;
            const nextY = dragonY + Math.sin(step * 0.2) * 20;

            this._sendAndRenderDrawCommand(dragonX, dragonY, nextX, nextY, dragonSize, '#8B0000');

            const wingSpan = dragonSize * 1.5;
            const wingAngle = Math.sin(step * 0.5) * 0.5;
            const wingY1 = nextY - wingSpan * Math.cos(wingAngle);
            const wingY2 = nextY + wingSpan * Math.cos(wingAngle);
            this._sendAndRenderDrawCommand(nextX, nextY, nextX - wingSpan, wingY1, dragonSize * 0.3, '#4B0000');
            this._sendAndRenderDrawCommand(nextX, nextY, nextX - wingSpan, wingY2, dragonSize * 0.3, '#4B0000');

            if (step > 20 && Math.abs(nextX - target.x) < 100) {
                for (let flame = 0; flame < 8; flame++) {
                    const flameAngle = Math.atan2(target.y - nextY, target.x - nextX) + (Math.random() - 0.5) * 0.5;
                    const flameDist = 30 + Math.random() * 50;
                    const flameX = nextX + flameDist * Math.cos(flameAngle);
                    const flameY = nextY + flameDist * Math.sin(flameAngle);
                    const flameColors = ['#ff4500', '#ff6600', '#ffa500'];
                    this._sendAndRenderDrawCommand(nextX, nextY, flameX, flameY, 6, flameColors[flame % 3]);
                }
            }
            dragonX = nextX;
            dragonY = nextY;
            await new Promise(r => setTimeout(r, 100));
        }
    }

    async _blackHole(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🌑 Creando agujero negro`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const centerX = target.x;
        const centerY = target.y;
        const duration = 50 + intensity * 50;
        const startTime = Date.now();

        while (Date.now() - startTime < duration) {
            if (this._stopSignal) break;

            const elapsed = Date.now() - startTime;
            const eventHorizon = 20 + intensity * 5;
            for (let horizon = 0; horizon < 16; horizon++) {
                const hAngle = (horizon / 16) * Math.PI * 2;
                const hX = centerX + eventHorizon * Math.cos(hAngle);
                const hY = centerY + eventHorizon * Math.sin(hAngle);
                this._sendAndRenderDrawCommand(centerX, centerY, hX, hY, 8, '#000000');
            }

            const accretionLayers = 5;
            for (let layer = 0; layer < accretionLayers; layer++) {
                const layerRadius = eventHorizon + (layer + 1) * (15 + intensity * 3);
                const layerSpeed = elapsed * (0.0001 + layer * 0.00005);
                const layerSegments = 20;

                for (let seg = 0; seg < layerSegments; seg++) {
                    const segAngle = (seg / layerSegments) * Math.PI * 2 + layerSpeed;
                    const nextAngle = ((seg + 1) / layerSegments) * Math.PI * 2 + layerSpeed;

                    const x1 = centerX + layerRadius * Math.cos(segAngle);
                    const y1 = centerY + layerRadius * Math.sin(segAngle) * 0.3;
                    const x2 = centerX + layerRadius * Math.cos(nextAngle);
                    const y2 = centerY + layerRadius * Math.sin(nextAngle) * 0.3;

                    const layerColors = ['#ff6600', '#ff4500', '#ff0000', '#8B0000', '#4B0000'];
                    this._sendAndRenderDrawCommand(x1, y1, x2, y2, 6 - layer, layerColors[layer]);
                }
            }

            const orbitalRadius = 80 + intensity * 20;
            const particleCount = 12 + intensity * 4;
            for (let particle = 0; particle < particleCount; particle++) {
                const particleAngle = (particle / particleCount) * Math.PI * 2 + elapsed * 0.002;
                const particleX = centerX + orbitalRadius * Math.cos(particleAngle);
                const particleY = centerY + orbitalRadius * Math.sin(particleAngle) * 0.6;

                const nextAngle = particleAngle + 0.1;
                const nextX = centerX + orbitalRadius * Math.cos(nextAngle);
                const nextY = centerY + orbitalRadius * Math.sin(nextAngle) * 0.6;

                this._sendAndRenderDrawCommand(particleX, particleY, nextX, nextY, 4, '#ffff00');

                if (particle % 2 === 0) {
                    const smallAngle = particleAngle + Math.PI;
                    const smallX = centerX + (orbitalRadius * 0.8) * Math.cos(smallAngle);
                    const smallY = centerY + (orbitalRadius * 0.8) * Math.sin(smallAngle) * 0.6;
                    const smallNextX = centerX + (orbitalRadius * 0.8) * Math.cos(smallAngle + 0.1);
                    const smallNextY = centerY + (orbitalRadius * 0.8) * Math.sin(smallAngle + 0.1) * 0.6;

                    this._sendAndRenderDrawCommand(smallX, smallY, smallNextX, smallNextY, 2, '#ffa500');
                }
            }
            await new Promise(r => setTimeout(r, 100));
        }
    }

    async _ufoInvasion(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🛸 Iniciando invasión OVNI`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const ufos = [];
        ufos.push({
            x: target.x,
            y: target.y - 100,
            angle: 0,
            size: 15 + intensity * 3
        });

        const invasionDuration = 1000;
        const startTime = Date.now();

        while (Date.now() - startTime < invasionDuration) {
            if (this._stopSignal) break;

            const elapsed = Date.now() - startTime;

            for (let i = 0; i < ufos.length; i++) {
                const ufo = ufos[i];

                ufo.x += Math.sin(elapsed * 0.003 + i) * 2;
                ufo.y += Math.cos(elapsed * 0.002 + i) * 1;
                ufo.angle += 0.1;

                const ufoPoints = 12;
                for (let p = 0; p < ufoPoints; p++) {
                    const pAngle = (p / ufoPoints) * Math.PI * 2 + ufo.angle;
                    const nextAngle = ((p + 1) / ufoPoints) * Math.PI * 2 + ufo.angle;

                    const x1 = ufo.x + ufo.size * Math.cos(pAngle);
                    const y1 = ufo.y + ufo.size * Math.sin(pAngle) * 0.3;
                    const x2 = ufo.x + ufo.size * Math.cos(nextAngle);
                    const y2 = ufo.y + ufo.size * Math.sin(nextAngle) * 0.3;

                    this._sendAndRenderDrawCommand(x1, y1, x2, y2, 4, '#c0c0c0');
                }

                const domeSize = ufo.size * 0.6;
                for (let d = 0; d < 8; d++) {
                    const dAngle = (d / 8) * Math.PI * 2;
                    const dX = ufo.x + domeSize * Math.cos(dAngle);
                    const dY = ufo.y - 10 + domeSize * Math.sin(dAngle) * 0.5;
                    this._sendAndRenderDrawCommand(ufo.x, ufo.y - 10, dX, dY, 3, '#87ceeb');
                }

                if (Math.random() < 0.3) {
                    const beamWidth = ufo.size * 0.8;
                    for (let beam = 0; beam < 6; beam++) {
                        const beamX = ufo.x + (beam - 3) * (beamWidth / 6);
                        const beamY = target.y + Math.random() * 30;
                        this._sendAndRenderDrawCommand(ufo.x, ufo.y + ufo.size * 0.3, beamX, beamY, 2, '#00ff00');
                    }
                }
            }
            await new Promise(r => setTimeout(r, 80));
        }
    }

    async _galacticLightsaber(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🗡️ Activando sable láser galáctico`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const centerX = target.x;
        const centerY = target.y;
        const bladeLength = 60 + intensity * 15;
        const rotations = 3;

        for (let charge = 0; charge < 10; charge++) {
            if (this._stopSignal) break;
            const currentLength = (bladeLength * charge) / 10;
            this._sendAndRenderDrawCommand(centerX, centerY - currentLength, centerX, centerY + currentLength, 8, '#00ff00');
            this._sendAndRenderDrawCommand(centerX, centerY - currentLength, centerX, centerY + currentLength, 4, '#ffffff');
            await new Promise(r => setTimeout(r, 100));
        }

        const totalSteps = rotations * 24;
        for (let step = 0; step < totalSteps; step++) {
            if (this._stopSignal) break;

            const angle = (step / 24) * Math.PI * 2;
            const blade1X = centerX + bladeLength * Math.cos(angle);
            const blade1Y = centerY + bladeLength * Math.sin(angle);
            const blade2X = centerX - bladeLength * Math.cos(angle);
            const blade2Y = centerY - bladeLength * Math.sin(angle);

            this._sendAndRenderDrawCommand(centerX, centerY, blade1X, blade1Y, 8, '#00ff00');
            this._sendAndRenderDrawCommand(centerX, centerY, blade2X, blade2Y, 8, '#00ff00');
            this._sendAndRenderDrawCommand(centerX, centerY, blade1X, blade1Y, 4, '#ffffff');
            this._sendAndRenderDrawCommand(centerX, centerY, blade2X, blade2Y, 4, '#ffffff');

            if (step % 4 === 0) {
                for (let spark = 0; spark < 5; spark++) {
                    const sparkAngle = angle + (Math.random() - 0.5) * 0.5;
                    const sparkDist = bladeLength * 0.8 + Math.random() * 20;
                    const sparkX = centerX + sparkDist * Math.cos(sparkAngle);
                    const sparkY = centerY + sparkDist * Math.sin(sparkAngle);
                    this._sendAndRenderDrawCommand(blade1X, blade1Y, sparkX, sparkY, 2, '#00ffff');
                }
            }
            await new Promise(r => setTimeout(r, 80));
        }
    }

    async _antimatterMissile(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🚀 Lanzando misil de antimateria`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        let missileX = this._gameCanvas.width / 2;
        let missileY = this._gameCanvas.height - 100;
        const targetX = target.x;
        const targetY = target.y;

        const flightSteps = 40;
        for (let step = 0; step < flightSteps; step++) {
            if (this._stopSignal) break;

            const progress = step / flightSteps;
            const nextX = (this._gameCanvas.width / 2) + (targetX - (this._gameCanvas.width / 2)) * progress;
            const nextY = (this._gameCanvas.height - 100) + (targetY - (this._gameCanvas.height - 100)) * progress;

            this._sendAndRenderDrawCommand(missileX, missileY, nextX, nextY, 6, '#c0c0c0');

            const thrustX = missileX - (nextX - missileX) * 2;
            const thrustY = missileY - (nextY - missileY) * 2;
            this._sendAndRenderDrawCommand(missileX, missileY, thrustX, thrustY, 4, '#00ffff');
            this._sendAndRenderDrawCommand(missileX, missileY, thrustX, thrustY, 2, '#ffffff');

            this._sendAndRenderDrawCommand(missileX, missileY, nextX, nextY, 2, '#87ceeb');

            missileX = nextX;
            missileY = nextY;

            await new Promise(r => setTimeout(r, 100));
        }

        const annihilationSteps = 20;
        for (let step = 0; step < annihilationSteps; step++) {
            if (this._stopSignal) break;

            const currentRadius = step * (15 + intensity * 5);

            for (let wave = 0; wave < 3; wave++) {
                const waveRadius = currentRadius + wave * 20;
                const segments = 16;

                for (let seg = 0; seg < segments; seg++) {
                    const angle1 = (seg / segments) * Math.PI * 2;
                    const angle2 = ((seg + 1) / segments) * Math.PI * 2;
                    const x1 = targetX + waveRadius * Math.cos(angle1);
                    const y1 = targetY + waveRadius * Math.sin(angle1);
                    const x2 = targetX + waveRadius * Math.cos(angle2);
                    const y2 = targetY + waveRadius * Math.sin(angle2);

                    const colors = ['#ffffff', '#00ffff', '#ff00ff'];
                    this._sendAndRenderDrawCommand(x1, y1, x2, y2, 8 - wave * 2, colors[wave]);
                }
            }
            await new Promise(r => setTimeout(r, 150));
        }
    }

    async _gravityVortex(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🌪️ Generando vórtice gravitacional`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const centerX = target.x;
        const centerY = target.y;
        const duration = 400 + intensity * 100;
        const startTime = Date.now();

        while (Date.now() - startTime < duration) {
            if (this._stopSignal) break;

            const elapsed = Date.now() - startTime;
            const rotationSpeed = elapsed * 0.008;

            const spiralArms = 4;
            for (let arm = 0; arm < spiralArms; arm++) {
                const armOffset = (arm / spiralArms) * Math.PI * 2;

                for (let segment = 0; segment < 20; segment++) {
                    const segmentProgress = segment / 20;
                    const radius = segmentProgress * (80 + intensity * 20);
                    const angle = armOffset + rotationSpeed + segmentProgress * Math.PI * 6;

                    const x = centerX + radius * Math.cos(angle);
                    const y = centerY + radius * Math.sin(angle);

                    const colors = ['#9400d3', '#4b0082', '#8a2be2'];
                    this._sendAndRenderDrawCommand(centerX, centerY, x, y, 8 - segmentProgress * 6, colors[arm % colors.length]);

                    if (Math.random() < 0.3) {
                        const distortX = x + (Math.random() - 0.5) * 20;
                        const distortY = y + (Math.random() - 0.5) * 20;
                        this._sendAndRenderDrawCommand(x, y, distortX, distortY, 2, '#ffffff');
                    }
                }
            }

            const coreSize = 10 + Math.sin(elapsed * 0.01) * 5;
            for (let core = 0; core < 8; core++) {
                const coreAngle = (core / 8) * Math.PI * 2;
                const coreX = centerX + coreSize * Math.cos(coreAngle);
                const coreY = centerY + coreSize * Math.sin(coreAngle);
                this._sendAndRenderDrawCommand(centerX, centerY, coreX, coreY, 6, '#000000');
            }
            await new Promise(r => setTimeout(r, 100));
        }
    }




    async _planetaryShield(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🛡️ Activando escudo planetario`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const centerX = target.x;
        const centerY = target.y;
        const shieldRadius = 60 + intensity * 20;
        const duration = 100 + intensity * 100;
        const startTime = Date.now();

        while (Date.now() - startTime < duration) {
            if (this._stopSignal) break;

            const elapsed = Date.now() - startTime;

            const shieldLayers = 4;
            for (let layer = 0; layer < shieldLayers; layer++) {
                const layerRadius = shieldRadius - layer * 10;
                const layerSpeed = elapsed * (0.002 + layer * 0.001);

                const segments = 20;
                for (let seg = 0; seg < segments; seg++) {
                    const angle1 = (seg / segments) * Math.PI * 2 + layerSpeed;
                    const angle2 = ((seg + 1) / segments) * Math.PI * 2 + layerSpeed;

                    const x1 = centerX + layerRadius * Math.cos(angle1);
                    const y1 = centerY + layerRadius * Math.sin(angle1);
                    const x2 = centerX + layerRadius * Math.cos(angle2);
                    const y2 = centerY + layerRadius * Math.sin(angle2);

                    const shieldColors = ['#00ffff', '#0080ff', '#0040ff', '#0020ff'];
                    this._sendAndRenderDrawCommand(x1, y1, x2, y2, 6 - layer, shieldColors[layer]);
                }
            }

            const nodes = 8;
            for (let node = 0; node < nodes; node++) {
                const nodeAngle = (node / nodes) * Math.PI * 2 + elapsed * 0.005;
                const nodeX = centerX + shieldRadius * Math.cos(nodeAngle);
                const nodeY = centerY + shieldRadius * Math.sin(nodeAngle);

                const pulseSize = 8 + Math.sin(elapsed * 0.01 + node) * 4;
                for (let pulse = 0; pulse < 6; pulse++) {
                    const pulseAngle = (pulse / 6) * Math.PI * 2;
                    const pulseX = nodeX + pulseSize * Math.cos(pulseAngle);
                    const pulseY = nodeY + pulseSize * Math.sin(pulseAngle);
                    this._sendAndRenderDrawCommand(nodeX, nodeY, pulseX, pulseY, 4, '#ffffff');
                }
            }
            await new Promise(r => setTimeout(r, 100));
        }
    }

    async _solarFireworks(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🎆 Lanzando fuegos artificiales solares`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const fireworkCount = 8 + intensity * 3;

        for (let fw = 0; fw < fireworkCount; fw++) {
            if (this._stopSignal) break;

            const launchX = target.x + (Math.random() - 0.5) * 200;
            const launchY = this._gameCanvas.height - 50;
            const explodeX = target.x + (Math.random() - 0.5) * 150;
            const explodeY = target.y + (Math.random() - 0.5) * 100;

            this._sendAndRenderDrawCommand(launchX, launchY, explodeX, explodeY, 4, '#ffff00');

            await new Promise(r => setTimeout(r, 400));

            const colors = ['#ff0000', '#ff4500', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#ff00ff'];
            const sparkCount = 20 + intensity * 5;

            for (let spark = 0; spark < sparkCount; spark++) {
                const sparkAngle = (spark / sparkCount) * Math.PI * 2;
                const sparkDist = 30 + Math.random() * 40;
                const sparkX = explodeX + sparkDist * Math.cos(sparkAngle);
                const sparkY = explodeY + sparkDist * Math.sin(sparkAngle);
                const color = colors[Math.floor(Math.random() * colors.length)];

                this._sendAndRenderDrawCommand(explodeX, explodeY, sparkX, sparkY, 3, color);

                if (Math.random() < 0.4) {
                    const subSparkX = sparkX + (Math.random() - 0.5) * 20;
                    const subSparkY = sparkY + (Math.random() - 0.5) * 20;
                    this._sendAndRenderDrawCommand(sparkX, sparkY, subSparkX, subSparkY, 1, color);
                }
            }
            await new Promise(r => setTimeout(r, 200));
        }
    }

    async _instantIceAge(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `❄️ Iniciando era de hielo instantánea`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const freezeRadius = 20 + intensity * 10;
        const centerX = target.x;
        const centerY = target.y;

        const iceSteps = 10;
        for (let step = 0; step < iceSteps; step++) {
            if (this._stopSignal) break;

            const currentRadius = (freezeRadius * step) / iceSteps;
            const iceColors = ['#e0ffff', '#b0e0e6', '#87ceeb', '#add8e6'];

            const crystalCount = 16;
            for (let crystal = 0; crystal < crystalCount; crystal++) {
                const angle = (crystal / crystalCount) * Math.PI * 2;
                const crystalX = centerX + currentRadius * Math.cos(angle);
                const crystalY = centerY + currentRadius * Math.sin(angle);

                for (let side = 0; side < 6; side++) {
                    const sideAngle = angle + (side / 6) * Math.PI * 2;
                    const sideLength = 8 + step;
                    const sideEndX = crystalX + sideLength * Math.cos(sideAngle);
                    const sideEndY = crystalY + sideLength * Math.sin(sideAngle);

                    const color = iceColors[step % iceColors.length];
                    this._sendAndRenderDrawCommand(crystalX, crystalY, sideEndX, sideEndY, 4, color);
                }

                this._sendAndRenderDrawCommand(centerX, centerY, crystalX, crystalY, 2, '#ffffff');
            }

            for (let flake = 0; flake < 15; flake++) {
                const flakeX = centerX + (Math.random() - 0.5) * currentRadius * 2;
                const flakeY = centerY - currentRadius + Math.random() * currentRadius * 2;
                const flakeEndX = flakeX + (Math.random() - 0.5) * 10;
                const flakeEndY = flakeY + Math.random() * 20;

                this._sendAndRenderDrawCommand(flakeX, flakeY, flakeEndX, flakeEndY, 1, '#ffffff');
            }
            await new Promise(r => setTimeout(r, 150));
        }
    }

    async _volcanicEruption(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🌋 Provocando erupción volcánica`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const volcanoX = target.x;
        const volcanoY = target.y + 50;

        const volcanoHeight = 60 + intensity * 15;
        for (let layer = 0; layer < 10; layer++) {
            if (this._stopSignal) break;

            const layerY = volcanoY - layer * (volcanoHeight / 10);
            const layerWidth = (40 - layer * 3) + intensity * 5;

            this._sendAndRenderDrawCommand(volcanoX - layerWidth, layerY, volcanoX + layerWidth, layerY, 13, '#8b4513');
        }

        await new Promise(r => setTimeout(r, 500));

        const eruptionSteps = 25;
        for (let step = 0; step < eruptionSteps; step++) {
            if (this._stopSignal) break;

            const lavaCount = 8 + intensity * 3;
            for (let lava = 0; lava < lavaCount; lava++) {
                const angle = (Math.random() - 0.5) * Math.PI;
                const velocity = 20 + Math.random() * (40 + intensity * 20);
                const lavaX = volcanoX + velocity * Math.cos(angle);
                const lavaY = volcanoY - volcanoHeight - velocity * Math.abs(Math.sin(angle));

                const lavaColors = ['#ff4500', '#ff6347', '#ffa500', '#ff0000'];
                const color = lavaColors[Math.floor(Math.random() * lavaColors.length)];

                this._sendAndRenderDrawCommand(volcanoX, volcanoY - volcanoHeight, lavaX, lavaY, 6, color);

                if (Math.random() < 0.6) {
                    const particleX = lavaX + (Math.random() - 0.5) * 20;
                    const particleY = lavaY + Math.random() * 30;
                    this._sendAndRenderDrawCommand(lavaX, lavaY, particleX, particleY, 2, color);
                }
            }

            for (let smoke = 0; smoke < 5; smoke++) {
                const smokeX = volcanoX + (Math.random() - 0.5) * 30;
                const smokeY = volcanoY - volcanoHeight - step * 15;
                const smokeDrift = smokeX + (Math.random() - 0.5) * 50;

                this._sendAndRenderDrawCommand(smokeX, volcanoY - volcanoHeight, smokeDrift, smokeY, 4, '#696969');
            }
            await new Promise(r => setTimeout(r, 200));
        }
    }

    async _spaceAnomaly(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🌀 Manifestando anomalía espacial`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const anomalyX = target.x;
        const anomalyY = target.y;
        const duration = 500 + intensity * 100;
        const startTime = Date.now();

        while (Date.now() - startTime < duration) {
            if (this._stopSignal) break;

            const elapsed = Date.now() - startTime;

            const distortionCount = 12;
            for (let distort = 0; distort < distortionCount; distort++) {
                const distortAngle = (distort / distortionCount) * Math.PI * 2 + elapsed * 0.005;
                const baseRadius = 60 + intensity * 15;
                const waveRadius = baseRadius + Math.sin(elapsed * 0.01 + distort) * 20;

                const normalX = anomalyX + waveRadius * Math.cos(distortAngle);
                const normalY = anomalyY + waveRadius * Math.sin(distortAngle);

                this._sendAndRenderDrawCommand(anomalyX, anomalyY, normalX, normalY, 3, '#9400d3');

                if (Math.random() < 0.3) {
                    const randomX = normalX + (Math.random() - 0.5) * 40;
                    const randomY = normalY + (Math.random() - 0.5) * 40;
                    this._sendAndRenderDrawCommand(normalX, normalY, randomX, randomY, 1, '#ff00ff');
                }
            }

            for (let quantum = 0; quantum < 8; quantum++) {
                const quantumAngle = Math.random() * Math.PI * 2;
                const quantumDist = Math.random() * 80;
                const quantumX = anomalyX + quantumDist * Math.cos(quantumAngle);
                const quantumY = anomalyY + quantumDist * Math.sin(quantumAngle);

                const newQuantumX = quantumX + (Math.random() - 0.5) * 100;
                const newQuantumY = quantumY + (Math.random() - 0.5) * 100;

                this._sendAndRenderDrawCommand(quantumX, quantumY, newQuantumX, newQuantumY, 2, '#00ffff');
            }

            const coreIntensity = Math.sin(elapsed * 0.02) * 10 + 15;
            for (let core = 0; core < 6; core++) {
                const coreAngle = (core / 6) * Math.PI * 2;
                const coreX = anomalyX + coreIntensity * Math.cos(coreAngle);
                const coreY = anomalyY + coreIntensity * Math.sin(coreAngle);
                this._sendAndRenderDrawCommand(anomalyX, anomalyY, coreX, coreY, 6, '#ffffff');
            }
            await new Promise(r => setTimeout(r, 120));
        }
    }




    async _laserBeam_v1_0(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `⚡ Disparando rayo láser a jugador ${playerId} (v1.0)`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const startX = Math.random() * this._gameCanvas.width;
        const startY = 0;
        const endX = target.x;
        const endY = target.y;

        const duration = 1000 + intensity * 500;
        const startTime = Date.now();

        while (Date.now() - startTime < duration) {
            if (this._stopSignal) break;

            this._sendAndRenderDrawCommand(startX, startY, endX, endY, 6 + intensity, '#00ffff');
            this._sendAndRenderDrawCommand(startX, startY, endX, endY, 3 + intensity, '#ffffff');

            for (let i = 0; i < 5; i++) {
                const offsetX = (Math.random() - 0.5) * 10;
                const offsetY = (Math.random() - 0.5) * 10;
                this._sendAndRenderDrawCommand(
                    startX + offsetX, startY + offsetY,
                    endX + offsetX, endY + offsetY,
                    1 + Math.random() * 2, '#87ceeb'
                );
            }
            await new Promise(r => setTimeout(r, 50));
        }
        await this._createEnergyImpact(endX, endY, intensity);
    }

    async _blackHole_v1_0(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🌑 Creando agujero negro en jugador ${playerId} (v1.0)`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const centerX = target.x;
        const centerY = target.y;
        const duration = 50 + intensity * 50;
        const startTime = Date.now();

        while (Date.now() - startTime < duration) {
            if (this._stopSignal) break;

            const elapsed = Date.now() - startTime;
            const progress = elapsed / duration;

            const coreRadius = 15 + intensity * 3;
            for (let angle = 0; angle < 16; angle++) {
                const radians = (angle / 16) * 2 * Math.PI;
                const x = centerX + coreRadius * Math.cos(radians);
                const y = centerY + coreRadius * Math.sin(radians);
                this._sendAndRenderDrawCommand(centerX, centerY, x, y, 6, '#000000');
            }

            const rings = 3;
            for (let ring = 0; ring < rings; ring++) {
                const ringRadius = (30 + ring * 20) * (1 + progress * 0.5);
                const ringSegments = 24;

                for (let seg = 0; seg < ringSegments; seg++) {
                    const angle1 = (seg / ringSegments) * 2 * Math.PI + elapsed * 0.005 * (ring + 1);
                    const angle2 = ((seg + 1) / ringSegments) * 2 * Math.PI + elapsed * 0.005 * (ring + 1);

                    const x1 = centerX + ringRadius * Math.cos(angle1);
                    const y1 = centerY + ringRadius * Math.sin(angle1) * 0.3;
                    const x2 = centerX + ringRadius * Math.cos(angle2);
                    const y2 = centerY + ringRadius * Math.sin(angle2) * 0.3;

                    const colors = ['#ff6600', '#ff9900', '#ffcc00'];
                    const color = colors[ring % colors.length];
                    this._sendAndRenderDrawCommand(x1, y1, x2, y2, 3 - ring, color);
                }
            }

            for (let p = 0; p < 5; p++) {
                const distance = 100 + Math.random() * 100;
                const angle = Math.random() * Math.PI * 2;
                const startX = centerX + distance * Math.cos(angle);
                const startY = centerY + distance * Math.sin(angle);

                this._sendAndRenderDrawCommand(startX, startY, centerX, centerY, 1, '#ffffff');
            }
            await new Promise(r => setTimeout(r, 100));
        }
    }

    async _ufoInvasion_v1_0(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🛸 Iniciando invasión OVNI contra jugador ${playerId} (v1.0)`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const ufoCount = 3 + intensity;
        const ufos = [];

        for (let i = 0; i < ufoCount; i++) {
            ufos.push({
                x: Math.random() * this._gameCanvas.width,
                y: 50 + i * 30,
                angle: 0,
                targetX: target.x + (Math.random() - 0.5) * 100,
                targetY: target.y + (Math.random() - 0.5) * 50
            });
        }

        const attackDuration = 4000;
        const startTime = Date.now();

        while (Date.now() - startTime < attackDuration) {
            if (this._stopSignal) break;

            for (let i = 0; i < ufos.length; i++) {
                const ufo = ufos[i];

                const dx = ufo.targetX - ufo.x;
                const dy = ufo.targetY - ufo.y;
                ufo.x += dx * 0.02;
                ufo.y += dy * 0.02;
                ufo.angle += 0.1;

                await this._drawUFO(ufo.x, ufo.y, ufo.angle, intensity);

                if (Math.random() < 0.3) {
                    await this._ufoLaserShot(ufo.x, ufo.y, target.x, target.y);
                }
            }
            await new Promise(r => setTimeout(r, 200));
        }
    }

    async _gravityVortex_v1_0(playerId, intensity = 3) {
        if (this._stopSignal) return;
        this.notify('info', `🌪️ Vórtice Gravitacional activado en jugador ${playerId}`);

        const target = this._getPlayerCoords(playerId);
        if (!target) return;

        const centerX = target.x;
        const centerY = target.y;
        const duration = 50 + intensity * 50;
        const startTime = Date.now();

        while (Date.now() - startTime < duration) {
            if (this._stopSignal) break;

            const elapsed = Date.now() - startTime;
            const progress = elapsed / duration;

            const coreRadius = 10 + intensity * 2;
            for (let angle = 0; angle < 16; angle++) {
                const radians = (angle / 16) * 2 * Math.PI;
                const x = centerX + coreRadius * Math.cos(radians);
                const y = centerY + coreRadius * Math.sin(radians);
                this._sendAndRenderDrawCommand(centerX, centerY, x, y, 6, '#000000');
            }

            const spirals = 4;
            for (let spiral = 0; spiral < spirals; spiral++) {
                const spiralRadius = (30 + spiral * 20) * (1 + progress * 0.5);
                const spiralSegments = 24;

                for (let seg = 0; seg < spiralSegments; seg++) {
                    const angle1 = (seg / spiralSegments) * 2 * Math.PI + elapsed * 0.008 * (spiral + 1);
                    const angle2 = ((seg + 1) / spiralSegments) * 2 * Math.PI + elapsed * 0.008 * (spiral + 1);

                    const x1 = centerX + spiralRadius * Math.cos(angle1);
                    const y1 = centerY + spiralRadius * Math.sin(angle1) * 0.8;
                    const x2 = centerX + spiralRadius * Math.cos(angle2);
                    const y2 = centerY + spiralRadius * Math.sin(angle2) * 0.8;

                    const colors = ['#9400d3', '#4b0082', '#8a2be2'];
                    const color = colors[spiral % colors.length];
                    this._sendAndRenderDrawCommand(x1, y1, x2, y2, 4 - spiral, color);
                }
            }

            for (let ray = 0; ray < 4; ray++) {
                const rayAngle = (ray / 4) * 2 * Math.PI;
                const rayX = centerX + 20 * Math.cos(rayAngle);
                const rayY = centerY + 20 * Math.sin(rayAngle);
                this._sendAndRenderDrawCommand(centerX, centerY, rayX, rayY, 6, '#ffff00');
            }

            const planets = [{
                radius: 40,
                color: '#4285f4'
            }, {
                radius: 70,
                color: '#ff4444'
            }, {
                radius: 100,
                color: '#d2691e'
            }];

            planets.forEach((planet, index) => {
                const planetAngle = elapsed * 0.005 + (index * 2);
                const planetX = centerX + planet.radius * Math.cos(planetAngle);
                const planetY = centerY + planet.radius * Math.sin(planetAngle) * 0.7;

                this._sendAndRenderDrawCommand(planetX, planetY, planetX + 3, planetY + 3, 4, planet.color);
            });

            for (let star = 0; star < 6; star++) {
                const starAngle = (star / 6) * 2 * Math.PI;
                const starX = centerX + 120 * Math.cos(starAngle);
                const starY = centerY + 120 * Math.sin(starAngle);
                this._sendAndRenderDrawCommand(starX, starY, starX + 1, starY + 1, 2, '#ffffff');
            }

            await new Promise(r => setTimeout(r, 100));
        }
    }



    async _createImpactExplosion(x, y, intensity) {
        if (this._stopSignal) return;
        const particles = 8 + intensity * 2;
        const colors = ['#ff4500', '#ffa500', '#ffff00'];

        for (let p = 0; p < particles; p++) {
            const angle = (p / particles) * 2 * Math.PI;
            const distance = 15 + Math.random() * 20;
            const endX = x + distance * Math.cos(angle);
            const endY = y + distance * Math.sin(angle);
            const color = colors[Math.floor(Math.random() * colors.length)];
            this._sendAndRenderDrawCommand(x, y, endX, endY, 2 + intensity, color);
        }
        await new Promise(r => setTimeout(r, 50));
    }

    async _createEnergyImpact(x, y, intensity) {
        if (this._stopSignal) return;
        const steps = 10;
        for (let step = 0; step < steps; step++) {
            const radius = step * 5;
            const sparkCount = 8;
            for (let spark = 0; spark < sparkCount; spark++) {
                const angle = (spark / sparkCount) * 2 * Math.PI;
                const endX = x + radius * Math.cos(angle);
                const endY = y + radius * Math.sin(angle);
                this._sendAndRenderDrawCommand(x, y, endX, endY, 4 - step * 0.3, '#00ffff');
            }
            await new Promise(r => setTimeout(r, 50));
        }
    }

    async _drawMushroomCloud(x, y, intensity) {
        if (this._stopSignal) return;
        const cloudSteps = 15;
        const colors = ['#8b4513', '#a0522d', '#cd853f'];

        for (let step = 0; step < cloudSteps; step++) {
            const stemHeight = step * 10;
            const capRadius = 20 + step * 3;

            this._sendAndRenderDrawCommand(x, y, x, y - stemHeight, 8 + intensity, colors[0]);

            const segments = 16;
            for (let seg = 0; seg < segments; seg++) {
                const angle1 = (seg / segments) * Math.PI;
                const angle2 = ((seg + 1) / segments) * Math.PI;
                const x1 = x + capRadius * Math.cos(angle1);
                const y1 = y - stemHeight + capRadius * Math.sin(angle1) * 0.5;
                const x2 = x + capRadius * Math.cos(angle2);
                const y2 = y - stemHeight + capRadius * Math.sin(angle2) * 0.5;
                this._sendAndRenderDrawCommand(x1, y1, x2, y2, 4, colors[step % colors.length]);
            }
            await new Promise(r => setTimeout(r, 200));
        }
    }

    async _breatheFire(startX, startY, targetX, targetY, intensity) {
        if (this._stopSignal) return;
        const flames = 5 + intensity;
        const colors = ['#ff0000', '#ff4500', '#ffa500', '#ffff00'];

        for (let f = 0; f < flames; f++) {
            const progress = f / flames;
            const x = startX + (targetX - startX) * progress;
            const y = startY + (targetY - startY) * progress;
            const offsetX = (Math.random() - 0.5) * 30;
            const offsetY = (Math.random() - 0.5) * 30;
            const color = colors[Math.floor(Math.random() * colors.length)];
            this._sendAndRenderDrawCommand(startX, startY, x + offsetX, y + offsetY, 3 + intensity, color);
        }
    }

    async _drawUFO(x, y, angle, intensity) {
        if (this._stopSignal) return;
        const size = 20 + intensity * 3;

        this._sendAndRenderDrawCommand(x - size, y, x + size, y, 6, '#c0c0c0');
        this._sendAndRenderDrawCommand(x - size * 0.7, y - size * 0.3, x + size * 0.7, y - size * 0.3, 4, '#c0c0c0');

        const lights = 6;
        for (let light = 0; light < lights; light++) {
            const lightAngle = (light / lights) * 2 * Math.PI + angle;
            const lightX = x + size * 0.8 * Math.cos(lightAngle);
            const lightY = y + size * 0.8 * Math.sin(lightAngle) * 0.3;
            this._sendAndRenderDrawCommand(lightX - 2, lightY - 2, lightX + 2, lightY + 2, 3, '#00ff00');
        }

        if (Math.random() < 0.5) {
            this._sendAndRenderDrawCommand(x, y + size * 0.5, x, y + size * 2, 8, '#00ffff');
        }
    }

    async _ufoLaserShot(startX, startY, targetX, targetY) {
        if (this._stopSignal) return;
        this._sendAndRenderDrawCommand(startX, startY, targetX, targetY, 3, '#ff0000');
        await new Promise(r => setTimeout(r, 100));
    }
}


function getGameSocket() {
    if (window._io && window._io.socket && window._io.socket.readyState === WebSocket.OPEN) {
        return window._io.socket;
    }
    if (globalThis.sockets && globalThis.sockets.length > 0) {
        const primarySocket = globalThis.sockets.find(s =>
            s.url.includes("drawaria.online/socket.io") && s.readyState === WebSocket.OPEN
        );
        if (primarySocket) return primarySocket;
    }
    return null;
}


function getHumanPlayersCount() {
    const playerlistElement = document.getElementById("playerlist");
    if (!playerlistElement) return 0;

    const playerRows = playerlistElement.querySelectorAll(".playerlist-row");
    let count = 0;

    playerRows.forEach(row => {
        const playerId = row.dataset.playerid;
        const isSelf = row.dataset.self === 'true';
        const isBot = playerId === "0";

        if (!isSelf && !isBot && playerId) {
            count++;
        }
    });

    return count;
}


class AvatarPhysicsPlatformerTool extends QBit {

    #avatar = null;
    #isRunning = false;
    #autoMovement = false;
    #autoJump = false;
    #boundaryBounce = false;


    #hitboxColorMap = {};
    #customHitboxTypes = [];
    #nextCustomHitboxId = 0;
    #selectedHitboxType = 'solid';


    #playerCountInterval = null;
    #lastPlayerCount = -1;


    #backgroundRange = { min: 32001, max: 32050 };
    #currentBackground = 32001;
    #isLoadingBackground = false;


    #originalAvatarStyles = {};
    #originalGestureStyles = [];
    #originalReturnButtonState = {};
    #spawnCoordinates = {

        top: 0,
        left: 0,
        captured: false,

        gameX: 50,
        gameY: 100,
        gameCoordsCaptured: false
    };
    #spawnDetectionInterval = null;
    #hasDetectedInitialSpawn = false;


    #ui = {};


    #STANDARD_COLORS = {
        black: { hex: 'var(--dark)', r: 0, g: 0, b: 0 },
        white: { hex: 'var(--light)', r: 255, g: 255, b: 255 },
        gray: { hex: 'var(--secondary)', r: 127, g: 127, b: 127 },
        red: { hex: 'var(--danger)', r: 220, g: 38, b: 127 },
        green: { hex: 'var(--success)', r: 72, g: 187, b: 120 },
        blue: { hex: 'var(--primary)', r: 66, g: 153, b: 225 },
        celeste: { hex: 'var(--info)', r: 147, g: 207, b: 255 },
        yellow: { hex: 'var(--warning)', r: 255, g: 255, b: 0 },
        orange: { hex: '#ff9300', r: 255, g: 147, b: 0 },
        violet: { hex: '#7f007f', r: 127, g: 0, b: 127 },
        light_pink: { hex: '#ffbfff', r: 255, g: 191, b: 223 },
        brown: { hex: '#7f3f00', r: 127, g: 63, b: 0 }
    };


    #DEFAULT_HITBOX_TYPES = {
        solid: { name: 'Solid', colorKey: 'black', type: 'solid', description: 'Solid platforms' },
        bounce: { name: 'Bounce', colorKey: 'yellow', type: 'bounce', description: 'Makes you bounce' },
        speed_boost: { name: 'Speed Boost', colorKey: 'red', type: 'speed_boost', description: 'Increases your speed' },
        water: { name: 'Swim', colorKey: 'blue', type: 'water', description: 'Allows swimming' },
        anti_gravity: { name: 'Anti Gravity', colorKey: 'green', type: 'anti_gravity', description: 'Inverts gravity' },
        normal_gravity: { name: 'Normal Gravity', colorKey: 'light_pink', type: 'normal_gravity', description: 'Restores gravity' },
        push_back: { name: 'Push Back', colorKey: 'violet', type: 'push_back', description: 'Pushes you in opposite direction' },
        coin: { name: 'Moneda 🪙', colorKey: 'orange', type: 'coin', description: 'Collectable coin' }
    };

    constructor() {
        super("🗡️Platformer Mode", '<i class="fas fa-running"></i>');
        this.#onStartup();
    }

    #onStartup() {
        this.#initHitboxColors();
        this.#loadInterface();
        this.#initPhysicsSystem();
        this.#startPlayerMonitoring();
        this.#startStatusUpdates();
        this.#startAutoSpawnDetection();
        this.notify("info", "Avatar Physics Platformer loaded with unified coordinate system!");
    }


    #startAutoSpawnDetection() {
        let detectionAttempts = 0;
        const maxAttempts = 50;

        this.#spawnDetectionInterval = setInterval(() => {
            detectionAttempts++;

            if (detectionAttempts > maxAttempts) {
                clearInterval(this.#spawnDetectionInterval);
                console.log('🕒 Auto spawn detection timeout after 5 seconds');
                return;
            }

            if (!this.#hasDetectedInitialSpawn) {
                const success = this.#detectAndCaptureInitialSpawn();
                if (success) {
                    this.#hasDetectedInitialSpawn = true;
                    clearInterval(this.#spawnDetectionInterval);
                    console.log('🎯 Auto spawn detection completed successfully!');
                }
            }
        }, 100);
    }


    #detectAndCaptureInitialSpawn() {
        const avatarElement = document.querySelector("body > div.spawnedavatar.spawnedavatar-self");
        if (!avatarElement) return false;


        const gameAreaElement = this.#findGameAreaElement();
        if (!gameAreaElement) return false;

        try {
            const avatarRect = avatarElement.getBoundingClientRect();
            const gameRect = gameAreaElement.getBoundingClientRect();


            if (avatarRect.width > 0 && avatarRect.height > 0 && gameRect.width > 0 && gameRect.height > 0) {

                this.#spawnCoordinates.top = avatarRect.top + window.scrollY;
                this.#spawnCoordinates.left = avatarRect.left + window.scrollX;
                this.#spawnCoordinates.captured = true;


                const gameCoords = this.#convertPixelsToGameCoordinates(avatarRect, gameRect);
                if (gameCoords) {
                    this.#spawnCoordinates.gameX = gameCoords.x;
                    this.#spawnCoordinates.gameY = gameCoords.y;
                    this.#spawnCoordinates.gameCoordsCaptured = true;

                    console.log('🏠 Spawn coordinates captured:', {
                        pixels: { top: this.#spawnCoordinates.top, left: this.#spawnCoordinates.left },
                        game: { x: this.#spawnCoordinates.gameX, y: this.#spawnCoordinates.gameY }
                    });


                    if (this.#ui['spawn-status']) {
                        this.#ui['spawn-status'].textContent = 'Auto-captured (Unified)';
                        this.#ui['spawn-status'].style.color = 'var(--success)';
                    }


                    return true;
                }
            }
        } catch (error) {
            console.warn('Error in auto spawn detection:', error);
        }

        return false;
    }


    #findGameAreaElement() {
        const selectors = [
            "#drawing-assistant-overlay",
            "#gamearea",
            "#canvas",
            ".game-container",
            "body"
        ];

        for (const selector of selectors) {
            const element = document.querySelector(selector);
            if (element) {
                return element;
            }
        }
        return null;
    }


    #convertPixelsToGameCoordinates(avatarRect, gameRect) {
        try {

            const avatarCenterX = avatarRect.left + (avatarRect.width / 2);
            const avatarCenterY = avatarRect.top + (avatarRect.height / 2);


            const relativeX = avatarCenterX - gameRect.left;
            const relativeY = avatarCenterY - gameRect.top;


            const gameX = (relativeX / gameRect.width) * 100;
            const gameY = (relativeY / gameRect.height) * 100;


            if (gameX >= 0 && gameX <= 100 && gameY >= 0 && gameY <= 100) {
                return { x: gameX, y: gameY };
            }

            return null;
        } catch (error) {
            console.warn('Error converting coordinates:', error);
            return null;
        }
    }


    #captureSpawnCoordinates() {
        const avatarElement = document.querySelector("body > div.spawnedavatar.spawnedavatar-self");
        if (!avatarElement) {
            console.warn('Avatar element not found for spawn capture');
            return false;
        }

        const gameAreaElement = this.#findGameAreaElement();
        if (!gameAreaElement) {
            console.warn('Game area element not found for spawn capture');
            return false;
        }

        try {
            const avatarRect = avatarElement.getBoundingClientRect();
            const gameRect = gameAreaElement.getBoundingClientRect();


            this.#spawnCoordinates.top = avatarRect.top + window.scrollY;
            this.#spawnCoordinates.left = avatarRect.left + window.scrollX;
            this.#spawnCoordinates.captured = true;


            const gameCoords = this.#convertPixelsToGameCoordinates(avatarRect, gameRect);
            if (gameCoords) {
                this.#spawnCoordinates.gameX = gameCoords.x;
                this.#spawnCoordinates.gameY = gameCoords.y;
                this.#spawnCoordinates.gameCoordsCaptured = true;

                console.log('📍 Spawn coordinates manually captured:', {
                    pixels: { top: this.#spawnCoordinates.top, left: this.#spawnCoordinates.left },
                    game: { x: this.#spawnCoordinates.gameX, y: this.#spawnCoordinates.gameY }
                });

                return true;
            }
        } catch (error) {
            console.warn('Error capturing spawn coordinates:', error);
            return false;
        }

        return false;
    }


    #resetToSpawnCoordinates() {
        if (!this.#spawnCoordinates.gameCoordsCaptured) {
            console.warn('No game coordinates available for reset');


            const emergencyCapture = this.#captureSpawnCoordinates();
            if (!emergencyCapture) {
                this.notify("warning", "No spawn coordinates available");
                return false;
            }
        }


        if (this.#avatar) {

            this.#avatar.position.x = this.#spawnCoordinates.gameX;
            this.#avatar.position.y = this.#spawnCoordinates.gameY;


            this.#avatar.velocity = new Vector2D(0, 0);


            this.#avatar.onGround = (this.#spawnCoordinates.gameY >= 95);
            this.#avatar.isSwimming = false;
            this.#avatar.gravity = this.#avatar.originalGravity;
            this.#avatar.isAntiGravityActive = false;
            this.#avatar.isSpeedBoostActive = false;

            console.log('🏠 Avatar reset to spawn using game coordinates:', {
                x: this.#spawnCoordinates.gameX,
                y: this.#spawnCoordinates.gameY
            });

            return true;
        }

        return false;
    }


    async #startPhysics() {
        if (this.#isRunning || !this.#avatar) return;


        this.#captureSpawnCoordinates();



        if (this.#ui['physics-status']) {
            this.#ui['physics-status'].textContent = 'Preparing...';
        }


        this.#disableAvatarInteraction();
        this.#disableGesturePickers();
        this.#disableReturnButton();


        await new Promise(resolve => setTimeout(resolve, 200));

        const resetSuccess = this.#resetToSpawnCoordinates();
        if (resetSuccess) {

            console.log('🏠 Avatar reset to spawn before physics activation (unified system)');
        }


        await new Promise(resolve => setTimeout(resolve, 300));


        this.#avatar.activatePhysics();
        this.#isRunning = true;


        if (this.#ui['physics-status']) {
            this.#ui['physics-status'].textContent = 'Active (Unified)';
        }


        console.log('🎮 Physics started with unified coordinate system');
    }


    #stopPhysics() {
        if (!this.#isRunning || !this.#avatar) return;


        this.#avatar.deactivatePhysics();
        this.#isRunning = false;


        this.#restoreAvatarInteraction();
        this.#restoreGesturePickers();
        this.#restoreReturnButton();


        if (this.#ui['physics-status']) {
            this.#ui['physics-status'].textContent = 'Stopped';
        }

        this.notify("info", "Avatar platformer deactivated and fully restored");
        console.log('🎮 Physics stopped with complete restoration');
    }

    #resetAvatar() {
        if (!this.#avatar) return;


        const resetSuccess = this.#resetToSpawnCoordinates();
        if (resetSuccess) {
            this.notify("info", "Avatar reset to spawn coordinates (unified system)");
        } else {

            this.#avatar.reset();
            this.notify("info", "Avatar physics reset (basic)");
        }
    }

    #boostAvatar() {
        if (!this.#avatar) return;
        this.#avatar.boost();
        this.notify("info", "Boost applied!");
    }


    #disableAvatarInteraction() {
        const avatarElement = document.querySelector("body > div.spawnedavatar.spawnedavatar-self");
        if (!avatarElement) return;

        this.#originalAvatarStyles = {
            pointerEvents: avatarElement.style.pointerEvents || 'auto',
            userSelect: avatarElement.style.userSelect || 'auto',
            cursor: avatarElement.style.cursor || 'default'
        };

        avatarElement.style.pointerEvents = 'none';
        avatarElement.style.userSelect = 'none';
        avatarElement.style.cursor = 'not-allowed';

        console.log('🚫 Avatar interaction disabled');
    }

    #restoreAvatarInteraction() {
        const avatarElement = document.querySelector("body > div.spawnedavatar.spawnedavatar-self");
        if (!avatarElement) return;

        avatarElement.style.pointerEvents = this.#originalAvatarStyles.pointerEvents || 'auto';
        avatarElement.style.userSelect = this.#originalAvatarStyles.userSelect || 'auto';
        avatarElement.style.cursor = this.#originalAvatarStyles.cursor || 'default';

        console.log('✅ Avatar interaction restored');
    }

    #disableGesturePickers() {
        const containers = document.querySelectorAll('.gesturespicker-container');

        containers.forEach(container => {
            const items = Array.from(container.children);
            let startIndex = -1;
            let endIndex = -1;

            for (let i = 0; i < items.length; i++) {
                const item = items[i];
                const text = this.#getAllTextContent(item);

                if (text.includes('jello')) {
                    startIndex = i;
                }
                if (text.includes('vibrate')) {
                    endIndex = i;
                }
            }

            if (startIndex !== -1 && endIndex !== -1) {
                const actualStartIndex = Math.min(startIndex, endIndex);
                const actualEndIndex = Math.max(startIndex, endIndex);

                for (let i = actualStartIndex; i <= actualEndIndex; i++) {
                    const item = items[i];
                    const hasImage = item.querySelector('img') !== null;

                    if (!hasImage) {
                        this.#originalGestureStyles.push({
                            element: item,
                            pointerEvents: item.style.pointerEvents || 'auto',
                            opacity: item.style.opacity || '1',
                            cursor: item.style.cursor || 'default'
                        });

                        item.style.pointerEvents = 'none';
                        item.style.opacity = '0.3';
                        item.style.cursor = 'not-allowed';
                    }
                }
            }
        });

        console.log('🎭 Gesture pickers disabled:', this.#originalGestureStyles.length, 'elements');
    }

    #restoreGesturePickers() {
        this.#originalGestureStyles.forEach(styleData => {
            const element = styleData.element;
            if (element) {
                element.style.pointerEvents = styleData.pointerEvents;
                element.style.opacity = styleData.opacity;
                element.style.cursor = styleData.cursor;
            }
        });

        this.#originalGestureStyles = [];
        console.log('🎭 Gesture pickers restored');
    }

    #disableReturnButton() {
        let returnButton = document.querySelector("#popover697783 > div.popover-body > button");

        if (!returnButton) {
            returnButton = document.querySelector('button.spawnavatarbutton');
        }

        if (!returnButton) {
            const buttons = document.querySelectorAll('button');
            for (const btn of buttons) {
                if (btn.textContent && btn.textContent.toLowerCase().includes('return')) {
                    returnButton = btn;
                    break;
                }
            }
        }

        if (!returnButton) {
            console.warn('Return button not found with any selector');
            return;
        }

        this.#originalReturnButtonState = {
            element: returnButton,
            disabled: returnButton.disabled,
            pointerEvents: returnButton.style.pointerEvents || 'auto',
            opacity: returnButton.style.opacity || '1',
            display: returnButton.style.display || 'block',
            visibility: returnButton.style.visibility || 'visible',
            parent: returnButton.parentNode
        };

        returnButton.disabled = true;
        returnButton.style.pointerEvents = 'none';
        returnButton.style.opacity = '0';
        returnButton.style.display = 'none';
        returnButton.style.visibility = 'hidden';

        if (returnButton.parentNode) {
            returnButton.parentNode.removeChild(returnButton);
        }

        console.log('🔴 Return button completely disabled, hidden and removed');
    }

    #restoreReturnButton() {
        if (!this.#originalReturnButtonState.element) {
            console.warn('No return button to restore');
            return;
        }

        const returnButton = this.#originalReturnButtonState.element;

        if (this.#originalReturnButtonState.parent && !returnButton.parentNode) {
            this.#originalReturnButtonState.parent.appendChild(returnButton);
        }

        returnButton.disabled = this.#originalReturnButtonState.disabled || false;
        returnButton.style.pointerEvents = this.#originalReturnButtonState.pointerEvents;
        returnButton.style.opacity = this.#originalReturnButtonState.opacity;
        returnButton.style.display = this.#originalReturnButtonState.display;
        returnButton.style.visibility = this.#originalReturnButtonState.visibility;

        console.log('🟢 Return button completely restored');
    }

    #getAllTextContent(element) {
        if (!element) return '';
        return Array.from(element.childNodes)
            .map(node => {
                if (node.nodeType === Node.TEXT_NODE) {
                    return node.textContent.trim();
                } else if (node.nodeType === Node.ELEMENT_NODE) {
                    return this.#getAllTextContent(node);
                }
                return '';
            })
            .filter(text => text)
            .join(' ')
            .toLowerCase();
    }




    #startPlayerMonitoring() {
        this.#playerCountInterval = setInterval(() => {
            const currentCount = getHumanPlayersCount();
            if (currentCount !== this.#lastPlayerCount) {
                this.#lastPlayerCount = currentCount;
                this.#updateFloorOffset(currentCount);
            }
        }, 1000);
    }

    #updateFloorOffset(playerCount) {
        if (!this.#avatar) return;

        let newOffset;

        if (playerCount === 1) {
            newOffset = -4;
        } else if (playerCount === 2) {
            newOffset = 53;
        } else if (playerCount === 3) {
            newOffset = 110;
        } else if (playerCount === 4) {
            newOffset = 167;
        } else if (playerCount === 5) {
            newOffset = 224;
        } else if (playerCount === 6) {
            newOffset = 281;
        } else if (playerCount === 7) {
            newOffset = 338;
        } else if (playerCount === 8) {
            newOffset = 395;
        } else if (playerCount === 9) {
            newOffset = 452;
        } else if (playerCount === 10) {
            newOffset = 509;
        } else {
            newOffset = 509 + ((playerCount - 10) * 57);
        }

        this.#avatar.visualFloorOffsetPixels = newOffset;

        if (this.#ui.floorOffsetSlider) {
            this.#ui.floorOffsetSlider.value = newOffset;
        }
        if (this.#ui.floorOffsetDisplay) {
            this.#ui.floorOffsetDisplay.textContent = newOffset.toString();
        }

        console.log(`🔄 Floor Offset: ${newOffset}px (${playerCount} players) - ${playerCount === 1 ? 'Solo mode' : 'Multi mode'}`);
    }

    #initHitboxColors() {
        for (const key in this.#DEFAULT_HITBOX_TYPES) {
            const typeInfo = this.#DEFAULT_HITBOX_TYPES[key];
            this.#hitboxColorMap[key] = this.#STANDARD_COLORS[typeInfo.colorKey].hex;
        }
    }

    #loadInterface() {
        this.#createControlSection();
        this.#createAutomationSection();
        this.#createPhysicsSection();
        this.#createHitboxColorsSection();
    }

    #startStatusUpdates() {
        setInterval(() => {
            try {
                if (this.#ui['avatar-position']) {
                    this.#ui['avatar-position'].textContent = this.#avatar ?
                        `${this.#avatar.position.x.toFixed(1)}, ${this.#avatar.position.y.toFixed(1)}` : 'N/A';
                }

                if (this.#ui['avatar-grounded']) {
                    let status = this.#avatar ? (this.#avatar.onGround ? 'Yes' : 'No') : 'N/A';
                    if (this.#avatar && this.#avatar.isSwimming) status += ' (Swimming)';
                    this.#ui['avatar-grounded'].textContent = status;
                }

                const gameSocket = getGameSocket();
                if (this.#ui['socket-status']) {
                    this.#ui['socket-status'].textContent = (gameSocket && gameSocket.readyState === WebSocket.OPEN) ?
                        '✅ Connected' : '❌ Disconnected';
                }

                if (this.#ui['velocity-info']) {
                    this.#ui['velocity-info'].textContent = this.#avatar ?
                        `${this.#avatar.velocity.x.toFixed(1)}, ${this.#avatar.velocity.y.toFixed(1)}` : 'N/A';
                }

                if (this.#ui['player-count']) {
                    this.#ui['player-count'].textContent = this.#lastPlayerCount.toString();
                }


                if (this.#ui['spawn-status']) {
                    if (this.#spawnCoordinates.gameCoordsCaptured) {
                        if (this.#hasDetectedInitialSpawn) {
                            this.#ui['spawn-status'].textContent = 'Auto-captured (Unified)';
                            this.#ui['spawn-status'].style.color = 'var(--success)';
                        } else {
                            this.#ui['spawn-status'].textContent = 'Manual (Unified)';
                            this.#ui['spawn-status'].style.color = 'var(--info)';
                        }
                    } else {
                        this.#ui['spawn-status'].textContent = 'Detecting...';
                        this.#ui['spawn-status'].style.color = 'var(--warning)';
                    }
                }

                this.#updateBackgroundStatus();
            } catch (error) {

            }
        }, 100);
    }

        destroy() {
        if (this.#playerCountInterval) {
            clearInterval(this.#playerCountInterval);
        }

        if (this.#spawnDetectionInterval) {
            clearInterval(this.#spawnDetectionInterval);
        }

        if (this.#avatar) {
            this.#avatar.destroy();
        }


        this.#restoreAvatarInteraction();
        this.#restoreGesturePickers();
        this.#restoreReturnButton();

        super.destroy?.();
    }


    #createControlSection() {
        const controlSection = domMake.Tree("details", { class: "physics-section", open: true });
        const summary = domMake.Tree("summary", { class: "physics-section-title" });
        summary.appendChild(domMake.Tree("h6", {}, ["🎮 Avatar Control"]));
        controlSection.appendChild(summary);

        const content = domMake.Tree("div", { class: "physics-section-content" });


        const buttonGrid = domMake.Tree("div", { class: "physics-button-grid" });
        buttonGrid.style.gridTemplateColumns = '1fr 1fr 1fr 1fr';

        this.#ui.startButton = domMake.Button('<i class="fas fa-play"></i> Start Platformer');
        this.#ui.startButton.className = 'physics-btn primary';
        this.#ui.startButton.addEventListener('click', () => this.#startPhysics());

        this.#ui.stopButton = domMake.Button('<i class="fas fa-stop"></i> Stop');
        this.#ui.stopButton.className = 'physics-btn secondary';
        this.#ui.stopButton.addEventListener('click', () => this.#stopPhysics());

        this.#ui.resetButton = domMake.Button('<i class="fas fa-redo"></i> Reset');
        this.#ui.resetButton.className = 'physics-btn warning';
        this.#ui.resetButton.addEventListener('click', () => this.#resetAvatar());

        this.#ui.boostButton = domMake.Button('<i class="fas fa-rocket"></i> Boost');
        this.#ui.boostButton.className = 'physics-btn special';
        this.#ui.boostButton.addEventListener('click', () => this.#boostAvatar());

        buttonGrid.appendAll(
            this.#ui.startButton,
            this.#ui.stopButton,
            this.#ui.resetButton,
            this.#ui.boostButton
        );
        content.appendChild(buttonGrid);


        const backgroundSection = domMake.Tree("div", { class: "physics-background-controls" });
        const backgroundTitle = domMake.Tree("h6", { class: "physics-background-title" }, ["🖼️ SVG Background Selection"]);
        backgroundSection.appendChild(backgroundTitle);

        const backgroundGrid = domMake.Tree("div", { class: "physics-background-grid" });

        this.#ui.backgroundPrevButton = domMake.Button('<i class="fas fa-chevron-left"></i>');
        this.#ui.backgroundPrevButton.className = 'physics-background-btn';
        this.#ui.backgroundPrevButton.addEventListener('click', () => this.#previousBackground());

        this.#ui.backgroundInput = domMake.Tree("input", {
            type: "number",
            min: this.#backgroundRange.min,
            max: this.#backgroundRange.max,
            value: this.#currentBackground,
            class: "physics-background-input"
        });
        this.#ui.backgroundInput.addEventListener('change', (e) => {
            const value = parseInt(e.target.value);
            if (value >= this.#backgroundRange.min && value <= this.#backgroundRange.max) {
                this.#currentBackground = value;
            } else {
                e.target.value = this.#currentBackground;
            }
        });

        this.#ui.backgroundNextButton = domMake.Button('<i class="fas fa-chevron-right"></i>');
        this.#ui.backgroundNextButton.className = 'physics-background-btn';
        this.#ui.backgroundNextButton.addEventListener('click', () => this.#nextBackground());

        this.#ui.drawBackgroundButton = domMake.Button('<i class="fas fa-pencil-alt"></i>');
        this.#ui.drawBackgroundButton.className = 'physics-background-btn draw';
        this.#ui.drawBackgroundButton.title = 'Draw selected background';
        this.#ui.drawBackgroundButton.addEventListener('click', () => this.#pasteBackground());

        this.#ui.backgroundRandomButton = domMake.Button('<i class="fas fa-random"></i>');
        this.#ui.backgroundRandomButton.className = 'physics-background-btn random';
        this.#ui.backgroundRandomButton.addEventListener('click', () => this.#randomBackground());

        this.#ui.backgroundClearButton = domMake.Button('<i class="fas fa-eraser"></i>');
        this.#ui.backgroundClearButton.className = 'physics-background-btn clear';
        this.#ui.backgroundClearButton.addEventListener('click', () => this.#clearCanvas());

        backgroundGrid.appendAll(
            this.#ui.backgroundPrevButton,
            this.#ui.backgroundInput,
            this.#ui.backgroundNextButton,
            this.#ui.drawBackgroundButton,
            this.#ui.backgroundRandomButton,
            this.#ui.backgroundClearButton
        );

        backgroundSection.appendChild(backgroundGrid);
        content.appendChild(backgroundSection);


        const statusDisplay = domMake.Tree("div", { class: "physics-status-display" });
        const statusItems = [
            { label: "Status:", id: "physics-status", value: "Stopped" },
            { label: "Position:", id: "avatar-position", value: "N/A" },
            { label: "On Ground:", id: "avatar-grounded", value: "N/A" },
            { label: "Socket:", id: "socket-status", value: "Detecting..." },
            { label: "Velocity:", id: "velocity-info", value: "0, 0" },
            { label: "Other Players:", id: "player-count", value: "0" },
            { label: "Current Background:", id: "current-background", value: "32001" }
        ];

        statusItems.forEach(item => {
            const statusItem = domMake.Tree("div", { class: "physics-status-item" });
            statusItem.appendChild(domMake.Tree("span", { class: "status-label" }, [item.label]));
            this.#ui[item.id] = domMake.Tree("span", { class: "status-value" }, [item.value]);
            statusItem.appendChild(this.#ui[item.id]);
            statusDisplay.appendChild(statusItem);
        });
        content.appendChild(statusDisplay);

        controlSection.appendChild(content);
        this.htmlElements.section.appendChild(controlSection);
    }


    #previousBackground() {
        if (this.#isLoadingBackground) return;

        this.#currentBackground--;
        if (this.#currentBackground < this.#backgroundRange.min) {
            this.#currentBackground = this.#backgroundRange.max;
        }
        this.#ui.backgroundInput.value = this.#currentBackground;
        this.#updateBackgroundStatus();
    }

    #nextBackground() {
        if (this.#isLoadingBackground) return;

        this.#currentBackground++;
        if (this.#currentBackground > this.#backgroundRange.max) {
            this.#currentBackground = this.#backgroundRange.min;
        }
        this.#ui.backgroundInput.value = this.#currentBackground;
        this.#updateBackgroundStatus();
    }

    #randomBackground() {
        if (this.#isLoadingBackground) return;

        const randomId = Math.floor(Math.random() * (this.#backgroundRange.max - this.#backgroundRange.min + 1)) + this.#backgroundRange.min;
        this.#currentBackground = randomId;
        this.#ui.backgroundInput.value = this.#currentBackground;
        this.#updateBackgroundStatus();
    }

    #updateBackgroundStatus() {
        if (this.#ui['current-background']) {
            this.#ui['current-background'].textContent = this.#currentBackground.toString();
        }
    }

    async #pasteBackground() {
        if (this.#isLoadingBackground) {
            this.notify("warning", "Background is already loading...");
            return;
        }

        this.#isLoadingBackground = true;
        this.#setLoadingState(true);

        this.#currentBackground = parseInt(this.#ui.backgroundInput.value) || this.#currentBackground;

        try {
            const canvas = document.getElementById('canvas');
            if (!canvas) {
                this.notify("error", "Canvas not found!");
                return;
            }

            const ctx = canvas.getContext('2d');
            const img = new Image();
            img.crossOrigin = 'anonymous';

            img.onload = () => {
                try {
                    const scaleX = canvas.width / img.width;
                    const scaleY = canvas.height / img.height;
                    const scale = Math.min(scaleX, scaleY) * 1.5;

                    const xOffset = (canvas.width - img.width * scale) / 2;
                    const yOffset = (canvas.height - img.height * scale) / 2;

                    ctx.drawImage(img, xOffset, yOffset, img.width * scale, img.height * scale);

                    this.notify("success", `Background ${this.#currentBackground} pasted successfully!`);
                    console.log(`🖼️ Background ${this.#currentBackground} pasted to canvas`);
                } catch (error) {
                    console.error('Error drawing background:', error);
                    this.notify("error", "Error drawing background");
                } finally {
                    this.#isLoadingBackground = false;
                    this.#setLoadingState(false);
                    this.#updateBackgroundStatus();
                }
            };

            img.onerror = () => {
                this.notify("error", `Failed to load background ${this.#currentBackground}`);
                console.warn(`Failed to load background ${this.#currentBackground}`);
                this.#isLoadingBackground = false;
                this.#setLoadingState(false);
                this.#updateBackgroundStatus();
            };

            const backgroundUrl = `https://drawaria.online/img/inventory/${this.#currentBackground}.svg`;
            img.src = backgroundUrl;

        } catch (error) {
            console.error('Error in pasteBackground:', error);
            this.notify("error", "Error pasting background");
            this.#isLoadingBackground = false;
            this.#setLoadingState(false);
            this.#updateBackgroundStatus();
        }
    }

    #clearCanvas() {
        const canvas = document.getElementById('canvas');
        if (!canvas) return;

        try {
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = '#ffffff';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            console.log('🧹 Canvas cleared');
            this.notify("info", "Canvas cleared");
        } catch (error) {
            console.warn('Error clearing canvas:', error);
            this.notify("error", "Error clearing canvas");
        }
    }

    #setLoadingState(isLoading) {
        const buttons = [
            this.#ui.backgroundPrevButton,
            this.#ui.backgroundNextButton,
            this.#ui.backgroundRandomButton,
            this.#ui.drawBackgroundButton,
            this.#ui.backgroundClearButton
        ];

        buttons.forEach(button => {
            if (button) {
                button.disabled = isLoading;
                button.style.opacity = isLoading ? '0.5' : '1';
                button.style.cursor = isLoading ? 'not-allowed' : 'pointer';
            }
        });

        if (this.#ui.backgroundInput) {
            this.#ui.backgroundInput.disabled = isLoading;
        }
    }

    #createAutomationSection() {
        const autoSection = domMake.Tree("details", { class: "physics-section" });
        const summary = domMake.Tree("summary", { class: "physics-section-title" });
        summary.appendChild(domMake.Tree("h6", {}, ["🤖 Automation & Controls"]));
        autoSection.appendChild(summary);

        const content = domMake.Tree("div", { class: "physics-section-content" });


        const controlsInfo = domMake.Tree("div", { class: "physics-controls-info" });
        const controlsTitle = domMake.Tree("h6", {}, ["⌨️ Manual Controls"]);
        const controlsList = domMake.Tree("div", { class: "physics-controls-list" });
        const controls = [
            { key: "←", desc: " - Move left" },
            { key: "→", desc: " - Move right" },
            { key: "↑", desc: " - Jump" }
        ];

        controls.forEach(control => {
            const item = domMake.Tree("div", { class: "physics-control-item" });
            item.appendChild(domMake.Tree("span", { class: "physics-key" }, [control.key]));
            item.appendChild(domMake.Tree("span", { class: "physics-desc" }, [control.desc]));
            controlsList.appendChild(item);
        });

        controlsInfo.appendAll(controlsTitle, controlsList);
        content.appendChild(controlsInfo);

        const separator = domMake.Tree("hr", { style: "margin: 15px 0; border: 1px solid var(--input-border-blue);" });
        content.appendChild(separator);

        const autoTitle = domMake.Tree("h6", {}, ["🔄 Automatic Controls"]);
        content.appendChild(autoTitle);

        const autoControls = domMake.Tree("div", { class: "physics-auto-controls" });

        const toggleGroup1 = domMake.Tree("div", { class: "physics-toggle-group" });
        this.#ui.autoMovement = domMake.Tree("input", { type: "checkbox" });
        this.#ui.autoMovement.addEventListener('change', (e) => {
            this.#autoMovement = e.target.checked;
            console.log('Auto Movement:', this.#autoMovement);
        });
        const label1 = domMake.Tree("label", { class: "physics-toggle-label" });
        label1.appendAll(this.#ui.autoMovement, domMake.Tree("span", {}, ["Auto Movement"]));
        toggleGroup1.appendChild(label1);

        const toggleGroup2 = domMake.Tree("div", { class: "physics-toggle-group" });
        this.#ui.autoJump = domMake.Tree("input", { type: "checkbox" });
        this.#ui.autoJump.addEventListener('change', (e) => {
            this.#autoJump = e.target.checked;
            console.log('Auto Jump:', this.#autoJump);
        });
        const label2 = domMake.Tree("label", { class: "physics-toggle-label" });
        label2.appendAll(this.#ui.autoJump, domMake.Tree("span", {}, ["Auto Jump"]));
        toggleGroup2.appendChild(label2);

        const toggleGroup3 = domMake.Tree("div", { class: "physics-toggle-group" });
        this.#ui.boundaryBounce = domMake.Tree("input", { type: "checkbox" });
        this.#ui.boundaryBounce.addEventListener('change', (e) => {
            this.#boundaryBounce = e.target.checked;
            console.log('Boundary Bounce:', this.#boundaryBounce);
        });
        const label3 = domMake.Tree("label", { class: "physics-toggle-label" });
        label3.appendAll(this.#ui.boundaryBounce, domMake.Tree("span", {}, ["Boundary Bounce"]));
        toggleGroup3.appendChild(label3);

        autoControls.appendAll(toggleGroup1, toggleGroup2, toggleGroup3);
        content.appendChild(autoControls);

        const stopAutoBtn = domMake.Button('<i class="fas fa-stop"></i> Stop Auto');
        stopAutoBtn.className = 'physics-btn secondary';
        stopAutoBtn.addEventListener('click', () => this.#stopAutomation());
        content.appendChild(stopAutoBtn);

        autoSection.appendChild(content);
        this.htmlElements.section.appendChild(autoSection);
    }

    #createPhysicsSection() {
        const physicsSection = domMake.Tree("details", { class: "physics-section" });
        const summary = domMake.Tree("summary", { class: "physics-section-title" });
        summary.appendChild(domMake.Tree("h6", {}, ["⚡ Physics Configuration"]));
        physicsSection.appendChild(summary);

        const content = domMake.Tree("div", { class: "physics-section-content" });

        const sliders = [
            { id: 'gravity', label: 'Gravity', min: 0, max: 2, step: 0.1, value: 0.8 },
            { id: 'moveForce', label: 'Movement Force', min: 5, max: 30, step: 1, value: 15 },
            { id: 'jumpForce', label: 'Jump Force', min: 10, max: 40, step: 1, value: 20 },
            { id: 'friction', label: 'Friction', min: 0, max: 0.5, step: 0.01, value: 0.1 },
            { id: 'mass', label: 'Mass', min: 0.1, max: 5, step: 0.1, value: 1.0 },
            { id: 'floorOffset', label: 'Floor Offset (Auto)', min: -20, max: 999, step: 1, value: -4 }
        ];

        sliders.forEach(slider => {
            const sliderGroup = domMake.Tree("div", { class: "physics-slider-group" });

            this.#ui[`${slider.id}Display`] = domMake.Tree("span", {}, [slider.value.toString()]);
            const label = domMake.Tree("label", {}, [
                slider.label + ': ',
                this.#ui[`${slider.id}Display`]
            ]);

            this.#ui[`${slider.id}Slider`] = domMake.Tree("input", {
                type: "range",
                min: slider.min,
                max: slider.max,
                step: slider.step,
                value: slider.value,
                class: "physics-slider"
            });

            if (slider.id === 'floorOffset') {
                this.#ui[`${slider.id}Slider`].disabled = true;
                this.#ui[`${slider.id}Slider`].title = "Controlled automatically by player count";
            } else {
                this.#ui[`${slider.id}Slider`].addEventListener('input', (e) => {
                    this.#ui[`${slider.id}Display`].textContent = e.target.value;
                    this.#updatePhysicsProperty(slider.id, parseFloat(e.target.value));
                });
            }

            sliderGroup.appendAll(label, this.#ui[`${slider.id}Slider`]);
            content.appendChild(sliderGroup);
        });

        const presetsContainer = domMake.Tree("div", { class: "physics-presets" });
        const presets = [
            { id: 'normal', label: '🔧 Normal' },
            { id: 'heavy', label: '🪨 Heavy' },
            { id: 'light', label: '🪶 Light' },
            { id: 'bouncy', label: '🏀 Bouncy' }
        ];

        presets.forEach(preset => {
            const presetBtn = domMake.Button(preset.label);
            presetBtn.className = 'physics-preset-btn';
            presetBtn.addEventListener('click', () => this.#setPreset(preset.id));
            presetsContainer.appendChild(presetBtn);
        });

        content.appendChild(presetsContainer);

        const debugBtn = domMake.Button('🔍 Debug Boundaries');
        debugBtn.className = 'physics-preset-btn';
        debugBtn.addEventListener('click', () => this.#debugBoundaries());
        content.appendChild(debugBtn);

        physicsSection.appendChild(content);
        this.htmlElements.section.appendChild(physicsSection);
    }

    #createHitboxColorsSection() {
        const hitboxSection = domMake.Tree("details", { class: "physics-section" });
        const summary = domMake.Tree("summary", { class: "physics-section-title" });
        summary.appendChild(domMake.Tree("h6", {}, ["🎨 Hitbox Colors and Effects"]));
        hitboxSection.appendChild(summary);

        const content = domMake.Tree("div", { class: "physics-section-content" });

        const colorControl = domMake.Tree("div", { class: "physics-color-control" });
        const colorLabel = domMake.Tree("label", {}, ["Edit Color for: "]);
        this.#ui.selectedTypeDisplay = domMake.Tree("span", { class: "selected-type" }, ["Solid"]);
        colorLabel.appendChild(this.#ui.selectedTypeDisplay);

        this.#ui.colorInput = domMake.Tree("input", { type: "color", value: "#000000" });
        this.#ui.colorInput.addEventListener('input', (e) => this.#updateHitboxColor(e.target.value));

        colorControl.appendAll(colorLabel, this.#ui.colorInput);
        content.appendChild(colorControl);

        const defaultTitle = domMake.Tree("h6", {}, ["Default Hitbox Types"]);
        content.appendChild(defaultTitle);

        this.#ui.defaultHitboxes = domMake.Tree("div", { class: "physics-hitbox-grid" });
        content.appendChild(this.#ui.defaultHitboxes);

        this.#populateHitboxColorsUI();

        hitboxSection.appendChild(content);
        this.htmlElements.section.appendChild(hitboxSection);
    }

    #populateHitboxColorsUI() {
        if (!this.#ui.defaultHitboxes) return;

        this.#ui.defaultHitboxes.innerHTML = '';

        for (const typeKey in this.#DEFAULT_HITBOX_TYPES) {
            const typeInfo = this.#DEFAULT_HITBOX_TYPES[typeKey];
            const currentHex = this.#hitboxColorMap[typeKey] || this.#STANDARD_COLORS[typeInfo.colorKey].hex;

            const hitboxItem = domMake.Tree("div", { class: "physics-hitbox-item" });
            const selectBtn = domMake.Button('');
            selectBtn.className = `physics-hitbox-select ${this.#selectedHitboxType === typeKey ? 'active' : ''}`;
            selectBtn.dataset.hitboxType = typeKey;

            const swatch = domMake.Tree("span", { class: "physics-hitbox-swatch" });
            swatch.style.backgroundColor = currentHex;

            const info = domMake.Tree("div", { class: "physics-hitbox-info" });
            const nameDiv = domMake.Tree("div", { class: "physics-hitbox-name" }, [typeInfo.name]);
            const descDiv = domMake.Tree("div", { class: "physics-hitbox-desc" }, [typeInfo.description]);
            info.appendAll(nameDiv, descDiv);

            selectBtn.appendAll(swatch, info);
            selectBtn.addEventListener('click', () => this.#selectHitboxType(typeKey));

            hitboxItem.appendChild(selectBtn);
            this.#ui.defaultHitboxes.appendChild(hitboxItem);
        }

        this.#updateSelectedColorInput();
    }

    #selectHitboxType(type) {
        this.#selectedHitboxType = type;

        const allButtons = this.#ui.defaultHitboxes.querySelectorAll('.physics-hitbox-select');
        allButtons.forEach(btn => btn.classList.remove('active'));

        const selectedButton = this.#ui.defaultHitboxes.querySelector(`[data-hitbox-type="${type}"]`);
        if (selectedButton) {
            selectedButton.classList.add('active');
        }

        this.#updateSelectedColorInput();
    }

    #updateSelectedColorInput() {
        if (this.#ui.colorInput && this.#hitboxColorMap[this.#selectedHitboxType]) {
            this.#ui.colorInput.value = this.#hitboxColorMap[this.#selectedHitboxType];
        }
        if (this.#ui.selectedTypeDisplay) {
            this.#ui.selectedTypeDisplay.textContent =
                this.#DEFAULT_HITBOX_TYPES[this.#selectedHitboxType]?.name || this.#selectedHitboxType;
        }
    }

    #updateHitboxColor(hex) {
        this.#hitboxColorMap[this.#selectedHitboxType] = hex;
        this.#updateHitboxColorSwatch(this.#selectedHitboxType, hex);
        this.#applyHitboxColorsToAvatar();
    }

    #updateHitboxColorSwatch(typeKey, hex) {
        const swatch = this.#ui.defaultHitboxes.querySelector(`[data-hitbox-type="${typeKey}"] .physics-hitbox-swatch`);
        if (swatch) {
            swatch.style.backgroundColor = hex;
        }
    }

    #applyHitboxColorsToAvatar() {
        if (!this.#avatar) return;

        this.#avatar.surfaceColors = {};
        for (const typeKey in this.#hitboxColorMap) {
            const hex = this.#hitboxColorMap[typeKey];
            const rgb = this.#hexToRgb(hex);
            if (rgb) {
                this.#avatar.surfaceColors[typeKey] = {
                    r: rgb.r, g: rgb.g, b: rgb.b,
                    type: typeKey,
                    hex: hex
                };
            }
        }
    }

    #hexToRgb(hex) {
        if (hex.startsWith('var(')) {
            const defaults = {
                'var(--dark)': { r: 0, g: 0, b: 0 },
                'var(--light)': { r: 255, g: 255, b: 255 },
                'var(--primary)': { r: 66, g: 153, b: 225 },
                'var(--secondary)': { r: 127, g: 127, b: 127 },
                'var(--success)': { r: 72, g: 187, b: 120 },
                'var(--danger)': { r: 220, g: 38, b: 127 },
                'var(--warning)': { r: 255, g: 255, b: 0 },
                'var(--info)': { r: 147, g: 207, b: 255 }
            };
            return defaults[hex] || { r: 127, g: 127, b: 127 };
        }

        if (hex.startsWith('#')) {
            const bigint = parseInt(hex.slice(1), 16);
            return {
                r: (bigint >> 16) & 255,
                g: (bigint >> 8) & 255,
                b: bigint & 255
            };
        }

        return null;
    }

    #initPhysicsSystem() {
        if (this.#avatar) return;

        this.#avatar = new PhysicsAvatar();
        this.#applyHitboxColorsToAvatar();
        this.#startUpdateLoop();
        console.log('🎮 Physics Avatar system initialized with unified coordinates.');
    }

    #updatePhysicsProperty(property, value) {
        if (!this.#avatar) return;

        switch(property) {
            case 'gravity':
                this.#avatar.gravity = value;
                this.#avatar.originalGravity = value;
                break;
            case 'moveForce':
                this.#avatar.moveForce = value;
                break;
            case 'jumpForce':
                this.#avatar.jumpForce = value;
                break;
            case 'friction':
                this.#avatar.friction = value;
                break;
            case 'mass':
                this.#avatar.mass = value;
                break;
        }
    }

    #setPreset(preset) {
        if (!this.#avatar) return;

        const presets = {
            normal: { gravity: 0.8, moveForce: 15, jumpForce: 20, friction: 0.1, mass: 1.0 },
            heavy: { gravity: 1.2, moveForce: 8, jumpForce: 15, friction: 0.15, mass: 3.0 },
            light: { gravity: 0.3, moveForce: 25, jumpForce: 30, friction: 0.05, mass: 0.3 },
            bouncy: { gravity: 0.6, moveForce: 20, jumpForce: 35, friction: 0.02, mass: 0.8 }
        };

        const config = presets[preset];
        if (!config) return;

        Object.keys(config).forEach(prop => {
            this.#updatePhysicsProperty(prop, config[prop]);
            if (this.#ui[`${prop}Slider`]) {
                this.#ui[`${prop}Slider`].value = config[prop];
                if (this.#ui[`${prop}Display`]) {
                    this.#ui[`${prop}Display`].textContent = config[prop];
                }
            }
        });

        this.notify("info", `${preset} preset applied`);
    }

    #debugBoundaries() {
        if (this.#avatar && this.#avatar.boundaryManager) {
            this.#avatar.boundaryManager.debugBounds();
        } else {
            this.notify("warning", "BoundaryManager not available for debug");
        }
    }

    #stopAutomation() {
        this.#autoMovement = false;
        this.#autoJump = false;
        this.#boundaryBounce = false;

        if (this.#ui.autoMovement) this.#ui.autoMovement.checked = false;
        if (this.#ui.autoJump) this.#ui.autoJump.checked = false;
        if (this.#ui.boundaryBounce) this.#ui.boundaryBounce.checked = false;

        this.notify("info", "Automation stopped");
    }

    #startUpdateLoop() {
        const update = () => {
            if (this.#isRunning && this.#avatar) {
                this.#avatar.update();

                if (this.#autoMovement && Math.random() < 0.02) {
                    this.#avatar.keys['left'] = false;
                    this.#avatar.keys['right'] = false;
                    const randomMove = Math.random();
                    if (randomMove < 0.4) this.#avatar.keys['left'] = true;
                    else if (randomMove < 0.8) this.#avatar.keys['right'] = true;
                }

                if (this.#autoJump && this.#avatar.onGround && Math.random() < 0.05) {
                    this.#avatar.keys['jump'] = true;
                    setTimeout(() => this.#avatar.keys['jump'] = false, 100);
                }

                if (this.#boundaryBounce && this.#avatar.boundaryManager) {
                    const bounds = this.#avatar.boundaryManager.realTimeBounds;
                    const pos = this.#avatar.position;

                    if (pos.x <= bounds.minX + 2) {
                        this.#avatar.velocity.x = Math.abs(this.#avatar.velocity.x) + 5;
                        this.#avatar.keys['right'] = true;
                        this.#avatar.keys['left'] = false;
                    } else if (pos.x >= bounds.maxX - 2) {
                        this.#avatar.velocity.x = -Math.abs(this.#avatar.velocity.x) - 5;
                        this.#avatar.keys['left'] = true;
                        this.#avatar.keys['right'] = false;
                    }

                    if (pos.y <= bounds.minY + 2) {
                        this.#avatar.velocity.y = Math.abs(this.#avatar.velocity.y) + 3;
                    }
                }
            }
            requestAnimationFrame(update);
        };
        update();
    }
}



class Vector2D {
    constructor(x = 0, y = 0) {
        this.x = x;
        this.y = y;
    }

    add(vector) {
        return new Vector2D(this.x + vector.x, this.y + vector.y);
    }

    subtract(vector) {
        return new Vector2D(this.x - vector.x, this.y - vector.y);
    }

    multiply(scalar) {
        return new Vector2D(this.x * scalar, this.y * scalar);
    }

    magnitude() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    }

    normalize() {
        const mag = this.magnitude();
        return mag > 0 ? new Vector2D(this.x / mag, this.y / mag) : new Vector2D(0, 0);
    }

    copy() {
        return new Vector2D(this.x, this.y);
    }
}


class PhysicsAvatar {
    constructor() {
        this.position = new Vector2D(50, 100);
        this.velocity = new Vector2D(0, 0);
        this.mass = 1.0;
        this.friction = 0.1;
        this.jumpForce = 20;
        this.moveForce = 15;
        this.gravity = 0.8;
        this.originalGravity = 0.8;
        this.onGround = false;
        this.keys = {};

        this.isPhysicsActive = false;
        this.isSwimming = false;
        this.isAntiGravityActive = false;
        this.isSpeedBoostActive = false;
        this.currentSpeedBoostFactor = 1.0;
        this.lastBounceSurface = 0;

        this.myAvatarElement = null;
        this.gameAreaElement = null;
        this.overlayCanvas = null;
        this.avatarDimensions = { width: 0, height: 0 };

        this.bottomUIOffsetPx = 0;
        this.visualFloorOffsetPixels = -4;
        this.lastSentPosition = { x: -1, y: -1 };

        this.surfaceColors = {};

        this.boundaryManager = null;
        this.animationShield = null;

        this.#setupInput();
        this.#startAvatarDetection();
    }

    #setupInput() {
        document.addEventListener('keydown', (e) => {
            if (!this.isPhysicsActive) return;

            if (e.key === 'ArrowLeft') {
                e.preventDefault();
                this.keys['left'] = true;
            }
            if (e.key === 'ArrowRight') {
                e.preventDefault();
                this.keys['right'] = true;
            }
            if (e.key === 'ArrowUp') {
                e.preventDefault();
                this.keys['jump'] = true;
            }
        });

        document.addEventListener('keyup', (e) => {
            if (!this.isPhysicsActive) return;

            if (e.key === 'ArrowLeft') {
                this.keys['left'] = false;
            }
            if (e.key === 'ArrowRight') {
                this.keys['right'] = false;
            }
            if (e.key === 'ArrowUp') {
                this.keys['jump'] = false;
            }
        });
    }

    #startAvatarDetection() {
        this.detectionInterval = setInterval(() => {
            if (!this.myAvatarElement) {
                const selectors = [
                    "body > div.spawnedavatar.spawnedavatar-self",
                    ".spawnedavatar-self",
                    "[class*='avatar'][class*='self']",
                    ".avatar.self"
                ];

                for (const selector of selectors) {
                    const element = document.querySelector(selector);
                    if (element) {
                        this.myAvatarElement = element;
                        console.log(`🎯 Avatar found: ${selector}`);
                        if (!this.animationShield) {
                            this.animationShield = new AnimationShield(this);
                        }
                        break;
                    }
                }
            }

            if (!this.overlayCanvas) {
                this.overlayCanvas = document.getElementById('drawing-assistant-overlay');
            }

            if (!this.gameAreaElement) {
                const selectors = ["#drawing-assistant-overlay", "#gamearea", "#canvas", "body"];
                for (const selector of selectors) {
                    const element = document.querySelector(selector);
                    if (element) {
                        this.gameAreaElement = element;
                        break;
                    }
                }
            }

            if (this.myAvatarElement && this.gameAreaElement && this.overlayCanvas && !this.boundaryManager) {
                this.boundaryManager = new BoundaryManager(this);
            }

            if (this.myAvatarElement && this.gameAreaElement && !this.isPhysicsActive) {
                this.#updatePositionFromDOM(true);
            }
        }, 50);
    }

    #updatePositionFromDOM(passiveSync = false) {
        if (!this.myAvatarElement || !this.gameAreaElement) return false;

        try {
            const avatarRect = this.myAvatarElement.getBoundingClientRect();
            const gameRect = this.gameAreaElement.getBoundingClientRect();

            this.avatarDimensions.width = (avatarRect.width / gameRect.width) * 100;
            this.avatarDimensions.height = (avatarRect.height / gameRect.height) * 100;

            const currentX = ((avatarRect.left + avatarRect.width / 2 - gameRect.left) / gameRect.width) * 100;
            const currentY = ((avatarRect.bottom - gameRect.top) / gameRect.height) * 100;

            if (!passiveSync ||
                (Math.abs(this.position.x - currentX) > 0.5 || Math.abs(this.position.y - currentY) > 0.5)) {
                this.position.x = currentX;
                this.position.y = currentY;

                if (passiveSync) {
                    this.velocity = new Vector2D(0, 0);
                    this.onGround = (currentY >= 99.5);
                }
            }
            return true;
        } catch (e) {
            return false;
        }
    }

    activatePhysics() {
        if (this.isPhysicsActive) return;

        this.isPhysicsActive = true;
        this.#updatePositionFromDOM();
        this.velocity = new Vector2D(0, 0);
        this.onGround = true;
        this.isSwimming = false;
        console.log('✅ Physics ACTIVATED with unified coordinates');
    }

    deactivatePhysics() {
        if (!this.isPhysicsActive) return;

        this.isPhysicsActive = false;
        this.keys = {};
        this.velocity = new Vector2D(0, 0);
        this.onGround = false;
        this.isSwimming = false;
        this.gravity = this.originalGravity;
        this.isAntiGravityActive = false;
        this.isSpeedBoostActive = false;
        console.log('❌ Physics DEACTIVATED');
    }

    update() {
        if (!this.isPhysicsActive) return;

        if (this.isSwimming) {
            this.velocity.y += this.gravity;
        } else {
            this.velocity.y += this.gravity;
        }

        const currentMoveForce = this.moveForce * this.currentSpeedBoostFactor;
        if (this.keys['left']) this.velocity.x -= currentMoveForce * (this.isSwimming ? 0.15 : 0.1);
        if (this.keys['right']) this.velocity.x += currentMoveForce * (this.isSwimming ? 0.15 : 0.1);

        if (this.keys['jump'] && (this.onGround || this.isSwimming)) {
            this.velocity.y = -this.jumpForce * (this.isSwimming ? 0.7 : 1.0);
            this.onGround = false;
        }

        this.velocity.x *= (1 - (this.isSwimming ? 0.15 : this.friction));
        if (Math.abs(this.velocity.x) < 0.01) this.velocity.x = 0;

        let nextX = this.position.x + (this.velocity.x * 0.1);
        let nextY = this.position.y + (this.velocity.y * 0.1);

        if (this.boundaryManager) {
            this.boundaryManager.updateCanvasBounds();
            const bounds = this.boundaryManager.realTimeBounds;
            nextX = Math.max(bounds.minX, Math.min(bounds.maxX, nextX));
            nextY = Math.max(bounds.minY, Math.min(bounds.maxY, nextY));
        }

        this.#handleCollisions(nextX, nextY);

        const playerCollision = this.#checkPlayerCollisions();
        if (playerCollision) {
            this.#handlePlayerCollision(playerCollision);
        }

        if (this.#hasPositionChanged()) {
            this.#sendPositionToDrawaria();
        }
    }




    #handleCollisions(nextX, nextY) {
        const collisions = this.#checkAvatarCollisions();

        let primaryCollision = null;
        if (collisions.center && this.surfaceColors[collisions.center.type]) {
            primaryCollision = collisions.center;
        } else if (collisions.bottom && this.surfaceColors[collisions.bottom.type]) {
            primaryCollision = collisions.bottom;
        }

        this.#handleSpecialEffects(primaryCollision);

        let resolvedX = nextX;
        if ((this.keys['left'] && collisions.left && collisions.left.type === 'solid') ||
            (this.keys['right'] && collisions.right && collisions.right.type === 'solid')) {
            this.velocity.x = 0;
            resolvedX = this.position.x;
        }
        this.position.x = resolvedX;

        let resolvedY = nextY;
        if (this.velocity.y > 0) {
            if (collisions.bottom && (collisions.bottom.type === 'solid' || collisions.bottom.type === 'bounce')) {
                this.velocity.y = 0;
                resolvedY = this.position.y;
                this.onGround = true;

                if (collisions.bottom.type === 'bounce' && Date.now() - this.lastBounceSurface > 200) {
                    this.velocity.y = -this.jumpForce * 1.5;
                    this.onGround = false;
                    this.lastBounceSurface = Date.now();
                }
            }
        } else if (this.velocity.y < 0) {
            if (collisions.top && collisions.top.type === 'solid') {
                this.velocity.y = 0;
                resolvedY = this.position.y;
            }
        }
        this.position.y = resolvedY;

        if (this.boundaryManager) {
            const maxY = this.boundaryManager.realTimeBounds.maxY;
            if (this.position.y >= maxY - this.boundaryManager.gameUnitFloorTolerance) {
                this.position.y = maxY;
                this.velocity.y = 0;
                this.onGround = true;
            }
        }
    }

    reset() {
        this.velocity = new Vector2D(0, 0);
        this.onGround = true;
        this.isSwimming = false;
        this.gravity = this.originalGravity;
        this.isAntiGravityActive = false;
        this.isSpeedBoostActive = false;
        console.log('🔄 Avatar physics reset (unified system)');
    }

    boost() {
        if (!this.isPhysicsActive) return;
        this.velocity.y = -this.jumpForce * 2;
        this.onGround = false;
        console.log('🚀 Boost applied');
    }

        destroy() {
        if (this.detectionInterval) {
            clearInterval(this.detectionInterval);
            this.detectionInterval = null;
        }
        if (this.animationShield) {
            this.animationShield.destroy();
            this.animationShield = null;
        }
        if (this.boundaryManager) {
            this.boundaryManager.destroy();
            this.boundaryManager = null;
        }


        this.myAvatarElement = null;
        this.gameAreaElement = null;
        this.overlayCanvas = null;


        this.isPhysicsActive = false;
        this.keys = {};
        this.velocity = new Vector2D(0, 0);

        console.log('🔥 PhysicsAvatar destroyed completely');
    }


    #handleSpecialEffects(primaryCollision) {
        if (!primaryCollision) {

            if (this.isSwimming) {
                this.isSwimming = false;
                console.log('🏊‍♂️ Exited water');
            }
            if (this.isSpeedBoostActive) {
                this.isSpeedBoostActive = false;
                this.currentSpeedBoostFactor = 1.0;
                console.log('⚡ Speed boost deactivated');
            }
            return;
        }

        switch (primaryCollision.type) {
            case 'water':
                if (!this.isSwimming) console.log('🏊‍♂️ Entered water');
                this.isSwimming = true;
                break;
            case 'anti_gravity':
                if (!this.isAntiGravityActive) {
                    this.isAntiGravityActive = true;
                    console.log('⬆️ Anti-gravity activated');
                }
                break;
            case 'normal_gravity':
                if (this.isAntiGravityActive) {
                    this.gravity = this.originalGravity;
                    this.isAntiGravityActive = false;
                    console.log('⬇️ Normal gravity restored');
                }
                break;
            case 'speed_boost':
                if (!this.isSpeedBoostActive) {
                    this.isSpeedBoostActive = true;
                    this.currentSpeedBoostFactor = 1.8;
                    console.log('⚡ Speed boost activated');
                }
                break;
            case 'push_back':
                if (this.onGround) {
                    this.velocity.x = (Math.random() > 0.5 ? 1 : -1) * 20;
                    this.velocity.y = -5;
                    this.onGround = false;
                    console.log('↩️ Push back activated');
                }
                break;
            case 'coin':
                this.#eraseCoin(this.position.x, this.position.y);
                if (window.animatorToolInstance && typeof window.animatorToolInstance._playAnimationSound === 'function') {
                    window.animatorToolInstance._playAnimationSound('_coinCollect');
                }
                break;
        }
    }

    #checkPlayerCollisions() {
        if (!this.myAvatarElement || !this.gameAreaElement) return null;

        const otherPlayers = document.querySelectorAll("body > div.spawnedavatar.spawnedavatar-otherplayer");
        if (otherPlayers.length === 0) return null;

        const myRect = this.myAvatarElement.getBoundingClientRect();
        const myScreenX = myRect.left + (myRect.width / 2);
        const myScreenY = myRect.top + (myRect.height / 2);

        for (const otherPlayer of otherPlayers) {
            const otherRect = otherPlayer.getBoundingClientRect();
            const otherScreenX = otherRect.left + (otherRect.width / 2);
            const otherScreenY = otherRect.top + (otherRect.height / 2);

            const deltaX = myScreenX - otherScreenX;
            const deltaY = myScreenY - otherScreenY;
            const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

            const collisionThreshold = (myRect.width + otherRect.width) / 2 * 0.8;

            if (distance < collisionThreshold) {
                const collisionX = distance > 0 ? deltaX / distance : 1;
                const collisionY = distance > 0 ? deltaY / distance : 0;

                return {
                    collided: true,
                    direction: { x: collisionX, y: collisionY },
                    otherPlayer: otherPlayer,
                    distance: distance
                };
            }
        }

        return null;
    }

    #handlePlayerCollision(collision) {
        if (!collision || !collision.collided) return;

        const bounceForce = 0.5;
        const upwardBounce = 0.5;

        this.velocity.x += collision.direction.x * bounceForce;
        this.velocity.y += collision.direction.y * bounceForce - upwardBounce;
        this.onGround = false;

        console.log(`👥 Player collision! Bouncing in direction: (${collision.direction.x.toFixed(2)}, ${collision.direction.y.toFixed(2)})`);
    }

    #eraseCoin(gameX, gameY) {
        const canvas = document.getElementById('canvas');
        if (!canvas) return;

        const pixelX = Math.floor((gameX / 100) * canvas.width);
        const pixelY = Math.floor((gameY / 100) * canvas.height);

        const eraseRadius = 15;
        const backgroundColor = '#ffffff';
        const socket = getGameSocket();


        for (let i = 0; i < 8; i++) {
            const angle = (i / 8) * Math.PI * 2;
            const offsetX = Math.cos(angle) * (eraseRadius * 0.7);
            const offsetY = Math.sin(angle) * (eraseRadius * 0.7);

            const startX = pixelX + offsetX;
            const startY = pixelY + offsetY;
            const endX = pixelX - offsetX;
            const endY = pixelY - offsetY;


            if (canvas.getContext) {
                const ctx = canvas.getContext('2d');
                ctx.strokeStyle = backgroundColor;
                ctx.lineWidth = 8;
                ctx.lineCap = 'round';
                ctx.beginPath();
                ctx.moveTo(startX, startY);
                ctx.lineTo(endX, endY);
                ctx.stroke();
            }


            if (socket) {
                try {
                    const normX1 = (startX / canvas.width).toFixed(4);
                    const normY1 = (startY / canvas.height).toFixed(4);
                    const normX2 = (endX / canvas.width).toFixed(4);
                    const normY2 = (endY / canvas.height).toFixed(4);
                    const cmd = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - 8},"${backgroundColor}",0,0,{}]]`;
                    socket.send(cmd);
                } catch (error) {
                    console.warn('Error sending coin erase command:', error);
                }
            }
        }


        for (let i = 0; i < 4; i++) {
            const radius = 3 + i * 2;
            const steps = 8;
            for (let j = 0; j < steps; j++) {
                const angle1 = (j / steps) * Math.PI * 2;
                const angle2 = ((j + 1) / steps) * Math.PI * 2;

                const startX = pixelX + Math.cos(angle1) * radius;
                const startY = pixelY + Math.sin(angle1) * radius;
                const endX = pixelX + Math.cos(angle2) * radius;
                const endY = pixelY + Math.sin(angle2) * radius;

                if (canvas.getContext) {
                    const ctx = canvas.getContext('2d');
                    ctx.strokeStyle = backgroundColor;
                    ctx.lineWidth = 6;
                    ctx.lineCap = 'round';
                    ctx.beginPath();
                    ctx.moveTo(startX, startY);
                    ctx.lineTo(endX, endY);
                    ctx.stroke();
                }

                if (socket) {
                    try {
                        const normX1 = (startX / canvas.width).toFixed(4);
                        const normY1 = (startY / canvas.height).toFixed(4);
                        const normX2 = (endX / canvas.width).toFixed(4);
                        const normY2 = (endY / canvas.height).toFixed(4);
                        const cmd = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${0 - 6},"${backgroundColor}",0,0,{}]]`;
                        socket.send(cmd);
                    } catch (error) {
                        console.warn('Error sending coin erase command:', error);
                    }
                }
            }
        }

        console.log('🪙 Coin collected and erased at:', gameX, gameY);
    }

    #checkAvatarCollisions() {
        const collisions = { bottom: null, top: null, left: null, right: null, center: null };

        const halfWidth = this.avatarDimensions.width / 2;
        const height = this.avatarDimensions.height;
        const checkOffset = 0.5;

        collisions.bottom = this.#checkPixelCollision(this.position.x, this.position.y + checkOffset);
        collisions.top = this.#checkPixelCollision(this.position.x, this.position.y - height - checkOffset);

        const midY = this.position.y - (height / 2);
        collisions.left = this.#checkPixelCollision(this.position.x - halfWidth - checkOffset, midY);
        collisions.right = this.#checkPixelCollision(this.position.x + halfWidth + checkOffset, midY);
        collisions.center = this.#checkPixelCollision(this.position.x, midY);

        return collisions;
    }

    #checkPixelCollision(gameX, gameY, gameOffsetX = 0, gameOffsetY = 0) {
        const canvas = document.getElementById('canvas');
        if (!canvas) return null;

        const ctx = canvas.getContext('2d');
        if (!ctx || !this.gameAreaElement) return null;

        try {
            const effectiveGameX = gameX + gameOffsetX;
            const effectiveGameY = gameY + gameOffsetY;

            const pixelX = Math.floor((effectiveGameX / 100) * canvas.width);
            const pixelY = Math.floor((effectiveGameY / 100) * canvas.height);

            if (pixelX < 0 || pixelX >= canvas.width || pixelY < 0 || pixelY >= canvas.height) {
                return null;
            }

            const imageData = ctx.getImageData(pixelX, pixelY, 1, 1);
            const [r, g, b, a] = imageData.data;

            if (a < 100) return null;

            for (const typeKey in this.surfaceColors) {
                const colorData = this.surfaceColors[typeKey];
                const tolerance = 40;
                if (Math.abs(r - colorData.r) < tolerance &&
                    Math.abs(g - colorData.g) < tolerance &&
                    Math.abs(b - colorData.b) < tolerance) {
                    return { color: colorData.hex, type: colorData.type, r, g, b };
                }
            }
            return null;
        } catch (error) {
            return null;
        }
    }

    #hasPositionChanged() {
        const threshold = 0.1;
        return Math.abs(this.position.x - this.lastSentPosition.x) > threshold ||
               Math.abs(this.position.y - this.lastSentPosition.y) > threshold;
    }

    #sendPositionToDrawaria() {
        const targetX = this.position.x;
        const targetYBottom = this.position.y;

        let adjustedDisplayY = targetYBottom - (this.avatarDimensions.height / 2);
        adjustedDisplayY = Math.max(0, Math.min(100, adjustedDisplayY));

        this.#updateVisualElements(targetX, adjustedDisplayY);

        const socket = getGameSocket();

        if (window.game && typeof window.game.sendMove === "function") {
            try {
                window.game.sendMove(targetX, adjustedDisplayY);
                this.lastSentPosition = { x: targetX, y: targetYBottom };
                return;
            } catch (error) {
                console.warn('Error with game.sendMove:', error);
            }
        }

        if (socket) {
            try {
                const encodedPos = 1e4 * Math.floor((targetX / 100) * 1e4) + Math.floor((adjustedDisplayY / 100) * 1e4);
                socket.send(`42["clientcmd",103,[${encodedPos},false]]`);
                this.lastSentPosition = { x: targetX, y: targetYBottom };
            } catch (error) {
                console.warn('Error sending movement via WebSocket:', error);
            }
        }
    }

    #updateVisualElements(targetX, targetYCenter) {
        const effectiveGameArea = this.overlayCanvas || this.gameAreaElement;
        if (!effectiveGameArea) return;

        const gameRect = effectiveGameArea.getBoundingClientRect();
        const displayWidth = gameRect.width;
        const displayHeight = gameRect.height;

        const basePlayableHeightPx = Math.max(1, displayHeight - this.bottomUIOffsetPx);
        const finalPlayableHeightPx = basePlayableHeightPx - this.visualFloorOffsetPixels;

        const effectiveAvatarGameHeight = this.avatarDimensions.height > 0 ? this.avatarDimensions.height : 12;
        const effectiveAvatarGameWidth = this.avatarDimensions.width > 0 ? this.avatarDimensions.width : 8;

        const avatarPixelWidth = (effectiveAvatarGameWidth / 100) * displayWidth;
        const avatarPixelHeight = (effectiveAvatarGameHeight / 100) * displayHeight;

        let avatarCenterPixelY = (targetYCenter / 100) * finalPlayableHeightPx;
        let pixelYTop = avatarCenterPixelY - (avatarPixelHeight / 2);
        const pixelX = (targetX / 100) * displayWidth - (avatarPixelWidth / 2);

        const constrainedPixelX = Math.max(0, Math.min(pixelX, displayWidth - avatarPixelWidth));
        const constrainedPixelYTop = Math.max(0, Math.min(pixelYTop, finalPlayableHeightPx - avatarPixelHeight));

        const brushCursor = document.querySelector('.brushcursor');
        if (brushCursor) {
            brushCursor.classList.remove('brushcursor-hidden');
            brushCursor.style.transform = `translate(${constrainedPixelX}px, ${constrainedPixelYTop}px)`;
            brushCursor.style.transition = 'transform 0.05s ease-out';
        }

        if (this.myAvatarElement) {
            this.myAvatarElement.style.transform = `translate(${constrainedPixelX}px, ${constrainedPixelYTop}px)`;
            this.myAvatarElement.style.position = 'absolute';
            this.myAvatarElement.style.zIndex = '100';
            this.myAvatarElement.style.transition = 'transform 0.05s ease-out';
        }
    }
}


class AnimationShield {
    constructor(avatar) {
        this.avatar = avatar;
        this.observer = null;
        this.reapplyInterval = null;
        this.#setupProtection();
    }

    #setupProtection() {
        this.reapplyInterval = setInterval(() => {
            if (this.avatar.myAvatarElement && !this.observer) {
                this.#protectElement(this.avatar.myAvatarElement);
            }
        }, 500);
    }

    #protectElement(element) {
        if (this.observer) this.observer.disconnect();

        this.observer = new MutationObserver((mutations) => {
            if (!this.avatar.isPhysicsActive) return;

            mutations.forEach((mutation) => {
                if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
                    const style = element.style;

                    if (style.animation || style.transition !== 'transform 0.05s ease-out') {
                        this.observer.disconnect();
                        style.animation = '';
                        style.transition = 'transform 0.05s ease-out';

                        setTimeout(() => {
                            if (this.observer) {
                                this.observer.observe(element, { attributes: true, attributeFilter: ['style'] });
                            }
                        }, 0);
                    }

                    if (style.position !== 'absolute') style.position = 'absolute';
                    if (style.zIndex !== '100') style.zIndex = '100';
                }
            });
        });

        this.observer.observe(element, { attributes: true, attributeFilter: ['style'] });
    }

    destroy() {
        if (this.observer) {
            this.observer.disconnect();
            this.observer = null;
        }
        if (this.reapplyInterval) {
            clearInterval(this.reapplyInterval);
            this.reapplyInterval = null;
        }
        console.log('🛡️ AnimationShield destroyed');
    }
}


class BoundaryManager {
    constructor(avatar) {
        this.avatar = avatar;
        this.canvasBounds = null;
        this.realTimeBounds = { minX: 0, maxX: 100, minY: 0, maxY: 100 };
        this.gameUnitFloorTolerance = 0.5;
        this.updateInterval = null;
        this.#startRealTimeUpdates();
    }

    #startRealTimeUpdates() {
        this.updateInterval = setInterval(() => {
            this.updateCanvasBounds();
        }, 16);
    }

    updateCanvasBounds() {
        const overlayCanvas = document.getElementById('drawing-assistant-overlay');
        const mainCanvas = document.getElementById('canvas');
        const gameArea = document.getElementById('gamearea');

        const activeElement = overlayCanvas || mainCanvas || gameArea;

        if (activeElement) {
            const rect = activeElement.getBoundingClientRect();
            const style = window.getComputedStyle(activeElement);

            const realWidth = overlayCanvas ?
                (overlayCanvas.width || parseFloat(style.width)) :
                (activeElement.offsetWidth || rect.width);

            const realHeight = overlayCanvas ?
                (overlayCanvas.height || parseFloat(style.height)) :
                (activeElement.offsetHeight || rect.height);

            this.canvasBounds = {
                element: activeElement,
                rect: rect,
                width: realWidth,
                height: realHeight,
                top: rect.top,
                left: rect.left
            };

            this.#calculateGameBounds();
        }
    }

    #calculateGameBounds() {
        if (!this.canvasBounds || !this.avatar.myAvatarElement) return;

        try {
            const avatarWidthGameUnits = this.avatar.avatarDimensions.width > 0 ? this.avatar.avatarDimensions.width : 8;
            const avatarHeightGameUnits = this.avatar.avatarDimensions.height > 0 ? this.avatar.avatarDimensions.height : 12;

            this.realTimeBounds = {
                minX: avatarWidthGameUnits / 2,
                maxX: 100 - (avatarWidthGameUnits / 2),
                minY: avatarHeightGameUnits,
                maxY: 100 - this.gameUnitFloorTolerance
            };
        } catch (error) {
            console.warn('Error calculating game bounds:', error);
        }
    }

    debugBounds() {
        console.log('🔍 Boundary Debug Info:');
        console.log('Canvas bounds:', this.canvasBounds);
        console.log('Game bounds (0-100):', this.realTimeBounds);
        console.log('Avatar position:', this.avatar.position);
        console.log('Avatar dimensions:', this.avatar.avatarDimensions);
    }

    destroy() {
        if (this.updateInterval) {
            clearInterval(this.updateInterval);
            this.updateInterval = null;
        }
        console.log('🗺️ BoundaryManager destroyed');
    }
}


if (typeof QBit !== 'undefined' && QBit.Styles) {
    QBit.Styles.addRules([
        `#${QBit.identifier} .physics-section {
            margin-bottom: 20px;
            border: 1px solid var(--CE-color);
            border-radius: 8px;
            background: var(--CE-bg_color);
        }`,
        `#${QBit.identifier} .physics-section-title {
            padding: 15px;
            margin: 0;
            cursor: pointer;
            background: var(--input-bg);
            border-radius: 8px 8px 0 0;
            user-select: none;
        }`,
        `#${QBit.identifier} .physics-section-title h6 {
            margin: 0;
            color: var(--primary);
            font-size: 16px;
            display: flex;
            align-items: center;
            gap: 8px;
        }`,
        `#${QBit.identifier} .physics-section-content {
            padding: 15px;
        }`,
        `#${QBit.identifier} .physics-button-grid {
            display: grid;
            gap: 8px;
            margin-bottom: 15px;
        }`,
        `#${QBit.identifier} .physics-btn {
            padding: 10px 12px;
            border: 1px solid var(--CE-color);
            border-radius: 4px;
            font-size: 12px;
            cursor: pointer;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
        }`,
        `#${QBit.identifier} .physics-btn.primary {
            background: var(--success);
            color: white;
        }`,
        `#${QBit.identifier} .physics-btn.secondary {
            background: var(--secondary);
            color: var(--CE-color);
        }`,
                `#${QBit.identifier} .physics-btn:hover {
            transform: translateY(-1px);
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }`,


        `#${QBit.identifier} .physics-btn.primary:hover {
            background: linear-gradient(135deg, var(--success), var(--info));
            transform: translateY(-2px) scale(1.02);
            box-shadow: 0 8px 25px rgba(72, 187, 120, 0.4);
        }`,

        `#${QBit.identifier} .physics-btn.warning:hover {
            background: linear-gradient(135deg, var(--warning), #ff9300);
            transform: translateY(-2px) rotate(1deg);
            box-shadow: 0 8px 25px rgba(255, 255, 0, 0.3);
        }`,

        `#${QBit.identifier} .physics-btn.special:hover {
            background: linear-gradient(45deg, var(--info), #7b1fa2, #3700b3);
            transform: translateY(-2px) scale(1.05);
            box-shadow: 0 0 20px rgba(147, 207, 255, 0.6);
        }`,


        `#${QBit.identifier} .physics-background-controls {
            margin: 15px 0;
            padding: 15px;
            background: linear-gradient(135deg, var(--input-bg) 0%, rgba(66, 153, 225, 0.1) 100%);
            border: 1px solid var(--input-border-blue);
            border-radius: 8px;
            position: relative;
            overflow: hidden;
        }`,

        `#${QBit.identifier} .physics-background-controls::before {
            content: '';
            position: absolute;
            top: -50%;
            left: -50%;
            width: 200%;
            height: 200%;
            background: conic-gradient(from 0deg, transparent, rgba(66, 153, 225, 0.1), transparent);
            opacity: 0;
            transition: opacity 0.3s ease;
            animation: rotate 20s linear infinite;
            pointer-events: none;
        }`,

        `#${QBit.identifier} .physics-background-controls:hover::before {
            opacity: 1;
        }`,

        `@keyframes rotate {
            from { transform: rotate(0deg); }
            to { transform: rotate(360deg); }
        }`,

        `#${QBit.identifier} .physics-background-title {
            margin: 0 0 12px 0;
            color: var(--primary);
            font-size: 14px;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 8px;
            position: relative;
            z-index: 2;
        }`,

        `#${QBit.identifier} .physics-background-grid {
            display: grid;
            grid-template-columns: auto 1fr auto auto auto auto;
            gap: 10px;
            align-items: center;
            position: relative;
            z-index: 2;
        }`,


        `#${QBit.identifier} .physics-background-btn {
            padding: 10px 12px;
            border: 1px solid var(--CE-color);
            border-radius: 6px;
            background: var(--input-bg);
            color: var(--CE-color);
            cursor: pointer;
            font-size: 14px;
            transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
            display: flex;
            align-items: center;
            justify-content: center;
            min-width: 40px;
            position: relative;
            overflow: hidden;
        }`,

        `#${QBit.identifier} .physics-background-btn::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
            transition: left 0.5s ease;
        }`,

        `#${QBit.identifier} .physics-background-btn:hover::before {
            left: 100%;
        }`,

        `#${QBit.identifier} .physics-background-btn:hover {
            background: var(--primary);
            color: white;
            transform: translateY(-2px) scale(1.05);
            box-shadow: 0 10px 20px rgba(66, 153, 225, 0.3);
        }`,


        `#${QBit.identifier} .physics-background-btn.draw {
            background: var(--success);
            color: white;
            border-radius: 25px;
        }`,

        `#${QBit.identifier} .physics-background-btn.draw:hover {
            background: conic-gradient(from 0deg, var(--success), var(--info), var(--success));
            transform: translateY(-2px) rotate(-2deg);
            border-radius: 10px;
            box-shadow: 0 15px 30px rgba(72, 187, 120, 0.4);
        }`,

        `#${QBit.identifier} .physics-background-btn.random {
            background: linear-gradient(135deg, var(--warning), #ff9300);
            color: var(--dark);
            font-weight: bold;
            border-radius: 20px;
        }`,

        `#${QBit.identifier} .physics-background-btn.random:hover {
            background: linear-gradient(135deg, #ff9300, var(--warning), #ff6b00);
            transform: translateY(-2px) rotate(3deg) scale(1.08);
            box-shadow: 0 12px 25px rgba(255, 147, 0, 0.5);
            border-radius: 15px;
        }`,

        `#${QBit.identifier} .physics-background-btn.clear {
            background: var(--danger);
            color: white;
            border-radius: 15px;
        }`,

        `#${QBit.identifier} .physics-background-btn.clear:hover {
            background: linear-gradient(45deg, var(--danger), #dc143c);
            transform: translateY(-2px) scale(1.1);
            box-shadow: 0 15px 30px rgba(220, 38, 127, 0.4);
            border-radius: 8px;
        }`,


        `#${QBit.identifier} .physics-background-input {
            padding: 12px;
            border: 2px solid var(--input-border-blue);
            border-radius: 8px;
            background: var(--CE-bg_color);
            color: var(--CE-color);
            text-align: center;
            font-size: 14px;
            font-weight: bold;
            transition: all 0.3s ease;
            position: relative;
        }`,

        `#${QBit.identifier} .physics-background-input:focus {
            outline: none;
            border-color: var(--primary);
            box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.2), 0 0 20px rgba(66, 153, 225, 0.3);
            transform: scale(1.02);
        }`,

        `#${QBit.identifier} .physics-background-input:hover {
            border-color: var(--info);
            box-shadow: 0 5px 15px rgba(147, 207, 255, 0.2);
        }`,


        `#${QBit.identifier} .physics-status-display {
            background: linear-gradient(135deg, var(--input-bg) 0%, rgba(127, 127, 127, 0.1) 100%);
            border: 1px solid var(--input-border-blue);
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 15px;
            position: relative;
            overflow: hidden;
        }`,

        `#${QBit.identifier} .physics-status-display::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 2px;
            background: linear-gradient(90deg, transparent, var(--primary), transparent);
            animation: scan 3s linear infinite;
        }`,

        `@keyframes scan {
            0% { left: -100%; }
            100% { left: 100%; }
        }`,

        `#${QBit.identifier} .physics-status-item {
            display: flex;
            justify-content: space-between;
            margin-bottom: 8px;
            font-size: 12px;
            padding: 4px 0;
            transition: all 0.2s ease;
        }`,

        `#${QBit.identifier} .physics-status-item:hover {
            background: rgba(66, 153, 225, 0.1);
            border-radius: 4px;
            padding: 4px 8px;
            transform: translateX(5px);
        }`,

        `#${QBit.identifier} .status-label {
            color: var(--CE-color);
            font-weight: 500;
        }`,

        `#${QBit.identifier} .status-value {
            color: var(--primary);
            font-weight: bold;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .status-value:hover {
            color: var(--info);
            transform: scale(1.05);
        }`,


        `#${QBit.identifier} .physics-controls-info {
            background: linear-gradient(135deg, var(--input-bg) 0%, rgba(72, 187, 120, 0.1) 100%);
            border: 1px solid var(--input-border-blue);
            border-radius: 8px;
            padding: 15px;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-controls-info:hover {
            transform: translateY(-2px);
            box-shadow: 0 8px 25px rgba(72, 187, 120, 0.2);
        }`,

        `#${QBit.identifier} .physics-controls-list {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }`,

        `#${QBit.identifier} .physics-control-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 6px 0;
            transition: all 0.2s ease;
        }`,

        `#${QBit.identifier} .physics-control-item:hover {
            background: rgba(72, 187, 120, 0.1);
            border-radius: 4px;
            padding: 6px 8px;
        }`,


        `#${QBit.identifier} .physics-key {
            background: linear-gradient(135deg, var(--primary), var(--info));
            color: white;
            padding: 4px 8px;
            border-radius: 4px;
            font-size: 10px;
            font-weight: bold;
            text-shadow: 0 0 5px rgba(255, 255, 255, 0.5);
            box-shadow: 0 2px 10px rgba(66, 153, 225, 0.3);
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-key:hover {
            transform: scale(1.1) rotate(5deg);
            box-shadow: 0 0 20px rgba(66, 153, 225, 0.6);
            text-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
        }`,

        `#${QBit.identifier} .physics-desc {
            color: var(--CE-color);
            font-size: 11px;
            transition: color 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-control-item:hover .physics-desc {
            color: var(--success);
        }`,


        `#${QBit.identifier} .physics-slider-group {
            margin-bottom: 15px;
            padding: 10px;
            background: rgba(66, 153, 225, 0.05);
            border-radius: 8px;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-slider-group:hover {
            background: rgba(66, 153, 225, 0.1);
            transform: translateX(5px);
        }`,

        `#${QBit.identifier} .physics-slider-group label {
            display: block;
            margin-bottom: 8px;
            color: var(--CE-color);
            font-size: 12px;
            font-weight: 500;
        }`,

        `#${QBit.identifier} .physics-slider {
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: linear-gradient(90deg, var(--secondary), var(--primary));
            outline: none;
            -webkit-appearance: none;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-slider:hover {
            transform: scaleY(1.2);
            box-shadow: 0 0 15px rgba(66, 153, 225, 0.4);
        }`,

        `#${QBit.identifier} .physics-slider::-webkit-slider-thumb {
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background: linear-gradient(135deg, var(--primary), var(--info));
            cursor: pointer;
            box-shadow: 0 2px 10px rgba(66, 153, 225, 0.5);
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-slider::-webkit-slider-thumb:hover {
            transform: scale(1.3);
            box-shadow: 0 0 20px rgba(66, 153, 225, 0.8);
        }`,


        `#${QBit.identifier} .physics-presets {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
            margin-top: 15px;
        }`,

        `#${QBit.identifier} .physics-preset-btn {
            padding: 12px;
            background: var(--input-bg);
            border: 1px solid var(--input-border-blue);
            border-radius: 20px;
            color: var(--CE-color);
            cursor: pointer;
            font-size: 11px;
            font-weight: 500;
            transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
            position: relative;
            overflow: hidden;
        }`,

        `#${QBit.identifier} .physics-preset-btn::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 0;
            height: 0;
            background: radial-gradient(circle, rgba(66, 153, 225, 0.3), transparent);
            transition: all 0.4s ease;
            transform: translate(-50%, -50%);
            border-radius: 50%;
        }`,

        `#${QBit.identifier} .physics-preset-btn:hover::before {
            width: 200%;
            height: 200%;
        }`,

        `#${QBit.identifier} .physics-preset-btn:hover {
            background: var(--primary);
            color: white;
            transform: translateY(-3px) scale(1.05);
            border-radius: 10px;
            box-shadow: 0 15px 30px rgba(66, 153, 225, 0.4);
        }`,


        `#${QBit.identifier} .physics-color-control {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-bottom: 20px;
            padding: 12px;
            background: linear-gradient(135deg, rgba(147, 207, 255, 0.1), rgba(66, 153, 225, 0.1));
            border-radius: 8px;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-color-control:hover {
            transform: scale(1.02);
            box-shadow: 0 5px 15px rgba(147, 207, 255, 0.2);
        }`,

        `#${QBit.identifier} .physics-color-control label {
            color: var(--CE-color);
            font-size: 12px;
            font-weight: 500;
        }`,

        `#${QBit.identifier} .selected-type {
            color: var(--primary);
            font-weight: bold;
            text-shadow: 0 0 5px rgba(66, 153, 225, 0.3);
        }`,

        `#${QBit.identifier} .physics-color-control input[type="color"] {
            width: 50px;
            height: 35px;
            border: 2px solid var(--CE-color);
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-color-control input[type="color"]:hover {
            transform: scale(1.1) rotate(5deg);
            box-shadow: 0 0 20px rgba(66, 153, 225, 0.5);
        }`,


        `#${QBit.identifier} .physics-hitbox-grid {
            display: grid;
            grid-template-columns: 1fr;
            gap: 8px;
        }`,

        `#${QBit.identifier} .physics-hitbox-item {
            width: 100%;
        }`,


        `#${QBit.identifier} .physics-hitbox-select {
            width: 100%;
            background: var(--input-bg);
            border: 1px solid var(--input-border-blue);
            border-radius: 8px;
            color: var(--CE-color);
            cursor: pointer;
            font-size: 11px;
            padding: 12px;
            text-align: left;
            display: flex;
            align-items: center;
            gap: 10px;
            transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
            position: relative;
            overflow: hidden;
        }`,

        `#${QBit.identifier} .physics-hitbox-select::before {
            content: '';
            position: absolute;
            top: 0;
            left: -100%;
            width: 100%;
            height: 100%;
            background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
            transition: left 0.6s ease;
        }`,

        `#${QBit.identifier} .physics-hitbox-select:hover::before {
            left: 100%;
        }`,

        `#${QBit.identifier} .physics-hitbox-select:hover {
            background: linear-gradient(135deg, var(--primary), rgba(66, 153, 225, 0.8));
            color: white;
            transform: translateX(8px) scale(1.02);
            box-shadow: 0 8px 25px rgba(66, 153, 225, 0.3);
        }`,

        `#${QBit.identifier} .physics-hitbox-select.active {
            background: linear-gradient(135deg, var(--primary), var(--info));
            color: white;
            border-color: var(--primary);
            transform: translateX(5px);
            box-shadow: 0 5px 15px rgba(66, 153, 225, 0.4);
        }`,

        `#${QBit.identifier} .physics-hitbox-swatch {
            width: 20px;
            height: 20px;
            border-radius: 4px;
            border: 2px solid var(--CE-color);
            flex-shrink: 0;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-hitbox-select:hover .physics-hitbox-swatch {
            transform: scale(1.2) rotate(10deg);
            border-color: white;
            box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
        }`,

        `#${QBit.identifier} .physics-hitbox-info {
            flex-grow: 1;
        }`,

        `#${QBit.identifier} .physics-hitbox-name {
            font-weight: bold;
            font-size: 12px;
            margin-bottom: 2px;
        }`,

        `#${QBit.identifier} .physics-hitbox-desc {
            font-size: 10px;
            opacity: 0.8;
            line-height: 1.2;
        }`,


        `#${QBit.identifier} .physics-auto-controls {
            margin-bottom: 15px;
        }`,

        `#${QBit.identifier} .physics-toggle-group {
            margin-bottom: 12px;
            padding: 8px;
            background: rgba(72, 187, 120, 0.05);
            border-radius: 6px;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-toggle-group:hover {
            background: rgba(72, 187, 120, 0.1);
            transform: translateX(3px);
        }`,

        `#${QBit.identifier} .physics-toggle-label {
            display: flex;
            align-items: center;
            gap: 10px;
            cursor: pointer;
            color: var(--CE-color);
            font-size: 12px;
            font-weight: 500;
            transition: color 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-toggle-label:hover {
            color: var(--success);
        }`,


        `#${QBit.identifier} .physics-toggle-label input[type="checkbox"] {
            width: 18px;
            height: 18px;
            cursor: pointer;
            position: relative;
            appearance: none;
            background: var(--input-bg);
            border: 2px solid var(--input-border-blue);
            border-radius: 4px;
            transition: all 0.3s ease;
        }`,

        `#${QBit.identifier} .physics-toggle-label input[type="checkbox"]:checked {
            background: linear-gradient(135deg, var(--success), var(--info));
            border-color: var(--success);
            transform: scale(1.1);
        }`,

        `#${QBit.identifier} .physics-toggle-label input[type="checkbox"]:checked::after {
            content: '✓';
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
            font-weight: bold;
            font-size: 12px;
        }`,

        `#${QBit.identifier} .physics-toggle-label input[type="checkbox"]:hover {
            box-shadow: 0 0 10px rgba(72, 187, 120, 0.4);
            transform: scale(1.05);
        }`,


        `#${QBit.identifier} .physics-background-btn:disabled {
            opacity: 0.5 !important;
            cursor: not-allowed !important;
            transform: none !important;
            box-shadow: none !important;
            background: var(--secondary) !important;
        }`,

        `#${QBit.identifier} .physics-background-btn:disabled::before {
            display: none !important;
        }`,


        `@keyframes pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.5; }
        }`,

        `#${QBit.identifier} .physics-background-btn:disabled {
            animation: pulse 2s ease-in-out infinite;
        }`,


        `@media (max-width: 768px) {
            #${QBit.identifier} .physics-button-grid {
                grid-template-columns: 1fr 1fr !important;
            }

            #${QBit.identifier} .physics-background-grid {
                grid-template-columns: 1fr !important;
                gap: 8px !important;
            }

            #${QBit.identifier} .physics-presets {
                grid-template-columns: 1fr !important;
            }
        }`
    ]);
}



})();

})();
长期地址
遇到问题?请前往 GitHub 提 Issues。