Motor de renderizado de mapas HD. Texturas vectoriales, sombras 3D, nubes, arbustos y castillo sin sacrificar el rendimiento instantáneo.
// ==UserScript==
// @name Drawaria Super Mario Levels 🍄
// @namespace http://tampermonkey.net/
// @version 2.0
// @description Motor de renderizado de mapas HD. Texturas vectoriales, sombras 3D, nubes, arbustos y castillo sin sacrificar el rendimiento instantáneo.
// @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';
/* ---------- SHARED GLOBALS ---------- */
let drawariaSocket = null;
let drawariaCanvas = null;
let drawariaCtx = null;
/* ---------- BATCH QUEUE SYSTEM ---------- */
const commandQueue =[];
let batchProcessor = null;
const BATCH_SIZE = 70; // Aumentado para soportar texturas HD
const BATCH_INTERVAL = 20;
const originalWebSocketSend = WebSocket.prototype.send;
WebSocket.prototype.send = function (...args) {
if (!drawariaSocket && this.url && this.url.includes('drawaria')) {
drawariaSocket = this;
console.log('🍄 Level Maker HD: WebSocket Interceptado.');
startBatchProcessor();
}
return originalWebSocketSend.apply(this, args);
};
function startBatchProcessor() {
if (batchProcessor) return;
batchProcessor = setInterval(() => {
if (!drawariaSocket || drawariaSocket.readyState !== WebSocket.OPEN || commandQueue.length === 0) return;
const batch = commandQueue.splice(0, BATCH_SIZE);
batch.forEach(cmd => { try { drawariaSocket.send(cmd); } catch (e) {} });
}, BATCH_INTERVAL);
}
// Nuevo parámetro CAP para permitir bordes redondos en nubes
function enqueueDrawCommand(x1, y1, x2, y2, color, thickness, cap = 'square') {
if (!drawariaCanvas) return;
if (drawariaCtx) {
drawariaCtx.strokeStyle = color;
drawariaCtx.lineWidth = thickness;
drawariaCtx.lineCap = cap;
drawariaCtx.lineJoin = 'miter';
drawariaCtx.beginPath();
drawariaCtx.moveTo(x1, y1);
drawariaCtx.lineTo(x2, y2);
drawariaCtx.stroke();
}
const normX1 = (x1 / drawariaCanvas.width).toFixed(4);
const normY1 = (y1 / drawariaCanvas.height).toFixed(4);
const normX2 = (x2 / drawariaCanvas.width).toFixed(4);
const normY2 = (y2 / drawariaCanvas.height).toFixed(4);
// El servidor Drawaria usa el boolean del array para determinar si es línea normal (false)
const cmd = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${-Math.abs(thickness)},"${color}",0,0,{"2":0,"3":0.5,"4":0.5}]]`;
commandQueue.push(cmd);
}
async function cleanCanvas(skyColor) {
if (!drawariaCanvas) return;
const w = drawariaCanvas.width;
const h = drawariaCanvas.height;
if (drawariaCtx) drawariaCtx.clearRect(0, 0, w, h);
const nukeThickness = Math.max(w, h) * 2;
enqueueDrawCommand(w/2, h/2, w/2 + 0.1, h/2 + 0.1, skyColor, nukeThickness);
await new Promise(r => setTimeout(r, 60));
}
/* ---------- HD TILE RENDER ENGINE ---------- */
const PALETTE = {
'G': { base: '#c84c0c' }, // Suelo
'B': { base: '#cc5400' }, // Ladrillos
'?': { base: '#f8b800' }, // Bloque Moneda
'T': { base: '#00a800' }, // Tubería Top
'P': { base: '#00a800' }, // Tubería Cuerpo
'H': { base: '#f8d8b0' }, // Bloque Duro
'U': { base: '#000000' }, // Muro subterráneo
'O': { base: '#008888' }, // Ladrillos subterráneos
'W': { base: '#ffffff', bg: true }, // Nubes
'S': { base: '#00a800', bg: true } // Arbustos
};
function drawDecorations(char, startX, endX, cy, len, blockW, h, isUnderground) {
const topY = cy - h/2;
const bottomY = cy + h/2;
const w = blockW * len;
switch(char) {
case 'W': // Nubes (Round caps)
enqueueDrawCommand(startX + blockW/2, cy, endX - blockW/2, cy, '#ffffff', h, 'round');
enqueueDrawCommand(startX + blockW, cy - h*0.4, endX - blockW, cy - h*0.4, '#ffffff', h*0.8, 'round');
break;
case 'S': // Arbustos (Round caps)
enqueueDrawCommand(startX + blockW/2, cy + h*0.2, endX - blockW/2, cy + h*0.2, '#00a800', h*0.6, 'round');
enqueueDrawCommand(startX + blockW, cy - h*0.1, endX - blockW, cy - h*0.1, '#00a800', h*0.5, 'round');
enqueueDrawCommand(startX + blockW/2, bottomY, endX - blockW/2, bottomY, '#000000', 3); // Sombra base
break;
case 'G': // Suelo HD
if (!isUnderground) enqueueDrawCommand(startX, topY + 2, endX, topY + 2, '#00a800', 4); // Pasto
enqueueDrawCommand(startX, topY, endX, topY, '#000000', 2); // Borde
// Textura rocosa
for(let i=0; i<len; i++) {
enqueueDrawCommand(startX + i*blockW + 5, cy, startX + i*blockW + 15, cy, '#a03000', 2);
enqueueDrawCommand(startX + i*blockW + 15, cy + h*0.3, startX + i*blockW + 25, cy + h*0.3, '#a03000', 2);
}
break;
case 'B': // Ladrillos HD
case 'O':
enqueueDrawCommand(startX, topY+1, endX, topY+1, '#ffffff', 2); // Bisel superior
enqueueDrawCommand(startX, bottomY-1, endX, bottomY-1, '#000000', 2); // Sombra inferior
enqueueDrawCommand(startX, cy, endX, cy, '#000000', 2); // Cemento medio
// Cemento vertical intercalado
for(let x = startX; x <= endX; x += blockW/2) {
let isStag = Math.floor((x - startX) / (blockW/2)) % 2 === 0;
if(isStag) enqueueDrawCommand(x, topY, x, cy, '#000000', 2);
else enqueueDrawCommand(x, cy, x, bottomY, '#000000', 2);
}
break;
case '?': // Bloque de Moneda HD
for(let i=0; i<len; i++) {
let bx = startX + i*blockW;
let cx = bx + blockW/2;
// Bordes
enqueueDrawCommand(bx, topY, bx+blockW, topY, '#000000', 2);
enqueueDrawCommand(bx, bottomY, bx+blockW, bottomY, '#000000', 2);
enqueueDrawCommand(bx, topY, bx, bottomY, '#000000', 2);
enqueueDrawCommand(bx+blockW, topY, bx+blockW, bottomY, '#000000', 2);
// Remaches
enqueueDrawCommand(bx+4, topY+4, bx+4.1, topY+4, '#c84c0c', 4, 'round');
enqueueDrawCommand(bx+blockW-4, topY+4, bx+blockW-4.1, topY+4, '#c84c0c', 4, 'round');
enqueueDrawCommand(bx+4, bottomY-4, bx+4.1, bottomY-4, '#c84c0c', 4, 'round');
enqueueDrawCommand(bx+blockW-4, bottomY-4, bx+blockW-4.1, bottomY-4, '#c84c0c', 4, 'round');
// Símbolo ?
enqueueDrawCommand(cx-4, cy-6, cx+4, cy-6, '#c84c0c', 3);
enqueueDrawCommand(cx+4, cy-6, cx+4, cy, '#c84c0c', 3);
enqueueDrawCommand(cx+4, cy, cx, cy, '#c84c0c', 3);
enqueueDrawCommand(cx, cy, cx, cy+4, '#c84c0c', 3);
enqueueDrawCommand(cx, cy+8, cx+0.1, cy+8, '#c84c0c', 4, 'round');
}
break;
case 'H': // Bloque Duro 3D
enqueueDrawCommand(startX, topY + 2, endX, topY + 2, '#ffffff', 4); // Bisel Blanco
for(let i=1; i<=len; i++) {
let bx = startX + i*blockW;
enqueueDrawCommand(bx - blockW + 2, topY, bx - blockW + 2, bottomY, '#ffffff', 4);
enqueueDrawCommand(bx, topY, bx, bottomY, '#000000', 3); // Sombra Negra
}
enqueueDrawCommand(startX, bottomY, endX, bottomY, '#000000', 3);
break;
case 'T': // Boquilla de Tubería
enqueueDrawCommand(startX - 4, cy, endX + 4, cy, PALETTE['T'].base, h);
enqueueDrawCommand(startX - 4, topY, endX + 4, topY, '#000000', 4);
enqueueDrawCommand(startX - 4, bottomY, endX + 4, bottomY, '#000000', 4);
enqueueDrawCommand(startX - 4, topY, startX - 4, bottomY, '#000000', 4);
enqueueDrawCommand(endX + 4, topY, endX + 4, bottomY, '#000000', 4);
enqueueDrawCommand(startX + w*0.2, topY, startX + w*0.2, bottomY, '#88f800', w*0.15); // Brillo
break;
case 'P': // Cuerpo de Tubería
enqueueDrawCommand(startX, topY, startX, bottomY, '#000000', 4);
enqueueDrawCommand(endX, topY, endX, bottomY, '#000000', 4);
enqueueDrawCommand(startX + w*0.2, topY, startX + w*0.2, bottomY, '#88f800', w*0.15); // Brillo
break;
}
}
function drawVectorProps(mapName, blockW, blockH) {
if(mapName !== 'screen4') return;
// DIBUJAR CASTILLO (Arte Vectorial Directo)
const cx = blockW * 26; // Posición X
const cy = blockH * 12.5; // Base Y (Toca el suelo)
const cw = blockW * 4;
const ch = blockH * 4;
enqueueDrawCommand(cx - cw/2, cy - ch/2, cx + cw/2, cy - ch/2, '#d8a800', ch); // Cuerpo
enqueueDrawCommand(cx - cw/2, cy - ch, cx + cw/2, cy - ch, '#000000', 3); // Borde techo
// Almenas (Techo)
for(let i=0; i<3; i++) {
let ax = (cx - cw/2) + i*(cw/2.5) + blockW*0.6;
enqueueDrawCommand(ax, cy - ch, ax, cy - ch - blockH, '#d8a800', blockW);
enqueueDrawCommand(ax - blockW/2, cy - ch - blockH, ax + blockW/2, cy - ch - blockH, '#000000', 2);
}
// Puerta negra
enqueueDrawCommand(cx, cy, cx, cy - ch/2, '#000000', blockW*1.5);
enqueueDrawCommand(cx, cy - ch/2, cx+0.1, cy - ch/2, '#000000', blockW*1.5, 'round'); // Arco puerta
// DIBUJAR ASTA Y BANDERA
const fx = blockW * 14;
const fyTop = blockH * 2;
enqueueDrawCommand(fx, cy, fx, fyTop, '#ffffff', 6); // Asta
enqueueDrawCommand(fx, fyTop, fx+0.1, fyTop, '#00a800', 16, 'round'); // Bola verde
enqueueDrawCommand(fx, fyTop + 10, fx - blockW*1.5, fyTop + blockH*0.8, '#00a800', 6); // Bandera
enqueueDrawCommand(fx - blockW*1.5, fyTop + blockH*0.8, fx, fyTop + blockH*1.6, '#00a800', 6);
// Base de piedra
enqueueDrawCommand(fx, cy - blockH/2, fx, cy - blockH/2, '#f8d8b0', blockH);
}
const LevelEngine = {
async render(mapName, mapArray, skyColor) {
await cleanCanvas(skyColor);
const isUnderground = mapName === 'underground';
const rows = mapArray.length;
const cols = mapArray[0].length;
const blockW = drawariaCanvas.width / cols;
const blockH = drawariaCanvas.height / rows;
// Renderizado en 2 Capas: 1. Fondos (Nubes/Arbustos), 2. Sólidos
[true, false].forEach(isBgPass => {
for (let r = 0; r < rows; r++) {
let c = 0;
while (c < cols) {
let char = mapArray[r][c];
if (char === ' ') { c++; continue; }
let isBgChar = PALETTE[char]?.bg === true;
if (isBgPass !== isBgChar) { c++; continue; } // Solo dibuja la capa correspondiente
let len = 1;
while (c + len < cols && mapArray[r][c + len] === char) len++;
const startX = c * blockW;
const endX = startX + (blockW * len);
const cy = (r * blockH) + (blockH / 2);
// 1. Dibujar Base Sólida
if (!isBgChar) {
enqueueDrawCommand(startX + 1, cy, endX - 1, cy, PALETTE[char].base, blockH);
}
// 2. Aplicar Texturas HD
drawDecorations(char, startX, endX, cy, len, blockW, blockH, isUnderground);
c += len;
}
}
});
// 3. Renderizar Modelos Vectoriales (Castillo, Bandera)
drawVectorProps(mapName, blockW, blockH);
console.log(`✅ Pantalla [${mapName}] HD renderizada al instante.`);
}
};
/* ---------- MAPAS HD EN ASCII (32x15 Grid) ---------- */
const MAPS = {
screen1:[
" ",
" WW W ",
" WWWW WWW ",
" ",
" ",
" ? ",
" ",
" B?B?B ",
" ",
" ",
" TT ",
" TT PP ",
" S PP PP S ",
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
],
screen2:[
" ",
" ",
" W WW ",
" WWW WWWW ",
" ",
" ",
" B?B ",
" ",
" BBBBBBBB ",
" TT ",
" PP S ",
" PP TT SSS ",
" PP PP ",
"GGGGGGGGGG GGGGGGGGGGGG GGGGG",
"GGGGGGGGGG GGGGGGGGGGGG GGGGG"
],
screen3:[
" ",
" ",
" W ",
" WWW ",
" BB?B ",
" ",
" ",
" ",
" ",
" H H ",
" HH HH ",
" HHH HHH ",
" HHHH HHHH ",
"GGGGGGGGGGGGGGGGHHHHH GGGGHHHHH",
"GGGGGGGGGGGGGGGGHHHHH GGGGHHHHH"
],
screen4:[
" ",
" ",
" W ",
" WWW ",
" ",
" ",
" ",
" ",
" ",
" H ",
" HH ",
" HHH ",
" HHHH ",
"GGGGGHHHHHGGGGGGGGGGGGGGGGGGGGGG",
"GGGGGHHHHHGGGGGGGGGGGGGGGGGGGGGG"
],
underground:[
"UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU",
"U U",
"U ????? U",
"U U",
"U OOOOOO U",
"U O O U",
"U OOOOOO U",
"U U",
"U U",
"U TT U",
"U PP U",
"U BBBBBBB PP U",
"U BBBBBBB PP U",
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"
]
};
/* ---------- UI DRAGGABLE ---------- */
function initUI() {
if (document.getElementById('mario-menu')) return;
const menu = document.createElement('div');
menu.id = 'mario-menu';
menu.style.cssText = `
position: fixed; top: 120px; left: 20px; width: 340px;
background: linear-gradient(135deg, #e52521, #b31b18);
border: 3px solid #f8b800; border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.9);
color: white; font-family: 'Segoe UI', Tahoma, sans-serif; z-index: 999999;
`;
menu.innerHTML = `
<div id="mario-header" style="background: #f8b800; padding: 12px; cursor: move; border-top-left-radius: 8px; border-top-right-radius: 8px; font-weight: bold; color: black; display: flex; justify-content: space-between; font-size: 15px;">
<span>🍄 Super Drawaria 1-1 (HD Remaster)</span>
<span id="mario-close" style="cursor: pointer;">×</span>
</div>
<div style="padding: 15px;">
<p style="font-size: 11px; color: #ffd8a8; margin-top: 0; margin-bottom: 12px; font-weight: bold;">
✨ Ahora con Texturas Vectoriales, Nubes, Arbustos y Sombras 3D instantáneas.
</p>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 15px;">
<button class="mario-btn" data-map="screen1">🏞️ Pantalla 1</button>
<button class="mario-btn" data-map="screen2">🏞️ Pantalla 2</button>
<button class="mario-btn" data-map="screen3">🏞️ Pantalla 3</button>
<button class="mario-btn" data-map="screen4">🏁 Pantalla 4 (Castillo)</button>
</div>
<button class="mario-btn" data-map="underground" style="width: 100%; background: #222; border-color: #555; margin-bottom: 15px;">
🚇 Zona Subterránea
</button>
<div style="background: rgba(0,0,0,0.4); padding: 10px; border-radius: 8px; font-size: 11px; border: 1px solid #f8b800; text-align: center;">
<b>⚙️ CONEXIÓN CON AVATAR PHYSICS:</b><br>
Añade estos colores a tus "Hitboxes" para tocar los bloques:<br>
<div style="margin-top: 5px; display: grid; grid-template-columns: 1fr 1fr;">
<span>Suelo: <b style="color:#c84c0c;">#c84c0c</b></span>
<span>Ladrillos: <b style="color:#cc5400;">#cc5400</b></span>
<span>Tubos: <b style="color:#00a800;">#00a800</b></span>
<span>Duros: <b style="color:#f8d8b0;">#f8d8b0</b></span>
<span>Moneda: <b style="color:#f8b800;">#f8b800</b></span>
<span>Under: <b style="color:#008888;">#008888</b></span>
</div>
</div>
</div>
`;
document.body.appendChild(menu);
const style = document.createElement('style');
style.innerHTML = `
.mario-btn { background: #00a800; color: white; border: 2px solid #00f800; padding: 10px; border-radius: 6px; cursor: pointer; font-weight: bold; transition: 0.2s; font-size: 12px; }
.mario-btn:hover { background: #00f800; color: black; transform: scale(1.03); box-shadow: 0 4px 10px rgba(0,248,0,0.4); }
`;
document.head.appendChild(style);
document.getElementById('mario-close').onclick = () => menu.style.display = 'none';
document.querySelectorAll('.mario-btn').forEach(btn => {
btn.onclick = () => {
const mapName = btn.getAttribute('data-map');
const skyColor = mapName === 'underground' ? '#5c94fc' : '#5c94fc';
LevelEngine.render(mapName, MAPS[mapName], skyColor);
};
});
let isDragging = false, offsetX, offsetY;
const header = document.getElementById('mario-header');
header.onmousedown = (e) => { isDragging = true; offsetX = e.clientX - menu.offsetLeft; offsetY = e.clientY - menu.offsetTop; };
document.onmousemove = (e) => { if (isDragging) { menu.style.left = Math.max(0, e.clientX - offsetX) + 'px'; menu.style.top = Math.max(0, e.clientY - offsetY) + 'px'; } };
document.onmouseup = () => isDragging = false;
}
function init() {
const canvas = document.getElementById('canvas');
if (canvas) { drawariaCanvas = canvas; drawariaCtx = canvas.getContext('2d'); initUI(); }
else { setTimeout(init, 1000); }
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();