Super Mario Bros. Level Editor for Drawaria.online

Transform Drawaria.online into an immersive Super Mario Bros. level editor with dynamic elements and enhanced realism.

// ==UserScript==
// @name         Super Mario Bros. Level Editor for Drawaria.online
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Transform Drawaria.online into an immersive Super Mario Bros. level editor with dynamic elements and enhanced realism.
// @author       YouTubeDrawaria
// @match        https://drawaria.online/*
// @grant        none
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    const EDITOR_WIDTH = 800; // Optimal width for the editor canvas
    const EDITOR_HEIGHT = 600; // Optimal height for the editor canvas
    const TILE_SIZE = 32; // Standard tile size for Mario elements
    const GRID_COLOR = 'rgba(0, 0, 0, 0.2)';

    // --- Global State ---
    let editorActive = false;
    let currentTool = 'block'; // 'block', 'enemy', 'powerup', 'mario_start', 'clear'
    let currentElement = null; // Specific element type (e.g., 'brick', 'goomba', 'mushroom')
    let levelData = []; // Stores the level layout: [{x, y, type}, ...]
    let gameCanvas = null;
    let gameCtx = null;
    let editorUI = null;
    let audioContext = null;
    let backgroundMusic = null;
    let gameInterval = null; // For game simulation

    // --- Asset Generation (Conceptual - Highly Complex in Reality) ---
    // In a real scenario, this would be done using pixel-by-pixel drawing on OffscreenCanvas
    // or through complex WebGL/SVG generation for high-fidelity vector graphics.
    // For sounds, Web Audio API's OscillatorNode and gain manipulation for synthesis.

    const MarioAssets = {
        // Example: Programmatic generation of a simple Mario sprite (very basic representation)
        generateMarioSprite: () => {
            const canvas = document.createElement('canvas');
            canvas.width = TILE_SIZE;
            canvas.height = TILE_SIZE;
            const ctx = canvas.getContext('2d');
            ctx.fillStyle = 'red'; // Mario's hat/shirt
            ctx.fillRect(TILE_SIZE * 0.1, TILE_SIZE * 0.1, TILE_SIZE * 0.8, TILE_SIZE * 0.8);
            ctx.fillStyle = 'brown'; // Mario's overalls
            ctx.fillRect(TILE_SIZE * 0.2, TILE_SIZE * 0.5, TILE_SIZE * 0.6, TILE_SIZE * 0.4);
            ctx.fillStyle = 'peachpuff'; // Mario's skin
            ctx.beginPath();
            ctx.arc(TILE_SIZE / 2, TILE_SIZE * 0.3, TILE_SIZE * 0.2, 0, Math.PI * 2);
            ctx.fill();
            return canvas; // Returns a canvas element as a sprite
        },
        // More sprites for blocks, enemies, power-ups would follow similar complex patterns.
        // Each would require meticulous drawing commands for every pixel or vector path.
        generateBlockSprite: (type) => {
            const canvas = document.createElement('canvas');
            canvas.width = TILE_SIZE;
            canvas.height = TILE_SIZE;
            const ctx = canvas.getContext('2d');
            ctx.strokeStyle = '#444';
            ctx.lineWidth = 2;
            ctx.strokeRect(0, 0, TILE_SIZE, TILE_SIZE);
            if (type === 'brick') {
                ctx.fillStyle = '#A0522D'; // Sienna
                ctx.fillRect(2, 2, TILE_SIZE - 4, TILE_SIZE - 4);
                ctx.fillStyle = '#8B4513'; // SaddleBrown
                ctx.fillRect(TILE_SIZE * 0.2, TILE_SIZE * 0.2, TILE_SIZE * 0.6, TILE_SIZE * 0.6);
            } else if (type === 'question_block') {
                ctx.fillStyle = '#FFD700'; // Gold
                ctx.fillRect(2, 2, TILE_SIZE - 4, TILE_SIZE - 4);
                ctx.fillStyle = '#DAA520'; // Goldenrod
                ctx.fillRect(TILE_SIZE * 0.2, TILE_SIZE * 0.2, TILE_SIZE * 0.6, TILE_SIZE * 0.6);
                ctx.font = `${TILE_SIZE * 0.7}px Arial`;
                ctx.fillStyle = 'black';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText('?', TILE_SIZE / 2, TILE_SIZE / 2);
            }
            return canvas;
        },
        generateGoombaSprite: () => {
            const canvas = document.createElement('canvas');
            canvas.width = TILE_SIZE;
            canvas.height = TILE_SIZE;
            const ctx = canvas.getContext('2d');
            ctx.fillStyle = '#8B4513'; // Goomba body color
            ctx.beginPath();
            ctx.ellipse(TILE_SIZE / 2, TILE_SIZE * 0.6, TILE_SIZE * 0.4, TILE_SIZE * 0.3, 0, 0, Math.PI * 2);
            ctx.fill();
            ctx.fillStyle = 'white'; // Eyes
            ctx.beginPath();
            ctx.arc(TILE_SIZE * 0.35, TILE_SIZE * 0.45, TILE_SIZE * 0.1, 0, Math.PI * 2);
            ctx.arc(TILE_SIZE * 0.65, TILE_SIZE * 0.45, TILE_SIZE * 0.1, 0, Math.PI * 2);
            ctx.fill();
            ctx.fillStyle = 'black'; // Pupils
            ctx.beginPath();
            ctx.arc(TILE_SIZE * 0.35, TILE_SIZE * 0.45, TILE_SIZE * 0.05, 0, Math.PI * 2);
            ctx.arc(TILE_SIZE * 0.65, TILE_SIZE * 0.45, TILE_SIZE * 0.05, 0, Math.PI * 2);
            ctx.fill();
            return canvas;
        },
        generateMushroomSprite: () => {
            const canvas = document.createElement('canvas');
            canvas.width = TILE_SIZE;
            canvas.height = TILE_SIZE;
            const ctx = canvas.getContext('2d');
            // Mushroom top
            ctx.fillStyle = 'red';
            ctx.beginPath();
            ctx.arc(TILE_SIZE / 2, TILE_SIZE * 0.4, TILE_SIZE * 0.4, Math.PI, Math.PI * 2);
            ctx.closePath();
            ctx.fill();
            // Mushroom stem
            ctx.fillStyle = 'white';
            ctx.fillRect(TILE_SIZE * 0.4, TILE_SIZE * 0.5, TILE_SIZE * 0.2, TILE_SIZE * 0.4);
            return canvas;
        },

        // Sound Synthesis: Very basic example, actual music requires complex sequencing
        // and instrument synthesis.
        generateCoinSound: () => {
            if (!audioContext) audioContext = new (window.AudioContext || window.webkitAudioContext)();
            const oscillator = audioContext.createOscillator();
            const gainNode = audioContext.createGain();

            oscillator.connect(gainNode);
            gainNode.connect(audioContext.destination);

            oscillator.type = 'triangle';
            oscillator.frequency.setValueAtTime(1000, audioContext.currentTime); // High frequency
            gainNode.gain.setValueAtTime(1, audioContext.currentTime);
            gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.2); // Quick decay

            oscillator.start();
            oscillator.stop(audioContext.currentTime + 0.2);
        },
        generateJumpSound: () => {
            if (!audioContext) audioContext = new (window.AudioContext || window.webkitAudioContext)();
            const oscillator = audioContext.createOscillator();
            const gainNode = audioContext.createGain();

            oscillator.connect(gainNode);
            gainNode.connect(audioContext.destination);

            oscillator.type = 'sine';
            oscillator.frequency.setValueAtTime(440, audioContext.currentTime); // A4
            oscillator.frequency.linearRampToValueAtTime(660, audioContext.currentTime + 0.1); // Rise
            gainNode.gain.setValueAtTime(0.5, audioContext.currentTime);
            gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.1);

            oscillator.start();
            oscillator.stop(audioContext.currentTime + 0.1);
        },
        generateBackgroundMusic: () => {
            if (!audioContext) audioContext = new (window.AudioContext || window.webkitAudioContext)();
            // This is a highly simplified placeholder.
            // Actual music would involve multiple oscillators, complex envelopes,
            // rhythm sequencing, and potentially custom waveforms.
            // For example, generating the Super Mario Bros. theme from scratch is a massive undertaking.
            // This function would return a Web Audio API graph or a playback function.
            console.warn("Generating complex background music from scratch is an advanced audio synthesis task.");
            // Example: A very simple, sustained low tone for 'background'
            const oscillator = audioContext.createOscillator();
            oscillator.type = 'square';
            oscillator.frequency.setValueAtTime(100, audioContext.currentTime);
            const gainNode = audioContext.createGain();
            gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
            oscillator.connect(gainNode);
            gainNode.connect(audioContext.destination);
            oscillator.start();
            backgroundMusic = {
                play: () => { if (audioContext.state === 'suspended') audioContext.resume(); },
                stop: () => { if (audioContext.state === 'running') audioContext.suspend(); }
            };
        }
    };

    // Store generated sprites
    const sprites = {
        mario: MarioAssets.generateMarioSprite(),
        brick_block: MarioAssets.generateBlockSprite('brick'),
        question_block: MarioAssets.generateBlockSprite('question_block'),
        goomba: MarioAssets.generateGoombaSprite(),
        mushroom: MarioAssets.generateMushroomSprite(),
        // Add more dynamically generated sprites here
    };

    // --- UI Creation ---
    function createEditorUI() {
        // Create main editor container
        editorUI = document.createElement('div');
        editorUI.id = 'mario-editor-container';
        Object.assign(editorUI.style, {
            position: 'fixed',
            top: '0',
            left: '0',
            width: '100vw',
            height: '100vh',
            backgroundColor: 'rgba(0, 0, 0, 0.8)',
            zIndex: '9999',
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'center',
            alignItems: 'center',
            fontFamily: 'Pixelify Sans, sans-serif', // Use a pixel font for authenticity
            color: 'white',
            overflow: 'hidden'
        });

        // Add Google Fonts Pixelify Sans
        const fontLink = document.createElement('link');
        fontLink.href = 'https://fonts.googleapis.com/css2?family=Pixelify+Sans&display=swap';
        fontLink.rel = 'stylesheet';
        document.head.appendChild(fontLink);

        // Header
        const header = document.createElement('h1');
        header.textContent = 'Super Mario Level Editor';
        Object.assign(header.style, {
            fontSize: '3em',
            marginBottom: '20px',
            textShadow: '4px 4px 0px blue', // Add some depth
        });
        editorUI.appendChild(header);

        // Editor Canvas
        gameCanvas = document.createElement('canvas');
        gameCanvas.id = 'mario-editor-canvas';
        gameCanvas.width = EDITOR_WIDTH;
        gameCanvas.height = EDITOR_HEIGHT;
        Object.assign(gameCanvas.style, {
            border: '5px solid #8B4513', // Brown border
            background: 'linear-gradient(to bottom, skyblue, lightblue 70%, green 70%)', // Sky and ground
            boxShadow: '0 0 20px rgba(255, 255, 0, 0.7)', // Glow
            cursor: 'crosshair'
        });
        editorUI.appendChild(gameCanvas);
        gameCtx = gameCanvas.getContext('2d');

        // Tool Panel
        const toolPanel = document.createElement('div');
        Object.assign(toolPanel.style, {
            display: 'flex',
            gap: '10px',
            marginTop: '20px',
            padding: '10px',
            background: 'rgba(0, 0, 0, 0.6)',
            borderRadius: '8px',
            boxShadow: '0 0 10px rgba(0, 0, 0, 0.5)'
        });
        editorUI.appendChild(toolPanel);

        // Tools
        const tools = [
            { id: 'tool-block', text: 'Block', type: 'block', elements: ['brick_block', 'question_block'] },
            { id: 'tool-enemy', text: 'Enemy', type: 'enemy', elements: ['goomba'] },
            { id: 'tool-powerup', text: 'Power-up', type: 'powerup', elements: ['mushroom'] },
            { id: 'tool-mario', text: 'Mario Start', type: 'mario_start', elements: ['mario'] },
            { id: 'tool-clear', text: 'Clear', type: 'clear' },
        ];

        tools.forEach(tool => {
            const button = document.createElement('button');
            button.id = tool.id;
            button.textContent = tool.text;
            Object.assign(button.style, {
                padding: '8px 15px',
                fontSize: '1em',
                backgroundColor: '#007bff', // Blue button
                color: 'white',
                border: 'none',
                borderRadius: '5px',
                cursor: 'pointer',
                transition: 'background-color 0.2s',
                fontFamily: 'inherit'
            });
            button.onmouseover = () => button.style.backgroundColor = '#0056b3';
            button.onmouseout = () => button.style.backgroundColor = '#007bff';
            button.onclick = () => {
                currentTool = tool.type;
                if (tool.elements) {
                    currentElement = tool.elements[0]; // Select first element by default
                } else {
                    currentElement = null;
                }
                updateToolPanelSelection();
            };
            toolPanel.appendChild(button);
        });

        // Element Selector (for block, enemy, powerup types)
        const elementSelector = document.createElement('select');
        elementSelector.id = 'element-selector';
        Object.assign(elementSelector.style, {
            padding: '8px',
            fontSize: '1em',
            borderRadius: '5px',
            border: '1px solid #ccc',
            backgroundColor: 'white',
            fontFamily: 'inherit',
            marginLeft: '10px'
        });
        elementSelector.onchange = (e) => {
            currentElement = e.target.value;
        };
        toolPanel.appendChild(elementSelector);

        // Action Buttons
        const actionPanel = document.createElement('div');
        Object.assign(actionPanel.style, {
            display: 'flex',
            gap: '10px',
            marginTop: '10px'
        });
        editorUI.appendChild(actionPanel);

        const playButton = document.createElement('button');
        playButton.textContent = 'Test Level (Conceptual)';
        Object.assign(playButton.style, {
            padding: '10px 20px',
            fontSize: '1.2em',
            backgroundColor: '#28a745', // Green button
            color: 'white',
            border: 'none',
            borderRadius: '5px',
            cursor: 'pointer',
            transition: 'background-color 0.2s',
            fontFamily: 'inherit'
        });
        playButton.onmouseover = () => playButton.style.backgroundColor = '#218838';
        playButton.onmouseout = () => playButton.style.backgroundColor = '#28a745';
        playButton.onclick = () => {
            alert('Level testing is a complex game simulation not fully implemented. Imagine Mario running here!');
            MarioAssets.generateJumpSound(); // Simulate a jump sound
        };
        actionPanel.appendChild(playButton);

        const exitButton = document.createElement('button');
        exitButton.textContent = 'Exit Editor';
        Object.assign(exitButton.style, {
            padding: '10px 20px',
            fontSize: '1.2em',
            backgroundColor: '#dc3545', // Red button
            color: 'white',
            border: 'none',
            borderRadius: '5px',
            cursor: 'pointer',
            transition: 'background-color 0.2s',
            fontFamily: 'inherit'
        });
        exitButton.onmouseover = () => exitButton.style.backgroundColor = '#c82333';
        exitButton.onmouseout = () => exitButton.style.backgroundColor = '#dc3545';
        exitButton.onclick = disableEditor;
        actionPanel.appendChild(exitButton);

        document.body.appendChild(editorUI);
        updateToolPanelSelection(); // Initial update
    }

    // --- Editor Logic ---
    function drawGrid() {
        gameCtx.strokeStyle = GRID_COLOR;
        for (let i = 0; i < EDITOR_WIDTH / TILE_SIZE; i++) {
            gameCtx.beginPath();
            gameCtx.moveTo(i * TILE_SIZE, 0);
            gameCtx.lineTo(i * TILE_SIZE, EDITOR_HEIGHT);
            gameCtx.stroke();
        }
        for (let i = 0; i < EDITOR_HEIGHT / TILE_SIZE; i++) {
            gameCtx.beginPath();
            gameCtx.moveTo(0, i * TILE_SIZE);
            gameCtx.lineTo(EDITOR_WIDTH, i * TILE_SIZE);
            gameCtx.stroke();
        }
    }

    function drawLevel() {
        gameCtx.clearRect(0, 0, EDITOR_WIDTH, EDITOR_HEIGHT);
        // The linear gradient is drawn as background of the canvas element itself
        // so it persists on clearRect.
        // gameCtx.fillStyle = 'linear-gradient(to bottom, skyblue, lightblue 70%, green 70%)'; // Sky and ground
        // gameCtx.fillRect(0, 0, EDITOR_WIDTH, EDITOR_HEIGHT);

        drawGrid();

        levelData.forEach(item => {
            const sprite = sprites[item.type];
            if (sprite) {
                gameCtx.drawImage(sprite, item.x, item.y, TILE_SIZE, TILE_SIZE);

                // Add glowing particle effects for power-ups (conceptual)
                if (item.type === 'mushroom') {
                    // This would involve rendering many small, semi-transparent circles
                    // or squares around the power-up, fading in and out.
                    // Example: simple static glow effect for demonstration
                    gameCtx.fillStyle = 'rgba(255, 255, 0, 0.3)'; // Yellow glow
                    gameCtx.beginPath();
                    gameCtx.arc(item.x + TILE_SIZE / 2, item.y + TILE_SIZE / 2, TILE_SIZE * 0.7, 0, Math.PI * 2);
                    gameCtx.fill();
                }
            }
        });
    }

    function handleCanvasClick(event) {
        if (!editorActive) return;

        const rect = gameCanvas.getBoundingClientRect();
        const mouseX = event.clientX - rect.left;
        const mouseY = event.clientY - rect.top;

        const gridX = Math.floor(mouseX / TILE_SIZE) * TILE_SIZE;
        const gridY = Math.floor(mouseY / TILE_SIZE) * TILE_SIZE;

        const existingElementIndex = levelData.findIndex(item => item.x === gridX && item.y === gridY);

        if (currentTool === 'clear') {
            if (existingElementIndex !== -1) {
                levelData.splice(existingElementIndex, 1);
            }
        } else if (currentElement) {
            if (existingElementIndex !== -1) {
                levelData[existingElementIndex] = { x: gridX, y: gridY, type: currentElement };
            } else {
                levelData.push({ x: gridX, y: gridY, type: currentElement });
            }
        }

        drawLevel();
        if (currentElement === 'brick_block' || currentElement === 'question_block') {
             MarioAssets.generateCoinSound(); // Play a sound when placing blocks
        }
    }

    function updateToolPanelSelection() {
        // Reset all button styles
        document.querySelectorAll('#mario-editor-container button').forEach(btn => {
            btn.style.backgroundColor = '#007bff';
        });

        // Highlight selected tool button
        let selectedButton = null;
        if (currentTool === 'block') selectedButton = document.getElementById('tool-block');
        else if (currentTool === 'enemy') selectedButton = document.getElementById('tool-enemy');
        else if (currentTool === 'powerup') selectedButton = document.getElementById('tool-powerup');
        else if (currentTool === 'mario_start') selectedButton = document.getElementById('tool-mario');
        else if (currentTool === 'clear') selectedButton = document.getElementById('tool-clear');

        if (selectedButton) {
            selectedButton.style.backgroundColor = '#0056b3'; // Darker blue for selected
        }

        // Update element selector options
        const elementSelector = document.getElementById('element-selector');
        elementSelector.innerHTML = ''; // Clear previous options

        let elementsForTool = [];
        if (currentTool === 'block') elementsForTool = ['brick_block', 'question_block'];
        else if (currentTool === 'enemy') elementsForTool = ['goomba'];
        else if (currentTool === 'powerup') elementsForTool = ['mushroom'];
        else if (currentTool === 'mario_start') elementsForTool = ['mario'];

        elementsForTool.forEach(el => {
            const option = document.createElement('option');
            option.value = el;
            option.textContent = el.replace(/_/g, ' ').toUpperCase();
            elementSelector.appendChild(option);
        });

        elementSelector.style.display = elementsForTool.length > 0 ? 'inline-block' : 'none';

        // Set currentElement to the first option if available, otherwise null
        currentElement = elementsForTool.length > 0 ? elementsForTool[0] : null;
        if (elementSelector.options.length > 0) {
            elementSelector.value = currentElement;
        }
    }

    // --- Editor Control ---
    function enableEditor() {
        if (editorActive) return;

        // Hide Drawaria.online's main content
        const mainContent = document.getElementById('main');
        if (mainContent) mainContent.style.display = 'none';
        const loginSection = document.getElementById('login');
        if (loginSection) loginSection.style.display = 'none';

        createEditorUI();
        drawLevel();
        editorActive = true;
        gameCanvas.addEventListener('click', handleCanvasClick);

        // Start conceptual background music
        MarioAssets.generateBackgroundMusic();
        if (backgroundMusic) backgroundMusic.play();
    }

    function disableEditor() {
        if (!editorActive) return;

        if (editorUI && editorUI.parentNode) {
            editorUI.parentNode.removeChild(editorUI);
        }

        // Show Drawaria.online's main content
        const mainContent = document.getElementById('main');
        if (mainContent) mainContent.style.display = '';
        const loginSection = document.getElementById('login');
        if (loginSection) loginSection.style.display = '';

        gameCanvas.removeEventListener('click', handleCanvasClick);
        editorActive = false;

        // Stop conceptual background music
        if (backgroundMusic) backgroundMusic.stop();
        backgroundMusic = null;
    }

    // --- Main Entry Point / Button to toggle editor ---
    function addToggleButton() {
        const toggleButton = document.createElement('button');
        toggleButton.textContent = 'Toggle Mario Editor';
        Object.assign(toggleButton.style, {
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            padding: '15px 30px',
            fontSize: '1.5em',
            backgroundColor: '#FF4500', // Orange-red
            color: 'white',
            border: 'none',
            borderRadius: '10px',
            cursor: 'pointer',
            zIndex: '10000',
            boxShadow: '0 5px 15px rgba(0,0,0,0.3)',
            transition: 'background-color 0.3s, transform 0.2s',
            fontFamily: 'Pixelify Sans, sans-serif'
        });
        toggleButton.onmouseover = () => {
            toggleButton.style.backgroundColor = '#E03C00';
            toggleButton.style.transform = 'scale(1.05)';
        };
        toggleButton.onmouseout = () => {
            toggleButton.style.backgroundColor = '#FF4500';
            toggleButton.style.transform = 'scale(1)';
        };
        toggleButton.onclick = () => {
            if (editorActive) {
                disableEditor();
            } else {
                enableEditor();
            }
        };
        document.body.appendChild(toggleButton);
    }

    // Initial setup when the script runs
    window.addEventListener('load', () => {
        addToggleButton();
    });

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址