// ==UserScript==
// @name Epic Pixel Laser Destroyer for Drawaria
// @namespace http://tampermonkey.net/
// @version 4.0
// @description Capture drawn pixels from canvas and DESTROY them with epic laser beams!
// @author YouTubeDrawaria
// @match https://drawaria.online/*
// @match https://*.drawaria.online/*
// @grant GM_xmlhttpRequest
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// ==/UserScript==
(function () {
'use strict';
/* ---------- CONFIGURACIÓN DEL SISTEMA LÁSER ---------- */
const LASER_MODES = {
'Láser Rojo Clásico': {
color: '#ff0000',
name: '🔴 Classic Red Laser',
particles: 'fire',
speed: 'fast'
},
'Láser Azul Industrial': {
color: '#00ffff',
name: '🔵 Industrial Blue Laser',
particles: 'electric',
speed: 'medium'
},
'Láser Verde Militar': {
color: '#00ff00',
name: '🟢 Military Green Laser',
particles: 'smoke',
speed: 'precise'
},
'Plasma Púrpura': {
color: '#ff00ff',
name: '🟣 Purple Plasma Beam',
particles: 'energy',
speed: 'slow'
},
'Láser Dorado Épico': {
color: '#ffd700',
name: '🟡 Epic Golden Laser',
particles: 'golden',
speed: 'instant'
}
};
/* ---------- VARIABLES GLOBALES ---------- */
let canvas, ctx, socket;
let currentLaserMode = 'Láser Rojo Clásico';
let laserIntensity = 3;
let isDestroying = false;
let drawingPixels = []; // Almacena píxeles dibujados capturados
let sampleRate = 4; // Rate de muestreo para reducir píxeles por rendimiento
let originalWebSocketSend;
/* ---------- SISTEMA DE CAPTURA DE PÍXELES ---------- */
function captureDrawingPixels() {
if (!canvas || !ctx) {
console.warn("Canvas no encontrado o no inicializado");
return false;
}
const cw = canvas.width;
const ch = canvas.height;
const imageData = ctx.getImageData(0, 0, cw, ch);
const data = imageData.data;
drawingPixels = [];
// Usar la misma lógica que GenerativeAnimatorTool
for (let y = 0; y < ch; y += sampleRate) {
for (let x = 0; x < cw; x += sampleRate) {
const i = (y * cw + x) * 4;
// Un píxel se considera "dibujado" si no es totalmente transparente (alpha > 100)
// Y si no es un blanco casi puro (R, G, B < 250)
if (data[i + 3] > 100 && (data[i] < 250 || data[i + 1] < 250 || data[i + 2] < 250)) {
drawingPixels.push({
x: x,
y: y,
normalizedX: x / cw,
normalizedY: y / ch,
color: `rgb(${data[i]},${data[i + 1]},${data[i + 2]})`,
r: data[i],
g: data[i + 1],
b: data[i + 2],
a: data[i + 3]
});
}
}
}
console.log(`🎨 Píxeles capturados: ${drawingPixels.length} puntos detectados`);
return drawingPixels.length > 0;
}
/* ---------- FUNCIONES AUXILIARES DE WEBSOCKET ---------- */
function initializeWebSocketInterceptor() {
originalWebSocketSend = WebSocket.prototype.send;
WebSocket.prototype.send = function (data) {
if (!socket) socket = this;
return originalWebSocketSend.call(this, data);
};
}
function getGameSocket() {
return socket;
}
function sendLaserToServer(x1, y1, x2, y2, color, thickness) {
if (!socket || !canvas) return;
const normX1 = (x1 / canvas.width).toFixed(4);
const normY1 = (y1 / canvas.height).toFixed(4);
const normX2 = (x2 / canvas.width).toFixed(4);
const normY2 = (y2 / canvas.height).toFixed(4);
const cmd = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${thickness},"${color}",0,0,{}]]`;
originalWebSocketSend.call(socket, cmd);
}
function sendEraseCommand(x, y, size) {
if (!socket || !canvas) return;
const normX1 = ((x - size) / canvas.width).toFixed(4);
const normY1 = ((y - size) / canvas.height).toFixed(4);
const normX2 = ((x + size) / canvas.width).toFixed(4);
const normY2 = ((y + size) / canvas.height).toFixed(4);
const eraseCmd = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${-(size * 2)},"#000000",0,0,{}]]`;
originalWebSocketSend.call(socket, eraseCmd);
}
/* ---------- EFECTOS VISUALES DEL LÁSER ---------- */
function drawLaserBeam(startX, startY, endX, endY, laserColor, intensity = 3) {
if (!ctx) return;
// Núcleo brillante del láser
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 3 + intensity;
ctx.lineCap = 'round';
ctx.shadowBlur = 15;
ctx.shadowColor = laserColor;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();
// Halo exterior
ctx.strokeStyle = laserColor;
ctx.lineWidth = 8 + intensity * 2;
ctx.shadowBlur = 25;
ctx.globalAlpha = 0.8;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();
// Limpiar efectos
ctx.shadowBlur = 0;
ctx.globalAlpha = 1.0;
// Enviar al servidor
sendLaserToServer(startX, startY, endX, endY, laserColor, 8 + intensity * 2);
}
function createVaporizationParticles(x, y, originalPixel, particleType) {
if (!ctx) return;
const particleCount = 8 + laserIntensity * 2;
let particleColors;
switch (particleType) {
case 'fire':
particleColors = ['#ff4500', '#ff6600', '#ffa500', '#ff0000'];
break;
case 'electric':
particleColors = ['#00ffff', '#87ceeb', '#ffffff', '#add8e6'];
break;
case 'smoke':
particleColors = ['#696969', '#808080', '#a0a0a0', '#778899'];
break;
case 'energy':
particleColors = ['#ff00ff', '#ff69b4', '#da70d6', '#dda0dd'];
break;
case 'golden':
particleColors = ['#ffd700', '#ffdf00', '#fff700', '#f0e68c'];
break;
default:
particleColors = [originalPixel.color, '#ffffff', '#f0f0f0'];
}
for (let i = 0; i < particleCount; i++) {
setTimeout(() => {
const angle = (Math.PI * 2 * i) / particleCount + Math.random() * 0.5;
const distance = 15 + Math.random() * 25;
const particleX = x + distance * Math.cos(angle);
const particleY = y + distance * Math.sin(angle);
const color = particleColors[Math.floor(Math.random() * particleColors.length)];
// Dibujar partícula local
ctx.fillStyle = color;
ctx.shadowBlur = 8;
ctx.shadowColor = color;
ctx.globalAlpha = 0.8;
ctx.fillRect(particleX - 2, particleY - 2, 4, 4);
ctx.globalAlpha = 1.0;
ctx.shadowBlur = 0;
// Enviar partícula al servidor
sendLaserToServer(x, y, particleX, particleY, color, 3);
}, i * 30);
}
}
function createPixelShockwave(x, y, intensity) {
if (!ctx) return;
const maxRadius = 30 + intensity * 8;
let currentRadius = 0;
const shockwaveInterval = setInterval(() => {
currentRadius += 4;
if (currentRadius > maxRadius) {
clearInterval(shockwaveInterval);
return;
}
const alpha = 1 - (currentRadius / maxRadius);
const color = LASER_MODES[currentLaserMode].color;
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.globalAlpha = alpha * 0.6;
ctx.shadowBlur = 8;
ctx.shadowColor = color;
ctx.beginPath();
ctx.arc(x, y, currentRadius, 0, Math.PI * 2);
ctx.stroke();
ctx.globalAlpha = 1.0;
ctx.shadowBlur = 0;
}, 50);
}
/* ---------- SISTEMA DE DESTRUCCIÓN LÁSER POR PÍXELES ---------- */
async function laserDestroyPixels() {
if (isDestroying) {
console.log('🚫 Ya hay una destrucción en curso');
return;
}
// Capturar píxeles actuales
const captured = captureDrawingPixels();
if (!captured || drawingPixels.length === 0) {
alert('🚫 No hay píxeles dibujados para destruir. ¡Dibuja algo primero!');
return;
}
if (!getGameSocket()) {
alert('🚫 No hay conexión WebSocket. Conéctate a una sala primero.');
return;
}
isDestroying = true;
console.log(`🔫 Iniciando destrucción láser de ${drawingPixels.length} píxeles`);
const laserMode = LASER_MODES[currentLaserMode];
const pixelsCopy = [...drawingPixels];
// Agrupar píxeles cercanos para mayor eficiencia
const pixelGroups = groupNearbyPixels(pixelsCopy, 20);
console.log(`📊 ${pixelsCopy.length} píxeles agrupados en ${pixelGroups.length} grupos`);
// Configurar velocidad según el modo
let destroySpeed;
switch (laserMode.speed) {
case 'instant': destroySpeed = 30; break;
case 'fast': destroySpeed = 80; break;
case 'medium': destroySpeed = 150; break;
case 'precise': destroySpeed = 300; break;
case 'slow': destroySpeed = 500; break;
default: destroySpeed = 150;
}
for (let i = 0; i < pixelGroups.length; i++) {
if (!isDestroying) break;
const group = pixelGroups[i];
const centerPixel = group[Math.floor(group.length / 2)]; // Pixel central del grupo
const targetX = centerPixel.x;
const targetY = centerPixel.y;
// Puntos de origen del láser (variados)
const laserOrigins = [
{ x: targetX + (Math.random() - 0.5) * 100, y: -30 }, // Desde arriba
{ x: -30, y: targetY + (Math.random() - 0.5) * 50 }, // Desde la izquierda
{ x: canvas.width + 30, y: targetY + (Math.random() - 0.5) * 50 }, // Desde la derecha
{ x: targetX + (Math.random() - 0.5) * 100, y: canvas.height + 30 } // Desde abajo
];
const origin = laserOrigins[i % laserOrigins.length];
// Efectos de pre-carga
setTimeout(() => {
if (!isDestroying) return;
// Punto de carga del láser
ctx.fillStyle = laserMode.color;
ctx.shadowBlur = 15;
ctx.shadowColor = laserMode.color;
ctx.globalAlpha = 0.8;
ctx.fillRect(origin.x - 2, origin.y - 2, 4, 4);
ctx.globalAlpha = 1.0;
ctx.shadowBlur = 0;
}, i * destroySpeed);
// Disparo del láser
setTimeout(() => {
if (!isDestroying) return;
// Rayo láser principal
drawLaserBeam(origin.x, origin.y, targetX, targetY, laserMode.color, laserIntensity);
// Efectos de impacto
createPixelShockwave(targetX, targetY, laserIntensity);
createVaporizationParticles(targetX, targetY, centerPixel, laserMode.particles);
}, i * destroySpeed + 100);
// Borrado de píxeles del grupo
setTimeout(() => {
if (!isDestroying) return;
// Borrar cada píxel del grupo
group.forEach(pixel => {
// Borrar localmente
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = '#000000';
ctx.fillRect(pixel.x - 2, pixel.y - 2, 4, 4);
ctx.globalCompositeOperation = 'source-over';
});
// Enviar comando de borrado al servidor para el grupo
const eraseSize = Math.max(4, group.length / 3);
sendEraseCommand(targetX, targetY, eraseSize);
}, i * destroySpeed + 250);
}
// Finalizar destrucción
setTimeout(() => {
drawingPixels = [];
isDestroying = false;
console.log('🔥 ¡Destrucción por píxeles completada!');
}, pixelGroups.length * destroySpeed + 1000);
}
function groupNearbyPixels(pixels, maxDistance) {
const groups = [];
const used = new Set();
pixels.forEach((pixel, index) => {
if (used.has(index)) return;
const group = [pixel];
used.add(index);
// Buscar píxeles cercanos
for (let i = index + 1; i < pixels.length; i++) {
if (used.has(i)) continue;
const otherPixel = pixels[i];
const distance = Math.sqrt(
Math.pow(pixel.x - otherPixel.x, 2) +
Math.pow(pixel.y - otherPixel.y, 2)
);
if (distance <= maxDistance) {
group.push(otherPixel);
used.add(i);
}
}
groups.push(group);
});
return groups;
}
function stopDestruction() {
isDestroying = false;
console.log('🛑 Destrucción láser detenida por el usuario');
}
/* ---------- INTERFAZ DE USUARIO ---------- */
function createLaserUI() {
const container = document.createElement('div');
container.style.cssText = `
position: fixed; top: 10px; right: 10px; z-index: 9999;
background: linear-gradient(135deg, #1a1a2e, #16213e, #0f3460);
color: #fff; padding: 20px; border-radius: 15px;
font-family: 'Orbitron', 'Segoe UI', Arial, sans-serif; font-size: 13px;
display: flex; flex-direction: column; gap: 15px;
box-shadow: 0 8px 25px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.1);
border: 2px solid #ff6b35; min-width: 320px;
backdrop-filter: blur(10px);
`;
// Título
const title = document.createElement('div');
title.innerHTML = '🎨 PIXEL LASER DESTROYER 🔫';
title.style.cssText = `
font-weight: bold; font-size: 16px; text-align: center; cursor: grab;
background: linear-gradient(45deg, #ff6b35, #f7931e, #ffcc02);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
text-shadow: 0 0 10px rgba(255, 107, 53, 0.5);
margin: -20px -20px 10px -20px; padding: 20px;
border-bottom: 2px solid rgba(255, 107, 53, 0.3);
border-radius: 15px 15px 0 0;
`;
container.appendChild(title);
// Estado de conexión
const connectionStatus = document.createElement('div');
connectionStatus.style.cssText = `
background: rgba(255, 107, 53, 0.2); padding: 10px; border-radius: 8px;
text-align: center; font-size: 12px; display: flex; align-items: center; gap: 8px;
`;
container.appendChild(connectionStatus);
// Información de píxeles capturados
const pixelInfo = document.createElement('div');
pixelInfo.style.cssText = `
background: rgba(0, 255, 255, 0.1); padding: 10px; border-radius: 8px;
text-align: center; font-size: 14px; font-weight: bold;
`;
container.appendChild(pixelInfo);
// Botón de captura manual
const captureBtn = document.createElement('button');
captureBtn.textContent = '📷 Scan Drawing Pixels';
captureBtn.style.cssText = `
padding: 12px 15px; border-radius: 8px; border: none; font-size: 14px;
background: linear-gradient(145deg, #00ffff, #0080ff); color: #000;
font-weight: bold; cursor: pointer; transition: all 0.3s ease;
`;
captureBtn.addEventListener('click', () => {
const captured = captureDrawingPixels();
if (captured) {
console.log(`📷 Captura manual: ${drawingPixels.length} píxeles detectados`);
}
});
container.appendChild(captureBtn);
// Selector de modo láser
const modeRow = createControlRow('🎯 Laser Mode:');
const modeSelect = document.createElement('select');
modeSelect.style.cssText = getSelectStyle();
Object.keys(LASER_MODES).forEach(modeName => {
const option = document.createElement('option');
option.value = modeName;
option.textContent = LASER_MODES[modeName].name;
modeSelect.appendChild(option);
});
modeSelect.value = currentLaserMode;
modeSelect.addEventListener('change', (e) => {
currentLaserMode = e.target.value;
console.log(`🔫 Modo láser: ${currentLaserMode}`);
});
modeRow.appendChild(modeSelect);
container.appendChild(modeRow);
// Control de intensidad
const intensityRow = createControlRow('⚡ Power:');
const intensitySlider = document.createElement('input');
intensitySlider.type = 'range';
intensitySlider.min = '1';
intensitySlider.max = '5';
intensitySlider.value = laserIntensity.toString();
intensitySlider.style.cssText = getSliderStyle();
intensitySlider.addEventListener('input', (e) => {
laserIntensity = parseInt(e.target.value);
});
intensityRow.appendChild(intensitySlider);
container.appendChild(intensityRow);
// Control de sample rate
const sampleRow = createControlRow('🔍 Quality:');
const sampleSlider = document.createElement('input');
sampleSlider.type = 'range';
sampleSlider.min = '1';
sampleSlider.max = '8';
sampleSlider.value = sampleRate.toString();
sampleSlider.style.cssText = getSliderStyle();
sampleSlider.addEventListener('input', (e) => {
sampleRate = parseInt(e.target.value);
console.log(`🔍 Sample rate: ${sampleRate} (menor = más píxeles)`);
});
sampleRow.appendChild(sampleSlider);
container.appendChild(sampleRow);
// Botón principal de destrucción
const destroyBtn = document.createElement('button');
destroyBtn.textContent = '🔥 VAPORIZE ALL PIXELS 🔥';
destroyBtn.style.cssText = `
padding: 15px 20px; border-radius: 10px; border: none; font-size: 16px;
background: linear-gradient(145deg, #ff6b35, #f7931e); color: white;
font-weight: bold; cursor: pointer; transition: all 0.3s ease;
box-shadow: 0 6px 20px rgba(255, 107, 53, 0.4); text-transform: uppercase;
`;
destroyBtn.addEventListener('click', laserDestroyPixels);
container.appendChild(destroyBtn);
// Botón de parada
const stopBtn = document.createElement('button');
stopBtn.textContent = '🛑 STOP VAPORIZATION';
stopBtn.style.cssText = `
padding: 12px 18px; border-radius: 8px; border: none; font-size: 14px;
background: linear-gradient(145deg, #ff1744, #d50000); color: white;
font-weight: bold; cursor: pointer; transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(255, 23, 68, 0.4);
`;
stopBtn.addEventListener('click', stopDestruction);
container.appendChild(stopBtn);
// Botón para limpiar píxeles
const clearBtn = document.createElement('button');
clearBtn.textContent = '🗑️ Clear Pixel Memory';
clearBtn.style.cssText = `
padding: 10px 15px; border-radius: 8px; border: none; font-size: 12px;
background: linear-gradient(145deg, #666, #444); color: white;
cursor: pointer; transition: all 0.3s ease;
`;
clearBtn.addEventListener('click', () => {
drawingPixels = [];
console.log('🗑️ Memoria de píxeles limpiada');
});
container.appendChild(clearBtn);
document.body.appendChild(container);
makeDraggable(container, title);
// Actualizar información periódicamente
setInterval(() => {
const isConnected = getGameSocket() !== null;
connectionStatus.innerHTML = `
<span style="width: 12px; height: 12px; border-radius: 50%; background: ${isConnected ? '#00ff00' : '#ff0000'}; display: inline-block;"></span>
<span>WebSocket: ${isConnected ? 'Connected' : 'Disconnected'}</span>
`;
pixelInfo.innerHTML = `
📊 Detected Pixels: <span style="color: #ff6b35;">${drawingPixels.length}</span><br>
🔍 Sample Rate: ${sampleRate} | ⚡ Power: ${laserIntensity}<br>
💡 Draw something, then VAPORIZE!
`;
}, 1000);
}
function createControlRow(label) {
const row = document.createElement('div');
row.style.cssText = 'display: flex; align-items: center; gap: 12px;';
const labelEl = document.createElement('span');
labelEl.textContent = label;
labelEl.style.cssText = 'color: #ff6b35; font-weight: bold; min-width: 110px;';
row.appendChild(labelEl);
return row;
}
function getSelectStyle() {
return `
flex-grow: 1; padding: 8px 12px; border-radius: 8px;
border: 2px solid #ff6b35; background: rgba(20, 20, 40, 0.8);
color: #ff6b35; font-size: 13px; cursor: pointer;
`;
}
function getSliderStyle() {
return `
flex-grow: 1; -webkit-appearance: none; height: 6px; border-radius: 5px;
background: linear-gradient(to right, #ff6b35 0%, #f7931e 100%);
`;
}
function makeDraggable(container, handle) {
let isDragging = false;
let dragOffsetX = 0;
let dragOffsetY = 0;
handle.addEventListener('mousedown', (e) => {
isDragging = true;
dragOffsetX = e.clientX - container.offsetLeft;
dragOffsetY = e.clientY - container.offsetTop;
handle.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
container.style.left = (e.clientX - dragOffsetX) + 'px';
container.style.top = (e.clientY - dragOffsetY) + 'px';
container.style.right = 'auto';
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
handle.style.cursor = 'grab';
});
}
/* ---------- INICIALIZACIÓN ---------- */
function initialize() {
console.log('🎨 Iniciando Pixel Laser Destroyer...');
const checkCanvas = setInterval(() => {
canvas = document.getElementById('canvas');
ctx = canvas ? canvas.getContext('2d') : null;
if (canvas && ctx) {
clearInterval(checkCanvas);
initializeWebSocketInterceptor();
createLaserUI();
console.log('🔫 Pixel Laser Destroyer listo!');
console.log('🎨 Dibuja algo, luego usa VAPORIZE ALL PIXELS');
console.log('📷 El script detecta píxeles automáticamente usando getImageData()');
}
}, 500);
}
// Inicializar cuando la página esté lista
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();