Drawaria Shape Animator 3.0

Enhanced shape animations with more features and shapes.

// ==UserScript==
// @name         Drawaria Shape Animator 3.0
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Enhanced shape animations with more features and shapes.
// @author       YourName
// @match        *://drawaria.online/*
// @license MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // CSS for the menu
    const style = document.createElement('style');
    style.textContent = `
        .menu {
            position: fixed;
            top: 10px;
            right: 10px;
            background-color: #333;
            color: #eee;
            padding: 10px;
            border-radius: 5px;
            z-index: 1000;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
            cursor: move;
            width: 300px;
            overflow-y: auto;
            max-height: 80vh;
        }
        .menu h1 {
            margin: 0 0 10px 0;
            font-size: 18px;
            text-align: center;
        }
        .menu label {
            display: block;
            margin-bottom: 5px;
        }
        .menu input[type="range"], .menu input[type="color"] {
            width: calc(100% - 10px);
        }
        .menu button {
            display: block;
            width: 100%;
            margin: 5px 0;
            padding: 8px;
            background-color: #555;
            color: #eee;
            border: none;
            border-radius: 3px;
            cursor: pointer;
        }
        .menu button:hover {
            background-color: #777;
        }
        .menu-toggle {
            position: absolute;
            top: 5px;
            right: 5px;
            background: none;
            border: none;
            color: #eee;
            font-size: 20px;
            cursor: pointer;
        }
    `;
    document.head.appendChild(style);

    // Function to send draw commands
    function sendDrawCommand(startX, startY, endX, endY, thickness, color) {
        const message = `42["drawcmd",0,[${startX},${startY},${endX},${endY},false,${0 - thickness},"${color}",0,0,{}]]`;
        try {
            sockets.forEach(socket => socket.send(message));
        } catch (error) {
            console.error("Error sending draw command:", error);
        }
    }

    // Shape drawing functions
    function drawCircle(x, y, radius, color, thickness, fill = false) {
        const angleIncrement = (2 * Math.PI) / 32;
        if (fill) {
            // Fill Circle Logic Here (if supported)
        }
        for (let i = 0; i < 32; i++) {
            const angle1 = i * angleIncrement;
            const angle2 = (i + 1) * angleIncrement;
            const startX = x + radius * Math.cos(angle1);
            const startY = y + radius * Math.sin(angle1);
            const endX = x + radius * Math.cos(angle2);
            const endY = y + radius * Math.sin(angle2);
            sendDrawCommand(startX, startY, endX, endY, thickness, color);
        }
    }

    function drawPolygon(x, y, size, sides, color, thickness) {
        const angleIncrement = (2 * Math.PI) / sides;
        for (let i = 0; i < sides; i++) {
            const angle1 = i * angleIncrement;
            const angle2 = (i + 1) * angleIncrement;
            const startX = x + size * Math.cos(angle1);
            const startY = y + size * Math.sin(angle1);
            const endX = x + size * Math.cos(angle2);
            const endY = y + size * Math.sin(angle2);
            sendDrawCommand(startX, startY, endX, endY, thickness, color);
        }
    }

    function drawSpiral(x, y, maxRadius, turns, color, thickness) {
        const segments = 100;
        for (let i = 0; i <= segments; i++) {
            const angle = (i / segments) * turns * 2 * Math.PI;
            const radius = (i / segments) * maxRadius;
            const startX = x + radius * Math.cos(angle);
            const startY = y + radius * Math.sin(angle);
            if (i > 0) {
                sendDrawCommand(prevX, prevY, startX, startY, thickness, color);
            }
            prevX = startX;
            prevY = startY;
        }
    }

    // Store active shapes for undo/redo
    let actions = [];
    let redoActions = [];

    function recordAction(action) {
        actions.push(action);
        redoActions = [];
    }

    // Menu creation
    const menu = document.createElement('div');
    menu.className = 'menu';
    menu.innerHTML = `<h1>Drawaria Shape Animator</h1>`;

    // Toggle button
    const toggleButton = document.createElement('button');
    toggleButton.className = 'menu-toggle';
    toggleButton.textContent = '▼';
    toggleButton.addEventListener('click', () => {
        if (menuContent.style.display === 'none') {
            menuContent.style.display = 'block';
            toggleButton.textContent = '▲';
        } else {
            menuContent.style.display = 'none';
            toggleButton.textContent = '▼';
        }
    });
    menu.appendChild(toggleButton);

    // Menu content
    const menuContent = document.createElement('div');
    menuContent.style.display = 'none';

    // Shape buttons
    const shapes = {
        'Circle': drawCircle,
        'Square': (x, y, size, color, thickness) => drawPolygon(x, y, size, 4, color, thickness),
        'Triangle': (x, y, size, color, thickness) => drawPolygon(x, y, size, 3, color, thickness),
        'Pentagon': (x, y, size, color, thickness) => drawPolygon(x, y, size, 5, color, thickness),
        'Hexagon': (x, y, size, color, thickness) => drawPolygon(x, y, size, 6, color, thickness),
        'Octagon': (x, y, size, color, thickness) => drawPolygon(x, y, size, 8, color, thickness),
        'Spiral': drawSpiral,
    };

    for (const name in shapes) {
        const button = document.createElement('button');
        button.textContent = name;
        button.addEventListener('click', () => {
            stopAllAnimations();
            const color = colorInput.value;
            const thickness = parseInt(thicknessRange.value);
            const maxSize = parseFloat(sizeRange.value);
            recordAction({ shape: name, color, thickness, size: maxSize });
            animateShape(shapes[name], 0.5, 0.5, maxSize, color, thickness);
        });
        menuContent.appendChild(button);
    }

    // Add color and thickness controls
    const colorInput = document.createElement('input');
    colorInput.type = 'color';
    colorInput.value = '#000000';
    menuContent.appendChild(document.createElement('label')).textContent = 'Color:';
    menuContent.appendChild(colorInput);

    const thicknessRange = document.createElement('input');
    thicknessRange.type = 'range';
    thicknessRange.min = 1;
    thicknessRange.max = 50;  // Expanded thickness range
    thicknessRange.value = 5;
    menuContent.appendChild(document.createElement('label')).textContent = 'Thickness:';
    menuContent.appendChild(thicknessRange);

    const sizeRange = document.createElement('input');
    sizeRange.type = 'range';
    sizeRange.min = 0.1;
    sizeRange.max = 5;  // Expanded max size
    sizeRange.value = 1;
    menuContent.appendChild(document.createElement('label')).textContent = 'Max Size:';
    menuContent.appendChild(sizeRange);

    // Save/Load Presets
    const saveButton = document.createElement('button');
    saveButton.textContent = 'Save Preset';
    saveButton.addEventListener('click', () => {
        const presetName = prompt("Enter preset name:");
        if (presetName) {
            localStorage.setItem(presetName, JSON.stringify(actions));
            alert(`Preset ${presetName} saved!`);
        }
    });
    menuContent.appendChild(saveButton);

    const loadButton = document.createElement('button');
    loadButton.textContent = 'Load Preset';
    loadButton.addEventListener('click', () => {
        const presetName = prompt("Enter preset name to load:");
        const preset = localStorage.getItem(presetName);
        if (preset) {
            actions = JSON.parse(preset);
            alert(`Preset ${presetName} loaded!`);
            // Here you would need to re-execute the actions to draw them on the canvas.
            actions.forEach(action => {
                animateShape(shapes[action.shape], 0.5, 0.5, action.size, action.color, action.thickness);
            });
        } else {
            alert("Preset not found!");
        }
    });
    menuContent.appendChild(loadButton);

    // Undo/Redo functionality
    const undoButton = document.createElement('button');
    undoButton.textContent = 'Undo';
    undoButton.addEventListener('click', () => {
        if (actions.length > 0) {
            const action = actions.pop();
            redoActions.push(action);
            // Here you would need to draw the canvas state before the action was taken.
            // Implement your canvas state management logic here.
        }
    });
    menuContent.appendChild(undoButton);

    const redoButton = document.createElement('button');
    redoButton.textContent = 'Redo';
    redoButton.addEventListener('click', () => {
        if (redoActions.length > 0) {
            const action = redoActions.pop();
            actions.push(action);
            animateShape(shapes[action.shape], 0.5, 0.5, action.size, action.color, action.thickness);
        }
    });
    menuContent.appendChild(redoButton);

    // Random Shape Generator
    const randomButton = document.createElement('button');
    randomButton.textContent = 'Random Shape';
    randomButton.addEventListener('click', () => {
        const shapeNames = Object.keys(shapes);
        const randomShape = shapeNames[Math.floor(Math.random() * shapeNames.length)];
        const randomColor = `#${Math.floor(Math.random()*16777215).toString(16)}`;
        const randomThickness = Math.floor(Math.random() * (thicknessRange.max - thicknessRange.min + 1)) + thicknessRange.min;
        const randomSize = Math.random() * (sizeRange.max - sizeRange.min) + sizeRange.min;
        animateShape(shapes[randomShape], 0.5, 0.5, randomSize, randomColor, randomThickness);
    });
    menuContent.appendChild(randomButton);

    // Clear/Stop Button
    const clearCanvasButton = document.createElement('button');
    clearCanvasButton.textContent = 'Clear/Stop';
    clearCanvasButton.addEventListener('click', () => {
        let data = ["drawcmd", 0, [0.5, 0.5, 0.5, 0.5, !0, -2000, "#FFFFFF", -1, !1]];
        window.sockets.forEach(socket => {
            if (socket.readyState === WebSocket.OPEN) {
                socket.send(`42${JSON.stringify(data)}`);
            }
        });
        stopAllAnimations();
        actions = [];
        redoActions = [];
        animationRunning = false;
    });
    menuContent.appendChild(clearCanvasButton);

    menu.appendChild(menuContent);

    // Animation functions
    let animationRunning = false;
    let animationId = null;

    function animateShape(shapeFunc, x, y, maxSize, color, thickness) {
        let currentSize = 0;
        const speed = 0.01;
        animationRunning = true;

        function drawFrame() {
            if (!animationRunning || currentSize > maxSize) {
                animationRunning = false;
                return;
            }
            shapeFunc(x, y, currentSize, color, thickness);
            currentSize += speed;
            animationId = requestAnimationFrame(drawFrame);
        }
        drawFrame();
    }

    // Stop all animations
    function stopAllAnimations() {
        animationRunning = false;
        if (animationId) {
            cancelAnimationFrame(animationId);
            animationId = null;
        }
    }

    // WebSocket interception
    const originalSend = WebSocket.prototype.send;
    let sockets = [];
    WebSocket.prototype.send = function (...args) {
        if (sockets.indexOf(this) === -1) sockets.push(this);
        return originalSend.apply(this, args);
    };

    // Make the menu draggable
    let isDragging = false;
    let offsetX, offsetY;

    menu.addEventListener('mousedown', e => {
        isDragging = true;
        offsetX = e.clientX - menu.getBoundingClientRect().left;
        offsetY = e.clientY - menu.getBoundingClientRect().top;
    });

    document.addEventListener('mousemove', e => {
        if (isDragging) {
            menu.style.left = `${e.clientX - offsetX}px`;
            menu.style.top = `${e.clientY - offsetY}px`;
        }
    });

    document.addEventListener('mouseup', () => isDragging = false);

    document.body.appendChild(menu);
})();