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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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);
    }

})();