// ==UserScript==
// @name Gato's PictoTools
// @namespace gato@pawslut.online
// @license GPL-3.0-or-later
// @version 0.4
// @description A compilation of Tools and QoL Improvements for Pict.chat drawing!
// @author creepycats, oeci (Pictobot)
// @match *.pict.chat/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=pict.chat
// @grant none
// @downloadURL https://update.greasyfork.org/scripts/494007/Gato%27s%20PictoTools.user.js
// @updateURL https://update.greasyfork.org/scripts/494007/Gato%27s%20PictoTools.meta.js
// ==/UserScript==
(function () {
'use strict';
let ToolkitStyle = document.createElement("style")
ToolkitStyle.textContent = `
#ToolkitHolder {
position: absolute;
top: 50%;
left: 0%;
transform:translateY(-50%);
width: 300px;
background-image: url("../images/intro_bg.png");
z-index:150;
}
#ToolkitHolder > * {
font-family:nds;
color: #242424;
text-shadow: 0.15em 0.15em #FFF;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
user-select: none; /* Standard syntax */
}
#OnlineUsersHolder {
position: absolute;
top: 50%;
right: 0%;
transform:translateY(-50%);
width: 300px;
background-image: url("../images/intro_bg.png");
z-index:150;
}
#OnlineUsersHolder > * {
font-family:nds;
color: #242424;
text-shadow: 0.15em 0.15em #FFF;
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
user-select: none; /* Standard syntax */
}
.Enabled {
color: #1c9e05
}
.Disabled {
color: #9e0505
}
.ToolkitSection {
display:flex-box;
flex-flow:column;
background-image: url("../images/bottom_screen.png");
width: calc(100% - 18px);
padding: 5px;
margin: 4px;
margin-bottom: 8px;
outline: 2px solid #242424;
}
.ToolkitSection:last-child {
margin-bottom: 4px;
}
.slider {
-webkit-appearance: none;
height: 10px;
background: #fff;
outline: 2px solid #0a0a0a;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 15px;
height: 20px;
background: #0a0a0a;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 15px;
height: 20px;
background: #0a0a0a;
cursor: pointer;
}
@keyframes bgColor {
0% { background-color: red;}
12.5% { background-color: #ff00a8;}
25% { background-color: #c400ff;}
37.5% { background-color: #00d3ff;}
50% { background-color: #00ffaf;}
62.5% { background-color: #1aff00;}
75% { background-color: #dbff00;}
87.5% { background-color: #ffc000;}
100% { background-color: red;}
}
/* Dark Mode Support */
html.dark #DeleteSavedMessage, html.dark #RemoveOnionSkin, html.dark #BotStatus, html.dark #PlayersSection > div > div { filter: invert(100%); }
`
document.body.appendChild(ToolkitStyle);
let ToolkitUIHolder = document.createElement("div")
ToolkitUIHolder.id = "ToolkitHolder"
document.body.appendChild(ToolkitUIHolder)
ToolkitUIHolder.innerHTML = `
`
let OnlineUsersHolder = document.createElement("div")
OnlineUsersHolder.id = "OnlineUsersHolder"
document.body.appendChild(OnlineUsersHolder)
OnlineUsersHolder.innerHTML = `
`
let PlayersSection = document.getElementById("PlayersSection")
// Undo/Redo Feature
let LastDrawingState = false
let RedoList = []
let UndoPoints = []
let UndoSplicePos = 0
document.addEventListener("keydown", (event) => {
if (event.key == "z" && event.ctrlKey) {
if (event.shiftKey)
else
UndoDrawing()
}
if (event.key == "y" && event.ctrlKey) {
RedoDrawing()
}
});
function GetUndoSplicePos() {
let DrawHistoryClone = JSON.parse(JSON.stringify(drawHistory));
DrawHistoryClone.splice(-1)
UndoSplicePos = DrawHistoryClone.findLastIndex(x => x.type == 1 && !x.i) + 1
console.log(UndoSplicePos)
}
function UndoDrawing() {
if (UndoPoints.length > 0) {
RedoList.unshift(drawHistory.splice(UndoPoints.shift()))
window.RedoList = RedoList
drawDrawing();
}
}
function RedoDrawing() {
if (RedoList.length > 0) {
drawHistory = drawHistory.concat(RedoList.shift())
window.RedoList = RedoList
GetUndoSplicePos()
UndoPoints.unshift(UndoSplicePos)
drawDrawing();
}
}
window.UndoDrawing = UndoDrawing
window.RedoDrawing = RedoDrawing
window.RedoList = RedoList
window.UndoPoints = UndoPoints
// Custom Colors
let colmult = 12
window.overrideRGB = false
window.targetRGB = 0
window.rgbClock = 0
let PenColorPreview = document.getElementById("PenColorPreview")
let PenColorSlider = document.getElementById("PenColorSlider")
function UpdateCustomPenColor() {
window.targetRGB = colmult * (PenColorSlider.value - 1)
if(window.targetRGB < 0) {
window.overrideRGB = false
PenColorPreview.style.backgroundColor = "#FF0000"
PenColorPreview.style.animation = "bgColor 15s infinite linear reverse"
} else {
window.overrideRGB = true
PenColorPreview.style.backgroundColor = `hsl(${window.targetRGB}deg 100% 50%)`
PenColorPreview.style.animation = ""
}
}
PenColorSlider.oninput = UpdateCustomPenColor
PenColorSlider.onchange = UpdateCustomPenColor
// Custom Tools
let CurrentCustomTool = null
let CustomToolDrawHistory = []
let CustomToolButtons = document.getElementById("CustomToolButtons")
window.SetCustomTool = (toolName, clickedElem) => {
if (CurrentCustomTool && CurrentCustomTool == toolName) {
CurrentCustomTool = null
}
else {
CurrentCustomTool = toolName
}
for (let i = 0; i < CustomToolButtons.children.length; i++) {
CustomToolButtons.children[i].style.opacity = 1
}
if (CurrentCustomTool && clickedElem) {
clickedElem.style.opacity = 0.5
}
}
function generateEllipsePoints(topLeft, bottomRight, n) {
let points = [];
let a = Math.abs(bottomRight[0] - topLeft[0]) / 2;
let b = Math.abs(bottomRight[1] - topLeft[1]) / 2;
let x = topLeft[0] + a;
let y = topLeft[1] + b;
for (let i = 0; i < n; i++) {
let theta = 2 * Math.PI * i / n;
let pointX = x + a * Math.cos(theta);
let pointY = y + b * Math.sin(theta);
points.push([pointX.toFixed(2), pointY.toFixed(2)]);
}
return points;
}
document.getElementById("MoveDrawingButton").onmousedown = () => {
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
for (let i = 0; i < drawHistory.length; i++) {
if (!(drawHistory[i].x == 0 && drawHistory[i].y == 0)) {
drawHistory[i].x = drawHistory[i].x + delta[0]
drawHistory[i].y = drawHistory[i].y + delta[1]
}
}
drawDrawing()
}, 10);
}
let DrawingInterval = setInterval(function () {
if (!playerData.name.includes("[✬]")) {
playerData.name = playerData.name + " [✬]"
}
if (isDrawing != LastDrawingState) {
LastDrawingState = isDrawing
if (LastDrawingState == true) {
// Mouse Down Is Drawing
RedoList = []
} else {
GetUndoSplicePos()
UndoPoints.unshift(UndoSplicePos)
}
}
if (!isDrawing) {
CustomToolDrawHistory = drawHistory
}
let lastItem = null
if (CurrentCustomTool != null) {
if (CurrentCustomTool == "line") {
if (!lastItem) {
lastItem = drawHistory[drawHistory.length - 1]
if (lastItem.type != 0)
lastItem = null
}
if (isDrawing) {
if (lastItem != null) {
drawHistory.splice(CustomToolDrawHistory.length)
drawHistory.push({ x: mousePos.x, y: mousePos.y, type: 0 })
}
drawDrawing();
} else {
lastItem = null
}
}
if (CurrentCustomTool == "square") {
if (!lastItem) {
lastItem = drawHistory[CustomToolDrawHistory.length - 1]
if (lastItem.type != 2)
lastItem = null
}
if (isDrawing) {
if (lastItem != null) {
drawHistory.splice(CustomToolDrawHistory.length)
drawHistory.push({ x: lastItem.x, y: mousePos.y, type: 0, i: true })
drawHistory.push({ x: mousePos.x, y: mousePos.y, type: 0, i: true })
drawHistory.push({ x: mousePos.x, y: lastItem.y, type: 0, i: true })
drawHistory.push({ x: lastItem.x, y: lastItem.y, type: 0, i: true })
drawHistory.push({ x: 0, y: 0, type: 1, i: true })
}
drawDrawing();
} else {
lastItem = null
}
}
if (CurrentCustomTool == "circle") {
if (!lastItem) {
lastItem = drawHistory[CustomToolDrawHistory.length - 1]
if (lastItem.type != 2)
lastItem = null
}
if (isDrawing) {
if (lastItem != null) {
drawHistory.splice(CustomToolDrawHistory.length)
let TopLeft = [(lastItem.x < mousePos.x ? lastItem.x : mousePos.x), (lastItem.y < mousePos.y ? lastItem.y : mousePos.y)]
let BottomRight = [(lastItem.x > mousePos.x ? lastItem.x : mousePos.x), (lastItem.y > mousePos.y ? lastItem.y : mousePos.y)]
let CircleCoords = generateEllipsePoints(TopLeft, BottomRight, 32)
drawHistory.push({ x: 0, y: 0, type: 1, i: true })
drawHistory.push({ x: Number(CircleCoords[0][0]), y: Number(CircleCoords[0][1]), type: 2, i: true })
for (let i = 1; i < CircleCoords.length; i++) {
drawHistory.push({ x: Number(CircleCoords[i][0]), y: Number(CircleCoords[i][1]), type: 0, i: true })
}
drawHistory.push({ x: Number(CircleCoords[0][0]), y: Number(CircleCoords[0][1]), type: 0, i: true })
drawHistory.push({ x: 0, y: 0, type: 1, i: true })
}
drawDrawing();
} else {
lastItem = null
}
}
} else {
let newHistory = []
let isRgb = false
window.rgbClock = 0
for (let i = 0; i < drawHistory.length; i++) {
let action = drawHistory[i];
newHistory.push(action)
let newInd = newHistory.length - 1
switch (action.type) {
case 0: {
//pc_sprites.drawing.lineTo(action.x, action.y);
if (isRgb) {
window.rgbClock = (window.rgbClock + 12) % 360
if (!action.m && window.overrideRGB) {
newHistory.push({ x: 0, y: 0, type: 1, m: true, i: true })
newHistory.push({ x: 0, y: 1000, type: 2, m: true, i: true })
while ((window.targetRGB + (12 * 29)) % 360 != window.rgbClock) {
window.rgbClock = (window.rgbClock + 12) % 360
console.log(window.rgbClock)
newHistory.push({ x: 0, y: 1000, type: 0, m: true, i: true })
}
newHistory.push({ x: 0, y: 0, type: 1, m: true, i: true })
newHistory.push({ x: action.x, y: action.y, type: 2, m: true, i: true })
newHistory.push({ x: action.x, y: action.y, type: 0, m: true, i: true })
}//pc_sprites.drawing.rainbowDeg = (pc_sprites.drawing.rainbowDeg + 12) % 360;
}
break;
}
case 6: {
isRgb = false
break;
}
case 7: {
isRgb = true
break;
}
}
newHistory[newInd].m = true
}
drawHistory = newHistory
}
}, 1)
// WebSocket Hooker
let websocketHookInterval = setInterval(function () {
if (websocket) {
console.log("%c Hooked Websocket", 'font-size: 18px; color: #4feaff')
websocket.onopen = new Proxy(websocket.onopen, {
apply(target, thisArgs, args) {
let res = null;
if (WebsocketOpen(args[0]))
res = Reflect.apply(...arguments)
return res;
}
});
websocket.onmessage = new Proxy(websocket.onmessage, {
apply(target, thisArgs, args) {
let res = null;
let canDo = WebsocketMessage(args[0])
if (canDo[0]) {
args[0] = canDo[1]
console.log(args[0])
res = Reflect.apply(...arguments)
}
return res;
}
});
websocket.send = new Proxy(websocket.send, {
apply(target, thisArgs, args) {
let res = null;
let canDo = WebsocketSend(args[0])
if (canDo[0]) {
args[0] = canDo[1]
res = Reflect.apply(...arguments)
}
return res;
}
});
clearInterval(websocketHookInterval);
}
}, 100)
function InsensitiveSort(a, b) {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
}
function AdjustColor(color, amount) {
return '#' + color.replace(/^#/, '').replace(/../g, color => ('0' + Math.min(255, Math.max(0, parseInt(color, 16) + amount)).toString(16)).substr(-2));
}
function WebsocketOpen(event) {
console.log("%c Websocket Connected", 'font-size: 18px; color: #4feaff')
console.log(event)
return true;
}
let awaitingList = false
let playerList = []
let blockedPlayers = []
function WebsocketMessage(event) {
try {
if (event.data !== "ping") {
let oldScrollPos;
let obj = JSON.parse(event.data);
console.log(obj)
switch (obj.type) {
case "sv_roomData": {
console.log("FETCHING")
awaitingList = true
websocket.send(JSON.stringify({
"type": "cl_sendMessage",
"message": {
"player": playerData,
"drawing": [
{
"x": 0,
"y": 0,
"type": 3
}
],
"textboxes": [
{
"text": "!list",
"x": 113,
"y": 211
}
],
"lines": 1
}
}))
break;
}
case "sv_receivedMessage": {
// Account for Player List Message
if (obj.message.player.name == "[SERVER]") {
let tboxes = obj.message.textboxes
let fulltext = ""
for (let i = 0; i < tboxes.length; i++) {
fulltext = fulltext + tboxes[i].text
}
if (awaitingList && !fulltext.startsWith("Your room code")) {
awaitingList = false
let splitList = fulltext.split(" ; ")
playerList = []
for (let i = 0; i < splitList.length; i++) {
playerList.push({
name: splitList[i],
color: -1
})
}
playerList = playerList.sort(InsensitiveSort)
RebuildActiveUsers()
return [false, event];
} else {
if (fulltext.startsWith("Unblocked: ")) {
// UNBLOCK USER
let userToBlock = fulltext.replace("Unblocked: ", "")
let ind = blockedPlayers.indexOf(userToBlock)
if (ind > -1) {
blockedPlayers.splice(ind, 1)
}
}
else if (fulltext.startsWith("Blocked: ")) {
// BLOCK USER
let userToBlock = fulltext.replace("Blocked: ", "")
if (!blockedPlayers.includes(userToBlock)) {
blockedPlayers.push(userToBlock)
}
}
}
} else {
let ind = playerList.findIndex(x => x.name == obj.message.player.name)
if (ind > -1) {
playerList[ind].color = obj.message.player.color
}
if (blockedPlayers.includes(obj.message.player.name))
return [false, event];
}
RebuildActiveUsers()
// Badge
if (obj.gato.badge) {
obj.message.player.name = obj.message.player.name + " [✬]"
}
break;
}
case "sv_playerLeft": {
let ind = playerList.findIndex(x => x.name == obj.player.name)
if (ind > -1) {
playerList.splice(ind, 1)
}
playerList = playerList.sort(InsensitiveSort)
RebuildActiveUsers()
break;
}
case "sv_playerJoined": {
playerList.push({
name: obj.player.name,
color: obj.player.color
})
playerList = playerList.sort(InsensitiveSort)
RebuildActiveUsers()
break;
}
default: {
}
}
event = new MessageEvent("message", {
data: JSON.stringify(obj)
})
return [true, event];
}
} catch (err) {
}
return [true, event];
}
function WebsocketSend(sentContent) {
try {
let obj = JSON.parse(sentContent)
console.log(obj)
if (obj.type == "cl_leaveRoom") {
playerList = []
RebuildActiveUsers()
}
if (obj.type == "cl_sendMessage") {
let tboxes = obj.message.textboxes
let fulltext = ""
for (let i = 0; i < tboxes.length; i++) {
fulltext = fulltext + tboxes[i].text
}
if (fulltext.startsWith("!block") || fulltext.startsWith("!unblock") || fulltext.startsWith("!ignore") || fulltext.startsWith("!unignore")) {
console.log("Intercept Block")
let userToBlock = fulltext.replace("!block ", "").replace("!unblock ", "").replace("!ignore ", "").replace("!unignore ", "")
let ind = blockedPlayers.indexOf(userToBlock)
if (ind > -1) {
blockedPlayers.splice(ind, 1)
ReceiveFakeMessage({
"drawing": [
{
"x": 0,
"y": 0,
"type": 3
}
],
"textboxes": [
{
"x": 113,
"y": 211,
"text": "Unblocked: " + userToBlock
}
],
"lines": 1,
"player": {
"name": "[PICTOTOOLS]",
"color": 51356
}
})
} else {
blockedPlayers.push(userToBlock)
ReceiveFakeMessage({
"drawing": [
{
"x": 0,
"y": 0,
"type": 3
}
],
"textboxes": [
{
"x": 113,
"y": 211,
"text": "Blocked: " + userToBlock
}
],
"lines": 1,
"player": {
"name": "[PICTOTOOLS]",
"color": 51356
}
})
}
return [false, JSON.stringify(obj)];
}
obj.gato = {
"badge": true
}
return [true, JSON.stringify(obj)];
}
} catch (err) {
}
return [true, sentContent];
}
// Fake Messages
function ReceiveFakeMessage(message) {
websocket.onmessage({
"data": JSON.stringify({
"type": "sv_receivedMessage",
"message": message
})
})
}
// Player List
function ToggleBlock(userToBlock) {
websocket.send(JSON.stringify({
"type": "cl_sendMessage",
"message": {
"player": playerData,
"drawing": [
{
"x": 0,
"y": 0,
"type": 3
}
],
"textboxes": [
{
"text": "!block " + userToBlock,
"x": 113,
"y": 211
}
],
"lines": 1
}
}))
}
function RebuildActiveUsers() {
PlayersSection.innerHTML = `
Total Online
${playerList.length}/16
`;
playerList.sort(InsensitiveSort)
for (let i = 0; i < playerList.length; i++) {
let newUserHolder = document.createElement("div")
newUserHolder.style = "display:flex; width:100%; margin-top: 4px"
PlayersSection.appendChild(newUserHolder)
if (playerList[i].name == playerData.name || playerList[i].name == playerData.name.replace(" [✬]", "")) {
playerList[i].color = playerData.color
}
let newUserText = document.createElement("div")
let lightCol = new String(playerList[i].color.toString(16)).toUpperCase().padStart(6, '0')
let darkCol = AdjustColor(lightCol, -50)
newUserText.style = `font-size:18px;margin-left:auto;margin-right:0;color:${(playerList[i].color != -1 ? `#${lightCol}` : "auto")};text-shadow:${(playerList[i].color != -1 ? ` 0.1em 0.1em ${darkCol}` : "auto")};`
newUserText.textContent = playerList[i].name
if (blockedPlayers.includes(playerList[i].name)) {
newUserText.innerHTML = "" + playerList[i].name + ""
newUserText.style.opacity = 0.3
}
if (playerList[i].name == playerData.name || playerList[i].name == playerData.name.replace(" [✬]", "")) {
newUserText.innerHTML = `(YOU) ` + playerList[i].name
newUserText.style.textShadow = "0.1em 0.1em #444"
} else {
newUserText.style.cursor = "pointer"
newUserText.onclick = () => {
ToggleBlock(playerList[i].name)
}
}
newUserHolder.appendChild(newUserText)
}
}
// Canvas Enabler
let checkIntervalCanvas = setInterval(function () {
if (document.getElementsByTagName("canvas").length > 0) {
createBotGUI();
BotStatus.className = "Enabled"
BotStatus.textContent = "Enabled"
console.log("%c Started Pictobot", 'font-size: 18px; color: #4feaff')
clearInterval(checkIntervalCanvas);
}
}, 100)
let inputBlockInterval = setInterval(function () {
if (window.onkeydown) {
window.onkeydown = new Proxy(window.onkeydown, {
apply(target, thisArgs, args) {
if (document.activeElement.tagName.toLowerCase() == "input") {
return;
}
return Reflect.apply(...arguments);
}
});
clearInterval(inputBlockInterval);
}
}, 100)
// Save Load Messages
let SavedMessages = JSON.parse(localStorage.getItem("SavedMessages") || "[]");
let SaveNewMessage = document.getElementById("SaveNewMessage")
let SaveMessageName = document.getElementById("SaveMessageName")
let DeleteSavedMessage = document.getElementById("DeleteSavedMessage")
let SavedMessagesDropdown = document.getElementById("SavedMessagesDropdown")
let LoadMessageButton = document.getElementById("LoadMessageButton")
function UpdateSavedMessages() {
SavedMessages = JSON.parse(localStorage.getItem("SavedMessages") || "[]");
console.log(SavedMessages)
SavedMessagesDropdown.innerHTML = ``
for (let i = 0; i < SavedMessages.length; i++) {
let newSelect = document.createElement("option")
newSelect.value = i
newSelect.textContent = SavedMessages[i].name
SavedMessagesDropdown.appendChild(newSelect)
}
}
UpdateSavedMessages()
LoadMessageButton.onclick = () => {
let SelValue = parseInt(SavedMessagesDropdown.value)
if (SelValue >= 0) {
let ClonedPost = SavedMessages[SelValue]
console.log(ClonedPost)
drawHistory = []
for (let i = 0; i < ClonedPost.drawing.length; i++) {
let value = {
x: ClonedPost.drawing[i].x + (pc_sprites.box.x / SCALE) * 0,
y: ClonedPost.drawing[i].y + (pc_sprites.box.y / SCALE) * 0,
type: ClonedPost.drawing[i].type
};
drawHistory.push(value);
}
for (let i = 0; i < ClonedPost.textboxes.length; i++) {
let txt = ClonedPost.textboxes[i];
let tb = new PIXI.BitmapText(txt.text, { font: '10px NintendoDSBIOS', align: 'center', tint: 0 });
tb.x = txt.x;
tb.y = txt.y;
pc_sprites.textboxes.push(tb);
app.stage.addChild(pc_sprites.textboxes[pc_sprites.textboxes.length - 1]);
}
selectedTextbox = 0;
drawHistory.push({ x: 0, y: 0, type: 3 });
drawHistory.push({ x: 0, y: 0, type: 5 });
drawHistory = cleanupDrawing(drawHistory)
scaleStage();
drawDrawing();
}
}
DeleteSavedMessage.onclick = () => {
let SelValue = parseInt(SavedMessagesDropdown.value)
if (SelValue >= 0) {
SavedMessages.splice(SelValue, 1);
localStorage.setItem("SavedMessages", JSON.stringify(SavedMessages))
UpdateSavedMessages()
}
}
SaveNewMessage.onclick = () => {
console.log("%c Saving New Message", 'font-size: 18px; color: #4feaff')
if (SaveMessageName.value.length > 0) {
// Make sure no other saved message shares this name
for (let i = 0; i < SavedMessages.length; i++) {
if (SavedMessages[i].name.toLowerCase() == SaveMessageName.value.toLowerCase()) {
return;
}
}
// No Dupes? Good we fuckin tonite :33333
let textBoxes = []
for (let i = 0; i < pc_sprites.textboxes.length; i++) {
if (pc_sprites.textboxes[i].text !== "") {
let tbObj = {
text: pc_sprites.textboxes[i].text,
x: pc_sprites.textboxes[i].x / SCALE,
y: pc_sprites.textboxes[i].y / SCALE
};
textBoxes.push(tbObj);
}
}
SavedMessages.push({
name: SaveMessageName.value,
drawing: cleanupDrawing(drawHistory),
textboxes: textBoxes
});
localStorage.setItem("SavedMessages", JSON.stringify(SavedMessages))
UpdateSavedMessages()
}
}
// Onion-skin
let OnionSkinFileUpload = document.getElementById("onionSkinUpload")
let RemoveOnionSkin = document.getElementById("RemoveOnionSkin")
let OnionOpacityText = document.getElementById("OnionOpacity")
let CurrentOnionSkin = null;
let CurrentOnionCanvas = null;
let OnionOpacity = 0.5
let OnionX = 0;
let OnionY = 0;
let OnionScale = 1;
let OnionMoveButton = document.getElementById("OnionMoveButton")
let OnionScaleButton = document.getElementById("OnionScaleButton")
let OnionOpacButton = document.getElementById("OnionOpacButton")
RemoveOnionSkin.onclick = () => {
if (CurrentOnionCanvas) {
CurrentOnionSkin = null;
CurrentOnionCanvas.remove();
CurrentOnionCanvas = null;
};
}
OnionMoveButton.onmousedown = () => {
if (CurrentOnionCanvas == null) return;
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
OnionX += delta[0];
OnionY -= delta[1];
moveOnionSkin()
}, 100);
}
OnionScaleButton.onmousedown = () => {
if (CurrentOnionCanvas == null) return;
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
let scaleFactor = 1 - delta[1] / 80;
if (scaleFactor < 0.75) scaleFactor = 0.75;
if (scaleFactor > 1.25) scaleFactor = 1.25;
OnionScale *= scaleFactor
if (OnionScale < 0.05) OnionScale = 0.05;
moveOnionSkin()
}, 100);
}
OnionOpacButton.onmousedown = () => {
if (CurrentOnionCanvas == null) return;
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
let scaleFactor = 1 - delta[1] / 20;
if (scaleFactor < 0.75) scaleFactor = 0.75;
if (scaleFactor > 1.25) scaleFactor = 1.25;
OnionOpacity *= scaleFactor
if (OnionOpacity < 0) OnionOpacity = 0;
if (OnionOpacity > 1) OnionOpacity = 1;
moveOnionSkin()
}, 100);
}
OnionSkinFileUpload.onchange = function (event) {
var files = event.target.files;
if (files && files.length > 0) {
var reader = new FileReader();
reader.readAsDataURL(files[0])
reader.onload = function (e) {
CurrentOnionSkin = new Image();
CurrentOnionSkin.onload = function () {
console.log("%c Changed Onion-skin", 'font-size: 18px; color: #4feaff')
drawOnionSkin();
}
CurrentOnionSkin.src = e.target.result;
}
}
};
function moveOnionSkin() {
if (CurrentOnionCanvas) {
CurrentOnionCanvas.style.bottom = OnionY + "px"
CurrentOnionCanvas.style.left = `calc(50% + ${OnionX}px)`
CurrentOnionCanvas.style.opacity = OnionOpacity
CurrentOnionCanvas.style.transform = `translate(-50%, 50%) scale(${OnionScale}, ${OnionScale})`
OnionOpacityText.textContent = Math.round(OnionOpacity * 1000) / 1000
}
}
function drawOnionSkin() {
if (!CurrentOnionSkin)
return;
if (CurrentOnionCanvas) {
CurrentOnionCanvas.remove()
}
CurrentOnionCanvas = document.createElement("canvas");
CurrentOnionCanvas.width = CurrentOnionSkin.width;
CurrentOnionCanvas.height = CurrentOnionSkin.height;
OnionScale = 1
OnionX = 0;
OnionY = CurrentOnionCanvas.height / 2;
let ctx = CurrentOnionCanvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.drawImage(CurrentOnionSkin, 0, 0, CurrentOnionCanvas.width, CurrentOnionCanvas.height);
document.getElementById("root").appendChild(CurrentOnionCanvas)
CurrentOnionCanvas.style = "position: absolute;pointer-events:none;z-index:50;"
moveOnionSkin()
}
// Port of Pictobot
//
// made by oeci, not me!!! I just added Userscript support!!!
let PictobotSection = document.getElementById("PictobotSection")
let BotStatus = document.getElementById("BotStatus")
let BrightnessAmount = document.getElementById("BrightnessAmount")
let ContrastAmount = document.getElementById("ContrastAmount")
let pixelX = null;
let pixelY = null;
let pixelWidth = null;
let pixelHeight = null;
let powerFactor = 1;
let brightness = 1;
let pixels = 3000;
function createBotGUI() {
let move = document.createElement("button");
let scale = document.createElement("button");
let bright = document.createElement("button");
let powUp = document.createElement("button");
let powDown = document.createElement("button");
let pixelCount = document.createElement("button");
move.innerHTML = "Move";
scale.innerHTML = "Scale";
bright.innerHTML = "Gamma";
powUp.innerHTML = "Contrast Up";
powDown.innerHTML = "Contrast Down";
pixelCount.innerHTML = "Resolution: " + pixels;
let parent = document.createElement("div");
let buttons = [scale, bright, move, powUp, powDown, pixelCount];
let ref = document.getElementsByTagName("canvas")[0].getBoundingClientRect();
let fontSize = 15;
let widthRef = ref.width;
let buttonWidth = widthRef / 7;
for (var i = 0; i < buttons.length; i++) {
parent.appendChild(buttons[i]);
buttons[i].style.width = "33%"
buttons[i].style.height = buttonWidth + "px"
buttons[i].style.fontSize = fontSize + "px"
buttons[i].style.verticalAlign = "top";
buttons[i].style.background = "url('../images/intro_bg.png') no-repeat";
buttons[i].style.backgroundSize = "750px"
buttons[i].style.cursor = "pointer"
buttons[i].style.fontFamily = "nds"
}
parent.style.display = "flex";
parent.style.zIndex = "100";
parent.style.width = "100%";
parent.style.flexDirection = "row";
parent.style.marginTop = "4px";
parent.style.flexWrap = "wrap";
powUp.onmousedown = () => {
if (theimage == null) return;
powerFactor++;
powUptext()
doDrawing();
}
powDown.onmousedown = () => {
if (theimage == null) return;
powerFactor--;
if (powerFactor < 1) powerFactor = 1
powUptext()
doDrawing();
}
function powUptext() {
ContrastAmount.textContent = powerFactor;
}
move.onmousedown = () => {
if (theimage == null) return;
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
pixelX += delta[0];
pixelY += delta[1];
doDrawing()
}, 100);
}
scale.onmousedown = () => {
if (theimage == null) return;
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
pixelWidth += delta[1];
pixelHeight = pixelWidth * theimage.width / theimage.height;
doDrawing()
}, 100);
}
bright.onmousedown = () => {
if (theimage == null) return;
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
let scaleFactor = 1 + delta[1] / 20;
if (scaleFactor < 0.75) scaleFactor = 0.75;
if (scaleFactor > 1.25) scaleFactor = 1.25;
brightness *= scaleFactor;
if (brightness < 0.05) brightness = 0.05;
BrightnessAmount.textContent = Math.round(((1 - brightness) + 1) * 1000) / 1000
doDrawing()
}, 100);
}
pixelCount.onmousedown = () => {
if (theimage == null) return;
let previousPos = [mouseX, mouseY];
let interval = setInterval(() => {
if (mousedown == false) {
clearInterval(interval);
return;
}
let newPos = [mouseX, mouseY];
let delta = [newPos[0] - previousPos[0], newPos[1] - previousPos[1]];
previousPos = newPos;
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
delta[0] /= pos.width;
delta[1] /= pos.height;
let xRate = (boundingPixel[2] - boundingPixel[0]) / (bounding[2] - bounding[0])
let yRate = (boundingPixel[3] - boundingPixel[1]) / (bounding[3] - bounding[1])
delta[0] *= xRate;
delta[1] *= yRate;
pixels += delta[1] * 50;
pixels = Math.floor(pixels);
if (pixels < 100) pixels = 100;
pixelCount.innerHTML = "Resolution: " + pixels;
doDrawing()
}, 100);
}
PictobotSection.appendChild(parent)
}
function doDrawing() {
if (theimage == null) return;
drawImage(pixelX, pixelY, pixelWidth, pixelHeight, theimage, brightness, powerFactor, pixels);
drawDrawing();
}
let pixelPrecision = 1000;
let drawImage = (posx, posy, poswidth, posheight, img, brightness, powerFactor, maxPixels) => {
drawHistory = [{
x: 0,
y: 0,
type: 3
}]
let canvas = document.createElement("canvas");
let pixelscale = Math.sqrt(maxPixels / (img.width * img.height))
let width = Math.floor(img.width * pixelscale);
let height = Math.floor(img.height * pixelscale);
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let data = imageData.data;
let scale = poswidth / width;
for (var y = 0; y < canvas.height; y++) {
for (var x = 0; x < canvas.width; x++) {
let index = (y * canvas.width + x) * 4;
if (data[index + 3] == 0) continue;
let darkness = (data[index] + data[index + 1] + data[index + 2]) / 3
darkness = -darkness + 255;
let length = darkness / 255;
length = length ** powerFactor;
length *= brightness;
if (length > 1) length = 1;
let PoffsetX = (1 - length) / 2;
let PoffsetX2 = 1 - (1 - length) / 2
let PoffsetY = PoffsetX;
let PoffsetY2 = PoffsetX2;
if ((x + y) % 2 == 1) [PoffsetX, PoffsetX2] = [PoffsetX2, PoffsetX];
let p1 = {
x: (x + PoffsetX) * scale + posx,
y: (y + PoffsetY) * scale + posy,
type: 2
};
let p2 = {
x: (x + PoffsetX2) * scale + posx,
y: (y + PoffsetY2) * scale + posy,
type: 0
}
p1.x = Math.floor(p1.x * pixelPrecision) / pixelPrecision;
p1.y = Math.floor(p1.y * pixelPrecision) / pixelPrecision;
p2.x = Math.floor(p2.x * pixelPrecision) / pixelPrecision;
p2.y = Math.floor(p2.y * pixelPrecision) / pixelPrecision;
drawHistory.push(p1)
drawHistory.push(p2)
}
}
}
let mousedown = false;
let theimage = null;
window.addEventListener("dragover", function (e) {
e = e || event;
e.preventDefault();
});
let mouseX = 0;
let mouseY = 0;
window.addEventListener("drop", function (e) {
e = e || event;
e.preventDefault();
for (var i = 0; i < e.dataTransfer.items.length; i++) {
let item = e.dataTransfer.items[i];
if (item.kind == "file") {
var blob = item.getAsFile();
var reader = new FileReader();
reader.onload = function (event) {
theimage = new Image();
theimage.src = event.target.result;
theimage.crossOrigin = "Anonymous";
theimage.style.position = "absolute";
theimage.style.width = "400px";
theimage.onload = () => {
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
let dX = (mouseX - pos.x) / pos.width;
let dY = (mouseY - pos.y) / pos.height;
pixelX = 30
pixelY = 210;
let width = parseInt(theimage.style.width) / pos.width;
pixelWidth = 100;
pixelHeight = Math.floor(theimage.height * pixelWidth / theimage.width);
doDrawing()
}
};
reader.readAsDataURL(blob);
}
}
}, false);
window.addEventListener("paste", function (e) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (index in items) {
var item = items[index];
if (item.kind === 'file') {
var blob = item.getAsFile();
var reader = new FileReader();
reader.onload = function (event) {
theimage = new Image();
theimage.src = event.target.result;
theimage.crossOrigin = "Anonymous";
theimage.style.position = "absolute";
theimage.style.width = "400px";
theimage.onload = () => {
let bounding = [0.09, 0.540, 0.98, 0.76];
let boundingPixel = [22, 208, 252, 289];
let pos = document.getElementsByTagName("canvas")[0].getBoundingClientRect()
let dX = (mouseX - pos.x) / pos.width;
let dY = (mouseY - pos.y) / pos.height;
pixelX = 30
pixelY = 210;
let width = parseInt(theimage.style.width) / pos.width;
pixelWidth = 100;
pixelHeight = Math.floor(theimage.height * pixelWidth / theimage.width);
doDrawing()
}
}; // data url!
reader.readAsDataURL(blob);
}
}
});
window.addEventListener("mousemove", function (e) {
mouseX = e.clientX;
mouseY = e.clientY;
}, false);
window.addEventListener("mousedown", () => {
mousedown = true;
})
window.addEventListener("mouseup", () => {
mousedown = false;
})
window.addEventListener("keyup", function (e) {
if (theimage != null) {
let char = String.fromCharCode(e.keyCode);
if (char == "S") {
let currWidth = parseInt(theimage.style.width.replace("px", ""));
currWidth += 30;
theimage.style.width = currWidth + "px";
}
if (char == "W") {
let currWidth = parseInt(theimage.style.width.replace("px", ""));
currWidth -= 30;
theimage.style.width = currWidth + "px";
}
e.preventDefault();
}
})
function cleanupDrawing(drawing) {
let fard = '';
for (let i = 0; i < drawing.length; i++) {
fard += drawing[i].type;
}
fard = fard.replace(/([34]+)([34])|([567]+)([567])/g, (_, a, b, c, d) => '_'.repeat((a ? a : c).length) + (b ? b : d));
let prev = 5;
let prev2 = null;
return drawing.filter((v, i) => {
if (fard[i] === '_') return false;
if (v.type === prev || v.type === prev2) return false;
if (v.type === 3 || v.type === 4) {
prev2 = v.type;
} else if (v.type === 5 || v.type === 6 || v.type === 7) {
prev = v.type;
}
return true;
});
}
function drawDrawing() {
pc_sprites.drawing.clear();
pc_sprites.drawing.drawMode = 0;
pc_sprites.drawing.rainbowDeg = 0;
for (let i = 0; i < drawHistory.length; i++) {
let action = drawHistory[i];
switch (action.type) {
case 0: {
pc_sprites.drawing.lineTo(action.x, action.y);
if (pc_sprites.drawing.drawMode === 0xffffff) pc_sprites.drawing.rainbowDeg = (pc_sprites.drawing.rainbowDeg + 12) % 360;
break;
}
case 1: {
pc_sprites.drawing.moveTo(action.x, action.y);
break;
}
case 2: {
pc_sprites.drawing.moveTo(action.x, action.y);
break;
}
case 3: {
pc_sprites.drawing.drawWidth = 2;
break;
}
case 4: {
pc_sprites.drawing.drawWidth = 1;
break;
}
case 5: {
pc_sprites.drawing.drawMode = 0;
break;
}
case 6: {
pc_sprites.drawing.drawMode = 0xfbfbfb;
break;
}
case 7: {
pc_sprites.drawing.drawMode = 0xffffff;
break;
}
}
if (pc_sprites.drawing.drawMode === 0xffffff) {
pc_sprites.drawing.lineStyle(pc_sprites.drawing.drawWidth, hsl2rgb2dec(pc_sprites.drawing.rainbowDeg, 1, 0.5));
} else {
pc_sprites.drawing.lineStyle(pc_sprites.drawing.drawWidth + ((pc_sprites.drawing.drawMode > 0) * (pc_sprites.drawing.drawWidth === 2)), pc_sprites.drawing.drawMode);
}
}
}
})()