// ==UserScript==
// @name Drawaria Harmony Unlocked: Pro!
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Harmony Unlocked Pro!
// @author YouTubeDrawaria
// @match https://drawaria.online/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// (function() { /*
// 'use strict';
// Function to get the user's language
function getUserLanguage() {
const navigatorLanguage = navigator.language || navigator.userLanguage;
return navigatorLanguage.split('-')[0]; // Get the primary language code
}
// Translations for the warning message and character's speech
const translations = {
en: {
title: 'Everything is blocked',
message: 'You should not play right now, you have important things to do.',
characterSpeech: 'Hey! Go outside.'
},
es: {
title: 'Todo está bloqueado',
message: 'No debes jugar en este momento, tienes cosas importantes que hacer ahora mismo.',
characterSpeech: '¡Oye! Sal afuera.'
},
fr: {
title: 'Tout est bloqué',
message: 'Vous ne devriez pas jouer en ce moment, vous avez des choses importantes à faire.',
characterSpeech: 'Hé ! Sors dehors.'
},
de: {
title: 'Alles ist blockiert',
message: 'Du solltest im Moment nicht spielen, du hast wichtige Dinge zu tun.',
characterSpeech: 'Hey! Geh nach draußen.'
},
it: {
title: 'Tutto è bloccato',
message: 'Non dovresti giocare in questo momento, hai cose importanti da fare.',
characterSpeech: 'Ehi! Esci fuori.'
},
pt: {
title: 'Tudo está bloqueado',
message: 'Você não deveria jogar agora, você tem coisas importantes para fazer.',
characterSpeech: 'Ei! Vá para fora.'
},
ru: {
title: 'Все заблокировано',
message: 'Вы не должны играть сейчас, у вас есть важные дела.',
characterSpeech: 'Эй! Иди на улицу.'
},
ja: {
title: 'すべてブロックされています',
message: '今はプレイすべきではありません。重要なことがあります。',
characterSpeech: 'ねえ!外に出ようよ。'
},
zh: {
title: '一切都被阻止了',
message: '您现在不应该玩,您有重要的事情要做。',
characterSpeech: '嘿!出去外面。'
},
// Add more languages as needed
};
//*
// The large marker
document.getElementById('drawwidthrange').min=-9999
let symmetryMode = null;
let currentBrushType = 0;
function initializeSymmetryDropdown() {
const symmetryDropdown = document.querySelector('#option-symmetry select');
const canvas = document.querySelector('canvas');
if (symmetryDropdown) {
symmetryDropdown.style.pointerEvents = 'auto';
symmetryDropdown.style.display = 'block';
symmetryDropdown.addEventListener('change', function () {
symmetryMode = parseInt(symmetryDropdown.value, 10);
if (canvas) {
canvas.setAttribute('data-symmetry', symmetryMode);
}
});
}
}
function trackBrushSelection() {
const markerButton = document.querySelector('.drawcontrols-popupbutton[data-buttonid="line"]');
const aliasedMarkerButton = document.querySelector('.drawcontrols-popupbutton[data-buttonid="aliasedline"]');
const inkBrushButton = document.querySelector('.drawcontrols-popupbutton[data-buttonid="brush"]');
if (markerButton) {
markerButton.addEventListener('click', () => {
currentBrushType = 0; // Marker (antialiased)
});
}
if (aliasedMarkerButton) {
aliasedMarkerButton.addEventListener('click', () => {
currentBrushType = 1; // Marker (not antialiased)
});
}
if (inkBrushButton) {
inkBrushButton.addEventListener('click', () => {
currentBrushType = 2; // Ink Brush
});
}
}
function enableMoveButtonDragging() {
const moveButton = document.querySelector("#canvasoverlays-movebutton");
const canvas = document.querySelector("canvas");
if (!moveButton || !canvas) {
return;
}
let isDragging = false;
let initialOffsetX = 0;
let initialOffsetY = 0;
moveButton.addEventListener("mousedown", (event) => {
isDragging = true;
initialOffsetX = event.clientX - (moveButton.offsetLeft + moveButton.offsetWidth / 2);
initialOffsetY = event.clientY - (moveButton.offsetTop + moveButton.offsetHeight / 2);
moveButton.style.cursor = "grabbing";
});
document.addEventListener("mousemove", (event) => {
if (isDragging) {
const newLeft = event.clientX - initialOffsetX - moveButton.offsetWidth / 2;
const newTop = event.clientY - initialOffsetY - moveButton.offsetHeight / 2;
const constrainedLeft = Math.max(0, Math.min(newLeft, canvas.offsetWidth - moveButton.offsetWidth));
const constrainedTop = Math.max(0, Math.min(newTop, canvas.offsetHeight - moveButton.offsetHeight));
moveButton.style.left = `${constrainedLeft}px`;
moveButton.style.top = `${constrainedTop}px`;
const normalizedX = (constrainedLeft + moveButton.offsetWidth / 2) / canvas.width;
const normalizedY = (constrainedTop + moveButton.offsetHeight / 2) / canvas.height;
canvas.setAttribute("data-center-x", normalizedX.toFixed(5));
canvas.setAttribute("data-center-y", normalizedY.toFixed(5));
}
});
document.addEventListener("mouseup", () => {
if (isDragging) {
isDragging = false;
moveButton.style.cursor = "grab";
}
});
}
function drawPolygon(ctx, x, y, radius, sides) {
const angleIncrement = (2 * Math.PI) / sides;
ctx.beginPath();
for (let i = 0; i <= sides; i++) {
const angle = i * angleIncrement;
const px = x + radius * Math.cos(angle);
const py = y + radius * Math.sin(angle);
if (i === 0) {
ctx.moveTo(px, py);
} else {
ctx.lineTo(px, py);
}
}
ctx.closePath();
ctx.fill();
}
function applyLocalSymmetry(ctx, x, y, symmetryMode, canvas) {
const width = canvas.width;
const height = canvas.height;
const centerX = (parseFloat(canvas.getAttribute("data-center-x")) || 0.5) * canvas.width;
const centerY = (parseFloat(canvas.getAttribute("data-center-y")) || 0.5) * canvas.height;
const symmetryMap = {
102: 2, 103: 3, 104: 4, 105: 5, 106: 6, 107: 7, 108: 8, 109: 9, // Radial
202: 2, 203: 3, 204: 4, 205: 5, 206: 6, 207: 7, 208: 8, 209: 9, // Radial + Mirror
1: "horizontal", // Horizontal mirroring
2: "vertical", // Vertical mirroring
3: "diagonal" // Diagonal mirroring
};
const numSectors = symmetryMap[symmetryMode];
ctx.imageSmoothingEnabled = currentBrushType === 0;
if (typeof numSectors === "number") {
const dx = x - centerX;
const dy = y - centerY;
for (let i = 0; i < numSectors; i++) {
const angle = (Math.PI * 2 * i) / numSectors;
// Compute coordinates for the rotated sector
const rotatedX = Math.round(centerX + dx * Math.cos(angle) - dy * Math.sin(angle));
const rotatedY = Math.round(centerY + dx * Math.sin(angle) + dy * Math.cos(angle));
if (currentBrushType === 0) {
// Marker 0 logic (antialiased)
ctx.beginPath();
ctx.moveTo(rotatedX, rotatedY);
ctx.lineTo(rotatedX, rotatedY);
ctx.stroke();
if (symmetryMode >= 202 && symmetryMode <= 209) {
const isOddSector = numSectors % 2 !== 0;
const offsetAngle = isOddSector ? Math.PI / numSectors : 0;
const mirroredX = centerX + dx * Math.cos(angle + offsetAngle) + dy * Math.sin(angle + offsetAngle);
const mirroredY = centerY + dx * Math.sin(angle + offsetAngle) - dy * Math.cos(angle + offsetAngle);
ctx.beginPath();
ctx.moveTo(mirroredX, mirroredY);
ctx.lineTo(mirroredX, mirroredY);
ctx.stroke();
}
} else if (currentBrushType === 1) {
const radius = Math.round(ctx.lineWidth / 2); // Use a pixel-perfect radius
ctx.imageSmoothingEnabled = false;
// Marker 1 logic (not antialiased)
const cornerRadius = Math.min(radius * 0.9, radius); // Adjust the corner radius
drawPolygon(ctx, rotatedX, rotatedY, radius, 20);
if (symmetryMode >= 202 && symmetryMode <= 209) {
const isOddSector = numSectors % 2 !== 0;
const offsetAngle = isOddSector ? Math.PI / numSectors : 0;
const mirroredX = centerX + dx * Math.cos(angle + offsetAngle) + dy * Math.sin(angle + offsetAngle);
const mirroredY = centerY + dx * Math.sin(angle + offsetAngle) - dy * Math.cos(angle + offsetAngle);
drawPolygon(ctx, mirroredX, mirroredY, radius, 20);
}
}
}
} else if (typeof numSectors === "string") {
const radius = Math.round(ctx.lineWidth / 2); // Use pixel-perfect radius
const snappedX = Math.round(x);
const snappedY = Math.round(y);
if (numSectors === "vertical") {
const mirroredY = centerY + (centerY - snappedY);
if (currentBrushType === 0) {
ctx.beginPath();
ctx.moveTo(snappedX, snappedY);
ctx.lineTo(snappedX, snappedY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(snappedX, mirroredY);
ctx.lineTo(snappedX, mirroredY);
ctx.stroke();
} else if (currentBrushType === 1) {
drawPolygon(ctx, snappedX, snappedY, radius, 20); // Draw 20-sided polygon
drawPolygon(ctx, snappedX, mirroredY, radius, 20);
}
} else if (numSectors === "horizontal") {
const mirroredX = centerX + (centerX - snappedX);
if (currentBrushType === 0) {
ctx.beginPath();
ctx.moveTo(snappedX, snappedY);
ctx.lineTo(snappedX, snappedY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(mirroredX, snappedY);
ctx.lineTo(mirroredX, snappedY);
ctx.stroke();
} else if (currentBrushType === 1) {
drawPolygon(ctx, snappedX, snappedY, radius, 20); // Draw 20-sided polygon
drawPolygon(ctx, mirroredX, snappedY, radius, 20);
}
} else if (numSectors === "diagonal") {
const mirroredX = centerX + (centerX - snappedX);
const mirroredY = centerY + (centerY - snappedY);
if (currentBrushType === 0) {
ctx.beginPath();
ctx.moveTo(snappedX, snappedY);
ctx.lineTo(snappedX, snappedY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(mirroredX, mirroredY);
ctx.lineTo(mirroredX, mirroredY);
ctx.stroke();
} else if (currentBrushType === 1) {
drawPolygon(ctx, snappedX, snappedY, radius, 20); // Draw 20-sided polygon
drawPolygon(ctx, mirroredX, mirroredY, radius, 20);
}
}
} else {
console.warn("Unsupported symmetry mode:", symmetryMode);
}
}
function interpolatePoints(lastX, lastY, currentX, currentY, steps) {
const points = [];
for (let i = 1; i <= steps; i++) {
const t = i / steps;
const interpolatedX = lastX + (currentX - lastX) * t;
const interpolatedY = lastY + (currentY - lastY) * t;
points.push([interpolatedX, interpolatedY]);
}
return points;
}
function forceLocalSymmetryRendering() {
const canvas = document.querySelector("canvas");
const colorFlowInput = document.querySelector('[data-localprop="colorflow"]');
if (!canvas) {
console.warn("Canvas not found.");
return;
}
const ctx = canvas.getContext("2d");
let isDrawing = false;
let hasStartedInsideCanvas = false;
let lastX = null;
let lastY = null;
function isWithinCanvasBounds(event) {
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
return x >= 0 && y >= 0 && x <= canvas.width && y <= canvas.height;
}
canvas.addEventListener("mousedown", (event) => {
if (event.button === 2) {
return;
}
if (isWithinCanvasBounds(event)) {
isDrawing = true;
hasStartedInsideCanvas = true;
lastX = event.offsetX;
lastY = event.offsetY;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
const moveButton = document.querySelector("#canvasoverlays-movebutton");
if (moveButton) {
moveButton.style.pointerEvents = "none";
} else {
console.warn("Move button not found.");
}
}
});
canvas.addEventListener("mousemove", (event) => {
if (!isDrawing || !hasStartedInsideCanvas) return;
if (!isWithinCanvasBounds(event)) {
lastX = null;
lastY = null;
return;
}
const x = event.offsetX;
const y = event.offsetY;
const symmetryMode = parseInt(canvas.getAttribute("data-symmetry"), 10);
// Handle color flow and symmetry for brush type 1 (not antialiased)
if (colorFlowInput && currentBrushType === 1) {
const colorFlowValue = parseFloat(colorFlowInput.value);
if (colorFlowValue > 0) {
const gradient = ctx.createLinearGradient(lastX, lastY, x, y);
ctx.strokeStyle = gradient;
}
}
if (symmetryMode) {
if (lastX !== null && lastY !== null) {
const interpolatedPoints = interpolatePoints(lastX, lastY, x, y, 10);
interpolatedPoints.forEach(([interpX, interpY]) => {
applyLocalSymmetry(ctx, interpX, interpY, symmetryMode, canvas);
});
} else {
applyLocalSymmetry(ctx, x, y, symmetryMode, canvas);
}
} else {
ctx.lineTo(x, y);
ctx.stroke();
}
lastX = x;
lastY = y;
});
document.addEventListener("mouseup", (event) => {
if (!isWithinCanvasBounds(event) && isDrawing) {
const evtDown = new MouseEvent("mousedown", {
bubbles: true,
cancelable: true,
view: window,
button: 0,
buttons: 1,
clientX: canvas.getBoundingClientRect().left + 1,
clientY: canvas.getBoundingClientRect().top + 1
});
const evtUp = new MouseEvent("mouseup", {
bubbles: true,
cancelable: true,
view: window,
button: 0,
buttons: 0,
clientX: canvas.getBoundingClientRect().left + 1,
clientY: canvas.getBoundingClientRect().top + 1
});
canvas.dispatchEvent(evtDown);
canvas.dispatchEvent(evtUp);
}
isDrawing = false;
hasStartedInsideCanvas = false;
ctx.closePath();
const moveButton = document.querySelector("#canvasoverlays-movebutton");
if (moveButton) {
moveButton.style.pointerEvents = "auto";
}
});
canvas.addEventListener("mouseout", () => {
if (isDrawing) {
return;
}
isDrawing = false;
hasStartedInsideCanvas = false;
ctx.closePath();
const moveButton = document.querySelector("#canvasoverlays-movebutton");
if (moveButton) {
moveButton.style.pointerEvents = "auto";
}
});
canvas.addEventListener("mouseenter", (event) => {
if (event.buttons === 1) {
isDrawing = true;
ctx.beginPath();
lastX = event.offsetX;
lastY = event.offsetY;
ctx.moveTo(lastX, lastY);
}
});
canvas.addEventListener('mousewheel', function (event) {
ctx.lineWidth += event.deltaY * 0.07; // Adjust the multiplier as necessary
event.preventDefault();
});
}
function interceptWebSocketMessages() {
let socket;
const originalSend = WebSocket.prototype.send;
let isCursorInsideCanvas = true;
let blockDrawingCommands = false;
const canvas = document.querySelector("canvas");
if (!canvas) {
console.warn("Canvas not found.");
return;
}
let blockTimer;
function setBlockDrawingCommands(block) {
clearTimeout(blockTimer); // Clear existing timer
blockDrawingCommands = block;
if (block) {
// Set timeout to automatically unblock after 0.5 second
blockTimer = setTimeout(() => {
blockDrawingCommands = false;
}, 500);
}
}
// Update cursor position
canvas.addEventListener("mouseenter", () => {
isCursorInsideCanvas = true;
setBlockDrawingCommands(false);
});
canvas.addEventListener("mouseleave", () => {
isCursorInsideCanvas = false;
setBlockDrawingCommands(true);
});
WebSocket.prototype.send = function (...args) {
if (!socket) {
socket = this;
socket.addEventListener("open", () => {
console.log("WebSocket connection successfully established.");
});
}
try {
if (typeof args[0] === "string" && args[0].startsWith("42")) {
const parsedData = JSON.parse(args[0].slice(2));
const command = parsedData[0];
if (command === "drawcmd" && Array.isArray(parsedData[2])) {
if (!isCursorInsideCanvas && blockDrawingCommands) {
console.warn("Blocked drawcmd WebSocket command because cursor is outside the canvas and within block period.");
return;
}
const subcommand = parsedData[1];
const payload = parsedData[2];
const centerX = parseFloat(canvas.getAttribute("data-center-x")) || 0.5;
const centerY = parseFloat(canvas.getAttribute("data-center-y")) || 0.5;
if (symmetryMode !== null) {
const symmetryData = {
"2": symmetryMode,
"3": centerX,
"4": centerY,
};
payload[8] = currentBrushType;
payload[9] = symmetryData;
}
const modifiedMessage = `42${JSON.stringify(["drawcmd", subcommand, payload])}`;
args[0] = modifiedMessage;
}
}
} catch (error) {
console.error("Error intercepting WebSocket message:", error);
}
originalSend.apply(this, args);
};
}
function enforceVisibilityAndFunctionality() {
const settingsContainers = document.querySelectorAll('.drawcontrols-settingscontainer');
settingsContainers.forEach(container => {
if (container.style.display === 'none') {
container.style.display = '';
}
const dropdowns = container.querySelectorAll('select');
dropdowns.forEach(dropdown => {
dropdown.style.pointerEvents = 'auto';
dropdown.style.display = 'block';
dropdown.style.position = 'relative';
dropdown.style.zIndex = '1000';
dropdown.addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
setTimeout(() => {
dropdown.style.display = 'block';
}, 10);
}, true);
});
});
const buttons = document.querySelectorAll('.drawcontrols-button');
buttons.forEach(button => {
const popupButtons = button.querySelectorAll('.drawcontrols-popupbutton');
popupButtons.forEach(popupButton => {
popupButton.style.display = '';
popupButton.addEventListener('click', () => {
popupButtons.forEach(btn => btn.classList.remove('drawcontrols-popupbutton-active'));
popupButton.classList.add('drawcontrols-popupbutton-active');
});
});
});
initializeSymmetryDropdown();
}
window.addEventListener('load', function() {
enforceVisibilityAndFunctionality();
enableMoveButtonDragging();
const observer = new MutationObserver(() => {
enforceVisibilityAndFunctionality();
enableMoveButtonDragging();
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style']
});
trackBrushSelection();
interceptWebSocketMessages();
forceLocalSymmetryRendering();
});
})();