Drawaria Generative Animator Suite

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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);

})();