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