Drawaria AutoDraw Vectorized (Fast)

Pega imágenes con dos métodos: Pixel (detallado) y Vectorizado (extremadamente rápido, <100 comandos para imágenes simples). Incluye dibujador de texto. Ahora con renderizado local de los comandos.

// ==UserScript==
// @name         Drawaria AutoDraw Vectorized (Fast)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Pega imágenes con dos métodos: Pixel (detallado) y Vectorizado (extremadamente rápido, <100 comandos para imágenes simples). Incluye dibujador de texto. Ahora con renderizado local de los comandos.
// @author       YouTubeDrawaria
// @include      https://drawaria.online*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// @license MIT
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    // =================================================================================
    // === 1. CAPTURA DEL WEBSOCKET Y CONFIGURACIÓN GLOBAL ===
    // =================================================================================

    const canvas = document.getElementById('canvas');
    const ctx = canvas ? canvas.getContext('2d') : null; // Ensure ctx is null if canvas isn't found
    let drawariaSocket;
    let socketStatus = 'disconnected'; // Current WebSocket connection status

    const originalSend = WebSocket.prototype.send;
    WebSocket.prototype.send = function (...args) {
        if (!drawariaSocket && (this.url.includes('drawaria') || this.url.includes('socket') || /:\d{4,5}/.test(this.url))) {
            drawariaSocket = this;
            this.addEventListener('open', () => updateConnectionStatus('connected'));
            this.addEventListener('close', () => updateConnectionStatus('disconnected'));
            this.addEventListener('error', () => updateConnectionStatus('disconnected'));
            if (this.readyState === WebSocket.OPEN) {
                updateConnectionStatus('connected');
            }
        }
        return originalSend.apply(this, args);
    };

    // Updates the connection status display in the UI
    function updateConnectionStatus(status) {
        socketStatus = status;
        const statusIndicator = document.getElementById('drwr-autodraw-connectionStatus');
        const statusText = document.getElementById('drwr-autodraw-statusText');

        if (statusIndicator && statusText) {
            if (status === 'connected') {
                statusIndicator.className = 'drwr-autodraw-status-indicator drwr-autodraw-status-connected';
                statusText.textContent = 'Connected';
            } else {
                statusIndicator.className = 'drwr-autodraw-status-indicator drwr-autodraw-status-disconnected';
                statusText.textContent = 'Disconnected';
            }
        }
    }

    // === Local Drawing Function ===
    // This function draws on the local canvas using normalized coordinates.
    // It's crucial for the "botless" local rendering.
    function drawLineLocally(startX_norm, startY_norm, endX_norm, endY_norm, thickness, color) {
        if (!canvas || !ctx) {
            console.error("Canvas or context not available for local drawing.");
            return;
        }

        // Convert normalized coordinates (0.0 to 1.0) to pixel coordinates for local drawing
        const startPixelX = startX_norm * canvas.width;
        const startPixelY = startY_norm * canvas.height;
        const endPixelX = endX_norm * canvas.width;
        const endPixelY = endY_norm * canvas.height;

        // Draw locally
        ctx.strokeStyle = color;
        // Drawaria uses negative thickness for drawing commands. Convert it to positive for local rendering.
        ctx.lineWidth = Math.abs(thickness);
        ctx.lineCap = 'round';
        ctx.beginPath();
        ctx.moveTo(startPixelX, startPixelY);
        ctx.lineTo(endPixelX, endPixelY);
        ctx.stroke();
    }


    // =================================================================================
    // === 2. NUEVA FUNCIONALIDAD: PEGADO VECTORIZADO ===
    // =================================================================================

    // --- Configuración para el método Vectorizado ---
    const VEC_RESOLUTION = 120; // Resolución a la que se procesa la imagen. 120 es un buen balance.
    const VEC_COLORS = 16;      // Número de colores a los que se reduce la imagen. Menos colores = menos comandos.
    const VEC_LINE_THICKNESS = 4; // Grosor de las líneas dibujadas. Ayuda a rellenar espacios.

    /**
     * Convierte una imagen en una serie de comandos de LÍNEAS HORIZONTALES.
     */
    function imageToScanlineCommands(img, callback) {
        const palette = [
            [0,0,0], [255,255,255], [255,0,0], [0,255,0], [0,0,255], [255,255,0],
            [0,255,255], [255,0,255], [128,128,128], [192,192,192], [128,0,0],
            [0,128,0], [0,0,128], [128,128,0], [128,0,128], [0,128,128]
        ].slice(0, VEC_COLORS);

        function findClosestColor(r, g, b) {
            let closest = palette[0];
            let minDistance = Infinity;
            for (const color of palette) {
                const dist = Math.sqrt(Math.pow(r - color[0], 2) + Math.pow(g - color[1], 2) + Math.pow(b - color[2], 2));
                if (dist < minDistance) {
                    minDistance = dist;
                    closest = color;
                }
            }
            return `rgb(${closest[0]},${closest[1]},${closest[2]})`;
        }

        const offCanvas = document.createElement('canvas');
        const aspectRatio = img.height / img.width;
        offCanvas.width = VEC_RESOLUTION;
        offCanvas.height = Math.round(VEC_RESOLUTION * aspectRatio);
        const offCtx = offCanvas.getContext('2d');
        offCtx.drawImage(img, 0, 0, offCanvas.width, offCanvas.height);

        const imgData = offCtx.getImageData(0, 0, offCanvas.width, offCanvas.height);
        const commands = [];
        const baseW = offCanvas.width;
        const baseH = offCanvas.height;

        const gameCanvasWidth = 780; // Drawaria's internal canvas width for normalization
        const gameCanvasHeight = 650; // Drawaria's internal canvas height for normalization

        for (let y = 0; y < baseH; y++) {
            let x = 0;
            while (x < baseW) {
                const i = (y * baseW + x) * 4;
                const r = imgData.data[i], g = imgData.data[i+1], b = imgData.data[i+2], a = imgData.data[i+3];

                if (a < 128) { // Ignorar píxeles transparentes
                    x++;
                    continue;
                }

                const color = findClosestColor(r, g, b);
                if (color === 'rgb(255,255,255)') { // Opcional: Ignorar el blanco para ahorrar comandos
                    x++;
                    continue;
                }

                let startX = x;
                while (x < baseW && findClosestColor(imgData.data[(y * baseW + x) * 4], imgData.data[(y * baseW + x) * 4 + 1], imgData.data[(y * baseW + x) * 4 + 2]) === color) {
                    x++;
                }
                let endX = x - 1;

                // Prepare parameters for both sending and local drawing
                const normX1 = ((startX * (gameCanvasWidth / baseW)) / gameCanvasWidth);
                const normY1 = ((y * (gameCanvasHeight / baseH)) / gameCanvasHeight);
                const normX2 = ((endX * (gameCanvasWidth / baseW)) / gameCanvasWidth);
                const protocolThickness = 0 - VEC_LINE_THICKNESS; // Negative thickness for Drawaria protocol

                // Store both the raw command string and the parameters for local drawing
                commands.push({
                    protocolCommand: `42["drawcmd",0,[${normX1.toFixed(4)},${normY1.toFixed(4)},${normX2.toFixed(4)},${normY1.toFixed(4)},false,${protocolThickness},"${color}",0,0,{}]]`,
                    localDrawParams: [normX1, normY1, normX2, normY1, protocolThickness, color]
                });
            }
        }
        callback(commands);
    }

    /**
     * Función genérica para enviar comandos y dibujarlos localmente.
     */
    function sendCommands(commands, statusElement, buttonElement) {
        if (!drawariaSocket || drawariaSocket.readyState !== WebSocket.OPEN) {
            statusElement.innerText = `Error: Not connected to Drawaria.`;
            if(buttonElement) buttonElement.disabled = false;
            updateConnectionStatus('disconnected'); // Ensure UI reflects disconnection
            return;
        }
        statusElement.innerText = `Sending ${commands.length} commands...`;
        if(buttonElement) buttonElement.disabled = true;

        // Clear the local canvas before drawing a new image
        if (ctx) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        }

        let i = 0;
        function send() {
            for (let j = 0; j < 20 && i < commands.length; j++, i++) { // Send in batches to avoid overwhelming
                const cmd = commands[i];
                // Draw locally
                drawLineLocally(...cmd.localDrawParams);
                // Send to server
                drawariaSocket.send(cmd.protocolCommand);
            }
            if (i < commands.length) {
                setTimeout(send, 1); // Minimal pause for next batch
            } else {
                 statusElement.innerText = `Image pasted with ${commands.length} commands!`;
                 if(buttonElement) buttonElement.disabled = false;
            }
        }
        send();
    }


    // =================================================================================
    // === 3. INTERFAZ DE USUARIO (UI UNIFICADA Y MEJORADA) ===
    // =================================================================================

    // === UI Elements CSS ===
    const style = document.createElement('style');
    style.textContent = `
        /* Boxicons import for icons */
        @import url('https://unpkg.com/[email protected]/css/boxicons.min.css');

        .drwr-autodraw-tool-container {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 280px;
            max-height: 90vh;
            background: #2c3e50;
            border-radius: 10px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
            color: #ecf0f1;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            z-index: 9999;
            overflow: hidden;
            display: none; /* Hidden by default, toggled by 'A' button */
            flex-direction: column;
        }

        .drwr-autodraw-tool-header {
            background: #34495e;
            padding: 12px 15px;
            cursor: grab;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #2c3e50;
            user-select: none;
        }
        .drwr-autodraw-tool-header.dragging {
            cursor: grabbing;
        }

        .drwr-autodraw-tool-title {
            font-weight: 600;
            font-size: 16px;
            color: #ecf0f1;
        }

        .drwr-autodraw-tool-close {
            background: none;
            border: none;
            color: #ecf0f1;
            font-size: 18px;
            cursor: pointer;
            transition: color 0.2s;
        }
        .drwr-autodraw-tool-close:hover {
            color: #e74c3c;
        }

        .drwr-autodraw-tool-content {
            padding: 15px;
            overflow-y: auto;
            flex-grow: 1;
        }

        .drwr-autodraw-section-title {
            font-size: 14px;
            color: #3498db;
            margin-top: 10px;
            margin-bottom: 10px;
            padding-bottom: 5px;
            border-bottom: 1px solid #2c3e50;
            font-weight: bold;
        }

        .drwr-autodraw-form-group {
            margin-bottom: 15px;
        }

        .drwr-autodraw-form-group label {
            display: block;
            margin-bottom: 5px;
            font-size: 13px;
            color: #bdc3c7;
        }

        .drwr-autodraw-btn {
            padding: 8px 10px;
            border-radius: 4px;
            font-size: 13px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
            border: none;
            color: white;
            background-color: #008000; /* Green for vectorized paste */
            width: 100%;
            margin-top: 5px;
        }
        .drwr-autodraw-btn:hover {
            background-color: #006400;
            transform: translateY(-1px);
        }
        .drwr-autodraw-btn:disabled {
            background-color: #555;
            cursor: not-allowed;
        }

        .drwr-autodraw-status-indicator {
            display: inline-block;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            margin-right: 5px;
            vertical-align: middle;
        }
        .drwr-autodraw-status-connected { background-color: #2ecc71; }
        .drwr-autodraw-status-disconnected { background-color: #e74c3c; }

        /* File Input Styling */
        .drwr-autodraw-tool-container input[type="file"] {
            width: 100%;
            padding: 8px;
            margin-top: 5px;
            background-color: #34495e;
            border: 1px solid #2c3e50;
            border-radius: 4px;
            color: #ecf0f1;
            cursor: pointer;
            font-size: 13px;
        }
        .drwr-autodraw-tool-container input[type="file"]::file-selector-button {
            background-color: #3498db;
            color: white;
            border: none;
            padding: 6px 12px;
            border-radius: 3px;
            cursor: pointer;
            margin-right: 10px;
            transition: background-color 0.2s;
        }
        .drwr-autodraw-tool-container input[type="file"]::file-selector-button:hover {
            background-color: #2980b9;
        }

        /* Toggle Button Styles */
        #drwr-autodraw-toggleButton {
            position: fixed;
            bottom: 10px;
            left: 10px;
            z-index: 1001;
            background-color: #3498db; /* Blue color */
            color: white;
            font-weight: bold;
            font-size: 18px;
            cursor: pointer;
            width: 45px;
            height: 45px;
            border-radius: 50%;
            border: none;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            transition: background-color 0.2s, transform 0.2s;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        #drwr-autodraw-toggleButton:hover {
            background-color: #2980b9;
            transform: scale(1.05);
        }
        #drwr-autodraw-toggleButton.active {
            background-color: #7f8c8d; /* Grey when active/menu open */
        }
    `;
    document.head.appendChild(style);

    // === UI Elements HTML Structure ===
    const mainContainer = document.createElement('div');
    mainContainer.className = 'drwr-autodraw-tool-container';
    mainContainer.innerHTML = `
        <div class="drwr-autodraw-tool-header">
            <div class="drwr-autodraw-tool-title">AutoDraw Vectorized (Fast)</div>
            <button class="drwr-autodraw-tool-close">×</button>
        </div>
        <div class="drwr-autodraw-tool-content">
            <div class="drwr-autodraw-section-title">Connection Status</div>
            <div style="margin-bottom: 15px;">
                <span id="drwr-autodraw-connectionStatus" class="drwr-autodraw-status-indicator drwr-autodraw-status-disconnected"></span>
                <span id="drwr-autodraw-statusText">Disconnected</span>
            </div>

            <div class="drwr-autodraw-section-title">Pick Image and Press Start</div>
            <div class="drwr-autodraw-form-group">
                <label for="drwr-autodraw-imageInput">Select Image</label>
                <input type="file" id="drwr-autodraw-imageInput" accept="image/*">
            </div>
            <button id="drwr-autodraw-vectorizedButton" class="drwr-autodraw-btn">Start (Vectorized - FAST)</button>
            <div id="drwr-autodraw-imageStatus" style="font-size: 0.8em; margin-top: 5px; color: #bdc3c7;"></div>
        </div>
    `;
    document.body.appendChild(mainContainer);

    // Toggle Button
    const toggleButton = document.createElement('button');
    toggleButton.id = 'drwr-autodraw-toggleButton';
    toggleButton.innerHTML = `<i class="fas fa-paint-brush"></i>`; // Paint brush icon
    toggleButton.title = "Toggle AutoDraw Vectorized";
    document.body.appendChild(toggleButton);

    // === UI Elements References ===
    const imageInput = document.getElementById('drwr-autodraw-imageInput');
    const vectorizedButton = document.getElementById('drwr-autodraw-vectorizedButton');
    const imageStatus = document.getElementById('drwr-autodraw-imageStatus');


    // =================================================================================
    // === 4. MANEJADORES DE EVENTOS ===
    // =================================================================================

    function handleImageFile(callback) {
        if (!imageInput.files.length) { alert('Please select an image first.'); return; }
        if (!drawariaSocket || drawariaSocket.readyState !== WebSocket.OPEN) { alert("Socket not ready. Draw something to activate the connection."); return; }

        const file = imageInput.files[0];
        const reader = new FileReader();
        reader.onload = (e) => {
            const img = new Image();
            img.onload = () => {
                callback(img);
            };
            img.src = e.target.result;
        };
        reader.readAsDataURL(file);
    }

    // Evento para el botón Vectorizado
    vectorizedButton.addEventListener('click', () => {
        handleImageFile((img) => {
            imageStatus.innerText = 'Processing image (Vectorizing)...';
            vectorizedButton.disabled = true;
            imageToScanlineCommands(img, (commands) => {
                sendCommands(commands, imageStatus, vectorizedButton);
            });
        });
    });

    // Make the main container draggable
    let isDragging = false;
    let offsetX, offsetY;
    const header = mainContainer.querySelector('.drwr-autodraw-tool-header');

    header.addEventListener('mousedown', (e) => {
        // Prevent dragging if click is on buttons within the header
        if (e.target.closest('button')) return;
        isDragging = true;
        offsetX = e.clientX - mainContainer.getBoundingClientRect().left;
        offsetY = e.clientY - mainContainer.getBoundingClientRect().top;
        mainContainer.classList.add('dragging');
        e.preventDefault(); // Prevent text selection
    });

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

    document.addEventListener('mouseup', () => {
        if (isDragging) {
            isDragging = false;
            mainContainer.classList.remove('dragging');
        }
    });

    // Close button for the main container
    mainContainer.querySelector('.drwr-autodraw-tool-close').addEventListener('click', () => {
        mainContainer.style.display = 'none';
        toggleButton.classList.remove('active'); // Deactivate toggle button
    });

    // Toggle Button Logic
    toggleButton.addEventListener('click', () => {
        const isHidden = mainContainer.style.display === 'none' || mainContainer.style.display === '';
        mainContainer.style.display = isHidden ? 'flex' : 'none'; // Use 'flex' for vertical layout

        if (isHidden) {
            toggleButton.classList.add('active'); // Style active state
            // Update canvas dimensions if they changed while menu was closed
            // No direct action needed here, as canvas.width/height are live properties.
        } else {
            toggleButton.classList.remove('active');
        }
    });

    // === Initialization ===
    function initializeScript() {
        // Wait for the main game canvas to be available and its dimensions to be non-zero
        // and for the chatbox_textinput (a common element that implies game is loaded)
        if (!document.getElementById('canvas') || !document.getElementById('chatbox_textinput') || !(canvas && canvas.width > 0 && canvas.height > 0)) {
            setTimeout(initializeScript, 500); // Retry after 500ms
            return;
        }

        // Initial setup for the UI and WebSocket status
        updateConnectionStatus(drawariaSocket && drawariaSocket.readyState === WebSocket.OPEN ? 'connected' : 'disconnected');
        console.log("Drawaria Bad AutoDraw Tool Loaded and Ready!");
    }

    // Run initialization function when document is ready
    if (document.readyState === "complete" || document.readyState === "interactive") {
        initializeScript();
    } else {
        window.addEventListener('load', initializeScript);
    }

})();