您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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); } })();