Drawaria Generative Animator Suite

Convierte tus dibujos en múltiples animaciones espectaculares usando un bot.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Drawaria Generative Animator Suite
// @namespace    http://tampermonkey.net/
// @version      5.1
// @description  Convierte tus dibujos en múltiples animaciones espectaculares usando un bot.
// @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';

    // --- ESTADO GLOBAL ---
    let isAnimating = false;
    let drawingPixels = [];
    let botSocket = null;
    let originalCanvas = null;
    let cw = 0, ch = 0;
    let isPanelVisible = true;

    console.log("Drawaria Animator Suite: Script iniciado.");

    // --- LÓGICA DEL BOT Y DIBUJO ---
    function findBotSocket() {
        if (window.___BOT && window.___BOT.conn && window.___BOT.conn.socket?.readyState === WebSocket.OPEN) {
            botSocket = window.___BOT.conn.socket;
            return true;
        }
        botSocket = null;
        return false;
    }

    function sendDrawCmd(start, end, color, thickness) {
        if (!findBotSocket()) {
            isAnimating = false;
            return false;
        }
        const p1x = Math.max(0, Math.min(1, start[0])), p1y = Math.max(0, Math.min(1, start[1]));
        const p2x = Math.max(0, Math.min(1, end[0])), p2y = Math.max(0, Math.min(1, end[1]));
        const payload = `42["drawcmd",0,[${p1x},${p1y},${p2x},${p2y},false,${0 - thickness},"${color}",0,0,{}]]`;
        botSocket.send(payload);
        return true;
    }

    async function clearCanvasViaBot() {
        if (findBotSocket()) {
            botSocket.send('42["drawcmd",4,[]]');
            await new Promise(resolve => setTimeout(resolve, 100));
        }
    }

    function captureDrawing() {
        if (!originalCanvas) { alert("Error: Canvas no encontrado."); return; }
        const ctx = originalCanvas.getContext('2d');
        const imageData = ctx.getImageData(0, 0, cw, ch);
        const data = imageData.data;
        drawingPixels = [];
        const sampleRate = 4;

        for (let y = 0; y < ch; y += sampleRate) {
            for (let x = 0; x < cw; x += sampleRate) {
                const i = (y * cw + x) * 4;
                if (data[i+3] > 100 && (data[i] < 250 || data[i+1] < 250 || data[i+2] < 250)) {
                    drawingPixels.push({ x: x / cw, y: y / ch, color: `rgb(${data[i]},${data[i+1]},${data[i+2]})` });
                }
            }
        }
        alert(`¡Dibujo capturado! ${drawingPixels.length} puntos de animación listos.`);
        updateUIState('idle');
    }

    // --- BIBLIOTECA DE ANIMACIONES ---
    async function runSelectedAnimation() {
        if (drawingPixels.length === 0) {
            alert("Primero, captura un dibujo.");
            return;
        }
        if (!findBotSocket()) {
            alert("El bot no está conectado. Invoca tu bot primero.");
            return;
        }
        if (isAnimating) return;

        const selectedEffect = document.getElementById('animation-select').value;
        const effectFunction = animations[selectedEffect];

        if (effectFunction) {
            isAnimating = true;
            updateUIState('animating');
            await clearCanvasViaBot();

            await effectFunction(); // Ejecuta la animación seleccionada

            isAnimating = false;
            updateUIState('idle');
            console.log(`Animator: Animación "${selectedEffect}" completada.`);
        }
    }

    const animations = {
        celestialBallet: async () => {
            const dancers = drawingPixels.map(p => ({ x: p.x, y: p.y, vx: (Math.random() - 0.5) * 0.015, vy: (Math.random() - 0.5) * 0.015, color: p.color, lastX: p.x, lastY: p.y }));
            for (let step = 0; step < 180 && isAnimating; step++) {
                for (const d of dancers) {
                    d.lastX = d.x; d.lastY = d.y;
                    d.vx += (0.5 - d.x) * 0.0001; d.vy += (0.5 - d.y) * 0.0001;
                    d.vx *= 0.98; d.vy *= 0.98;
                    d.x += d.vx; d.y += d.vy;
                    if (d.x < 0.01 || d.x > 0.99) d.vx *= -1;
                    if (d.y < 0.01 || d.y > 0.99) d.vy *= -1;
                    if (!sendDrawCmd([d.lastX, d.lastY], [d.x, d.y], d.color, 3)) return;
                }
                await new Promise(resolve => setTimeout(resolve, 30));
            }
        },
        fireworks: async () => {
            const particleCount = 30; // Chispas por cada píxel
            for (const pixel of drawingPixels) {
                if (!isAnimating) break;
                const explosionCenterX = pixel.x;
                const explosionCenterY = pixel.y;
                for (let i = 0; i < particleCount; i++) {
                    const angle = Math.random() * 2 * Math.PI;
                    const distance = Math.random() * 0.1 + 0.02; // Longitud de la chispa
                    const endX = explosionCenterX + distance * Math.cos(angle);
                    const endY = explosionCenterY + distance * Math.sin(angle);
                    sendDrawCmd([explosionCenterX, explosionCenterY], [endX, endY], pixel.color, 2);
                }
                 await new Promise(resolve => setTimeout(resolve, 5)); // Pausa entre explosiones
            }
        },
        hueBlast: async () => {
            const steps = 100;
            for (let i = 0; i < steps && isAnimating; i++) {
                 const progress = i / steps;
                 // Seleccionar un píxel aleatorio de nuestro dibujo para usar su color
                 const basePixel = drawingPixels[Math.floor(Math.random() * drawingPixels.length)];
                 const [r,g,b] = basePixel.color.match(/\d+/g).map(Number);
                 const hue = rgbToHsl(r,g,b)[0] * 360;
                 const newHue = (hue + progress * 180) % 360;
                 const color = `hsl(${newHue}, 100%, 60%)`;

                 // Dibujar una ráfaga desde el centro
                 const angle = Math.random() * 2 * Math.PI;
                 const startX = 0.5;
                 const startY = 0.5;
                 const endX = startX + progress * 0.6 * Math.cos(angle);
                 const endY = startY + progress * 0.6 * Math.sin(angle);

                 if (!sendDrawCmd([startX, startY], [endX, endY], color, 10 + i * 0.1)) return;
                 await new Promise(resolve => setTimeout(resolve, 20));
            }
        },
        colorFestival: async () => {
            const numShapes = 80;
            for (let i = 0; i < numShapes && isAnimating; i++) {
                 const pixel = drawingPixels[Math.floor(Math.random() * drawingPixels.length)];
                 const x = Math.random() * 0.9 + 0.05;
                 const y = Math.random() * 0.9 + 0.05;
                 const size = Math.random() * 0.06 + 0.02;
                 const thickness = Math.floor(Math.random() * 8) + 3;

                 // Dibujar una forma aleatoria
                 const shapeType = Math.floor(Math.random() * 3);
                 if (shapeType === 0) { // Cuadrado
                     sendDrawCmd([x-size, y-size], [x+size, y-size], pixel.color, thickness);
                     sendDrawCmd([x+size, y-size], [x+size, y+size], pixel.color, thickness);
                     sendDrawCmd([x+size, y+size], [x-size, y+size], pixel.color, thickness);
                     sendDrawCmd([x-size, y+size], [x-size, y-size], pixel.color, thickness);
                 } else if (shapeType === 1) { // Estrella
                     for (let k = 0; k < 5; k++) {
                         const angle = (k / 5) * 2 * Math.PI;
                         sendDrawCmd([x, y], [x + size * Math.cos(angle), y + size * Math.sin(angle)], pixel.color, thickness);
                     }
                 } else { // Espiral
                     let lastX = x, lastY = y;
                     for (let k = 0; k <= 20; k++) {
                         const angle = (k / 20) * 4 * Math.PI;
                         const radius = (k / 20) * size;
                         const currentX = x + radius * Math.cos(angle);
                         const currentY = y + radius * Math.sin(angle);
                         sendDrawCmd([lastX, lastY], [currentX, currentY], pixel.color, thickness);
                         lastX = currentX; lastY = currentY;
                     }
                 }
                 await new Promise(resolve => setTimeout(resolve, 50));
            }
        },
        pixelArt: async () => {
            await clearCanvasViaBot();
            const pixelSize = 0.01; // Tamaño de cada "píxel" en el lienzo
            const quadrant = { xMin: 0.1, yMin: 0.1, xMax: 0.9, yMax: 0.9 }; // Dibujar en el centro
            const totalSpriteW = (cw / 4) * pixelSize; // Ajustar al tamaño del canvas
            const totalSpriteH = (ch / 4) * pixelSize;
            const startX = quadrant.xMin + (quadrant.xMax - quadrant.xMin - totalSpriteW) / 2;
            const startY = quadrant.yMin + (quadrant.yMax - quadrant.yMin - totalSpriteH) / 2;

            for(const pixel of drawingPixels) {
                if(!isAnimating) break;
                // Mapear la posición original del píxel a la nueva ubicación pixelada
                const drawX = startX + (pixel.x * totalSpriteW);
                const drawY = startY + (pixel.y * totalSpriteH);
                const thickness = pixelSize * Math.min(cw, ch) * 0.9;
                if (!sendDrawCmd([drawX, drawY], [drawX + 0.0001, drawY + 0.0001], pixel.color, thickness)) break;
                await new Promise(resolve => setTimeout(resolve, 1));
            }
        }
    };

    // --- HELPERS ---
    function rgbToHsl(r, g, b) {
        r /= 255; g /= 255; b /= 255;
        const max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;
        if (max === min) { h = s = 0; }
        else {
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch (max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }
        return [h, s, l];
    }

    // --- UI ---
    function createAnimationPanel() {
        const panel = document.createElement('div');
        panel.id = 'animator-panel';
        panel.innerHTML = `
            <div id="animator-header">
                <h3>Animador Generativo</h3>
                <button id="toggle-panel-btn" class="btn btn-outline-secondary">Ocultar</button>
            </div>
            <div id="bot-status" class="status-disconnected">Esperando al Bot...</div>
            <hr>
            <div class="animator-row">
                <button id="capture-btn" class="btn btn-outline-secondary" title="Analiza tu dibujo actual">1. Capturar Dibujo</button>
            </div>
            <div class="animator-row">
                <select id="animation-select" class="form-control" title="Elige un efecto de animación">
                    <option value="celestialBallet">Ballet Celestial</option>
                    <option value="fireworks">Fuegos Artificiales</option>
                    <option value="hueBlast">Ráfaga de Tono</option>
                    <option value="colorFestival">Festival de Color</option>
                    <option value="pixelArt">Pixelizar Dibujo</option>
                </select>
                <button id="animate-btn" class="btn btn-primary" title="Inicia la animación con el bot" disabled>2. Animar</button>
            </div>
            <div class="animator-row" style="margin-top: 5px;">
                <button id="stop-animation-btn" class="btn btn-danger" title="Detiene la animación en curso" disabled style="width:100%">Detener Animación</button>
            </div>
        `;

        const style = document.createElement('style');
        style.innerHTML = `
            #animator-panel { position: fixed; bottom: 15px; right: 15px; z-index: 9999;
                background: rgba(245, 245, 245, 0.95); padding: 15px; border: 1px solid #ccc;
                border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2);
                backdrop-filter: blur(5px); font-family: sans-serif; width: 280px; }
            #animator-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
            #animator-header h3 { margin: 0; text-align: left; font-size: 16px; color: #333; }
            #toggle-panel-btn { cursor: pointer; }
            #bot-status { text-align: center; font-weight: bold; padding: 5px; border-radius: 4px; margin-bottom: 10px; transition: background-color 0.3s; }
            .status-connected { background-color: #28a745; color: white; }
            .status-disconnected { background-color: #ffc107; color: black; }
            .animator-row { display: flex; gap: 8px; justify-content: center; margin-bottom: 8px; }
            #animator-panel button, #animator-panel select { flex-grow: 1; cursor: pointer; }
            #animator-panel button:disabled { cursor: not-allowed; opacity: 0.6; }
            hr { border: none; border-top: 1px solid #ddd; margin: 15px 0; }
        `;
        document.head.appendChild(style);
        document.body.appendChild(panel);

        // Hacer el panel arrastrable
        const header = document.getElementById('animator-header');
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        header.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            // Obtener la posición inicial del cursor
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            // Mover el elemento al mover el cursor
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            // Calcular la nueva posición del cursor
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            // Establecer la nueva posición del elemento
            const panel = document.getElementById('animator-panel');
            panel.style.top = (panel.offsetTop - pos2) + "px";
            panel.style.left = (panel.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            // Detener el movimiento al soltar el botón del mouse
            document.onmouseup = null;
            document.onmousemove = null;
        }

        document.getElementById('capture-btn').addEventListener('click', captureDrawing);
        document.getElementById('animate-btn').addEventListener('click', runSelectedAnimation);
        document.getElementById('stop-animation-btn').addEventListener('click', () => isAnimating = false);
        document.getElementById('toggle-panel-btn').addEventListener('click', togglePanelVisibility);
    }

    function togglePanelVisibility() {
        const panel = document.getElementById('animator-panel');
        const toggleBtn = document.getElementById('toggle-panel-btn');
        if (isPanelVisible) {
            panel.style.display = 'none';
            toggleBtn.textContent = 'Mostrar';
        } else {
            panel.style.display = 'block';
            toggleBtn.textContent = 'Ocultar';
        }
        isPanelVisible = !isPanelVisible;
    }

    function updateUIState(state) {
        const captureBtn = document.getElementById('capture-btn');
        const animateBtn = document.getElementById('animate-btn');
        const stopBtn = document.getElementById('stop-animation-btn');
        const select = document.getElementById('animation-select');

        if (!captureBtn || !animateBtn || !stopBtn) return;
        const botIsConnected = findBotSocket();

        const isBusy = state === 'animating';
        captureBtn.disabled = isBusy;
        select.disabled = isBusy;
        animateBtn.disabled = isBusy || drawingPixels.length === 0 || !botIsConnected;
        stopBtn.disabled = !isBusy;
    }

    function updateBotStatusUI(isConnected) {
        const statusDiv = document.getElementById('bot-status');
        if (!statusDiv) return;
        statusDiv.textContent = isConnected ? "Bot Detectado" : "Bot no encontrado";
        statusDiv.className = isConnected ? "status-connected" : "status-disconnected";
        updateUIState(isAnimating ? 'animating' : 'idle');
    }

    // --- INICIALIZACIÓN ---
    function initialize() {
        setInterval(() => {
            const gameCanvas = document.getElementById('canvas');
            const panelExists = document.getElementById('animator-panel');
            if (gameCanvas && gameCanvas.offsetParent && !panelExists) {
                originalCanvas = gameCanvas;
                cw = originalCanvas.width;
                ch = originalCanvas.height;
                createAnimationPanel();
            }
            if (panelExists) {
                updateBotStatusUI(findBotSocket());
            }
        }, 1000);
    }

    window.addEventListener('load', initialize);

})();