Drawaria Super Mario Levels 🍄

Motor de renderizado de mapas HD. Texturas vectoriales, sombras 3D, nubes, arbustos y castillo sin sacrificar el rendimiento instantáneo.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

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

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

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