// ==UserScript==
// @name Woomy Mockup Browser
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Does some shenanigans to recreate a class tree. SHIFT + M to open the mockup browser.
// @author PowfuArras // Discord: @xskt
// @match *://*.woomy.app/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=woomy.app
// @grant none
// @run-at document-start
// @license FLORRIM DEVELOPER GROUP LICENSE (https://github.com/Florrim/license/blob/main/LICENSE.md)
// ==/UserScript==
(function() {
"use strict";
let socket = null;
const postMessageToUser = (function () {
let intervalID = null;
function post(message) {
socket.dispatchEvent(new MessageEvent("message", {
data: protocol.encode(["displayText", message.length !== 0, message, "#FFFFFF"])
}));
}
return function text(message) {
clearInterval(intervalID);
post(message);
intervalID = setTimeout(function () {
post("");
}, 2500);
};
})();
const mockups = {
count: 0,
entries: new Map(),
getByName(name) {
const values = [...this.entries.values()];
return values[values.findIndex(m => m.name === name)];
}
};
const natives = {
log: console.log,
send: WebSocket.prototype.send
};
WebSocket.prototype.send = function (data) {
natives.send.call(this, data);
WebSocket.prototype.send = natives.send;
socket = this;
}
console.log = (...text) => void 0;
const packets = {
incoming: new Map(),
outgoing: new Map()
};
function addEntryToEntryMap(map, key, value) {
if (!map.has(key)) map.set(key, [value]);
else map.get(key).push(value);
}
const protocol = {
encode: function (message, callback) {
addEntryToEntryMap(packets.outgoing, message[0], message);
return callback(message);
},
decode: function (data, callback) {
const message = callback(data);
addEntryToEntryMap(packets.incoming, message[0], message);
switch (message[0]) {
case "da":
mockups.count = message[3];
break;
case "mu":
mockups.entries.set(message[1], JSON.parse(message[2]));
break;
}
return message;
}
};
for (const key in protocol) {
const callback = protocol[key];
Object.defineProperty(Object.prototype, key, {
get() {
return function (data) {
return callback(data, protocol[key]);
};
},
set(value) {
protocol[key] = value;
}
});
}
Object.defineProperty(Object.prototype, "statsdata", {
get() {
return this._statsdata;
},
set(value) {
this._statsdata = value;
natives.log(this);
return value;
}
});
async function fetchMockups(doFetch) {
if (doFetch) {
postMessageToUser("Preparing for mockups.");
mockups.entries.clear();
mockups.count = 0;
postMessageToUser("Fetching mockup count.");
socket.talk("da");
await new Promise((function () {
let intervalID = null;
return function (resolve) {
intervalID = setInterval(function () {
if (mockups.count !== 0) {
clearInterval(intervalID);
resolve();
}
}, 10);
}
})());
let requested = 0;
const digits = (Math.log10((mockups.count ^ (mockups.countx >> 31)) - (mockups.count >> 31)) | 0) + 1;
postMessageToUser(`Mockups to fetch: ${mockups.count}.`);
let intervalID = setInterval(function () {
postMessageToUser(`Fetching Mockups. Requested: (${`${requested}`.padStart(digits, "0")}/${mockups.count}) Received: (${`${mockups.entries.size}`.padStart(digits, "0")}/${mockups.count})`);
}, 20);
for (let i = 0, length = mockups.count; i < length; i++) {
setTimeout(function() {
socket.talk("mu", i);
requested++;
}, i);
}
await new Promise((function () {
let intervalID = null;
return function (resolve) {
intervalID = setInterval(function () {
if (mockups.entries.size >= mockups.count) {
clearInterval(intervalID);
resolve();
}
}, 10);
}
})());
clearInterval(intervalID);
postMessageToUser(`Mockups fetched, opening mockup browser! Total: ${mockups.count}.`);
} else {
postMessageToUser(`Loading mockup browser without fetching new data.`);
}
const holder = document.createElement("div");
holder.style.position = "absolute";
holder.style.width = "100vw";
holder.style.height = "100vh";
holder.style.backgroundColor = "#00000088";
holder.style.top = "0";
holder.style.display = "flex";
holder.style.alignItems = "center";
holder.style.justifyContent = "center";
holder.style.zIndex = "101";
const body = document.createElement("div");
body.style.padding = "2vmin";
body.style.width = "70vmin";
body.style.height = "70vmin";
body.style.backgroundColor = "#CCCCCC";
body.style.borderRadius = "2vmin";
body.style.boxShadow = "0 0 1vmin #00000066, 0 0 2vmin #00000066";
body.style.overflowY = "scroll";
body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height: 512px; width: 512px;"><g class="" transform="translate(0,0)" style=""><path d="M217 28.098v455.804l142-42.597V70.697zm159.938 26.88l.062 2.327V87h16V55zM119 55v117.27h18V73h62V55zm258 50v16h16v-16zm0 34v236h16V139zm-240 58.727V233H41v46h96v35.273L195.273 256zM244 232c6.627 0 12 10.745 12 24s-5.373 24-12 24-12-10.745-12-24 5.373-24 12-24zM137 339.73h-18V448h18zM377 393v14h16v-14zm0 32v23h16v-23zM32 471v18h167v-18zm290.652 0l-60 18H480v-18z" fill="#ff0000" fill-opacity="1"/></g></svg>`;
const exitButton = body.childNodes[0];
exitButton.style.width = "8vmin";
exitButton.style.height = "8vmin";
exitButton.style.float = "right";
exitButton.style.filter = "drop-shadow(0 0 0.5vmin #00000066)";
exitButton.style.cursor = "pointer";
exitButton.onclick = function () {
holder.remove();
postMessageToUser("Mockup browser closed");
};
holder.appendChild(body);
function buildTree(mockup, parentElement, suffix, isRoot) {
const liElement = document.createElement("li");
const spanElement = document.createElement("span");
liElement.style.fontSize = spanElement.style.fontSize = "2vmin";
spanElement.textContent = `${mockup.name} - ${mockup.index}${suffix}`;
liElement.appendChild(spanElement);
liElement.style.listStyle = "none";
liElement.style.margin = "0";
liElement.style.padding = "0";
if (!isRoot) liElement.style.marginLeft = "2vmin";
if (mockup.upgrades !== undefined) {
let isHidden = true;
spanElement.style.cursor = "pointer";
spanElement.textContent = `◇ ${spanElement.textContent}`;
spanElement.onclick = function () {
if (isHidden) {
spanElement.textContent = `◈ ${spanElement.textContent.slice(2)}`;
const ulElement = document.createElement("ul");
ulElement.style.margin = "0";
ulElement.style.padding = "0";
ulElement.style.marginLeft = "2vmin";
mockup.upgrades.forEach(function (upgrade) {
buildTree(mockups.entries.get(upgrade.index), ulElement, ` - tier ${upgrade.tier}`, false);
});
liElement.appendChild(ulElement);
} else {
spanElement.textContent = `◇ ${spanElement.textContent.slice(2)}`;
liElement.children[1].remove();
}
isHidden = !isHidden;
};
}
parentElement.appendChild(liElement);
}
const ulElement = document.createElement("ul");
ulElement.style.margin = "0";
ulElement.style.padding = "0";
buildTree(mockups.getByName("Basic"), ulElement, "", true);
body.appendChild(ulElement);
holder.appendChild(body);
document.body.appendChild(holder);
body.focus();
};
function doPrompt(text, options = []) {
let choice = -1;
const holder = document.createElement("div");
holder.style.position = "absolute";
holder.style.width = "100vw";
holder.style.height = "100vh";
holder.style.backgroundColor = "#00000088";
holder.style.top = "0";
holder.style.display = "flex";
holder.style.alignItems = "center";
holder.style.justifyContent = "center";
holder.style.zIndex = "102";
const body = document.createElement("div");
body.style.padding = "2vmin";
body.style.width = "60vmin";
body.style.height = "25vmin";
body.style.backgroundColor = "#CCCCCC";
body.style.borderRadius = "2vmin";
body.style.boxShadow = "0 0 1vmin #00000066, 0 0 2vmin #00000066";
body.style.overflowY = "scroll";
body.style.display = "flex";
body.style.flexDirection = "column";
const span = document.createElement("span");
span.textContent = text;
span.style.fontSize = "2vmin";
body.appendChild(span);
const buttonHolder = document.createElement("div");
buttonHolder.style.marginTop = "auto";
buttonHolder.style.display = "flex";
for (let i = 0; i < options.length; i++) {
const option = options[i];
const iiiiiiii = i;
switch (option.type) {
case "button": {
const button = document.createElement("div");
button.style.marginRight = "1vmin";
button.style.padding = "1vmin";
button.style.backgroundColor = option.color;
button.style.display = "flex";
button.style.alignItems = "center";
button.style.justifyContent = "center";
button.style.width = "fit-content";
button.style.height = "3vmin";
button.style.borderRadius = "1vmin";
button.style.cursor = "pointer";
button.style.float = "bottom";
const text = document.createElement("p");
text.textContent = option.text;
text.style.color = "#FFFFFF";
text.style.fontSize = "2vmin";
button.appendChild(text);
buttonHolder.appendChild(button);
button.addEventListener("click", function () {
choice = iiiiiiii;
holder.remove();
});
}; break;
}
}
body.appendChild(buttonHolder);
holder.appendChild(body);
document.body.appendChild(holder);
body.focus();
let intervalID = null;
return new Promise(resolve => {
intervalID = setInterval(function () {
if (choice !== -1) {
resolve(choice);
clearInterval(intervalID);
}
}, 10);
});
}
window.addEventListener("load", function () {
let nextTime = 0;
let hasData = false;
document.getElementById("gameCanvas").addEventListener("keydown", async function (event) {
if (event.shiftKey && event.keyCode === 77) {
const now = performance.now();
if (nextTime > now) {
postMessageToUser(`Please wait 20 seconds between fetching mockups! Time: (${(Math.ceil((nextTime - now) * 0.01) * 0.1) | 0}/20)`);
return;
}
nextTime = performance.now() + 20_000;
if (hasData) {
const option = await doPrompt("Do you want to refetch or use cache?", [{ type: "button", color: "#00AA00", text: "Yes, refetch" }, { type: "button", color: "#AA0000", text: "No, use cache" }]);
fetchMockups(option === 0);
} else {
fetchMockups(true);
hasData = true;
}
}
});
});
window.packeteer = {
protocol: protocol,
packets: packets,
natives: natives,
mockups: mockups,
doPrompt: doPrompt,
get socket() {
return socket;
}
};
})();