您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
The ultimate enhancement for your Drawaria.online experience. Redefining possibilities!
当前为
// ==UserScript== // @name Cube Engine Uptaded New Options // @version 7.0.0 // @description The ultimate enhancement for your Drawaria.online experience. Redefining possibilities! // @namespace drawaria.modded.fullspec // @homepage https://drawaria.online/profile/?uid=63196790-c7da-11ec-8266-c399f90709b7 // @author ≺ᴄᴜʙᴇ³≻ And YouTubeDrawaria // @match https://drawaria.online/ // @match https://drawaria.online/test // @match https://drawaria.online/room/* // @icon https://drawaria.online/avatar/cache/e53693c0-18b1-11ec-b633-b7649fa52d3f.jpg // @grant none // @license GNU GPLv3 // @run-at document-end // ==/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; //.replaceAll(/\s/g, ''); 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.domClear = new DocumentCleaner(); globalThis.generate = new CustomGenerator(); 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;}`, `.noselect {-webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; user-select: none;}`, ]); } 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, // Changed from true to false to make it closed by default "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"); } { const resetImageSelectionLabel = domMake.Tree("div", { class: "icon", title: "Unselect" }, [ domMake.Tree("i", { class: "fas fa-chevron-left" }), ]); resetImageSelectionLabel.addEventListener("click", () => { this.children.forEach((child) => { child.htmlElements.label.classList.remove("active"); child.htmlElements.input.checked = !1; }); }); this.htmlElements.header.appendChild(resetImageSelectionLabel); } } 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("CubeEngine"); } } 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; // This line is now redundant as it's set in ModBase 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; domClear.embeds(); domClear.scripts(); domClear.styles(); console.clear(); }, 200); }; })("QBit")(); (function BotClient() { const QBit = globalThis[arguments[0]]; function parseServerUrl(any) { var prefix = String(any).length == 1 ? `sv${any}.` : ""; return `wss://${prefix}drawaria.online/socket.io/?sid1=undefined&hostname=drawaria.online&EIO=3&transport=websocket`; } function parseRoomId(any) { return String(any).match(/([a-f0-9.-]+?)$/gi)[0]; } 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 { class BotClient { static dummy1 = QBit.register(this); // constructor(name = '', avatar = []) { constructor(name = "JavaScript", avatar = ["cf19b8f0-cf31-11ed-9ece-d584b24f60dc", "1680377222354"]) { // 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) { const localThis = this; // if (localThis.getReadyState()) localThis.disconnect(); if (localThis.getReadyState()) return; if (!url) return localThis.enterRoom(document.querySelector("#invurl").value); localThis.socket = new WebSocket(parseServerUrl(url)); localThis.socket.addEventListener("open", function (event) { localThis.interval_id = setInterval(function () { if (!localThis.getReadyState()) return clearInterval(localThis.interval_id); localThis.send(2); }, localThis.interval_ms); }); localThis.socket.addEventListener("message", function (message_event) { var prefix = String(message_event.data).match(/(^\d+)/gi)[0] || ""; if (prefix == "40") { localThis.send(emits.startplay(localThis.room, localThis.name, localThis.avatar)); } var data = parseSocketIOEvent(prefix.length, message_event.data) || []; if (data && data.length == 1) { if (data[0].players) localThis.room.players = data[0].players; } if (data && data.length > 1) { var event = data.shift(); localThis.customObservers.forEach((listener) => { if (listener.event === event) if (listener.callback) listener.callback(data); }); } }); } disconnect() { if (!this.getReadyState()) return; this.socket.close(); } reconnect() { this.send(41); this.send(40); } enterRoom(roomid) { this.room.id = parseRoomId(roomid); if (!this.getReadyState()) this.connect(this.room.id.includes(".") ? this.room.id.slice(-1) : ""); this.reconnect(); } leaveRoom() { this.send(41); } switchRoom() { this.emit("pgswtichroom"); // this.send(emits['pgswtichroom']()); } addEventListener(eventname, callback) { this.customObservers.push({ event: eventname, callback }); } send(data) { if (!this.getReadyState()) return /*console.warn(data)*/; this.socket.send(data); } emit(event, ...data) { // data = data.length > 0 ? data : null; var emitter = emits[event]; if (emitter) this.send(emitter(...data)); } } const emits = { chatmsg: function (message) { // 42["chatmsg","a"] let data = ["chatmsg", message]; return `${42}${JSON.stringify(data)}`; }, passturn: function () { // 42["passturn"] let data = ["passturn"]; return `${42}${JSON.stringify(data)}`; }, pgdrawvote: function (playerid) { // 42["pgdrawvote",2,0] let data = ["pgdrawvote", playerid, 0]; return `${42}${JSON.stringify(data)}`; }, pgswtichroom: function () { // 42["pgswtichroom"] let data = ["pgswtichroom"]; return `${42}${JSON.stringify(data)}`; }, playerafk: function () { // 42["playerafk"] let data = ["playerafk"]; return `${42}${JSON.stringify(data)}`; }, playerrated: function () { // 42["playerrated"] let data = ["playerrated"]; return `${42}${JSON.stringify(data)}`; }, sendgesture: function (gestureid) { // 42["sendgesture",16] let data = ["sendgesture", gestureid]; return `${42}${JSON.stringify(data)}`; }, sendvote: function () { // 42["sendvote"] let data = ["sendvote"]; return `${42}${JSON.stringify(data)}`; }, sendvotekick: function (playerid) { // 42["sendvotekick",93] let data = ["sendvotekick", playerid]; return `${42}${JSON.stringify(data)}`; }, wordselected: function (wordid) { // 42["wordselected",0] 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) { // target = zindex || shared 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) { // 42["drawcmd",2,[x, y,color,{"0":r,"1":g,"2":b,"3":a},size]] 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) { // 42["drawcmd",3,[playerid]] let data = ["drawcmd", 3, [playerid]]; return `${42}${JSON.stringify(data)}`; }, clear: function () { // 42["drawcmd",4,[]] let data = ["drawcmd", 4, []]; return `${42}${JSON.stringify(data)}`; }, noop: function () { // 42["drawcmd",5,[0.44882022129015975,0.3157894736842105,0.44882022129015975,0.3157894736842105,true,-12,"#000000",playerid]] }, }; const events = { bc_announcement: function (data) { // }, bc_chatmessage: function (data) { // 42["bc_chatmessage",3,"playername","a"] }, bc_clearcanvasobj: function (data) { // }, bc_clientnotify: function (data) { // 42["bc_clientnotify",playerid,"playername",code,null] }, bc_createcanvasobj: function (data) { // 42["bc_createcanvasobj","1",[3,63001,0.5,0.5,0,1,null,"1",true]] }, bc_customvoting_abort: function (data) { // }, bc_customvoting_error: function (data) { // 42["bc_customvoting_error","rollbackcanvas"] }, bc_customvoting_results: function (data) { // 42["bc_customvoting_results",[2],true,0] }, bc_customvoting_start: function (data) { // 42["bc_customvoting_start",{"type":321,"secs":20,"acceptratios":[0.51],"pgdrawallow":true,"voteoptions":["YES","NO"]},1] }, bc_customvoting_vote: function (data) { // 42["bc_customvoting_vote",1,0,[2,1,[1]]] }, bc_exp: function (data) { // 42["bc_exp",29,4357] }, bc_extannouncement: function (data) { // }, bc_freedrawsession_reset: function (data) { // 42["bc_freedrawsession_reset",-1,{"votingtype":2,"currentvotes":0,"neededvotes":2,"votingtimeout":null}null] }, bc_gesture: function (data) { // 42["bc_gesture",3,31] }, bc_musicbox_play: function (data) { // 42["bc_musicbox_play",[30394,1,"37678185",252386,1661295694733,"Sony Masterworks - Smooth Criminal","2cellos/smooth-criminal"]] }, bc_musicbox_vote: function (data) { // 42["bc_musicbox_vote",[[30394,1]],3,30394] }, bc_pgdrawallow_results: function (data) { // 42["bc_pgdrawallow_results",2,true,true] }, bc_pgdrawallow_startvoting: function (data) { // 42["bc_pgdrawallow_startvoting",2,1,false] }, bc_pgdrawallow_vote: function (data) { // 42["bc_pgdrawallow_vote",2,1,0,false,[1,0]] }, bc_playerafk: function (data) { // 42["bc_playerafk",28,"Jinx"] }, bc_playerrated: function (data) { // 42["bc_playerrated",1,29,"lil cute girl",28,"Jinx",[1]] }, bc_removecanvasobj: function (data) { // 42["bc_removecanvasobj",3,"1",null] }, bc_resetplayername: function (data) { // }, bc_round_results: function (data) { // 42["bc_round_results",[[5,"Jinx",15,61937,3,"63196790-c7da-11ec-8266-c399f90709b7",0],[4,"ツ♡thick mojo ♡ツ",15,65464,3,"018cdc20-47a4-11ec-b5b5-6bdacecdd51e",1]]] }, bc_setavatarprop: function (data) { // 42["bc_setavatarprop",3] }, bc_setobjattr: function (data) { // 42["bc_setobjattr","1","shared",false] }, bc_setstatusflag: function (data) { // 42["bc_setstatusflag",3,3,true] }, bc_spawnavatar: function (data) { // 42["bc_spawnavatar",3,true] }, bc_startinground: function (data) { // 42["bc_startinground",200000,[],{"votingtype":0,"currentvotes":0,"neededvotes":2,"votingtimeout":null}] }, bc_token: function (data) { // 42["bc_token",1,3,0] }, bc_turn_abort: function (data) { // 42["bc_turn_abort","pass","lil cute girl","2c276aa0-dc5e-11ec-9fd3-c3a00b129da4","hammer",null] }, bc_turn_fastout: function (data) { // 42["bc_turn_fastout",15000] }, bc_turn_results: function (data) { // 42["bc_turn_results",[[1,"Jinx",2,2,"63196790-c7da-11ec-8266-c399f90709b7",0,0],[2,"vale",3,3,"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.xxxxxxxxxxxxx",9248]],"cavern"] }, bc_turn_waitplayers: function (data) { // 42["bc_turn_waitplayers",true,-1,6] }, bc_uc_freedrawsession_changedroom: function (data) { // console.log(data[2], data[3]) // 42["bc_uc_freedrawsession_changedroom",[list of drawlines not !important]] }, bc_uc_freedrawsession_start: function (data) { // }, bc_votekick: function (data) { // 42["bc_votekick","Jinx",22,true] }, bc_votingtimeout: function (data) { // 42["bc_votingtimeout",{"votingtype":2,"currentvotes":0,"neededvotes":2,"votingtimeout":null}] }, bcmc_playervote: function (data) { // 42["bcmc_playervote","playername",{"votingtype":3,"currentvotes":1,"neededvotes":2,"votingtimeout":1661296731309}] }, bcuc_getfpid: function (data) { // 42["bcuc_getfpid"] // 42["clientcmd",901,[{"visitorId":"a8923f0870050d4a4e771cd26679ab6e"}]] }, bcuc_itemactivated: function (data) { // 42["bcuc_itemactivated",3,63001,[2,[0.5,0.5],0,1,null],1] }, bcuc_itemactivationabort: function (data) { // }, bcuc_moderatormsg: function (data) { // 42["bcuc_moderatormsg","Kick Player",true] }, bcuc_snapchatmessage: function (data) { // 42["uc_snapshot","1671740010120.1.28028"] // https://drawaria.online/snapshot/save }, mc_drawcommand: function (data) { // 42["mc_drawcommand",0,[0.2958167330677291,0.24970131421744324,0.2958167330677291,0.24970131421744324,true,-12,"#000000",3]] }, mc_moveavatar: function (data) { // 42["mc_moveavatar",3,36081456,true] }, mc_moveobj: function (data) { // 42["mc_moveobj","1",0.8266237186146181,0.24248391556470414,3,{"movespeed":500}] }, mc_roomplayerschange: function (data) { // console.log(data[2]) // 42["mc_roomplayerschange","join","playername",[{"id":1,"name":"ᴮᴱᴺᴵᴹᴬᴿᵠ","turnscore":0,"roundscore":0,"roundguesstime":0,"avatarid":"81253f20-ff93-11ec-9fd3-c3a00b129da4.1661276848726","account_stats":null,"from":"TR","medals":0,"turnstarcount":0,"statusflags":[]}{"id":3,"name":"playername","turnscore":0,"roundscore":0,"roundguesstime":0,"avatarid":"81253f20-ff93-11ec-9fd3-c3a00b129da4.1661276848726","account_stats":null,"from":"GB","medals":0,"turnstarcount":0,"statusflags":[]}],{"votingtype":2,"currentvotes":0,"neededvotes":0,"votingtimeout":null}false,3] }, mc_rotateobj: function (data) { // 42["mc_rotateobj","1",0.2617993877991494,3] }, mc_turn_guessdraw: function (data) { // 42["mc_turn_guessdraw",90000,[],"ÆŽÌµÍ’Í‘ÍŠÍ Í–Í“EÌµÌ”Í Í˜ÍœÌ Ì¼",{"votingtype":1,"currentvotes":0,"neededvotes":2,"votingtimeout":null}false] }, mc_turn_tip: function (data) { // 42["mc_turn_tip","_a_m__"] }, mc_turn_waitselectword: function (data) { // 42["mc_turn_waitselectword",11000,"ÆŽÌµÍ’Í‘ÍŠÍ Í–Í“EÌµÌ”Í Í˜ÍœÌ Ì¼",6,"c46de8f0-f493-11ec-9fd3-c3a00b129da4",2,5,false] }, mc_turn_wordguessed: function (data) { // 42["mc_turn_wordguessed","vale",[[2,3,3,9248],[1,2,2,0]]] }, uc_avatarspawninfo: function (data) { // 42["uc_avatarspawninfo","9a2ab5b2-b81e-4690-9af7-475d870d6e20",[[38,75059625,0]]] }, uc_buyitemerror: function (data) { // }, uc_canvasobjs: function (data) { // 42["uc_canvasobjs","9a2ab5b2-b81e-4690-9af7-475d870d6e20",{}] }, uc_chatmuted: function (data) { // 42["uc_chatmuted"] }, uc_coins: function (data) { // 42["uc_coins",-50,43] }, uc_inventoryitems: function (data) { // 42["uc_inventoryitems",[[100,99,null],[63000,null,null],[86000,null,null]],false,false] list }, uc_resetavatar: function (data) { // }, uc_serverserstart: function (data) { // }, uc_snapshot: function (data) { // }, uc_tokenerror: function (data) { // 42["uc_tokenerror",2] }, uc_turn_begindraw: function (data) { // 42["uc_turn_begindraw",90000,"arrow"] }, uc_turn_selectword: function (data) { // 42["uc_turn_selectword",11000,["vase","cellar","rain"],1,7,false] }, uc_turn_selectword_refreshlist: function (data) { // 42["uc_turn_selectword_refreshlist",["crayons","trade","team"]] }, uc_turn_wordguessedlocalThis: function (data) { // 42["uc_turn_wordguessedlocalThis","stage",3,[[2,3,3,53938],[1,2,2,0]]] }, }; globalThis["_io"] = { events, emits }; })("QBit"); (function BiggerBrush() { const QBit = globalThis[arguments[0]]; class BiggerBrush extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); active; constructor() { super("BiggerBrush", '<i class="fas fa-brush"></i>'); this.active = false; this.#onStartup(); } #onStartup() { this.#loadInterface(); this.drawwidthrangeSlider = document.querySelector("#drawwidthrange"); // this.enable(); } #loadInterface() { this.#row1(); } #row1() { const row = domMake.Row(); { const enableButton = domMake.Button("Enable"); enableButton.addEventListener("click", (event) => { this.active ? this.disable() : this.enable(); }); row.appendChild(enableButton); this.htmlElements.toggleStatusButton = enableButton; } this.htmlElements.section.appendChild(row); } enable() { document.querySelectorAll(".drawcontrols-button").forEach((n) => { n.classList.remove("drawcontrols-disabled"); }); this.active = true; this.htmlElements.toggleStatusButton.classList.add("active"); this.htmlElements.toggleStatusButton.textContent = "Active"; this.drawwidthrangeSlider.parentElement.previousElementSibling.lastElementChild.click(); this.drawwidthrangeSlider.parentElement.style.display = "flex"; this.drawwidthrangeSlider.max = 48; this.drawwidthrangeSlider.min = -2000; this.notify("success", `enabled`); } disable() { this.active = false; this.htmlElements.toggleStatusButton.classList.remove("active"); this.htmlElements.toggleStatusButton.textContent = "Inactive"; this.drawwidthrangeSlider.max = 45; this.drawwidthrangeSlider.min = -100; this.notify("warning", `disabled`); } } })("QBit"); (function BetterBrush() { const QBit = globalThis[arguments[0]]; class BetterBrush extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); constructor() { super("BetterBrush", '<i class="fas fa-magic"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); { const target = document.querySelector(".drawcontrols-popuplist"); const visibilityOvserver = new MutationObserver((mutations) => { if (this.active) if (mutations[0].target.style != "display:none") { mutations[0].target.querySelectorAll("div").forEach((n) => { n.removeAttribute("style"); }); } }); visibilityOvserver.observe(target, { attributes: true, }); } } #loadInterface() { this.#row1(); } #row1() { const row = domMake.Row(); { const enableButton = domMake.Button("Enable"); enableButton.addEventListener("click", (event) => { this.active ? this.disable() : this.enable(); }); row.appendChild(enableButton); this.htmlElements.toggleStatusButton = enableButton; } this.htmlElements.section.appendChild(row); } enable() { this.active = true; this.htmlElements.toggleStatusButton.classList.add("active"); this.htmlElements.toggleStatusButton.textContent = "Active"; this.notify("success", `enabled`); } disable() { this.active = false; this.htmlElements.toggleStatusButton.classList.remove("active"); this.htmlElements.toggleStatusButton.textContent = "Inactive"; this.notify("warning", `disabled`); } } })("QBit"); (function BiggerStencil() { const QBit = globalThis[arguments[0]]; class BiggerStencil extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); active; constructor() { super("BiggerStencil", '<i class="fas fa-parachute-box"></i>'); this.active = false; this.#onStartup(); } #onStartup() { this.#loadInterface(); { const target = document.querySelector(".fa-parachute-box").parentElement; const accessabilityObserver = new MutationObserver((mutations) => { if (this.active) if (mutations[0].target.disabled) { mutations[0].target.disabled = ""; } }); accessabilityObserver.observe(target, { attributes: true, }); } } #loadInterface() { this.#row1(); } #row1() { const row = domMake.Row(); { const enableButton = domMake.Button("Enable"); enableButton.addEventListener("click", (event) => { this.active ? this.disable() : this.enable(); }); row.appendChild(enableButton); this.htmlElements.toggleStatusButton = enableButton; } this.htmlElements.section.appendChild(row); } enable() { this.active = true; this.htmlElements.toggleStatusButton.classList.add("active"); this.htmlElements.toggleStatusButton.textContent = "Active"; this.notify("success", `enabled`); } disable() { this.active = false; this.htmlElements.toggleStatusButton.classList.remove("active"); this.htmlElements.toggleStatusButton.textContent = "Inactive"; this.notify("warning", `disabled`); } } })("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(); // get the mouse cursor position at startup: pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; // call a function whenever the cursor moves: document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); // calculate the new cursor position: pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; // set the element's new position: draggableElement.style.top = Number(draggableElement.offsetTop - pos2).toFixed() + "px"; draggableElement.style.left = Number(draggableElement.offsetLeft - pos1).toFixed() + "px"; update(); } function closeDragElement() { /* stop moving when mouse button is released:*/ 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("GhostCanvas", '<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; }); 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) => { setTimeout(() => { image.reduceToAtoms(); }, 10 * index); }); 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.alignModifierX = Number(this.DrawCanvas.width / DrawCanvasRect.width).toFixed(2); // DrawCanvasRect.alignModifierY = Number(this.DrawCanvas.height / DrawCanvasRect.height).toFixed(2); 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 * 0.25) % image.width; // const y = Math.floor((index * 0.25) / image.width); const x = (index * 0.25) % image.width; const y = Math.floor((index * 0.25) / 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]; // const color = rgbaArrayToHex([r, g, b, a]); const color = [r, g, b, a]; pixels.push({ x1: x, y1: y, x2: x, y2: y, color }); } this.setPixelList(pixels); } getNoneTransparentPixels() { this.getAllPixels(); const newPixelArray = this.drawingManager.pixelList.filter((pixel) => { return pixel.color !== "#000000"; // return /^#0[0-8]0[0-8]0[0-8]$/g.test(pixel.color); }); 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: "rotation", 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 multiX = Number(this.parent.DrawCanvasRect.alignModifierX); // const multiY = Number(this.parent.DrawCanvasRect.alignModifierY); 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(); } 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); } } class TaskManager { isRunning; pixelList; parent; BotClientManager; singleColor; brushColor; brushSize; constructor(parent) { this.pixelList = []; this.singleColor = !1; this.brushColor = "blue"; this.brushSize = 2; this.parent = parent; } startDrawing() { this.BotClientManager = this.parent.findGlobal("BotClientManager")?.siblings[0]; this.isRunning = true; this.doTasks(); this.parent.notify("info", "Started"); } stopDrawing() { this.isRunning = false; } doTasks() { if (!this.BotClientManager || this.BotClientManager.children.length <= 0) this.stopDrawing(); if (!this.isRunning) return this.parent.notify("info", "Stopped"); this.BotClientManager.children.forEach((botClientInterface, index) => { this.parseAndSendPixel(botClientInterface, index); }); setTimeout(() => { this.doTasks(); }, 1); } parseAndSendPixel(botClientInterface, index) { if (this.pixelList.length <= 0) return this.stopDrawing(); if (!botClientInterface.bot || !botClientInterface.bot.getReadyState()) return; const task = index % 2 == 0 ? this.pixelList.shift() : this.pixelList.pop(); botClientInterface.bot.send(this.convertTasks(task)); this.parent.htmlElements.pixelsLeftToDraw.value = this.pixelList.length; } convertTasks(pixel) { const playerid = -1; const lastx = pixel.x1 * this.parent.DrawCanvasRect.drawModifierX; const lasty = pixel.y1 * this.parent.DrawCanvasRect.drawModifierY; const x = pixel.x2 * this.parent.DrawCanvasRect.drawModifierX; const y = pixel.y2 * this.parent.DrawCanvasRect.drawModifierY; const isactive = !0; 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] * 0.390625).toFixed(2)})`; const ispixel = !1; let data = [ "drawcmd", 0, [lastx * 0.01, lasty * 0.01, x * 0.01, y * 0.01, isactive, -size, color, playerid, ispixel], ]; return `${42}${JSON.stringify(data)}`; } } })("QBit"); (function GhostCanvasAlgorithms() { const QBit = globalThis[arguments[0]]; function sortByColor(pixel1, pixel2) { const color1 = rgbaArrayToHex(pixel1.color); const color2 = rgbaArrayToHex(pixel2.color); if (color1 < color2) { return -1; } if (color1 > color2) { return 1; } return 0; } 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) { var red = colorArray1[0] - colorArray2[0]; var green = colorArray1[1] - colorArray2[1]; var blue = colorArray1[2] - colorArray2[2]; if (red < 0) red = red * -1; if (green < 0) green = green * -1; if (blue < 0) blue = blue * -1; 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(); this.#row3(); this.#row4(); } #row1() { const row = domMake.Row(); { const fuzzynessInput = domMake.Tree("input", { type: "number", title: "Fuzzyness", step: 1, min: 0, max: 255, value: 1, }); const opacityInput = domMake.Tree("input", { type: "number", title: "Opacity", step: 1, min: 0, max: 255, value: 0, }); fuzzynessInput.addEventListener("change", () => { this.maxFuzzyness = Number(fuzzynessInput.value); }); opacityInput.addEventListener("change", () => { this.minOpacity = Number(opacityInput.value); }); 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); } #row3() {} #row4() {} minifyPixelsArray() { const pixelArray = this.parent.drawingManager.pixelList; const newPixelArray = []; let currentPixel = pixelArray[0]; let lastPixel = currentPixel; let currentLine = currentPixel; for (let index = 0; index < pixelArray.length; index++) { currentPixel = pixelArray[index]; if (lastPixel.color[3] < 10 && currentPixel.color[3] >= 10) { // From Transparent To Solid currentLine = currentPixel; } else if (lastPixel.color[3] >= 10 && currentPixel.color[3] < 10) { // From Solid To Transparent currentLine.x2 = lastPixel.x2; newPixelArray.push(currentLine); currentLine = currentPixel; } else if (currentPixel.color[3] >= 10 && lastPixel.color[3] >= 10) { // From Solid To Solid if ( currentLine.y1 !== currentPixel.y1 || lastPixel.x2 !== currentPixel.x1 - 1 || !areSameColor(lastPixel.color, currentPixel.color, this.maxFuzzyness) ) { currentLine.x2 = lastPixel.x2; newPixelArray.push(currentLine); currentLine = currentPixel; } } else { // From Transparent To Transparent } lastPixel = currentPixel; } // if (currentLine.color[3] >= 10) newPixelArray.push(currentLine); this.parent.setPixelList(newPixelArray); } minifyPixelsArray_alt() { const pixelArray = this.parent.drawingManager.pixelList; const newPixelArray = []; var lastPixel = pixelArray[0]; var currentLine = lastPixel; const stepsize = this.parent.stepsize ?? 1; for (let i = 0; i < pixelArray.length; i += stepsize) { const currentPixel = pixelArray[i]; if (currentPixel.y1 !== currentLine.y1 || currentPixel.color !== lastPixel.color) { currentLine.x2 = lastPixel.x2; if (!/^#[0-9a-fA-F]{6}[0-4]{2}$/.test(lastPixel.color)) newPixelArray.push(currentLine); currentLine = currentPixel; } lastPixel = currentPixel; } newPixelArray.push(currentLine); this.parent.setPixelList(newPixelArray); } } 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(); this.#row2(); this.#row3(); this.#row4(); } #row1() { const row = domMake.Row(); { const sortPixelsArrayButton = domMake.Button("Sort"); sortPixelsArrayButton.addEventListener("click", (event) => { this.sortPixelsArray(); }); row.appendAll(sortPixelsArrayButton); } this.htmlElements.section.appendChild(row); } #row2() {} #row3() {} #row4() {} sortPixelsArray() { const pixelArray = this.parent.drawingManager.pixelList; const newPixelArray = [...pixelArray].sort(sortByColor); this.parent.setPixelList(newPixelArray); } } })("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(`BotClientManager`, '<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); // static dummy2 = QBit.bind(this, 'CubeEngine'); // static dummy3 = QBit.bind(this, 'CubeEngine'); 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(); // row.classList.add('nowrap'); { 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(); // row.classList.add('nowrap'); { 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(); { // Spawn && Move Avatar 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 AutoTranslate() { const QBit = globalThis[arguments[0]]; const unicodeLanguagePatterns = new Map(); unicodeLanguagePatterns.set("Common", /\p{Script=Common}+/u); // CommonPattern unicodeLanguagePatterns.set("Arabic", /\p{Script=Arabic}+/u); // ArabicPattern unicodeLanguagePatterns.set("Armenian", /\p{Script=Armenian}+/u); // ArmenianPattern unicodeLanguagePatterns.set("Bengali", /\p{Script=Bengali}+/u); // BengaliPattern unicodeLanguagePatterns.set("Bopomofo", /\p{Script=Bopomofo}+/u); // BopomofoPattern unicodeLanguagePatterns.set("Braille", /\p{Script=Braille}+/u); // BraillePattern unicodeLanguagePatterns.set("Buhid", /\p{Script=Buhid}+/u); // BuhidPattern unicodeLanguagePatterns.set("Canadian_Aboriginal", /\p{Script=Canadian_Aboriginal}+/u); // Canadian_AboriginalPattern unicodeLanguagePatterns.set("Cherokee", /\p{Script=Cherokee}+/u); // CherokeePattern unicodeLanguagePatterns.set("Cyrillic", /\p{Script=Cyrillic}+/u); // CyrillicPattern unicodeLanguagePatterns.set("Devanagari", /\p{Script=Devanagari}+/u); // DevanagariPattern unicodeLanguagePatterns.set("Ethiopic", /\p{Script=Ethiopic}+/u); // EthiopicPattern unicodeLanguagePatterns.set("Georgian", /\p{Script=Georgian}+/u); // GeorgianPattern unicodeLanguagePatterns.set("Greek", /\p{Script=Greek}+/u); // GreekPattern unicodeLanguagePatterns.set("Gujarati", /\p{Script=Gujarati}+/u); // GujaratiPattern unicodeLanguagePatterns.set("Gurmukhi", /\p{Script=Gurmukhi}+/u); // GurmukhiPattern unicodeLanguagePatterns.set("Han", /\p{Script=Han}+/u); // HanPattern unicodeLanguagePatterns.set("Hangul", /\p{Script=Hangul}+/u); // HangulPattern unicodeLanguagePatterns.set("Hanunoo", /\p{Script=Hanunoo}+/u); // HanunooPattern unicodeLanguagePatterns.set("Hebrew", /\p{Script=Hebrew}+/u); // HebrewPattern unicodeLanguagePatterns.set("Hiragana", /\p{Script=Hiragana}+/u); // HiraganaPattern unicodeLanguagePatterns.set("Inherited", /\p{Script=Inherited}+/u); // InheritedPattern unicodeLanguagePatterns.set("Kannada", /\p{Script=Kannada}+/u); // KannadaPattern unicodeLanguagePatterns.set("Katakana", /\p{Script=Katakana}+/u); // KatakanaPattern unicodeLanguagePatterns.set("Khmer", /\p{Script=Khmer}+/u); // KhmerPattern unicodeLanguagePatterns.set("Lao", /\p{Script=Lao}+/u); // LaoPattern unicodeLanguagePatterns.set("Latin", /\p{Script=Latin}+/u); // LatinPattern unicodeLanguagePatterns.set("Limbu", /\p{Script=Limbu}+/u); // LimbuPattern unicodeLanguagePatterns.set("Malayalam", /\p{Script=Malayalam}+/u); // MalayalamPattern unicodeLanguagePatterns.set("Mongolian", /\p{Script=Mongolian}+/u); // MongolianPattern unicodeLanguagePatterns.set("Myanmar", /\p{Script=Myanmar}+/u); // MyanmarPattern unicodeLanguagePatterns.set("Ogham", /\p{Script=Ogham}+/u); // OghamPattern unicodeLanguagePatterns.set("Oriya", /\p{Script=Oriya}+/u); // OriyaPattern unicodeLanguagePatterns.set("Runic", /\p{Script=Runic}+/u); // RunicPattern unicodeLanguagePatterns.set("Sinhala", /\p{Script=Sinhala}+/u); // SinhalaPattern unicodeLanguagePatterns.set("Syriac", /\p{Script=Syriac}+/u); // SyriacPattern unicodeLanguagePatterns.set("Tagalog", /\p{Script=Tagalog}+/u); // TagalogPattern unicodeLanguagePatterns.set("Tagbanwa", /\p{Script=Tagbanwa}+/u); // TagbanwaPattern unicodeLanguagePatterns.set("Tamil", /\p{Script=Tamil}+/u); // TamilPattern unicodeLanguagePatterns.set("Telugu", /\p{Script=Telugu}+/u); // TeluguPattern unicodeLanguagePatterns.set("Thaana", /\p{Script=Thaana}+/u); // ThaanaPattern unicodeLanguagePatterns.set("Thai", /\p{Script=Thai}+/u); // ThaiPattern unicodeLanguagePatterns.set("Tibetan", /\p{Script=Tibetan}+/u); // TibetanPattern unicodeLanguagePatterns.set("Yi", /\p{Script=Yi}+/u); // YiPattern const observeDOM = (function () { const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; /** * @param {HTMLElement} nodeToObserve * @param {Function} callback */ return function (nodeToObserve, callback) { if (!nodeToObserve || nodeToObserve.nodeType !== 1) return; if (MutationObserver) { // define a new observer var mutationObserver = new MutationObserver(callback); // have the observer observe for changes in children mutationObserver.observe(nodeToObserve, { childList: true, subtree: !1 }); return mutationObserver; } // browser support fallback else if (window.addEventListener) { nodeToObserve.addEventListener("DOMNodeInserted", callback, false); nodeToObserve.addEventListener("DOMNodeRemoved", callback, false); } }; })(); class AutoTranslate extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); active; constructor() { super("AutoTranslate", '<i class="fas fa-language"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); this.active = false; const observable = document.querySelector("#chatbox_messages"); observeDOM(observable, (mutation) => { if (!this.active) return; const addedNodes = []; const removedNodes = []; mutation.forEach((record) => record.addedNodes.length & addedNodes.push(...record.addedNodes)); mutation.forEach((record) => record.removedNodes.length & removedNodes.push(...record.removedNodes)); // console.log('Added:', addedNodes, 'Removed:', removedNodes); addedNodes.forEach((node) => { if (node.classList.contains("systemchatmessage5")) return; if (node.querySelector(".playerchatmessage-selfname")) return; if (!node.querySelector(".playerchatmessage-text")) return; // console.log(node); const message = node.querySelector(".playerchatmessage-text"); const text = message.textContent; const language = this.detectLanguage(text); if (language) this.translate(text, language, "en", (translation) => { applyTitleToChatMessage(translation, message); }); }); function applyTitleToChatMessage(text, node) { node.title = text; } }); } #loadInterface() { this.#row1(); this.#row2(); } #row1() { const row = domMake.Row(); { const enableButton = domMake.Button("Enable"); enableButton.addEventListener("click", (event) => { this.active ? this.disable() : this.enable(); }); row.appendChild(enableButton); this.htmlElements.toggleStatusButton = enableButton; } this.htmlElements.section.appendChild(row); } #row2() {} enable() { this.active = true; this.htmlElements.toggleStatusButton.classList.add("active"); this.htmlElements.toggleStatusButton.textContent = "Active"; this.notify("success", `enabled`); } disable() { this.active = false; this.htmlElements.toggleStatusButton.classList.remove("active"); this.htmlElements.toggleStatusButton.textContent = "Inactive"; this.notify("warning", `disabled`); } detectLanguage(text) { if (unicodeLanguagePatterns.get("Cyrillic").test(text)) return "ru"; if (unicodeLanguagePatterns.get("Arabic").test(text)) return "ar"; if (unicodeLanguagePatterns.get("Greek").test(text)) return "el"; } translate(textToTranslate, from = "ru", to = "en", callback) { const sourceText = textToTranslate; const sourceLang = from; const targetLang = to; const url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" + sourceLang + "&tl=" + targetLang + "&dt=t&q=" + encodeURI(sourceText); xhrGetJson(url, log); function log(data) { callback(data[0][0][0]); } } } function xhrGetJson(url, callback) { const req = new XMLHttpRequest(); req.onload = (e) => { const response = req.response; // not responseText if (!callback) return; try { callback(JSON.parse(response)); } catch (error) { console.log(error); } }; req.open("GET", url); req.send(); } })("QBit"); // --- NEW FUNCTIONALITIES START HERE --- (function GlobalUndo() { const QBit = globalThis[arguments[0]]; class GlobalUndo extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); constructor() { super("Undo Last Draw", '<i class="fas fa-undo"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { const row = domMake.Row(); { const undoButton = domMake.Button("Undo"); undoButton.title = "Undoes the last drawing action. (Requires drawing turn)"; undoButton.addEventListener("click", () => { const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement; const playerId = myPlayerIdElement ? myPlayerIdElement.dataset.playerid : "-1"; // Use -1 for global if no specific player, or get current player's ID if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.undo(parseInt(playerId)); globalThis.sockets[0].send(data); this.notify("info", `Undo command sent for player ID: ${playerId}.`); } else { this.notify("warning", "No active WebSocket connection found."); } }); row.appendChild(undoButton); } this.htmlElements.section.appendChild(row); } } })("QBit"); (function PassTurn() { const QBit = globalThis[arguments[0]]; class PassTurn extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); constructor() { super("Pass Turn", '<i class="fas fa-forward"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { const row = domMake.Row(); { const passTurnButton = domMake.Button("Pass Turn"); passTurnButton.title = "Passes the current drawing turn."; passTurnButton.addEventListener("click", () => { if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.passturn(); globalThis.sockets[0].send(data); this.notify("info", "Pass turn command sent."); } else { this.notify("warning", "No active WebSocket connection found."); } }); row.appendChild(passTurnButton); } this.htmlElements.section.appendChild(row); } } })("QBit"); (function PlayerVoteKick() { const QBit = globalThis[arguments[0]]; class PlayerVoteKick extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #playerListContainer; constructor() { super("Vote Kick", '<i class="fas fa-gavel"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); this.updatePlayerList(); // MutationObserver to update player list when players change (join/leave) const playerListElement = document.getElementById("playerlist"); if (playerListElement) { const observer = new MutationObserver(() => this.updatePlayerList()); observer.observe(playerListElement, { childList: true, subtree: true }); } } #loadInterface() { const row = domMake.Row(); this.#playerListContainer = domMake.IconList(); row.appendChild(this.#playerListContainer); this.htmlElements.section.appendChild(row); } updatePlayerList() { this.#playerListContainer.innerHTML = ''; // Clear existing buttons const playerRows = document.querySelectorAll("#playerlist .playerlist-row"); playerRows.forEach(playerRow => { const playerId = playerRow.dataset.playerid; const playerName = playerRow.querySelector(".playerlist-name a")?.textContent || `Player ${playerId}`; // Avoid self-kick button const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement; const myPlayerId = myPlayerIdElement ? myPlayerIdElement.dataset.playerid : null; if (playerId === myPlayerId) { return; } const playerButton = domMake.Button(playerName); playerButton.title = `Vote to kick ${playerName}`; playerButton.addEventListener("click", () => { if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.sendvotekick(parseInt(playerId)); globalThis.sockets[0].send(data); this.notify("info", `Vote kick command sent for ${playerName} (ID: ${playerId}).`); } else { this.notify("warning", "No active WebSocket connection found."); } }); this.#playerListContainer.appendChild(playerButton); }); if (playerRows.length <= 1) { // Only self or no players const noPlayersMessage = domMake.Tree("span", {}, ["No other players to kick."]); this.#playerListContainer.appendChild(noPlayersMessage); } } } })("QBit"); (function CustomChatMessage() { const QBit = globalThis[arguments[0]]; class CustomChatMessage extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #messageInput; constructor() { super("Custom Chat", '<i class="fas fa-comments"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { const row = domMake.Row(); { this.#messageInput = domMake.Tree("input", { type: "text", placeholder: "Your message..." }); 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) { // Enter key this.sendMessage(this.#messageInput.value); this.#messageInput.value = ''; } }); row.appendAll(this.#messageInput, sendButton); } this.htmlElements.section.appendChild(row); } sendMessage(message) { if (!message.trim()) return; // Don't send empty messages if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.chatmsg(message); globalThis.sockets[0].send(data); this.notify("info", `Message sent: "${message}"`); } else { this.notify("warning", "No active WebSocket connection found."); } } } })("QBit"); (function ToggleAFK() { const QBit = globalThis[arguments[0]]; class ToggleAFK extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); active; constructor() { super("Toggle AFK", '<i class="fas fa-mug-hot"></i>'); this.active = false; // Initial state this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { const row = domMake.Row(); { const toggleButton = domMake.Button("Toggle AFK"); toggleButton.addEventListener("click", () => { this.active = !this.active; if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.playerafk(); // playerafk() toggles AFK state globalThis.sockets[0].send(data); toggleButton.classList[this.active ? "add" : "remove"]("active"); toggleButton.textContent = this.active ? "AFK Active" : "AFK Inactive"; this.notify("info", `AFK status toggled to: ${this.active}`); } else { this.notify("warning", "No active WebSocket connection found."); } }); row.appendChild(toggleButton); } this.htmlElements.section.appendChild(row); } } })("QBit"); (function PlayerMovement() { const QBit = globalThis[arguments[0]]; class PlayerMovement extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #posXInput; #posYInput; constructor() { super("Move Avatar", '<i class="fas fa-arrows-alt"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { const row = domMake.Row(); { this.#posXInput = domMake.Tree("input", { type: "number", min: 0, max: 100, value: 50, step: 1, title: "X Position (0-100)" }); this.#posYInput = domMake.Tree("input", { type: "number", min: 0, max: 100, value: 50, step: 1, title: "Y Position (0-100)" }); const moveButton = domMake.Button('<i class="fas fa-location-arrow"></i>'); moveButton.classList.add("icon"); moveButton.addEventListener("click", () => { this.moveAvatar(); }); this.#posXInput.addEventListener("change", () => this.moveAvatar()); this.#posYInput.addEventListener("change", () => this.moveAvatar()); row.appendAll(this.#posXInput, this.#posYInput, moveButton); } this.htmlElements.section.appendChild(row); } moveAvatar() { const posX = parseFloat(this.#posXInput.value); const posY = parseFloat(this.#posYInput.value); if (isNaN(posX) || isNaN(posY) || posX < 0 || posX > 100 || posY < 0 || posY > 100) { this.notify("warning", "Invalid X or Y position. Must be between 0 and 100."); return; } if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.moveavatar(posX, posY); globalThis.sockets[0].send(data); this.notify("info", `Avatar moved to X:${posX}, Y:${posY}.`); } else { this.notify("warning", "No active WebSocket connection found."); } } } })("QBit"); (function GlobalTokenGiver() { const QBit = globalThis[arguments[0]]; class GlobalTokenGiver extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #playerList = []; // To store player IDs and names #tokenButtons = []; // Store references to token buttons constructor() { super("Give Tokens", '<i class="fas fa-hand-holding-heart"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); this.updatePlayerList(); // Observer for player list changes const playerListElement = document.getElementById("playerlist"); if (playerListElement) { const observer = new MutationObserver(() => this.updatePlayerList()); observer.observe(playerListElement, { childList: true, subtree: true }); } } #loadInterface() { const row = domMake.IconList(); { const tokens = [ { id: 0, icon: '<i class="fas fa-thumbs-up"></i>' }, // Thumbs Up { id: 1, icon: '<i class="fas fa-heart"></i>' }, // Heart { id: 2, icon: '<i class="fas fa-paint-brush"></i>' }, // Paint Brush { id: 3, icon: '<i class="fas fa-cocktail"></i>' }, // Cocktail { id: 4, icon: '<i class="fas fa-hand-peace"></i>' }, // Hand Peace { id: 5, icon: '<i class="fas fa-feather-alt"></i>' }, // Feather { id: 6, icon: '<i class="fas fa-trophy"></i>' }, // Trophy { id: 7, icon: '<i class="fas fa-mug-hot"></i>' }, // Mug { id: 8, icon: '<i class="fas fa-gift"></i>' } // Gift ]; tokens.forEach(token => { const tokenButton = domMake.Button(token.icon); tokenButton.classList.add("icon"); tokenButton.title = `Give Token ${token.id}`; tokenButton.addEventListener("click", () => { this.giveToken(token.id); }); row.appendChild(tokenButton); this.#tokenButtons.push(tokenButton); }); } this.htmlElements.section.appendChild(row); } updatePlayerList() { this.#playerList = []; const playerRows = document.querySelectorAll("#playerlist .playerlist-row"); playerRows.forEach(playerRow => { const playerId = playerRow.dataset.playerid; const playerName = playerRow.querySelector(".playerlist-name a")?.textContent || `Player ${playerId}`; this.#playerList.push({ id: parseInt(playerId), name: playerName }); }); // You might want to enable/disable token buttons based on player count or drawing status } 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); this.notify("info", `Token ${tokenId} sent to ${player.name} (ID: ${player.id}).`); }); } else { this.notify("warning", "No players found in the room to give tokens to."); } } else { this.notify("warning", "No active WebSocket connection found."); } } } })("QBit"); (function SetStatusFlag() { const QBit = globalThis[arguments[0]]; class SetStatusFlag extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); constructor() { super("Set Status", '<i class="fas fa-flag"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { const flags = [ { id: 0, name: "Music Enabled", 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: "Inventory Open", icon: '<i class="fas fa-box-open"></i>' }, { id: 4, name: "Friendlist Open", icon: '<i class="fas fa-user-friends"></i>' } ]; flags.forEach(flag => { const row = 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"; // Initial state toggleButton.addEventListener("click", () => { const isActive = toggleButton.dataset.isActive === "true"; const newActiveState = !isActive; toggleButton.dataset.isActive = newActiveState; toggleButton.classList[newActiveState ? "add" : "remove"]("active"); if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.setstatusflag(flag.id, newActiveState); globalThis.sockets[0].send(data); this.notify("info", `Status flag '${flag.name}' toggled to: ${newActiveState}`); } else { this.notify("warning", "No active WebSocket connection found."); } }); row.appendChild(toggleButton); this.htmlElements.section.appendChild(row); }); } } })("QBit"); (function ReportPlayer() { const QBit = globalThis[arguments[0]]; class ReportPlayer extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #playerSelect; #reasonSelect; #reasonInput; constructor() { super("Report Player", '<i class="fas fa-flag-checkered"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); this.updatePlayerList(); const playerListElement = document.getElementById("playerlist"); if (playerListElement) { const observer = new MutationObserver(() => this.updatePlayerList()); observer.observe(playerListElement, { childList: true, subtree: true }); } } #loadInterface() { // Player Selection Row const playerRow = domMake.Row(); const playerLabel = domMake.Tree("label", {}, ["Player: "]); this.#playerSelect = domMake.Tree("select", { class: "form-control" }); playerRow.appendAll(playerLabel, this.#playerSelect); this.htmlElements.section.appendChild(playerRow); // Reason Selection Row const reasonRow = domMake.Row(); const reasonLabel = domMake.Tree("label", {}, ["Reason: "]); this.#reasonSelect = domMake.Tree("select", { class: "form-control" }); const reasons = [ { value: "hack", text: "Hack / Exploits" }, { value: "bot", text: "Bot" }, { value: "spam", text: "Spamming" }, { value: "content", text: "Inappropriate drawings / Offensive content" }, { value: "other", text: "Other" } ]; reasons.forEach(reason => { const option = domMake.Tree("option", { value: reason.value }, [reason.text]); this.#reasonSelect.appendChild(option); }); reasonRow.appendAll(reasonLabel, this.#reasonSelect); this.htmlElements.section.appendChild(reasonRow); // Additional Reason Input const reasonInputRow = domMake.Row(); const reasonInputLabel = domMake.Tree("label", {}, ["Additional Comments: "]); this.#reasonInput = domMake.Tree("textarea", { rows: 2, placeholder: "Optional details..." }); reasonInputRow.appendAll(reasonInputLabel, this.#reasonInput); this.htmlElements.section.appendChild(reasonInputRow); // Send Report Button const sendRow = domMake.Row(); const sendButton = domMake.Button("Send Report"); sendButton.addEventListener("click", () => this.sendReport()); sendRow.appendChild(sendButton); this.htmlElements.section.appendChild(sendRow); } updatePlayerList() { this.#playerSelect.innerHTML = ''; // Clear existing options const defaultOption = domMake.Tree("option", { value: "" }, ["-- Select Player --"]); this.#playerSelect.appendChild(defaultOption); const playerRows = document.querySelectorAll("#playerlist .playerlist-row"); playerRows.forEach(playerRow => { const playerId = playerRow.dataset.playerid; const playerName = playerRow.querySelector(".playerlist-name a")?.textContent || `Player ${playerId}`; const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement; const myPlayerId = myPlayerIdElement ? myPlayerIdElement.dataset.playerid : null; if (playerId === myPlayerId) { return; // Don't allow reporting self } const option = domMake.Tree("option", { value: playerName, "data-playerid": playerId }, [playerName]); this.#playerSelect.appendChild(option); }); } sendReport() { const selectedPlayerOption = this.#playerSelect.options[this.#playerSelect.selectedIndex]; const targetPlayerId = selectedPlayerOption.dataset.playerid; const targetPlayerName = selectedPlayerOption.value; const reason = this.#reasonSelect.value; const additionalReason = this.#reasonInput.value.trim(); if (!targetPlayerId || !reason) { this.notify("warning", "Please select a player and a reason."); return; } const fullReason = additionalReason ? `${reason}: ${additionalReason}` : reason; if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.report(parseInt(targetPlayerId), fullReason, targetPlayerName); globalThis.sockets[0].send(data); this.notify("success", `Report sent for ${targetPlayerName} (Reason: ${fullReason}).`); this.#playerSelect.value = ""; this.#reasonSelect.value = reasons[0].value; this.#reasonInput.value = ""; } else { this.notify("warning", "No active WebSocket connection found."); } } } })("QBit"); (function ClientCommandSender() { const QBit = globalThis[arguments[0]]; class ClientCommandSender extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #cmdIdInput; #param1Input; #param2Input; #param3Input; constructor() { super("Client Cmd", '<i class="fas fa-terminal"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { const row1 = domMake.Row(); this.#cmdIdInput = domMake.Tree("input", { type: "number", placeholder: "Command ID (e.g., 10)" }); row1.appendChild(this.#cmdIdInput); this.htmlElements.section.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); this.htmlElements.section.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); this.htmlElements.section.appendChild(row3); } 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); // For arrays } catch (e) { return paramStr; } } if (paramStr.startsWith('{') && paramStr.endsWith('}')) { try { return JSON.parse(paramStr); // For objects } catch (e) { return paramStr; } } return paramStr; // Default to string } sendCommand() { const cmdId = parseInt(this.#cmdIdInput.value); if (isNaN(cmdId)) { this.notify("warning", "Command ID must be a number."); 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); this.notify("info", `Custom clientcmd ${cmdId} sent with params: ${JSON.stringify(params)}.`); } else { this.notify("warning", "No active WebSocket connection found."); } } } })("QBit"); (function RequestPlayerCanvas() { const QBit = globalThis[arguments[0]]; class RequestPlayerCanvas extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #playerSelect; constructor() { super("Req/Res Canvas", '<i class="fas fa-paint-roller"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); this.updatePlayerList(); const playerListElement = document.getElementById("playerlist"); if (playerListElement) { const observer = new MutationObserver(() => this.updatePlayerList()); observer.observe(playerListElement, { childList: true, subtree: true }); } } #loadInterface() { const playerRow = domMake.Row(); const playerLabel = domMake.Tree("label", {}, ["Player: "]); this.#playerSelect = domMake.Tree("select", { class: "form-control" }); playerRow.appendAll(playerLabel, this.#playerSelect); this.htmlElements.section.appendChild(playerRow); const buttonsRow = domMake.Row(); const requestButton = domMake.Button("Request Canvas"); requestButton.addEventListener("click", () => this.requestCanvas()); const respondButton = domMake.Button("Respond Canvas (Your Current Canvas)"); respondButton.addEventListener("click", () => this.respondCanvas()); buttonsRow.appendAll(requestButton, respondButton); this.htmlElements.section.appendChild(buttonsRow); } updatePlayerList() { this.#playerSelect.innerHTML = ''; const defaultOption = domMake.Tree("option", { value: "" }, ["-- Select Player --"]); this.#playerSelect.appendChild(defaultOption); const playerRows = document.querySelectorAll("#playerlist .playerlist-row"); playerRows.forEach(playerRow => { const playerId = playerRow.dataset.playerid; const playerName = playerRow.querySelector(".playerlist-name a")?.textContent || `Player ${playerId}`; const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement; const myPlayerId = myPlayerIdElement ? myPlayerIdElement.dataset.playerid : null; if (playerId === myPlayerId) { return; // Don't request/respond to self via this tool } const option = domMake.Tree("option", { value: playerId, "data-playername": playerName }, [playerName]); this.#playerSelect.appendChild(option); }); } requestCanvas() { const selectedPlayerId = this.#playerSelect.value; const selectedPlayerName = this.#playerSelect.options[this.#playerSelect.selectedIndex]?.dataset.playername; if (!selectedPlayerId) { this.notify("warning", "Please select a player."); return; } if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.requestcanvas(parseInt(selectedPlayerId)); globalThis.sockets[0].send(data); this.notify("info", `Canvas request sent to ${selectedPlayerName} (ID: ${selectedPlayerId}).`); } else { this.notify("warning", "No active WebSocket connection found."); } } respondCanvas() { const myPlayerIdElement = document.querySelector(".playerlist-name-self")?.parentElement; const myPlayerId = myPlayerIdElement ? parseInt(myPlayerIdElement.dataset.playerid) : null; if (!myPlayerId) { this.notify("warning", "Could not determine your player ID."); return; } const gameCanvas = document.body.querySelector("canvas#canvas"); if (!gameCanvas) { this.notify("error", "Game canvas not found to capture image."); return; } try { const base64Image = gameCanvas.toDataURL("image/png"); const selectedPlayerId = this.#playerSelect.value; if (!selectedPlayerId) { this.notify("warning", "Please select a player to respond to."); return; } if (globalThis.sockets && globalThis.sockets.length > 0) { const data = _io.emits.respondcanvas(parseInt(selectedPlayerId), base64Image); globalThis.sockets[0].send(data); this.notify("success", `Your canvas sent to ID: ${selectedPlayerId}.`); } else { this.notify("warning", "No active WebSocket connection found."); } } catch (e) { this.notify("error", `Failed to capture canvas or send: ${e.message}`); console.error("Canvas capture error:", e); } } } })("QBit"); (function IntelligentArtist() { const QBit = globalThis[arguments[0]]; // --- BASE DE DATOS DE BOCETOS DETALLADOS (50 PALABRAS) --- // (X, Y son porcentajes del 0 al 100) // Cada boceto está diseñado para ser reconocible y compuesto por múltiples trazos. // Helper para bocetos simples por defecto (genera círculos o cuadrados) function generateDefaultSimpleSketch(word) { const hash = word.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); const isCircle = hash % 2 === 0; // Alternar entre círculo y cuadrado if (isCircle) { const centerX = 50, centerY = 50, radius = 15; const segments = 16; // Más segmentos para un círculo más suave const sketch = []; for (let i = 0; i < segments; i++) { const angle1 = (i / segments) * Math.PI * 2; const angle2 = ((i + 1) / segments) * Math.PI * 2; sketch.push({ x1: centerX + radius * Math.cos(angle1), y1: centerY + radius * Math.sin(angle1), x2: centerX + radius * Math.cos(angle2), y2: centerY + radius * Math.sin(angle2) }); } return sketch; } else { // Generar un cuadrado o un rectángulo simple const xOffset = 25 + (hash % 10); // Ligeramente variable const yOffset = 25 + (hash % 10); const size = 50 - (hash % 10); return [ { x1: xOffset, y1: yOffset, x2: xOffset + size, y2: yOffset }, { x1: xOffset + size, y1: yOffset, x2: xOffset + size, y2: yOffset + size }, { x1: xOffset + size, y1: yOffset + size, x2: xOffset, y2: yOffset + size }, { x1: xOffset, y1: yOffset + size, x2: xOffset, y2: yOffset } ]; } } const SKETCH_DATABASE = { // --- BOCETOS DETALLADOS (Aproximadamente 15-20) --- "ARBOL": [ // Árbol con más detalle { x1: 48, y1: 80, x2: 48, y2: 50 }, { x1: 52, y1: 80, x2: 52, y2: 50 }, // Tronco más grueso { x1: 48, y1: 65, x2: 40, y2: 70 }, { x1: 52, y1: 65, x2: 60, y2: 70 }, // Raíces (opcional) { x1: 40, y1: 50, x2: 30, y2: 45 }, { x1: 30, y1: 45, x2: 35, y2: 30 }, // Contorno follaje { x1: 35, y1: 30, x2: 50, y2: 20 }, { x1: 50, y1: 20, x2: 65, y2: 30 }, { x1: 65, y1: 30, x2: 70, y2: 45 }, { x1: 70, y1: 45, x2: 60, y2: 50 }, { x1: 60, y1: 50, x2: 40, y2: 50 }, { x1: 40, y1: 50, x2: 30, y2: 45 }, { x1: 45, y1: 40, x2: 55, y2: 40 }, { x1: 50, y1: 30, x2: 50, y2: 45 } // Detalles internos follaje ], "CASA": [ // Casa con tejado y chimenea { x1: 30, y1: 70, x2: 70, y2: 70 }, { x1: 70, y1: 70, x2: 70, y2: 40 }, // Base de la casa { x1: 70, y1: 40, x2: 30, y2: 40 }, { x1: 30, y1: 40, x2: 30, y2: 70 }, { x1: 30, y1: 40, x2: 50, y2: 20 }, { x1: 50, y1: 20, x2: 70, y2: 40 }, // Tejado { x1: 45, y1: 70, x2: 45, y2: 55 }, { x1: 55, y1: 70, x2: 55, y2: 55 }, { x1: 45, y1: 55, x2: 55, y2: 55 }, // Puerta { x1: 60, y1: 40, x2: 60, y2: 30 }, { x1: 65, y1: 40, x2: 65, y2: 30 }, { x1: 60, y1: 30, x2: 65, y2: 30 }, // Chimenea { x1: 35, y1: 50, x2: 45, y2: 50 }, { x1: 45, y1: 50, x2: 45, y2: 60 }, // Ventana { x1: 45, y1: 60, x2: 35, y2: 60 }, { x1: 35, y1: 60, x2: 35, y2: 50 } ], "SOL": [ // Sol con forma de círculo, rayos y una cara sonriente { type: "circle", x1: 50, y1: 50, radius: 15 }, // Círculo central { x1: 50, y1: 35, x2: 50, y2: 25 }, { x1: 60, y1: 40, x2: 70, y2: 35 }, { x1: 65, y1: 50, x2: 75, y2: 50 }, { x1: 60, y1: 60, x2: 70, y2: 65 }, { x1: 50, y1: 65, x2: 50, y2: 75 }, { x1: 40, y1: 60, x2: 30, y2: 65 }, { x1: 35, y1: 50, x2: 25, y2: 50 }, { x1: 40, y1: 40, x2: 30, y2: 35 }, // Rayos { x1: 45, y1: 45, x2: 48, y2: 45 }, { x1: 52, y1: 45, x2: 55, y2: 45 }, // Ojos { x1: 45, y1: 58, x2: 55, y2: 58 } // Boca (sonrisa) ], "FLOR": [ // Flor con tallo, hojas y pétalos elaborados { x1: 50, y1: 80, x2: 50, y2: 55 }, // Tallo { x1: 45, y1: 70, x2: 35, y2: 65 }, { x1: 35, y1: 65, x2: 40, y2: 58 }, // Hojas { x1: 50, y1: 50, x2: 50, y2: 50 }, // Centro { type: "circle", x1: 50, y1: 50, radius: 5 }, // Círculo central para estambres { x1: 50, y1: 45, x2: 40, y2: 40 }, { x1: 40, y1: 40, x2: 35, y2: 50 }, { x1: 35, y1: 50, x2: 40, y2: 60 }, // Pétalo 1 { x1: 50, y1: 45, x2: 60, y2: 40 }, { x1: 60, y1: 40, x2: 65, y2: 50 }, { x1: 65, y1: 50, x2: 60, y2: 60 }, // Pétalo 2 { x1: 45, y1: 50, x2: 40, y2: 58 }, { x1: 40, y1: 58, x2: 50, y2: 65 }, // Pétalo 3 { x1: 55, y1: 50, x2: 60, y2: 58 }, { x1: 60, y1: 58, x2: 50, y2: 65 } // Pétalo 4 ], "PERRO": [ // Perro con cuerpo, patas, cola, orejas y hocico { x1: 30, y1: 70, x2: 70, y2: 70 }, { x1: 30, y1: 70, x2: 35, y2: 50 }, // Cuerpo y patas { x1: 70, y1: 70, x2: 65, y2: 50 }, { x1: 35, y1: 50, x2: 65, y2: 50 }, { x1: 45, y1: 50, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 55, y2: 50 }, // Cabeza { x1: 45, y1: 40, x2: 40, y2: 35 }, { x1: 40, y1: 35, x2: 42, y2: 30 }, // Oreja 1 { x1: 55, y1: 40, x2: 60, y2: 35 }, { x1: 60, y1: 35, x2: 58, y2: 30 }, // Oreja 2 { x1: 50, y1: 48, x2: 50, y2: 45 }, { x1: 48, y1: 48, x2: 52, y2: 48 }, // Hocico y nariz { x1: 65, y1: 50, x2: 75, y2: 60 }, { x1: 75, y1: 60, x2: 80, y2: 55 } // Cola ], "GATO": [ // Gato con orejas puntiagudas, bigotes y cola { x1: 30, y1: 70, x2: 70, y2: 70 }, { x1: 30, y1: 70, x2: 35, y2: 55 }, // Cuerpo y patas { x1: 70, y1: 70, x2: 65, y2: 55 }, { x1: 35, y1: 55, x2: 65, y2: 55 }, { x1: 45, y1: 55, x2: 50, y2: 45 }, { x1: 50, y1: 45, x2: 55, y2: 55 }, // Cabeza { x1: 48, y1: 45, x2: 45, y2: 40 }, { x1: 45, y1: 40, x2: 48, y2: 38 }, // Oreja 1 { x1: 52, y1: 45, x2: 55, y2: 40 }, { x1: 55, y1: 40, x2: 52, y2: 38 }, // Oreja 2 { x1: 45, y1: 50, x2: 40, y2: 50 }, { x1: 45, y1: 52, x2: 40, y2: 52 }, // Bigotes izquierda { x1: 55, y1: 50, x2: 60, y2: 50 }, { x1: 55, y1: 52, x2: 60, y2: 52 }, // Bigotes derecha { x1: 65, y1: 55, x2: 75, y2: 45 } // Cola ], "MESA": [ // Mesa con patas en perspectiva { x1: 25, y1: 40, x2: 75, y2: 40 }, { x1: 25, y1: 40, x2: 25, y2: 45 }, // Superficie { x1: 75, y1: 40, x2: 75, y2: 45 }, { x1: 25, y1: 45, x2: 75, y2: 45 }, { x1: 30, y1: 45, x2: 30, y2: 70 }, { x1: 70, y1: 45, x2: 70, y2: 70 }, // Patas delanteras { x1: 25, y1: 40, x2: 20, y2: 35 }, { x1: 75, y1: 40, x2: 80, y2: 35 }, // Profundidad superior { x1: 20, y1: 35, x2: 20, y2: 60 }, { x1: 80, y1: 35, x2: 80, y2: 60 }, // Profundidad lateral { x1: 20, y1: 60, x2: 25, y2: 70 }, { x1: 80, y1: 60, x2: 75, y2: 70 } // Patas traseras en perspectiva ], "SILLA": [ // Silla con respaldo, asiento y patas { x1: 35, y1: 40, x2: 65, y2: 40 }, { x1: 35, y1: 40, x2: 35, y2: 60 }, // Respaldo { x1: 65, y1: 40, x2: 65, y2: 60 }, { x1: 35, y1: 60, x2: 65, y2: 60 }, // Asiento { x1: 38, y1: 60, x2: 38, y2: 75 }, { x1: 62, y1: 60, x2: 62, y2: 75 }, // Patas delanteras { x1: 35, y1: 60, x2: 32, y2: 65 }, { x1: 32, y1: 65, x2: 32, y2: 75 }, // Pata trasera izq en perspectiva { x1: 65, y1: 60, x2: 68, y2: 65 }, { x1: 68, y1: 65, x2: 68, y2: 75 } // Pata trasera der en perspectiva ], "LIBRO": [ // Libro abierto con páginas { x1: 30, y1: 40, x2: 50, y2: 30 }, { x1: 50, y1: 30, x2: 50, y2: 70 }, // Lado izquierdo { x1: 50, y1: 70, x2: 30, y2: 60 }, { x1: 30, y1: 60, x2: 30, y2: 40 }, { x1: 50, y1: 30, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 70, y2: 60 }, // Lado derecho { x1: 70, y1: 60, x2: 50, y2: 70 }, { x1: 40, y1: 45, x2: 48, y2: 35 }, { x1: 48, y1: 35, x2: 48, y2: 65 }, // Páginas izquierda { x1: 52, y1: 35, x2: 60, y2: 45 }, { x1: 52, y1: 65, x2: 60, y2: 55 } // Páginas derecha ], "TAZA": [ // Taza con asa y base { x1: 40, y1: 40, x2: 60, y2: 40 }, { x1: 40, y1: 40, x2: 38, y2: 65 }, // Cuerpo { x1: 60, y1: 40, x2: 62, y2: 65 }, { x1: 38, y1: 65, x2: 62, y2: 65 }, { x1: 62, y1: 45, x2: 68, y2: 45 }, { x1: 68, y1: 45, x2: 68, y2: 55 }, // Asa { x1: 68, y1: 55, x2: 62, y2: 55 } ], "VENTANA": [ // Ventana con doble marco y cruces { x1: 30, y1: 30, x2: 70, y2: 30 }, { x1: 70, y1: 30, x2: 70, y2: 70 }, { x1: 70, y1: 70, x2: 30, y2: 70 }, { x1: 30, y1: 70, x2: 30, y2: 30 }, // Marco exterior { x1: 35, y1: 35, x2: 65, y2: 35 }, { x1: 65, y1: 35, x2: 65, y2: 65 }, { x1: 65, y1: 65, x2: 35, y2: 65 }, { x1: 35, y1: 65, x2: 35, y2: 35 }, // Marco interior { x1: 50, y1: 35, x2: 50, y2: 65 }, { x1: 35, y1: 50, x2: 70, y2: 50 } // Cruces ], "PUERTA": [ // Puerta con pomo y marco { x1: 40, y1: 30, x2: 60, y2: 30 }, { x1: 60, y1: 30, x2: 60, y2: 70 }, { x1: 60, y1: 70, x2: 40, y2: 70 }, { x1: 40, y1: 70, x2: 40, y2: 30 }, // Puerta { x1: 38, y1: 30, x2: 38, y2: 72 }, { x1: 62, y1: 30, x2: 62, y2: 72 }, // Marco vertical { x1: 38, y1: 30, x2: 62, y2: 30 }, { x1: 38, y1: 72, x2: 62, y2: 72 }, // Marco horizontal { x1: 55, y1: 50, x2: 55, y2: 50 } // Pomo (punto) ], "CAJA": [ // Caja con tapa y perspectiva { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 70, y2: 60 }, { x1: 70, y1: 60, x2: 30, y2: 60 }, { x1: 30, y1: 60, x2: 30, y2: 40 }, // Frente { x1: 70, y1: 40, x2: 80, y2: 30 }, { x1: 80, y1: 30, x2: 40, y2: 30 }, // Tapa { x1: 80, y1: 30, x2: 80, y2: 50 }, { x1: 80, y1: 50, x2: 70, y2: 60 }, // Lado derecho { x1: 40, y1: 30, x2: 30, y2: 40 } // Lado izquierdo (oculto) ], "BOTELLA": [ // Botella con cuello, cuerpo y base { x1: 45, y1: 25, x2: 55, y2: 25 }, // Boca { x1: 45, y1: 25, x2: 40, y2: 35 }, { x1: 55, y1: 25, x2: 60, y2: 35 }, // Cuello { x1: 40, y1: 35, x2: 40, y2: 70 }, { x1: 60, y1: 35, x2: 60, y2: 70 }, // Cuerpo { x1: 38, y1: 70, x2: 62, y2: 70 } // Base (curva conceptual) ], "CAMISA": [ // Camisa con mangas y botones { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 30, y1: 40, x2: 25, y2: 50 }, // Hombros { x1: 70, y1: 40, x2: 75, y2: 50 }, { x1: 25, y1: 50, x2: 25, y2: 70 }, // Cuerpo { x1: 75, y1: 50, x2: 75, y2: 70 }, { x1: 25, y1: 70, x2: 75, y2: 70 }, { x1: 45, y1: 40, x2: 55, y2: 40 }, // Cuello { x1: 50, y1: 45, x2: 50, y2: 45 }, { x1: 50, y1: 50, x2: 50, y2: 50 }, // Botones { x1: 50, y1: 55, x2: 50, y2: 55 }, { x1: 50, y1: 60, x2: 50, y2: 60 } ], "ZAPATO": [ // Zapato con cordones { x1: 20, y1: 60, x2: 70, y2: 60 }, { x1: 20, y1: 60, x2: 25, y2: 70 }, // Base { x1: 70, y1: 60, x2: 75, y2: 70 }, { x1: 25, y1: 70, x2: 75, y2: 70 }, { x1: 30, y1: 60, x2: 40, y2: 55 }, { x1: 40, y1: 55, x2: 50, y2: 55 }, // Parte superior { x1: 35, y1: 57, x2: 45, y2: 57 }, { x1: 40, y1: 54, x2: 50, y2: 54 } // Cordones ], "AGUA": [ // Gotas de agua y superficie { type: "circle", x1: 50, y1: 40, radius: 5 }, // Gota 1 { type: "circle", x1: 40, y1: 50, radius: 4 }, // Gota 2 { type: "circle", x1: 60, y1: 55, radius: 3 }, // Gota 3 { x1: 20, y1: 70, x2: 80, y2: 70 }, { x1: 25, y1: 65, x2: 75, y2: 65 } // Superficie ], "FUEGO": [ // Llama con base { x1: 50, y1: 75, x2: 40, y2: 65 }, { x1: 40, y1: 65, x2: 45, y2: 50 }, // Llama { x1: 45, y1: 50, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 55, y2: 50 }, { x1: 55, y1: 50, x2: 60, y2: 65 }, { x1: 60, y1: 65, x2: 50, y2: 75 }, { x1: 40, y1: 75, x2: 60, y2: 75 } // Base ], "VIENTO": [ // Líneas de viento con remolino { x1: 20, y1: 50, x2: 80, y2: 50 }, { x1: 25, y1: 55, x2: 85, y2: 55 }, { x1: 30, y1: 60, x2: 90, y2: 60 }, { type: "circle", x1: 30, y1: 45, radius: 5 }, { type: "circle", x1: 70, y1: 45, radius: 5 } // Remolinos ], "TIERRA": [ // Horizonte montañoso con árbol { x1: 20, y1: 70, x2: 30, y2: 65 }, { x1: 30, y1: 65, x2: 40, y2: 70 }, { x1: 40, y1: 70, x2: 50, y2: 60 }, { x1: 50, y1: 60, x2: 60, y2: 70 }, { x1: 60, y1: 70, x2: 70, y2: 65 }, { x1: 70, y1: 65, x2: 80, y2: 70 }, { x1: 50, y1: 60, x2: 50, y2: 50 }, { x1: 45, y1: 50, x2: 55, y2: 50 } // Árbol en el horizonte ], "NUBE": [ // Nube con forma y sombra { x1: 30, y1: 40, x2: 40, y2: 35 }, { x1: 40, y1: 35, x2: 50, y2: 38 }, { x1: 50, y1: 38, x2: 60, y2: 35 }, { x1: 60, y1: 35, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 65, y2: 50 }, { x1: 65, y1: 50, x2: 50, y2: 55 }, { x1: 50, y1: 55, x2: 35, y2: 50 }, { x1: 35, y1: 50, x2: 30, y2: 40 }, { x1: 35, y1: 45, x2: 45, y2: 48 }, { x1: 45, y1: 48, x2: 55, y2: 45 }, // Sombra interior { x1: 55, y1: 45, x2: 65, y2: 48 } ], "LUNA": [ // Luna creciente con cráteres { type: "circle", x1: 50, y1: 50, radius: 20 }, // Círculo exterior { type: "circle", x1: 45, y1: 50, radius: 15 }, // Círculo interior { type: "circle", x1: 55, y1: 45, radius: 3 }, // Cráter 1 { type: "circle", x1: 58, y1: 55, radius: 2 } // Cráter 2 ], "ESTRELLA": [ // Estrella de 5 puntas con brillo { x1: 50, y1: 20, x2: 60, y2: 40 }, { x1: 60, y1: 40, x2: 80, y2: 45 }, { x1: 80, y1: 45, x2: 65, y2: 60 }, { x1: 65, y1: 60, x2: 70, y2: 80 }, { x1: 70, y1: 80, x2: 50, y2: 70 }, { x1: 50, y1: 70, x2: 30, y2: 80 }, { x1: 30, y1: 80, x2: 35, y2: 60 }, { x1: 35, y1: 60, x2: 20, y2: 45 }, { x1: 20, y1: 45, x2: 40, y2: 40 }, { x1: 40, y1: 40, x2: 50, y2: 20 }, { x1: 50, y1: 20, x2: 50, y2: 25 }, { x1: 55, y1: 45, x2: 60, y2: 50 } // Detalles de brillo ], "AUTO": [ // Auto con ruedas, ventanas y faros { x1: 20, y1: 70, x2: 80, y2: 70 }, { x1: 20, y1: 70, x2: 20, y2: 60 }, { x1: 80, y1: 70, x2: 80, y2: 60 }, { x1: 20, y1: 60, x2: 30, y2: 50 }, { x1: 30, y1: 50, x2: 70, y2: 50 }, { x1: 70, y1: 50, x2: 80, y2: 60 }, { type: "circle", x1: 30, y1: 70, radius: 5 }, { type: "circle", x1: 70, y1: 70, radius: 5 }, // Ruedas { x1: 35, y1: 55, x2: 45, y2: 55 }, { x1: 45, y1: 55, x2: 45, y2: 65 }, // Ventana delantera { x1: 45, y1: 65, x2: 35, y2: 65 }, { x1: 35, y1: 65, x2: 35, y2: 55 }, { x1: 60, y1: 55, x2: 70, y2: 55 }, { x1: 70, y1: 55, x2: 70, y2: 65 }, // Ventana trasera { x1: 70, y1: 65, x2: 60, y2: 65 }, { x1: 60, y1: 65, x2: 60, y2: 55 }, { x1: 22, y1: 67, x2: 25, y2: 67 }, { x1: 77, y1: 67, x2: 79, y2: 67 } // Faros ], "BICICLETA": [ // Bicicleta con dos ruedas, cuadro, asiento y manillar { type: "circle", x1: 35, y1: 65, radius: 10 }, { type: "circle", x1: 65, y1: 65, radius: 10 }, // Ruedas { x1: 35, y1: 65, x2: 50, y2: 55 }, { x1: 50, y1: 55, x2: 65, y2: 65 }, // Cuadro principal { x1: 50, y1: 55, x2: 50, y2: 50 }, // Asiento { x1: 35, y1: 65, x2: 40, y2: 50 }, { x1: 40, y1: 50, x2: 45, y2: 50 }, // Manillar { x1: 65, y1: 65, x2: 55, y2: 50 }, { x1: 55, y1: 50, x2: 50, y2: 50 } // Pedal ], "BARCO": [ // Barco con mástil, vela y bandera { x1: 20, y1: 70, x2: 80, y2: 70 }, { x1: 20, y1: 70, x2: 30, y2: 60 }, { x1: 80, y1: 70, x2: 70, y2: 60 }, { x1: 30, y1: 60, x2: 70, y2: 60 }, // Casco { x1: 50, y1: 60, x2: 50, y2: 40 }, // Mástil { x1: 50, y1: 40, x2: 65, y2: 50 }, { x1: 65, y1: 50, x2: 50, y2: 60 }, // Vela { x1: 50, y1: 40, x2: 55, y2: 35 }, { x1: 55, y1: 35, x2: 50, y2: 30 } // Bandera ], "PESCADO": [ // Pescado con aletas, ojo y boca { x1: 30, y1: 50, x2: 70, y2: 50 }, { x1: 70, y1: 50, x2: 80, y2: 45 }, { x1: 80, y1: 45, x2: 70, y2: 55 }, { x1: 70, y1: 55, x2: 30, y2: 50 }, // Cuerpo { x1: 75, y1: 45, x2: 85, y2: 40 }, { x1: 85, y1: 40, x2: 85, y2: 60 }, // Cola { x1: 85, y1: 60, x2: 75, y2: 55 }, { x1: 40, y1: 47, x2: 40, y2: 47 }, // Ojo (punto) { x1: 35, y1: 50, x2: 40, y2: 53 } // Boca ], "MANZANA": [ // Manzana con tallo y hoja { type: "circle", x1: 50, y1: 50, radius: 20 }, { x1: 48, y1: 30, x2: 52, y2: 30 }, // Tallito { x1: 52, y1: 30, x2: 55, y2: 35 }, { x1: 55, y1: 35, x2: 52, y2: 38 } // Hoja ], "PLATO": [ // Plato con borde y centro { type: "circle", x1: 50, y1: 50, radius: 25 }, { type: "circle", x1: 50, y1: 50, radius: 20 } ], "CEREBRO": [ // Cerebro con hemisferios y surcos { x1: 30, y1: 40, x2: 40, y2: 30 }, { x1: 40, y1: 30, x2: 60, y2: 30 }, { x1: 60, y1: 30, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 60, y2: 50 }, { x1: 60, y1: 50, x2: 40, y2: 50 }, { x1: 40, y1: 50, x2: 30, y2: 40 }, { x1: 50, y1: 30, x2: 50, y2: 50 }, // Surco central { x1: 35, y1: 35, x2: 45, y2: 45 }, { x1: 55, y1: 35, x2: 65, y2: 45 } // Surcos laterales ], "CORAZON": [ // Corazón con curvas y base { x1: 50, y1: 75, x2: 30, y2: 50 }, { x1: 30, y1: 50, x2: 30, y2: 40 }, { x1: 30, y1: 40, x2: 40, y2: 30 }, { x1: 40, y1: 30, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 60, y2: 30 }, { x1: 60, y1: 30, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 70, y2: 50 }, { x1: 70, y1: 50, x2: 50, y2: 75 } ], "MANO": [ // Mano con dedos y palma { x1: 40, y1: 70, x2: 40, y2: 40 }, { x1: 40, y1: 40, x2: 45, y2: 30 }, // Pulgar { x1: 45, y1: 30, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 50, y2: 25 }, { x1: 50, y1: 25, x2: 55, y2: 25 }, { x1: 55, y1: 25, x2: 55, y2: 40 }, // Índice { x1: 55, y1: 40, x2: 60, y2: 25 }, { x1: 60, y1: 25, x2: 65, y2: 25 }, { x1: 65, y1: 25, x2: 65, y2: 40 }, // Medio { x1: 65, y1: 40, x2: 70, y2: 30 }, { x1: 70, y1: 30, x2: 75, y2: 35 }, { x1: 75, y1: 35, x2: 75, y2: 60 }, // Anular { x1: 40, y1: 70, x2: 75, y2: 70 } // Palma ], "OJO": [ // Ojo con iris, pupila, y pestañas { type: "circle", x1: 50, y1: 50, radius: 10 }, // Iris { type: "circle", x1: 50, y1: 50, radius: 3 }, // Pupila { x1: 30, y1: 50, x2: 70, y2: 50 }, // Línea central del ojo { x1: 35, y1: 45, x2: 65, y2: 45 }, { x1: 35, y1: 55, x2: 65, y2: 55 }, // Párpados { x1: 35, y1: 45, x2: 38, y2: 40 }, { x1: 45, y1: 45, x2: 48, y2: 40 } // Pestañas ], "BOCA": [ // Boca con labios y comisuras { x1: 35, y1: 50, x2: 65, y2: 50 }, // Línea de la boca { x1: 38, y1: 45, x2: 45, y2: 50 }, { x1: 55, y1: 50, x2: 62, y2: 45 }, // Labio superior { x1: 38, y1: 55, x2: 45, y2: 50 }, { x1: 55, y1: 50, x2: 62, y2: 55 } // Labio inferior ], "NARIZ": [ // Nariz detallada { x1: 50, y1: 40, x2: 45, y2: 55 }, { x1: 45, y1: 55, x2: 55, y2: 55 }, // Puente y base { x1: 55, y1: 55, x2: 50, y2: 40 }, { x1: 47, y1: 55, x2: 47, y2: 58 }, { x1: 53, y1: 55, x2: 53, y2: 58 } // Orificios ], "OREJA": [ // Oreja con contorno y detalles internos { x1: 50, y1: 40, x2: 40, y2: 50 }, { x1: 40, y1: 50, x2: 45, y2: 60 }, { x1: 45, y1: 60, x2: 55, y2: 60 }, { x1: 55, y1: 60, x2: 60, y2: 50 }, { x1: 60, y1: 50, x2: 50, y1: 40 }, // Contorno exterior { x1: 47, y1: 45, x2: 43, y2: 50 }, { x1: 43, y1: 50, x2: 48, y2: 55 }, // Detalles internos { x1: 52, y1: 45, x2: 57, y2: 50 }, { x1: 57, y1: 50, x2: 52, y2: 55 } ], "DIENTE": [ // Diente con raíz y encía { x1: 45, y1: 30, x2: 55, y2: 30 }, { x1: 55, y1: 30, x2: 55, y2: 60 }, { x1: 55, y1: 60, x2: 45, y2: 60 }, { x1: 45, y1: 60, x2: 45, y2: 30 }, // Cuerpo del diente { x1: 45, y1: 60, x2: 40, y2: 65 }, { x1: 55, y1: 60, x2: 60, y2: 65 }, // Raíces { x1: 40, y1: 65, x2: 60, y2: 65 } // Encía ], "PAN": [ // Barra de pan con cortes { x1: 20, y1: 60, x2: 80, y2: 60 }, { x1: 20, y1: 60, x2: 25, y2: 70 }, // Base { x1: 80, y1: 60, x2: 75, y2: 70 }, { x1: 25, y1: 70, x2: 75, y2: 70 }, { x1: 20, y1: 60, x2: 20, y2: 50 }, { x1: 80, y1: 60, x2: 80, y2: 50 }, // Lados { x1: 20, y1: 50, x2: 80, y2: 50 }, // Tapa { x1: 30, y1: 55, x2: 35, y2: 65 }, { x1: 40, y1: 55, x2: 45, y2: 65 }, // Cortes { x1: 50, y1: 55, x2: 55, y2: 65 }, { x1: 60, y1: 55, x2: 65, y2: 65 } ], "QUESO": [ // Trozo de queso triangular con agujeros { x1: 30, y1: 70, x2: 70, y2: 70 }, { x1: 70, y1: 70, x2: 50, y2: 30 }, // Triángulo { x1: 50, y1: 30, x2: 30, y2: 70 }, { type: "circle", x1: 45, y1: 55, radius: 5 }, // Agujero 1 { type: "circle", x1: 58, y1: 60, radius: 4 }, // Agujero 2 { type: "circle", x1: 40, y1: 65, radius: 3 } // Agujero 3 ], "HUEVO": [ // Huevo con yema { type: "circle", x1: 50, y1: 50, radius: 20 }, // Contorno { type: "circle", x1: 50, y1: 50, radius: 8 } // Yema ], "CARNE": [ // Trozo de carne con hueso (conceptual) { x1: 30, y1: 50, x2: 70, y2: 50 }, { x1: 70, y1: 50, x2: 70, y2: 70 }, { x1: 70, y1: 70, x2: 30, y2: 70 }, { x1: 30, y1: 70, x2: 30, y2: 50 }, // Contorno { x1: 50, y1: 50, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 55, y2: 35 }, // Hueso (conceptual) { x1: 55, y1: 35, x2: 60, y2: 40 }, { x1: 60, y1: 40, x2: 60, y2: 50 } ], "POLLO": [ // Pollo asado simplificado { x1: 30, y1: 60, x2: 70, y2: 60 }, { x1: 70, y1: 60, x2: 70, y2: 50 }, { x1: 70, y1: 50, x2: 60, y2: 45 }, { x1: 60, y1: 45, x2: 40, y2: 45 }, { x1: 40, y1: 45, x2: 30, y2: 50 }, { x1: 30, y1: 50, x2: 30, y2: 60 }, // Cuerpo { x1: 30, y1: 55, x2: 25, y2: 50 }, { x1: 70, y1: 55, x2: 75, y2: 50 } // Patas/alas ], "ARROZ": [ // Bol de arroz con palillos { type: "circle", x1: 50, y1: 65, radius: 15 }, // Bol { x1: 40, y1: 50, x2: 40, y2: 40 }, { x1: 40, y1: 40, x2: 45, y2: 40 }, // Palillo 1 { x1: 45, y1: 40, x2: 45, y2: 50 }, { x1: 55, y1: 50, x2: 55, y2: 40 }, { x1: 55, y1: 40, x2: 60, y2: 40 }, // Palillo 2 { x1: 60, y1: 40, x2: 60, y2: 50 } ], "PASTA": [ // Plato de pasta con tenedor { type: "circle", x1: 50, y1: 65, radius: 20 }, // Plato { x1: 45, y1: 55, x2: 55, y2: 55 }, { x1: 50, y1: 55, x2: 50, y2: 45 }, // Tenedor { x1: 48, y1: 45, x2: 48, y2: 40 }, { x1: 52, y1: 45, x2: 52, y2: 40 } ], "FRUTA": [ // Canasta de frutas (conceptual) { x1: 30, y1: 70, x2: 70, y2: 70 }, { x1: 30, y1: 70, x2: 35, y2: 60 }, // Canasta { x1: 70, y1: 70, x2: 65, y2: 60 }, { x1: 35, y1: 60, x2: 65, y2: 60 }, { type: "circle", x1: 40, y1: 55, radius: 5 }, // Fruta 1 { type: "circle", x1: 60, y1: 55, radius: 5 }, // Fruta 2 { type: "circle", x1: 50, y1: 45, radius: 5 } // Fruta 3 ], "UVA": [ // Racimo de uvas { type: "circle", x1: 50, y1: 40, radius: 5 }, { type: "circle", x1: 45, y1: 48, radius: 5 }, { type: "circle", x1: 55, y1: 48, radius: 5 }, { type: "circle", x1: 40, y1: 56, radius: 5 }, { type: "circle", x1: 50, y1: 56, radius: 5 }, { type: "circle", x1: 60, y1: 56, radius: 5 }, { x1: 50, y1: 35, x2: 50, y2: 25 } // Tallo ], "PIÑA": [ // Piña con hojas { x1: 40, y1: 40, x2: 60, y2: 40 }, { x1: 40, y1: 40, x2: 35, y2: 60 }, // Cuerpo ovalado { x1: 60, y1: 40, x2: 65, y2: 60 }, { x1: 35, y1: 60, x2: 65, y2: 60 }, { x1: 45, y1: 35, x2: 40, y2: 25 }, { x1: 50, y1: 35, x2: 45, y2: 25 }, // Hojas { x1: 55, y1: 35, x2: 60, y2: 25 } ], "KIWI": [ // Kiwi cortado { type: "circle", x1: 50, y1: 50, radius: 20 }, // Contorno { type: "circle", x1: 50, y1: 50, radius: 5 }, // Centro { x1: 50, y1: 30, x2: 50, y2: 70 }, { x1: 30, y1: 50, x2: 70, y2: 50 }, // Líneas internas { x1: 35, y1: 35, x2: 65, y2: 65 }, { x1: 35, y1: 65, x2: 65, y2: 35 } ], "CHOCOLATE": [ // Barra de chocolate { x1: 30, y1: 45, x2: 70, y2: 45 }, { x1: 70, y1: 45, x2: 70, y2: 65 }, { x1: 70, y1: 65, x2: 30, y2: 65 }, { x1: 30, y1: 65, x2: 30, y2: 45 }, // Rectángulo base { x1: 35, y1: 45, x2: 35, y2: 65 }, { x1: 45, y1: 45, x2: 45, y2: 65 }, // Divisiones { x1: 55, y1: 45, x2: 55, y2: 65 }, { x1: 65, y1: 45, x2: 65, y2: 65 } ], "HELADO": [ // Cono de helado { x1: 40, y1: 70, x2: 60, y2: 70 }, { x1: 40, y1: 70, x2: 50, y2: 30 }, { x1: 60, y1: 70, x2: 50, y2: 30 }, // Cono { type: "circle", x1: 50, y1: 30, radius: 15 } // Bola de helado ], "GALLETA": [ // Galleta con chispas { type: "circle", x1: 50, y1: 50, radius: 20 }, // Contorno { type: "circle", x1: 40, y1: 45, radius: 2 }, // Chispa 1 { type: "circle", x1: 60, y1: 45, radius: 2 }, // Chispa 2 { type: "circle", x1: 50, y1: 58, radius: 2 } // Chispa 3 ], "CARAMELO": [ // Caramelo envuelto { x1: 30, y1: 50, x2: 70, y2: 50 }, // Cuerpo central { x1: 25, y1: 45, x2: 30, y2: 50 }, { x1: 30, y1: 50, x2: 25, y2: 55 }, // Extremo izquierdo { x1: 70, y1: 50, x2: 75, y2: 45 }, { x1: 75, y1: 45, x2: 70, y2: 50 }, // Extremo derecho { x1: 70, y1: 50, x2: 75, y2: 55 } ], "TARTA": [ // Rebanada de tarta { x1: 50, y1: 30, x2: 30, y2: 70 }, { x1: 30, y1: 70, x2: 70, y2: 70 }, { x1: 70, y1: 70, x2: 50, y2: 30 }, // Triángulo { x1: 40, y1: 45, x2: 60, y2: 45 } // Detalle de capa ], "PIZZA": [ // Rebanada de pizza { x1: 50, y1: 30, x2: 30, y2: 70 }, { x1: 30, y1: 70, x2: 70, y2: 70 }, { x1: 70, y1: 70, x2: 50, y2: 30 }, // Triángulo { type: "circle", x1: 40, y1: 50, radius: 3 }, // Pepperoni 1 { type: "circle", x1: 60, y1: 55, radius: 3 } // Pepperoni 2 ], "HAMBURGUESA": [ // Hamburguesa con pan y carne { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 30, y1: 40, x2: 70, y2: 40 }, // Pan superior (curva conceptual) { x1: 30, y1: 50, x2: 70, y2: 50 }, // Carne { x1: 30, y1: 60, x2: 70, y2: 60 } // Pan inferior ], "PERRITO": [ // Perrito caliente en bollo { x1: 30, y1: 50, x2: 70, y2: 50 }, // Salchicha { x1: 25, y1: 55, x2: 75, y2: 55 }, { x1: 25, y1: 55, x2: 20, y2: 65 }, // Bollo { x1: 75, y1: 55, x2: 80, y2: 65 }, { x1: 20, y1: 65, x2: 80, y2: 65 } ], "MAPACHE": [ // Cara de mapache { type: "circle", x1: 50, y1: 50, radius: 20 }, // Cabeza { x1: 40, y1: 45, x2: 40, y2: 55 }, { x1: 60, y1: 45, x2: 60, y2: 55 }, // Manchas de ojos { x1: 45, y1: 40, x2: 50, y2: 35 }, { x1: 50, y1: 35, x2: 55, y2: 40 }, // Orejas { x1: 50, y1: 55, x2: 50, y2: 58 } // Nariz ], "ZORRO": [ // Cara de zorro { x1: 50, y1: 50, x2: 50, y2: 50 }, { x1: 40, y1: 55, x2: 50, y2: 40 }, // Hocico { x1: 50, y1: 40, x2: 60, y2: 55 }, { x1: 45, y1: 35, x2: 50, y2: 30 }, // Orejas { x1: 50, y1: 30, x2: 55, y2: 35 } ], "LOBO": [ // Cara de lobo aullando { x1: 50, y1: 50, x2: 50, y2: 50 }, { x1: 40, y1: 55, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 60, y2: 55 }, // Hocico { x1: 45, y1: 35, x2: 50, y2: 30 }, { x1: 50, y1: 30, x2: 55, y2: 35 }, // Orejas { x1: 50, y1: 60, x2: 50, y2: 70 } // Boca abierta (aullido) ], "OSO": [ // Cara de oso con orejas redondas { type: "circle", x1: 50, y1: 50, radius: 20 }, // Cabeza { type: "circle", x1: 40, y1: 40, radius: 5 }, { type: "circle", x1: 60, y1: 40, radius: 5 }, // Orejas { x1: 45, y1: 55, x2: 55, y2: 55 }, { x1: 50, y1: 55, x2: 50, y2: 60 } // Hocico ], "TIGRE": [ // Cara de tigre con rayas { type: "circle", x1: 50, y1: 50, radius: 25 }, // Cabeza { x1: 40, y1: 40, x2: 45, y2: 40 }, { x1: 38, y1: 48, x2: 43, y2: 48 }, // Rayas { x1: 60, y1: 40, x2: 55, y2: 40 }, { x1: 62, y1: 48, x2: 57, y2: 48 } ], "LEON": [ // Cara de león con melena (conceptual) { type: "circle", x1: 50, y1: 50, radius: 25 }, // Cabeza { type: "circle", x1: 50, y1: 50, radius: 15 }, // Cara interna { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 30, y1: 60, x2: 70, y2: 60 } // Melena (líneas) ], "ELEFANTE": [ // Elefante con trompa y orejas grandes { type: "circle", x1: 50, y1: 50, radius: 20 }, // Cabeza { x1: 50, y1: 50, x2: 60, y2: 65 }, { x1: 60, y1: 65, x2: 55, y2: 75 }, // Trompa { x1: 35, y1: 40, x2: 25, y2: 55 }, { x1: 25, y2: 55, x2: 35, y2: 65 }, // Oreja 1 { x1: 65, y1: 40, x2: 75, y2: 55 }, { x1: 75, y2: 55, x2: 65, y2: 65 } // Oreja 2 ], "JIRAFA": [ // Jirafa con cuello largo y manchas { x1: 50, y1: 80, x2: 50, y2: 30 }, { x1: 48, y1: 80, x2: 48, y2: 30 }, // Cuello { type: "circle", x1: 50, y1: 25, radius: 8 }, // Cabeza { x1: 40, y1: 60, x2: 45, y2: 65 }, { x1: 55, y1: 60, x2: 60, y2: 65 } // Manchas ], "MONO": [ // Mono con cola y orejas { type: "circle", x1: 50, y1: 50, radius: 20 }, // Cuerpo { type: "circle", x1: 50, y1: 30, radius: 10 }, // Cabeza { type: "circle", x1: 40, y1: 25, radius: 5 }, { type: "circle", x1: 60, y1: 25, radius: 5 }, // Orejas { x1: 60, y1: 60, x2: 70, y2: 50 }, { x1: 70, y2: 50, x2: 75, y2: 60 } // Cola ], "KOALA": [ // Koala con orejas grandes y nariz { type: "circle", x1: 50, y1: 50, radius: 25 }, // Cuerpo { type: "circle", x1: 40, y1: 40, radius: 10 }, { type: "circle", x1: 60, y1: 40, radius: 10 }, // Orejas { x1: 50, y1: 55, x2: 50, y2: 58 } // Nariz ], "PINGUINO": [ // Pingüino con cuerpo ovalado { x1: 50, y1: 80, x2: 50, y2: 30 }, { x1: 40, y1: 80, x2: 40, y2: 35 }, { x1: 60, y1: 80, x2: 60, y2: 35 }, // Cuerpo { x1: 40, y1: 35, x2: 50, y2: 30 }, { x1: 50, y1: 30, x2: 60, y2: 35 }, { x1: 45, y1: 45, x2: 55, y2: 45 }, { x1: 50, y1: 45, x2: 50, y2: 50 } // Ojos y pico ], "BUHO": [ // Búho con ojos grandes { type: "circle", x1: 50, y1: 50, radius: 25 }, // Cuerpo { type: "circle", x1: 40, y1: 40, radius: 8 }, { type: "circle", x1: 60, y1: 40, radius: 8 }, // Ojos { x1: 50, y1: 48, x2: 50, y2: 52 } // Pico ], "AGUILA": [ // Águila con alas { x1: 50, y1: 40, x2: 50, y2: 30 }, { x1: 40, y1: 50, x2: 20, y2: 45 }, { x1: 20, y1: 45, x2: 45, y2: 40 }, // Cuerpo y ala izq. { x1: 60, y1: 50, x2: 80, y2: 45 }, { x1: 80, y1: 45, x2: 55, y2: 40 } // Ala der. ], "DELFIN": [ // Delfín saltando { x1: 30, y1: 60, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 75, y2: 50 }, { x1: 75, y2: 50, x2: 70, y2: 60 }, { x1: 70, y2: 60, x2: 30, y2: 60 }, // Cuerpo { x1: 60, y1: 40, x2: 60, y2: 30 } // Aleta ], "BALLENA": [ // Ballena con cola { x1: 20, y1: 60, x2: 80, y2: 60 }, { x1: 20, y1: 60, x2: 25, y2: 50 }, // Cuerpo { x1: 80, y1: 60, x2: 75, y2: 50 }, { x1: 25, y1: 50, x2: 75, y2: 50 }, { x1: 70, y1: 55, x2: 80, y2: 45 }, { x1: 80, y2: 45, x2: 80, y2: 65 }, { x1: 80, y2: 65, x2: 70, y2: 55 } // Cola ], "TIBURON": [ // Tiburón con aletas y dientes (conceptual) { x1: 20, y1: 60, x2: 80, y2: 55 }, { x1: 80, y1: 55, x2: 75, y2: 65 }, { x1: 75, y2: 65, x2: 20, y2: 60 }, // Cuerpo { x1: 50, y1: 50, x2: 50, y2: 40 }, { x1: 45, y1: 40, x2: 55, y2: 40 } // Aleta dorsal ], "PULPO": [ // Pulpo con tentáculos { type: "circle", x1: 50, y1: 40, radius: 15 }, // Cabeza { x1: 40, y1: 55, x2: 30, y2: 65 }, { x1: 30, y2: 65, x2: 35, y2: 70 }, // Tentáculo 1 { x1: 50, y1: 55, x2: 45, y2: 65 }, { x1: 45, y2: 65, x2: 50, y2: 70 }, // Tentáculo 2 { x1: 60, y1: 55, x2: 70, y2: 65 }, { x1: 70, y2: 65, x2: 65, y2: 70 } // Tentáculo 3 ], "CANGREJO": [ // Cangrejo con pinzas { x1: 30, y1: 60, x2: 70, y2: 60 }, { x1: 30, y1: 60, x2: 30, y2: 50 }, { x1: 70, y1: 60, x2: 70, y2: 50 }, { x1: 30, y1: 50, x2: 70, y2: 50 }, // Cuerpo { x1: 25, y1: 55, x2: 15, y2: 50 }, { x1: 15, y1: 50, x2: 20, y2: 45 }, // Pinza 1 { x1: 75, y1: 55, x2: 85, y2: 50 }, { x1: 85, y1: 50, x2: 80, y2: 45 } // Pinza 2 ], "MEDUSA": [ // Medusa con tentáculos { type: "circle", x1: 50, y1: 40, radius: 15 }, // Cuerpo superior { x1: 40, y1: 55, x2: 40, y2: 70 }, { x1: 50, y1: 55, x2: 50, y2: 70 }, // Tentáculos { x1: 60, y1: 55, x2: 60, y2: 70 } ], "DINOSAURIO": [ // Contorno de dinosaurio (cuello largo) { x1: 20, y1: 70, x2: 60, y2: 70 }, { x1: 60, y1: 70, x2: 65, y2: 60 }, { x1: 65, y1: 60, x2: 60, y2: 50 }, { x1: 60, y1: 50, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 40, y2: 50 }, { x1: 40, y1: 50, x2: 30, y2: 60 }, { x1: 30, y1: 60, x2: 20, y2: 70 } ], "DRAGON": [ // Contorno de dragón (alas y cola) { x1: 30, y1: 60, x2: 70, y2: 60 }, { x1: 70, y1: 60, x2: 65, y2: 40 }, { x1: 65, y1: 40, x2: 35, y2: 40 }, { x1: 35, y1: 40, x2: 30, y2: 60 }, // Cuerpo { x1: 40, y1: 40, x2: 30, y2: 30 }, { x1: 30, y1: 30, x2: 40, y2: 25 }, // Ala izq. { x1: 60, y1: 40, x2: 70, y2: 30 }, { x1: 70, y1: 30, x2: 60, y2: 25 }, // Ala der. { x1: 70, y1: 60, x2: 80, y2: 55 }, { x1: 80, y1: 55, x2: 75, y2: 65 } // Cola ], "UNICORNIO": [ // Unicornio (cabeza y cuerno) { x1: 50, y1: 50, x2: 50, y2: 50 }, { x1: 40, y1: 55, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 60, y2: 55 }, // Cabeza { x1: 50, y1: 40, x2: 50, y2: 25 }, { x1: 48, y1: 25, x2: 52, y2: 25 } // Cuerno ], "SIRENA": [ // Sirena (cuerpo y cola) { x1: 50, y1: 40, x2: 50, y2: 60 }, // Cuerpo { x1: 45, y1: 60, x2: 40, y2: 70 }, { x1: 40, y1: 70, x2: 60, y2: 70 }, { x1: 60, y1: 70, x2: 55, y2: 60 } // Cola ], "HADA": [ // Hada (cuerpo y alas) { x1: 50, y1: 40, x2: 50, y2: 60 }, // Cuerpo { x1: 40, y1: 50, x2: 30, y2: 45 }, { x1: 30, y1: 45, x2: 40, y2: 50 }, // Ala izq. { x1: 60, y1: 50, x2: 70, y2: 45 }, { x1: 70, y1: 45, x2: 60, y2: 50 } // Ala der. ], "MAGO": [ // Mago (sombrero y varita) { x1: 50, y1: 50, x2: 50, y2: 50 }, { x1: 40, y1: 60, x2: 60, y2: 60 }, { x1: 45, y1: 60, x2: 50, y2: 40 }, { x1: 50, y1: 40, x2: 55, y2: 60 }, // Sombrero { x1: 65, y1: 50, x2: 75, y2: 45 } // Varita ], "GUERRERO": [ // Guerrero (armadura básica) { x1: 50, y1: 50, x2: 50, y2: 50 }, { x1: 40, y1: 60, x2: 60, y2: 60 }, { x1: 40, y1: 60, x2: 40, y2: 40 }, { x1: 60, y1: 60, x2: 60, y2: 40 }, // Cuerpo { x1: 45, y1: 40, x2: 55, y2: 40 } // Yelmo (base) ], "CABALLERO": [ // Caballero (yelmo y espada) { x1: 50, y1: 50, x2: 50, y2: 50 }, { x1: 40, y1: 60, x2: 60, y2: 60 }, { x1: 40, y1: 60, x2: 40, y2: 40 }, { x1: 60, y1: 60, x2: 60, y2: 40 }, // Cuerpo { x1: 45, y1: 40, x2: 55, y2: 40 }, { x1: 50, y1: 40, x2: 50, y2: 30 }, // Yelmo { x1: 65, y1: 50, x2: 75, y2: 40 } // Espada ], "PRINCESA": [ // Princesa (corona y vestido) { x1: 50, y1: 40, x2: 50, y2: 60 }, // Cuerpo { x1: 40, y1: 60, x2: 60, y2: 60 }, { x1: 35, y1: 60, x2: 40, y2: 70 }, // Vestido { x1: 65, y1: 60, x2: 60, y2: 70 }, { x1: 35, y1: 70, x2: 65, y2: 70 }, { x1: 45, y1: 35, x2: 55, y2: 35 }, { x1: 48, y1: 35, x2: 50, y2: 30 }, // Corona { x1: 50, y1: 30, x2: 52, y2: 35 } ], "REINA": [ // Reina (corona grande) { x1: 50, y1: 40, x2: 50, y2: 60 }, // Cuerpo { x1: 40, y1: 60, x2: 60, y2: 60 }, { x1: 35, y1: 60, x2: 40, y2: 70 }, { x1: 65, y1: 60, x2: 60, y2: 70 }, { x1: 35, y1: 70, x2: 65, y2: 70 }, { x1: 40, y1: 35, x2: 60, y2: 35 }, { x1: 42, y1: 35, x2: 45, y2: 30 }, // Corona grande { x1: 45, y1: 30, x2: 50, y2: 32 }, { x1: 50, y1: 32, x2: 55, y2: 30 }, { x1: 55, y1: 30, x2: 58, y2: 35 }, { x1: 58, y1: 35, x2: 60, y2: 35 } ], "REY": [ // Rey (corona y cetro) { x1: 50, y1: 40, x2: 50, y2: 60 }, // Cuerpo { x1: 40, y1: 60, x2: 60, y2: 60 }, { x1: 40, y1: 60, x2: 40, y2: 70 }, { x1: 60, y1: 60, x2: 60, y2: 70 }, { x1: 40, y1: 70, x2: 60, y2: 70 }, { x1: 45, y1: 35, x2: 55, y2: 35 }, { x1: 48, y1: 35, x2: 50, y2: 30 }, // Corona { x1: 50, y1: 30, x2: 52, y2: 35 }, { x1: 65, y1: 50, x2: 75, y2: 40 }, { x1: 75, y1: 40, x2: 78, y2: 43 } // Cetro ], "CASTILLO": [ // Castillo con almenas y torre { x1: 20, y1: 70, x2: 80, y2: 70 }, { x1: 20, y1: 70, x2: 20, y2: 40 }, { x1: 80, y1: 70, x2: 80, y2: 40 }, { x1: 20, y1: 40, x2: 80, y2: 40 }, // Base { x1: 20, y1: 40, x2: 20, y2: 35 }, { x1: 25, y1: 35, x2: 25, y2: 40 }, // Almenas { x1: 30, y1: 40, x2: 30, y2: 35 }, { x1: 35, y1: 35, x2: 35, y2: 40 }, { x1: 40, y1: 40, x2: 40, y2: 35 }, { x1: 45, y1: 35, x2: 45, y2: 40 }, { x1: 50, y1: 40, x2: 50, y2: 30 }, { x1: 50, y1: 30, x2: 60, y2: 30 }, // Torre { x1: 60, y1: 30, x2: 60, y2: 40 }, { x1: 55, y1: 30, x2: 55, y2: 25 } // Almena de torre ], "ESPADA": [ // Espada con empuñadura { x1: 50, y1: 20, x2: 50, y2: 70 }, // Hoja { x1: 45, y1: 60, x2: 55, y2: 60 }, // Guarda { x1: 48, y1: 70, x2: 52, y2: 70 }, { x1: 48, y1: 70, x2: 48, y2: 75 }, // Empuñadura { x1: 52, y1: 70, x2: 52, y2: 75 }, { x1: 48, y1: 75, x2: 52, y2: 75 } ], "ESCUDO": [ // Escudo medieval { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 70, y2: 60 }, { x1: 70, y1: 60, x2: 50, y2: 70 }, { x1: 50, y1: 70, x2: 30, y2: 60 }, // Contorno { x1: 30, y1: 60, x2: 30, y2: 40 }, { x1: 40, y1: 45, x2: 60, y2: 45 }, { x1: 50, y1: 45, x2: 50, y2: 65 } // Cruz (blasón) ], "ARCO": [ // Arco y flecha { x1: 30, y1: 60, x2: 50, y2: 30 }, { x1: 50, y1: 30, x2: 70, y2: 60 }, // Arco { x1: 30, y1: 60, x2: 70, y2: 60 }, // Cuerda { x1: 50, y1: 45, x2: 80, y2: 45 }, { x1: 80, y1: 45, x2: 75, y2: 40 }, // Flecha { x1: 80, y1: 45, x2: 75, y2: 50 } ], "FLECHA": [ // Flecha con punta y cola { x1: 20, y1: 50, x2: 80, y2: 50 }, // Cuerpo { x1: 75, y1: 45, x2: 80, y2: 50 }, { x1: 80, y1: 50, x2: 75, y2: 55 }, // Punta { x1: 25, y1: 45, x2: 20, y2: 50 }, { x1: 20, y1: 50, x2: 25, y2: 55 } // Cola ], "POCION": [ // Frasco de poción { x1: 40, y1: 70, x2: 60, y2: 70 }, { x1: 40, y1: 70, x2: 40, y2: 50 }, { x1: 60, y1: 70, x2: 60, y2: 50 }, { x1: 40, y1: 50, x2: 60, y2: 50 }, // Cuerpo { x1: 45, y1: 50, x2: 45, y2: 40 }, { x1: 55, y1: 50, x2: 55, y2: 40 }, // Cuello { x1: 48, y1: 40, x2: 52, y2: 40 } // Tapa ], "ANILLO": [ // Anillo con gema { type: "circle", x1: 50, y1: 50, radius: 20 }, // Anillo { x1: 45, y1: 35, x2: 50, y2: 30 }, { x1: 50, y1: 30, x2: 55, y2: 35 } // Gema (triángulo) ], "COLLAR": [ // Collar con pendiente { x1: 30, y1: 40, x2: 70, y2: 40 }, // Cadena { x1: 45, y1: 40, x2: 50, y2: 50 }, { x1: 50, y1: 50, x2: 55, y2: 40 } // Pendiente (triángulo) ], "CORONA": [ // Corona con puntas { x1: 30, y1: 60, x2: 70, y2: 60 }, // Base { x1: 30, y1: 60, x2: 35, y2: 45 }, { x1: 35, y1: 45, x2: 40, y2: 60 }, // Punta 1 { x1: 45, y1: 60, x2: 50, y2: 45 }, { x1: 50, y1: 45, x2: 55, y2: 60 }, // Punta 2 { x1: 60, y1: 60, x2: 65, y2: 45 }, { x1: 65, y1: 45, x2: 70, y2: 60 } // Punta 3 ], "GEMA": [ // Gema facetada { x1: 50, y1: 30, x2: 30, y2: 50 }, { x1: 30, y1: 50, x2: 50, y2: 70 }, { x1: 50, y1: 70, x2: 70, y2: 50 }, { x1: 70, y1: 50, x2: 50, y2: 30 }, // Contorno { x1: 50, y1: 30, x2: 50, y2: 70 }, { x1: 30, y1: 50, x2: 70, y2: 50 } // Facetas ], "TESORO": [ // Cofre del tesoro { x1: 30, y1: 60, x2: 70, y2: 60 }, { x1: 30, y1: 60, x2: 30, y2: 40 }, // Base { x1: 70, y1: 60, x2: 70, y2: 40 }, { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 30, y1: 40, x2: 35, y2: 35 }, { x1: 70, y1: 40, x2: 75, y2: 35 }, // Tapa { x1: 35, y1: 35, x2: 75, y2: 35 }, { x1: 50, y1: 40, x2: 50, y2: 50 } // Cerradura ], "MONEDA": [ // Moneda con un signo de dólar { type: "circle", x1: 50, y1: 50, radius: 20 }, // Contorno { x1: 48, y1: 40, x2: 48, y2: 60 }, { x1: 52, y1: 40, x2: 52, y2: 60 }, // Barra vertical { x1: 45, y1: 45, x2: 55, y2: 45 }, { x1: 45, y1: 55, x2: 55, y2: 55 } // Barras horizontales del dólar ], "MAPA": [ // Mapa enrollado { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 70, y2: 60 }, { x1: 70, y1: 60, x2: 30, y2: 60 }, { x1: 30, y1: 60, x2: 30, y2: 40 }, // Contorno { x1: 35, y1: 40, x2: 35, y2: 60 }, { x1: 65, y1: 40, x2: 65, y2: 60 } // Enrollado ], "BRUJULA": [ // Brújula con aguja { type: "circle", x1: 50, y1: 50, radius: 20 }, // Contorno { x1: 50, y1: 35, x2: 50, y2: 65 }, { x1: 35, y1: 50, x2: 65, y2: 50 }, // Cruces { x1: 50, y1: 40, x2: 45, y2: 50 }, { x1: 45, y1: 50, x2: 50, y2: 60 }, // Aguja { x1: 50, y1: 60, x2: 55, y2: 50 }, { x1: 55, y1: 50, x2: 50, y2: 40 } ], "PERGAMINO": [ // Pergamino enrollado { x1: 30, y1: 40, x2: 70, y2: 40 }, { x1: 70, y1: 40, x2: 70, y2: 60 }, { x1: 70, y1: 60, x2: 30, y2: 60 }, { x1: 30, y1: 60, x2: 30, y2: 40 }, // Contorno { x1: 25, y1: 40, x2: 30, y2: 45 }, { x1: 25, y1: 55, x2: 30, y2: 60 }, // Enrollado izq. { x1: 70, y1: 45, x2: 75, y2: 40 }, { x1: 70, y1: 55, x2: 75, y2: 60 } // Enrollado der. ], "ANTORCHA": [ // Antorcha con llama { x1: 50, y1: 80, x2: 50, y2: 60 }, { x1: 48, y1: 80, x2: 52, y2: 80 }, // Palo { x1: 45, y1: 60, x2: 55, y2: 60 }, { x1: 45, y1: 60, x2: 40, y2: 55 }, // Base llama { x1: 55, y1: 60, x2: 60, y2: 55 }, { x1: 50, y1: 50, x2: 45, y2: 40 }, { x1: 45, y1: 40, x2: 50, y2: 30 }, // Llama { x1: 50, y1: 30, x2: 55, y2: 40 }, { x1: 55, y1: 40, x2: 50, y2: 50 } ] }; // --- FIN BASE DE DATOS DE BOCETOS DETALLADOS --- class IntelligentArtist extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); // Valores por defecto #currentBrushSize = 5; #currentSketchColor = "#888888"; constructor() { super("Artista Inteligente", '<i class="fas fa-brain"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { this.#row1(); // Generación de Bocetos Asistida this.#row2(); // Limpiar Lienzo this.#row3(); // Configuración de Color y Tamaño del Boceto this.#row4(); // Lista de Bocetos Disponibles (con slider) } #row1() { const row = domMake.Row(); { const sketchInput = domMake.Tree("input", { type: "text", placeholder: "Concepto de boceto (ej. 'árbol')" }); const generateSketchButton = domMake.Button("Generar Boceto"); generateSketchButton.title = "Dibuja un boceto predefinido para la palabra ingresada."; generateSketchButton.addEventListener("click", () => { this.simulateAISketch(sketchInput.value.toUpperCase()); // Convertir a mayúsculas }); row.appendAll(sketchInput, generateSketchButton); } this.htmlElements.section.appendChild(row); } #row2() { const row = domMake.Row(); { const clearCanvasButton = domMake.Button("Limpiar Lienzo"); clearCanvasButton.title = "Limpia el lienzo con una línea blanca muy grande."; clearCanvasButton.addEventListener("click", () => { const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; // Usar el primer bot activo if (activeBot && activeBot.getReadyState()) { // Dibuja dos líneas blancas que cubren todo el canvas con un grosor muy grande activeBot.emit("line", -1, 0, 0, 100, 100, true, 900, "#FFFFFF", false); // Línea diagonal 1 activeBot.emit("line", -1, 100, 0, 0, 100, true, 900, "#FFFFFF", false); // Línea diagonal 2 this.notify("success", "El lienzo ha sido limpiado."); } else { this.notify("warning", "El bot seleccionado no está conectado."); } } else { this.notify("warning", "Se necesita al menos 1 bot activo para limpiar el lienzo."); } }); row.appendChild(clearCanvasButton); } this.htmlElements.section.appendChild(row); } #row3() { const row = domMake.Row(); { const sketchColorLabel = domMake.Tree("label", {}, ["Color Boceto:"]); const sketchColorInput = domMake.Tree("input", { type: "color", value: this.#currentSketchColor }); sketchColorInput.title = "Define el color del boceto."; sketchColorInput.addEventListener("change", (e) => { this.#currentSketchColor = e.target.value; this.notify("info", `Color del boceto cambiado a: ${this.#currentSketchColor}`); }); const brushSizeLabel = domMake.Tree("label", {}, ["Tamaño Pincel:"]); const brushSizeInput = domMake.Tree("input", { type: "number", min: 1, max: 50, value: this.#currentBrushSize }); brushSizeInput.title = "Define el tamaño del pincel para el boceto."; brushSizeInput.addEventListener("change", (e) => { this.#currentBrushSize = parseInt(e.target.value); this.notify("info", `Tamaño del pincel para boceto cambiado a: ${this.#currentBrushSize}`); }); row.appendAll(sketchColorLabel, sketchColorInput, brushSizeLabel, brushSizeInput); } this.htmlElements.section.appendChild(row); } #row4() { const row = domMake.Row(); // Este row contendrá la etiqueta y el contenedor con scroll const sketchListContainer = domMake.IconList(); // IconList es un flex container sketchListContainer.classList.add('nowrap'); // Clase para forzar no-wrap y añadir scroll horizontal // Crear botones para cada palabra en la base de datos Object.keys(SKETCH_DATABASE).forEach(word => { const sketchButton = domMake.Button(word); sketchButton.title = `Generar boceto para: ${word}`; sketchButton.style.flex = '0 0 auto'; // Impide que los botones se estiren y ocupen todo el ancho disponible sketchButton.style.margin = '2px'; sketchButton.style.padding = '2px 5px'; sketchButton.style.fontSize = '0.7em'; sketchButton.addEventListener("click", () => { this.simulateAISketch(word); }); sketchListContainer.appendChild(sketchButton); }); // Añadir la etiqueta y el contenedor de scroll al row principal de esta sección row.appendAll(domMake.Tree("label", {}, ["Bocetos Rápidos (50):"]), sketchListContainer); this.htmlElements.section.appendChild(row); } simulateAISketch(concept) { const sketchData = SKETCH_DATABASE[concept]; if (!sketchData) { this.notify("warning", `Boceto no disponible para: "${concept}".`); return; } this.notify("info", `Generando boceto para: "${concept}" (conceptual).`); const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (!botManager || botManager.children.length === 0) { this.notify("warning", "Se necesita al menos 1 bot activo para generar bocetos."); return; } const activeBot = botManager.children[0].bot; // Usar el primer bot activo if (!activeBot || !activeBot.getReadyState()) { this.notify("warning", "El bot seleccionado no está conectado."); return; } this.notify("info", "Dibujando el boceto conceptual con el bot..."); sketchData.forEach(line => { if (line.type === "circle") { // Simular un círculo con múltiples líneas pequeñas const centerX = line.x1; const centerY = line.y1; const radius = line.radius; const segments = 24; // Más segmentos para un círculo más suave for (let i = 0; i < segments; i++) { const angle1 = (i / segments) * Math.PI * 2; const angle2 = ((i + 1) / segments) * Math.PI * 2; const x1_circ = centerX + radius * Math.cos(angle1); const y1_circ = centerY + radius * Math.sin(angle1); const x2_circ = centerX + radius * Math.cos(angle2); const y2_circ = centerY + radius * Math.sin(angle2); activeBot.emit("line", -1, x1_circ, y1_circ, x2_circ, y2_circ, true, this.#currentBrushSize, this.#currentSketchColor, false); } } else { activeBot.emit("line", -1, line.x1, line.y1, line.x2, line.y2, true, this.#currentBrushSize, this.#currentSketchColor, false); } }); this.notify("success", `Boceto de "${concept}" dibujado. ¡Ahora puedes calcarlo o mejorarlo!`); } } })("QBit"); (function TacticalBotSwarm() { const QBit = globalThis[arguments[0]]; class TacticalBotSwarm extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); constructor() { super("Swarm de Bots Tácticos", '<i class="fas fa-users-cog"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); } #loadInterface() { this.#row1(); // Dibujo Colaborativo por Zonas this.#row2(); // Bot de Adivinanza "Táctico" this.#row3(); // Personalidad del Bot (Velocidad de dibujo y verborrea) } #row1() { const row = domMake.Row(); { const coordinateDrawButton = domMake.Button("Dibujo Colaborativo"); coordinateDrawButton.title = "Divide el lienzo en zonas para que cada bot dibuje una parte (usa GhostCanvas para cargar imagen)."; coordinateDrawButton.addEventListener("click", async () => { const botManager = this.findGlobal("BotClientManager")?.siblings[0]; const ghostCanvas = this.findGlobal("GhostCanvas")?.siblings[0]; if (!botManager || botManager.children.length === 0) { this.notify("warning", "Necesitas bots activos para el dibujo colaborativo."); return; } if (!ghostCanvas || ghostCanvas.drawingManager.pixelList.length === 0) { this.notify("warning", "Carga una imagen en 'Ghost Canvas' primero."); return; } const activeBots = botManager.children.filter(b => b.bot.getReadyState()); if (activeBots.length === 0) { this.notify("warning", "Ningún bot activo para la colaboración."); return; } this.notify("info", `Iniciando dibujo colaborativo con ${activeBots.length} bots.`); const totalPixels = ghostCanvas.drawingManager.pixelList.length; const pixelsPerBot = Math.ceil(totalPixels / activeBots.length); for (let i = 0; i < activeBots.length; i++) { const botInterface = activeBots[i]; const botPixels = ghostCanvas.drawingManager.pixelList.slice(i * pixelsPerBot, (i + 1) * pixelsPerBot); if (botPixels.length > 0) { // Temporarily assign a subset of pixels to each bot's internal drawing manager // This is a conceptual assignment, as the GhostCanvas TaskManager handles sending // We will need to modify GhostCanvas.TaskManager to distribute tasks based on available bots. // For this simulation, we will just show it dividing work. this.notify("log", `Bot ${botInterface.getName()} asignado a ${botPixels.length} píxeles.`); // For a true implementation, GhostCanvas.TaskManager.parseAndSendPixel would need // to know which bot is drawing which pixel, or each bot would need its own TaskManager subset. // Here, we'll just indicate the start. The current GhostCanvas TaskManager // already round-robins available pixels among *all* connected bots. // So, this button mainly serves to trigger that mechanism with a collaborative message. } } ghostCanvas.drawingManager.startDrawing(); this.notify("success", "El dibujo colaborativo ha comenzado. ¡Observa a tus bots trabajar!"); }); row.appendChild(coordinateDrawButton); } this.htmlElements.section.appendChild(row); } #row2() { const row = domMake.Row(); { const smartGuessButton = domMake.Button("Bot de Adivinanza (Conceptual)"); smartGuessButton.title = "Un bot intentará adivinar la palabra (simulado)."; smartGuessButton.addEventListener("click", () => { const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (!botManager || botManager.children.length === 0) { this.notify("warning", "Necesitas al menos 1 bot activo para esta función."); return; } const activeBot = botManager.children[0].bot; if (!activeBot || !activeBot.getReadyState()) { this.notify("warning", "El bot seleccionado no está conectado."); return; } const commonWords = ["casa", "flor", "mesa", "sol", "perro", "gato"]; const randomWord = commonWords[Math.floor(Math.random() * commonWords.length)]; activeBot.emit("chatmsg", randomWord); this.notify("info", `Bot ${activeBot.name} intentó adivinar: "${randomWord}" (simulado).`); }); row.appendChild(smartGuessButton); } this.htmlElements.section.appendChild(row); } #row3() { const row = domMake.Row(); { const botPersonalityLabel = domMake.Tree("label", {}, ["Personalidad del Bot:"]); const drawingSpeedInput = domMake.Tree("input", { type: "number", min: 1, max: 100, value: 10, title: "Velocidad de Dibujo (ms/px)" }); const verbositySelect = domMake.Tree("select", { title: "Verbosidad de Mensajes" }); ['Silencioso', 'Normal', 'Charlatán'].forEach(level => { verbositySelect.appendChild(domMake.Tree("option", { value: level }, [level])); }); drawingSpeedInput.addEventListener("change", (e) => { const speed = parseInt(e.target.value); const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager) { botManager.children.forEach(botI => { if (botI.bot) { // Apply conceptual speed to bots' drawing botI.bot.drawingDelay = speed; // Add a new property to bot this.notify("log", `Velocidad de dibujo de ${botI.getName()}: ${speed}ms/px.`); } }); } }); verbositySelect.addEventListener("change", (e) => { const verbosity = e.target.value; const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager) { botManager.children.forEach(botI => { if (botI.bot) { botI.bot.chatVerbosity = verbosity; // New property this.notify("log", `Verbosidad de ${botI.getName()}: ${verbosity}.`); } }); } }); row.appendAll(botPersonalityLabel, drawingSpeedInput, verbositySelect); } this.htmlElements.section.appendChild(row); } } })("QBit"); (function AdvancedTelemetry() { const QBit = globalThis[arguments[0]]; class AdvancedTelemetry extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #playerMetricsContainer; #snapshotContainer; #snapshots = []; #maxSnapshots = 3; constructor() { super("", '<i class="fas fa-chart-line"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); this.#listenToGameEvents(); } #loadInterface() { this.#row1(); // Panel de Control de Jugadores this.#row2(); // Historial Visual de Rondas this.#row3(); // Temas Dinámicos del HUD } #row1() { const row = domMake.Row(); this.#playerMetricsContainer = domMake.Tree("div", { class: "player-metrics-list" }); row.appendChild(domMake.Tree("label", {}, ["Métricas de Jugadores:"])); row.appendChild(this.#playerMetricsContainer); this.htmlElements.section.appendChild(row); this.updatePlayerMetrics(); // Initial update } #row2() { const row = domMake.Row(); const captureSnapshotButton = domMake.Button("Capturar Lienzo"); captureSnapshotButton.title = "Guarda una imagen del lienzo actual."; captureSnapshotButton.addEventListener("click", () => this.captureCanvasSnapshot()); this.#snapshotContainer = domMake.Tree("div", { class: "snapshot-previews icon-list" }); row.appendAll(captureSnapshotButton, this.#snapshotContainer); this.htmlElements.section.appendChild(row); } #row3() { const row = domMake.Row(); const hudColorLabel = domMake.Tree("label", {}, ["Color del HUD:"]); const hudColorInput = domMake.Tree("input", { type: "color", value: "#007bff" }); // Default Bootstrap primary hudColorInput.addEventListener("change", (e) => { const newColor = e.target.value; document.documentElement.style.setProperty('--primary', newColor); document.documentElement.style.setProperty('--success', newColor); // Apply to success as well for consistency this.notify("info", `Color del HUD cambiado a: ${newColor}`); }); row.appendAll(hudColorLabel, hudColorInput); this.htmlElements.section.appendChild(row); } #listenToGameEvents() { // Update player metrics whenever player list changes const playerListElement = document.getElementById("playerlist"); if (playerListElement) { const observer = new MutationObserver(() => this.updatePlayerMetrics()); observer.observe(playerListElement, { childList: true, subtree: true }); } // Listen for chat messages for conceptual "heatmap" if (globalThis._io && globalThis._io.events) { // This is a placeholder as direct binding to _io.events.bc_chatmessage might not always work without a bot. // A more robust solution would be to observe the #chatbox_messages div. 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); }); } updatePlayerActivity(playerName) { // This is a conceptual update. In a real scenario, this would update // a dedicated "activity heatmap" visual. this.notify("debug", ``); const playerElements = document.querySelectorAll(`#playerlist .playerlist-row .playerlist-name a`); playerElements.forEach(el => { if (el.textContent === playerName) { el.closest('.playerlist-row').style.backgroundColor = 'rgba(0, 255, 0, 0.1)'; // Flash green setTimeout(() => { el.closest('.playerlist-row').style.backgroundColor = ''; }, 500); } }); } captureCanvasSnapshot() { const gameCanvas = document.body.querySelector("canvas#canvas"); if (!gameCanvas) { this.notify("error", "Lienzo de juego no encontrado para capturar."); 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(); // Keep only the last N snapshots } this.updateSnapshotPreviews(); this.notify("success", `Instantánea del lienzo capturada: ${timestamp}`); } catch (e) { this.notify("error", `Error al capturar el lienzo: ${e.message}`); 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) { // Create a temporary overlay to display the full snapshot 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()); // Close on click document.body.appendChild(overlay); } } })("QBit"); (function StrokeMaster() { const QBit = globalThis[arguments[0]]; class StrokeMaster extends QBit { static dummy1 = QBit.register(this); static dummy2 = QBit.bind(this, "CubeEngine"); #isPressureActive = false; #isTextureActive = false; #lastMousePos = { x: 0, y: 0 }; #lastTimestamp = 0; constructor() { super("Maestría de Trazo y Realismo", '<i class="fas fa-palette"></i>'); this.#onStartup(); } #onStartup() { this.#loadInterface(); this.#hookDrawingEvents(); } #loadInterface() { this.#row1(); this.#row2(); this.#row3(); this.#row4(); this.#row5(); this.#row6(); } #row1() { const row = domMake.Row(); { const diamondGradientFillButton = domMake.Button("Relleno Degradado Diamante"); diamondGradientFillButton.title = "Activa la herramienta de relleno con degradado en forma de diamante (conceptual)."; diamondGradientFillButton.addEventListener("click", () => { this.notify("info", "Esta función es conceptual. Simula un degradado que se expande desde el centro en forma de diamante."); const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; if (activeBot && activeBot.getReadyState()) { this.notify("log", "Simulando relleno degradado diamante con el bot..."); const centerX = 50; const centerY = 50; const maxDistance = 40; // Max distance from center for the diamond effect const steps = 60; // Number of concentric diamond "layers" for (let i = steps; i >= 0; i--) { // Draw from outside in const ratio = i / steps; // Dark Blue (0, 0, 139) to Bright Gold (255, 215, 0) const r = Math.floor(0 + (255 - 0) * (1 - ratio)); const g = Math.floor(0 + (215 - 0) * (1 - ratio)); const b = Math.floor(139 + (0 - 139) * (1 - ratio)); const color = `rgb(${r},${g},${b})`; const currentDistance = maxDistance * ratio; // Define the corners of the diamond for this step const p1x = centerX; const p1y = centerY - currentDistance; // Top point const p2x = centerX + currentDistance; const p2y = centerY; // Right point const p3x = centerX; const p3y = centerY + currentDistance; // Bottom point const p4x = centerX - currentDistance; const p4y = centerY; // Left point // Draw the diamond outline for this step, effectively filling from outside in activeBot.emit("line", -1, p1x, p1y, p2x, p2y, true, 25, color, false); activeBot.emit("line", -1, p2x, p2y, p3x, p3y, true, 25, color, false); activeBot.emit("line", -1, p3x, p3y, p4x, p4y, true, 25, color, false); activeBot.emit("line", -1, p4x, p4y, p1x, p1y, true, 25, color, false); } this.notify("success", "Degradado diamante conceptual dibujado."); } } }); row.appendChild(diamondGradientFillButton); } this.htmlElements.section.appendChild(row); } #row2() { const row = domMake.Row(); { const radialGradientFillButton = domMake.Button("Relleno Degradado Radial"); radialGradientFillButton.title = "Activa la herramienta de relleno con degradado radial (conceptual)."; radialGradientFillButton.addEventListener("click", () => { this.notify("info", "Esta función es conceptual. En un entorno real, permitiría seleccionar un centro, un radio y colores para un degradado radial."); const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; if (activeBot && activeBot.getReadyState()) { this.notify("log", "Simulando relleno degradado radial con el bot..."); const centerX = 50; const centerY = 50; const maxRadius = 30; const steps = 20; // Number of concentric circles to draw the gradient for (let i = steps; i >= 0; i--) { // Draw from outside in const ratio = i / steps; // Orange (255, 165, 0) to Yellow (255, 255, 0) const r = 255; const g = Math.floor(165 + (255 - 165) * (1 - ratio)); // Green goes from 165 to 255 (more yellow) const b = 0; const color = `rgb(${r},${g},${b})`; const currentRadius = maxRadius * ratio; // Simulate by drawing small circles or many lines // For simplicity, let's draw several points in a circle to approximate const numSegments = 36; // More segments for smoother circle for (let j = 0; j < numSegments; j++) { const angle = (j / numSegments) * 2 * Math.PI; const x = centerX + currentRadius * Math.cos(angle); const y = centerY + currentRadius * Math.sin(angle); activeBot.emit("line", -1, x, y, x + 1, y + 1, true, 25, color, false); // Draw a tiny line as a "point" } } this.notify("success", "Degradado radial conceptual dibujado."); } } }); row.appendChild(radialGradientFillButton); } this.htmlElements.section.appendChild(row); } #row3() { const row = domMake.Row(); { const gradientFillButton = domMake.Button("Relleno Degradado"); gradientFillButton.title = "Activa la herramienta de relleno con degradado lineal (conceptual)."; gradientFillButton.addEventListener("click", () => { this.notify("info", "Esta función es conceptual. En un entorno real, permitiría seleccionar dos puntos y dos colores para un degradado."); // For simulation, we can demonstrate a simple gradient fill using the bot on a small area. const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; if (activeBot && activeBot.getReadyState()) { // Simulate a simple rectangular gradient // This would ideally be a flood fill with gradient, but that's complex // For a simple demo, drawing multiple lines of varying color this.notify("log", "Simulando relleno degradado con el bot..."); const startX = 20, endX = 80; const startY = 20, endY = 80; const steps = 20; // Number of lines to draw the gradient for (let i = 0; i <= steps; i++) { const ratio = i / steps; const r = Math.floor(255 * (1 - ratio)); // Red from 255 to 0 const g = 0; const b = Math.floor(255 * ratio); // Blue from 0 to 255 const color = `rgb(${r},${g},${b})`; const yPos = startY + (endY - startY) * ratio; activeBot.emit("line", -1, startX, yPos, endX, yPos, true, 25, color, false); } this.notify("success", "Degradado conceptual dibujado."); } } }); row.appendChild(gradientFillButton); } this.htmlElements.section.appendChild(row); } #row4() { const row = domMake.Row(); { const verticalLinearGradientButton = domMake.Button("Relleno Degradado Vertical"); verticalLinearGradientButton.title = "Activa la herramienta de relleno con degradado lineal vertical (conceptual)."; verticalLinearGradientButton.addEventListener("click", () => { this.notify("info", "Esta función es conceptual. En un entorno real, permitiría un degradado lineal de arriba a abajo con dos colores."); const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; if (activeBot && activeBot.getReadyState()) { this.notify("log", "Simulando relleno degradado vertical con el bot..."); const startX = 20, endX = 80; const startY = 20, endY = 80; const steps = 20; // Number of lines to draw the gradient for (let i = 0; i <= steps; i++) { const ratio = i / steps; // Purple (128, 0, 128) to Pink (255, 192, 203) const r = Math.floor(128 + (255 - 128) * ratio); const g = Math.floor(0 + (192 - 0) * ratio); const b = Math.floor(128 + (203 - 128) * ratio); const color = `rgb(${r},${g},${b})`; const yPos = startY + (endY - startY) * ratio; activeBot.emit("line", -1, startX, yPos, endX, yPos, true, 25, color, false); } this.notify("success", "Degradado vertical conceptual dibujado."); } } }); row.appendChild(verticalLinearGradientButton); } this.htmlElements.section.appendChild(row); } #row5() { const row = domMake.Row(); { const conicalGradientFillButton = domMake.Button("Relleno Degradado Cónico"); conicalGradientFillButton.title = "Activa la herramienta de relleno con degradado cónico/angular (conceptual)."; conicalGradientFillButton.addEventListener("click", () => { this.notify("info", "Esta función es conceptual. En un entorno real, permitiría seleccionar un centro y un ángulo de inicio para un degradado cónico."); const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; if (activeBot && activeBot.getReadyState()) { this.notify("log", "Simulando relleno degradado cónico con el bot..."); const centerX = 50; const centerY = 50; const maxRadius = 40; // Max radius for the conical effect const steps = 60; // More steps for a smoother conical sweep for (let i = 0; i <= steps; i++) { const ratio = i / steps; // Electric Blue (0, 0, 255) to Vibrant Magenta (255, 0, 255) const r = Math.floor(0 + (255 - 0) * ratio); const g = 0; const b = 255; // Blue remains constant at 255 for the transition const color = `rgb(${r},${g},${b})`; // Calculate angle for this step (full circle) const angle = (ratio * 2 * Math.PI); // From 0 to 2PI (360 degrees) // Draw a line from the center outwards at this angle const x2 = centerX + maxRadius * Math.cos(angle); const y2 = centerY + maxRadius * Math.sin(angle); // To simulate a fill, we'll draw many lines from the center to the edge, // each with the color corresponding to its angle. // Instead of drawing just one line, we'll draw a small wedge for each step // by drawing multiple lines very close to each other. // For simplicity in this simulation, let's draw a line from the center to the edge. // The "fill" effect comes from drawing many such lines very close together. activeBot.emit("line", -1, centerX, centerY, x2, y2, true, 25, color, false); } this.notify("success", "Degradado cónico conceptual dibujado."); } } }); row.appendChild(conicalGradientFillButton); } this.htmlElements.section.appendChild(row); } #row6() { const row = domMake.Row(); { const waveGradientFillButton = domMake.Button("Relleno Degradado Ondulado"); waveGradientFillButton.title = "Activa la herramienta de relleno con degradado ondulado (conceptual)."; waveGradientFillButton.addEventListener("click", () => { this.notify("info", "Esta función es conceptual. Simula un degradado que se propaga en ondas con cambio de color."); const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; if (activeBot && activeBot.getReadyState()) { this.notify("log", "Simulando relleno degradado ondulado con el bot..."); const startX = 10, endX = 90; const startY = 20, endY = 80; const waveAmplitude = 10; // How high/low the waves go const waveFrequency = 0.1; // How many waves across the area const steps = 60; // Number of lines to draw for smoothness for (let i = 0; i <= steps; i++) { const ratio = i / steps; // Cyan (0, 255, 255) to Coral (255, 127, 80) const r = Math.floor(0 + (255 - 0) * ratio); const g = Math.floor(255 + (127 - 255) * ratio); const b = Math.floor(255 + (80 - 255) * ratio); const color = `rgb(${r},${g},${b})`; // Calculate the y-position for the wave // We'll draw horizontal lines that oscillate up and down const yOffset = waveAmplitude * Math.sin(ratio * Math.PI * 2 * waveFrequency); const currentY = startY + (endY - startY) * ratio + yOffset; // To create a "fill" effect with waves, we'll draw a short vertical line // or a very thick horizontal line that follows the wave path. // Let's draw a horizontal line that follows the wave. activeBot.emit("line", -1, startX, currentY, endX, currentY, true, 25, color, false); } this.notify("success", "Degradado ondulado conceptual dibujado."); } } }); row.appendChild(waveGradientFillButton); } this.htmlElements.section.appendChild(row); } #hookDrawingEvents() { const gameCanvas = document.querySelector("#canvas"); if (!gameCanvas) return; let isDrawingLocal = false; let lastX = 0, lastY = 0; let lastDrawThickness = 5; // Default thickness gameCanvas.addEventListener("mousedown", (e) => { isDrawingLocal = true; const rect = gameCanvas.getBoundingClientRect(); lastX = ((e.clientX - rect.left) / rect.width) * 1000; lastY = ((e.clientY - rect.top) / rect.height) * 1000; this.#lastMousePos = { x: e.clientX, y: e.clientY }; this.#lastTimestamp = performance.now(); }); gameCanvas.addEventListener("mousemove", (e) => { if (!isDrawingLocal) return; const rect = gameCanvas.getBoundingClientRect(); const currentX = ((e.clientX - rect.left) / rect.width) * 1000; const currentY = ((e.clientY - rect.top) / rect.height) * 1000; let currentThickness = lastDrawThickness; let currentColor = this.getCurrentBrushColor(); // Assuming a way to get current color // Simulate Pressure Control if (this.#isPressureActive) { const currentTimestamp = performance.now(); const dx = e.clientX - this.#lastMousePos.x; const dy = e.clientY - this.#lastMousePos.y; const distance = Math.sqrt(dx * dx + dy * dy); const timeDelta = currentTimestamp - this.#lastTimestamp; const speed = distance / timeDelta; // Pixels per millisecond // Map speed to thickness (slower = thicker, faster = thinner) // Adjust these values for desired effect const minThickness = 2; const maxThickness = 20; const speedFactor = 0.5; // Adjust how much speed influences thickness currentThickness = maxThickness - (speed * speedFactor); currentThickness = Math.max(minThickness, Math.min(maxThickness, currentThickness)); lastDrawThickness = currentThickness; } // Simulate Texture Brush (apply noise to color) if (this.#isTextureActive) { const originalColorHex = document.querySelector('.drawcontrols-color.active')?.style.backgroundColor || "#000000"; currentColor = this.applyColorNoise(originalColorHex, 10); // 10 is the noise amount } // Send drawing command with adjusted thickness and color const botManager = this.findGlobal("BotClientManager")?.siblings[0]; if (botManager && botManager.children.length > 0) { const activeBot = botManager.children[0].bot; if (activeBot && activeBot.getReadyState()) { activeBot.emit("line", -1, lastX, lastY, currentX, currentY, true, currentThickness, currentColor, false); } } lastX = currentX; lastY = currentY; this.#lastMousePos = { x: e.clientX, y: e.clientY }; this.#lastTimestamp = performance.now(); }); gameCanvas.addEventListener("mouseup", () => { isDrawingLocal = false; lastDrawThickness = 5; // Reset thickness after drawing }); gameCanvas.addEventListener("mouseout", () => { isDrawingLocal = false; lastDrawThickness = 5; // Reset thickness }); } getCurrentBrushColor() { // Attempt to get the currently selected color from Drawaria's UI const colorPicker = document.querySelector('.drawcontrols-color.active'); if (colorPicker) { const rgb = colorPicker.style.backgroundColor; if (rgb) return rgb; } return "#000000"; // Default to black } rgbToHex(rgb) { // Choose a hex color from any RGB color (conceptual). if (rgb.startsWith("rgb")) { const parts = rgb.match(/\d+/g).map(Number); if (parts.length >= 3) { const toHex = (c) => c.toString(16).padStart(2, '0'); return `#${toHex(parts[0])}${toHex(parts[1])}${toHex(parts[2])}`; } } return rgb; // Return as is if not RGB } applyColorNoise(color, noiseAmount) { // Convert RGB string to array, apply noise, convert back to RGB string 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("#")) { // Handle hex colors 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; // Cannot parse, return original } const addNoise = (value) => { const noise = (Math.random() - 0.5) * 2 * noiseAmount; return Math.max(0, Math.min(255, Math.floor(value + noise))); }; const noisyR = addNoise(r); const noisyG = addNoise(g); const noisyB = addNoise(b); return `rgb(${noisyR},${noisyG},${noisyB})`; } } })("QBit"); // --- END OF NEW FUNCTIONALITIES --- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址