Drawaria.online GTA San Andreas

Transforms Drawaria.online/test with GTA-inspired visuals and sounds, including an auto-visible map.

// ==UserScript==
// @name         Drawaria.online GTA San Andreas
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Transforms Drawaria.online/test with GTA-inspired visuals and sounds, including an auto-visible map.
// @author       YouTube
// @match        https://drawaria.online/*
// @grant        none
// @license      MIT
// @icon         https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// ==/UserScript==

(function() {
    'use strict';

    // --- IMPORTANT NOTE REGARDING REALISM AND DETAIL ---
    // The user requested GTA-like elements, including a recognizable map, music,
    // sound effects, particles, glows, and animated areas, while strictly forbidding
    // any external assets (like .mp3, .png, or base64 data) and demanding
    // all content be generated purely by the script itself.
    //
    // This constraint means that true realism and high detail, characteristic
    // of Grand Theft Auto, are not achievable. The enhancements below are
    // symbolic and created using only native browser capabilities (CSS, SVG,
    // Canvas drawing, Web Audio API primitives). This will result in a
    // "GTA-inspired" feel rather than a realistic replica.
    // ---

    const injectCSS = (css) => {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.appendChild(document.createTextNode(css));
        document.head.appendChild(style);
    };

    // --- 1. GTA-inspired CSS Overlays & Animations ---
    injectCSS(`
        body {
            background: linear-gradient(to bottom, #1a1a1a, #0a0a0a) !important;
            filter: grayscale(10%) brightness(1.2); /* Slight filter for mood */
            overflow: hidden; /* Hide scrollbars if content overflows */
        }

        #main {
            background-color: rgba(0, 0, 0, 0.7) !important; /* Darker main background */
            border: 2px solid #ffcc00; /* Gold/yellow border */
            box-shadow: 0 0 20px rgba(255, 204, 0, 0.5); /* Glow effect */
            border-radius: 8px;
            overflow: hidden;
            position: relative;
            z-index: 100; /* Ensure it's above new elements */
        }

        #leftbar, #rightbar {
            background-color: rgba(20, 20, 20, 0.8) !important;
            border: 1px solid #666;
            border-radius: 5px;
        }

        /* Text color adjustments for readability */
        #main, #leftbar, #rightbar, #chatbox_messages, .bubble, .playerlist-name, .systemchatmessage {
            color: #fff !important;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
        }

        /* Buttons and interactive elements */
        .btn {
            border-radius: 3px !important;
            border: 1px solid #ffcc00 !important;
            background-color: rgba(255, 204, 0, 0.2) !important;
            color: #ffcc00 !important;
            text-shadow: 0 0 5px rgba(255,255,0,0.5);
            transition: all 0.2s ease-in-out;
        }
        .btn:hover {
            background-color: rgba(255, 204, 0, 0.4) !important;
            box-shadow: 0 0 10px rgba(255,255,0,0.7);
        }

        /* Pulsating effect for selected/active elements */
        .playerlist-drawerhighlight, .roomlist-highlight {
            animation: gta-pulse 1.5s infinite alternate;
        }
        @keyframes gta-pulse {
            from { background-color: rgba(255, 204, 0, 0.2); }
            to { background-color: rgba(255, 204, 0, 0.5); }
        }

        /* Simple "glitch" effect for certain text elements */
        .playerlist-name a, .roomlist-descr {
            position: relative;
            display: inline-block;
            overflow: hidden;
        }
        .playerlist-name a::before, .roomlist-descr::before {
            content: attr(data-text); /* Use a data attribute for text */
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            color: #00ff00; /* Green glitch color */
            animation: gta-glitch 0.8s infinite linear alternate;
            text-shadow: none;
            clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
        }
        @keyframes gta-glitch {
            0%, 10% { transform: translate(0, 0); clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
            20% { transform: translate(-2px, 2px); clip-path: polygon(0 0, 100% 0, 80% 100%, 20% 100%); }
            40% { transform: translate(3px, -3px); clip-path: polygon(20% 0, 100% 0, 100% 100%, 0 100%); }
            60% { transform: translate(-1px, 1px); clip-path: polygon(0 0, 80% 0, 100% 100%, 0 100%); }
            80% { transform: translate(2px, -1px); clip-path: polygon(0 0, 100% 0, 100% 80%, 0 20%); }
            100% { transform: translate(0, 0); clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
        }

        /* Small icons for GTA-style elements (Requires Font Awesome to be loaded by Drawaria) */
        .gta-icon {
            font-family: 'Font Awesome 5 Free';
            font-weight: 900;
            margin-right: 5px;
            color: #ffcc00;
        }
        .gta-icon-car::before { content: '\\f1b9'; } /* Car icon */
        .gta-icon-dollar::before { content: '\\f155'; } /* Dollar icon */
        .gta-icon-gun::before { content: '\\f2a0'; } /* Crosshairs/Target icon */
        .gta-icon-star::before { content: '\\f005'; } /* Star icon */

        /* Override specific Drawaria styles for better integration */
        .playerlist-name a {
            text-decoration: none !important; /* Remove original underline */
        }
    `);

    // Add data-text attribute for glitch effect
    document.querySelectorAll('.playerlist-name a, .roomlist-descr').forEach(el => {
        el.setAttribute('data-text', el.textContent);
    });


    // --- 2. GTA-inspired Map Overlay (Simple Canvas Drawing) ---
    const createGTAMap = () => {
        const canvas = document.createElement('canvas');
        canvas.id = 'gta-map-overlay';
        canvas.width = 200; // Internal canvas resolution
        canvas.height = 200; // Internal canvas resolution
        canvas.style.position = 'fixed';
        canvas.style.top = '10px';
        canvas.style.right = '10px';
        canvas.style.width = '200px'; // Display size
        canvas.style.height = '200px'; // Display size
        canvas.style.border = '2px solid #ffcc00';
        canvas.style.borderRadius = '5px';
        canvas.style.backgroundColor = 'rgba(0,0,0,0.8)';
        canvas.style.zIndex = '9999';
        document.body.appendChild(canvas);

        const ctx = canvas.getContext('2d');
        const size = canvas.width; // Use internal canvas width/height for drawing

        const drawMap = () => {
            ctx.clearRect(0, 0, size, size);

            // Background "street" pattern (simple lines)
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 2;
            for (let i = 0; i < size; i += 20) {
                ctx.beginPath();
                ctx.moveTo(i, 0);
                ctx.lineTo(i, size);
                ctx.stroke();
                ctx.beginPath();
                ctx.moveTo(0, i);
                ctx.lineTo(size, i);
                ctx.stroke();
            }

            // "Buildings" (colored rectangles)
            ctx.fillStyle = '#4a4a4a'; // Dark grey building
            ctx.fillRect(10, 10, 80, 50);
            ctx.fillRect(120, 30, 60, 70);
            ctx.fillStyle = '#6a6a6a'; // Lighter grey building
            ctx.fillRect(30, 100, 70, 60);
            ctx.fillRect(150, 120, 40, 40);

            // "Player" indicator (pulsating red dot)
            const playerX = size / 2;
            const playerY = size / 2;
            const radius = 5 + Math.sin(Date.now() * 0.005) * 2; // Pulsating effect
            ctx.beginPath();
            ctx.arc(playerX, playerY, radius, 0, Math.PI * 2);
            ctx.fillStyle = `rgba(255, 0, 0, ${0.8 + Math.sin(Date.now() * 0.003) * 0.2})`; // Red, pulsating opacity
            ctx.shadowColor = 'rgba(255,0,0,0.8)';
            ctx.shadowBlur = 10;
            ctx.fill();
            ctx.shadowBlur = 0; // Reset shadow

            // "Target" or "Mission Zone" (pulsating green square)
            const targetX = 160;
            const targetY = 160;
            const targetSize = 15 + Math.cos(Date.now() * 0.004) * 3;
            ctx.fillStyle = `rgba(0, 255, 0, ${0.7 + Math.cos(Date.now() * 0.003) * 0.2})`; // Green, pulsating opacity
            ctx.shadowColor = 'rgba(0,255,0,0.8)';
            ctx.shadowBlur = 8;
            ctx.fillRect(targetX - targetSize / 2, targetY - targetSize / 2, targetSize, targetSize);
            ctx.shadowBlur = 0;

            requestAnimationFrame(drawMap);
        };
        drawMap();
    };

    // --- 3. GTA-inspired Sound Effects & Music (Web Audio API) ---
    let audioContext;
    let musicOscillator = null;
    let musicGain = null;
    let currentTimeoutId = null; // Variable to hold the timeout for music scheduling

    const initAudioContext = () => {
        if (!audioContext) {
            audioContext = new (window.AudioContext || window.webkitAudioContext)();
        }
    };

    // Basic synthesized music (simple melody loop)
    const playGTAMusic = () => {
        if (!audioContext) initAudioContext();
        if (musicOscillator) {
            // If context was suspended, try to resume
            if (audioContext.state === 'suspended') {
                audioContext.resume().then(() => {
                    console.log('AudioContext resumed!');
                    // Reschedule notes from current time
                    clearTimeout(currentTimeoutId);
                    scheduleSequence(); // Restart sequence from current time
                });
            }
            return; // Already playing
        }

        musicOscillator = audioContext.createOscillator();
        musicGain = audioContext.createGain();

        musicOscillator.type = 'square'; // Chiptune feel
        musicOscillator.connect(musicGain);
        musicGain.connect(audioContext.destination);
        musicGain.gain.setValueAtTime(0.05, audioContext.currentTime); // Low volume

        // Simple arpeggiated melody loop
        const notes = [
            { freq: 220, duration: 0.2 }, // A3
            { freq: 261.6, duration: 0.2 }, // C4
            { freq: 329.6, duration: 0.2 }, // E4
            { freq: 261.6, duration: 0.2 }, // C4
            { freq: 220, duration: 0.2 }, // A3
            { freq: 196, duration: 0.2 }, // G3
            { freq: 220, duration: 0.4 }  // A3
        ];

        let startTime = audioContext.currentTime;
        let currentIndex = 0;

        const scheduleSequence = () => {
            const note = notes[currentIndex];
            musicOscillator.frequency.setValueAtTime(note.freq, startTime);
            // Use linearRampToValueAtTime for smooth volume changes (attack/decay)
            musicGain.gain.linearRampToValueAtTime(0.05, startTime + 0.01); // slight attack
            musicGain.gain.linearRampToValueAtTime(0, startTime + note.duration - 0.05); // short decay

            startTime += note.duration;
            currentIndex = (currentIndex + 1) % notes.length;

            // Schedule the next note. Use setTimeout for a delay that matches the note duration.
            // Add a small buffer for the gap between notes if needed, here it's part of duration.
            currentTimeoutId = setTimeout(scheduleSequence, (startTime - audioContext.currentTime) * 1000);
        };

        musicOscillator.start();
        scheduleSequence(); // Start the sequence

        // Handle audio context suspension/resumption on user interaction
        const resumeAudio = () => {
            if (audioContext.state === 'suspended') {
                audioContext.resume().then(() => {
                    console.log('AudioContext resumed!');
                    // Reschedule notes from current time
                    clearTimeout(currentTimeoutId); // Clear any pending old timeouts
                    startTime = audioContext.currentTime; // Reset start time to current
                    scheduleSequence(); // Restart sequence
                });
            }
            // Remove listeners once audio is active or confirmed to be active
            document.removeEventListener('click', resumeAudio);
            document.removeEventListener('keydown', resumeAudio);
        };
        // Add listeners to resume audio on first user interaction
        document.addEventListener('click', resumeAudio);
        document.addEventListener('keydown', resumeAudio);
    };

    const stopGTAMusic = () => {
        if (musicOscillator) {
            clearTimeout(currentTimeoutId); // Clear any pending scheduled notes
            musicOscillator.stop();
            musicOscillator.disconnect();
            musicGain.disconnect();
            musicOscillator = null;
            musicGain = null;
            // Remove the audio context listeners as well
            document.removeEventListener('click', resumeAudio);
            document.removeEventListener('keydown', resumeAudio);
        }
    };

    // Basic synthesized sound effect (e.g., "gunshot" for drawing action)
    const playSoundEffect = (type) => {
        if (!audioContext) initAudioContext();
        // Ensure context is running before playing
        if (audioContext.state === 'suspended') {
            audioContext.resume();
        }

        const oscillator = audioContext.createOscillator();
        const gainNode = audioContext.createGain();

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

        switch (type) {
            case 'draw': // Simple laser-like sound
                oscillator.type = 'sine';
                oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
                oscillator.frequency.exponentialRampToValueAtTime(100, audioContext.currentTime + 0.1);
                gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
                gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.2);
                oscillator.start(audioContext.currentTime);
                oscillator.stop(audioContext.currentTime + 0.2);
                break;
            case 'guess': // Simple "ding" for correct guess
                oscillator.type = 'triangle';
                oscillator.frequency.setValueAtTime(600, audioContext.currentTime);
                oscillator.frequency.exponentialRampToValueAtTime(800, audioContext.currentTime + 0.05);
                gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
                gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.15);
                oscillator.start(audioContext.currentTime);
                oscillator.stop(audioContext.currentTime + 0.15);
                break;
            case 'error': // Simple "buzz" for error
                oscillator.type = 'sawtooth';
                oscillator.frequency.setValueAtTime(120, audioContext.currentTime);
                gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
                gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.3);
                oscillator.start(audioContext.currentTime);
                oscillator.stop(audioContext.currentTime + 0.3);
                break;
            case 'car_horn': // Short honk
                oscillator.type = 'sine';
                oscillator.frequency.setValueAtTime(200, audioContext.currentTime);
                oscillator.frequency.linearRampToValueAtTime(250, audioContext.currentTime + 0.05);
                oscillator.frequency.linearRampToValueAtTime(200, audioContext.currentTime + 0.1);
                gainNode.gain.setValueAtTime(0.15, audioContext.currentTime);
                gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.3);
                oscillator.start(audioContext.currentTime);
                oscillator.stop(audioContext.currentTime + 0.3);
                break;
        }
    };

    // --- 4. Integrate with Drawaria's existing events (where possible) ---
    const observeGameEvents = () => {
        const chatMessagesContainer = document.getElementById('chatbox_messages');
        if (chatMessagesContainer) {
            const observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    if (mutation.addedNodes.length) {
                        mutation.addedNodes.forEach(node => {
                            if (node.classList && node.classList.contains('chatmessage')) {
                                // Check if it's a "correct guess" message
                                if (node.textContent.includes('guessed the word!') || node.textContent.includes('угадал слово!') || node.textContent.includes('adivinó la palabra!')) {
                                    playSoundEffect('guess');
                                } else {
                                    // Other chat messages
                                    // playSoundEffect('chat'); // Could add a subtle chat sound
                                }
                            }
                        });
                    }
                });
            });
            observer.observe(chatMessagesContainer, { childList: true });
        }

        // Hook into drawing actions (conceptual, needs to intercept Drawaria's internal drawing events for precision)
        // As direct drawing events are not easily exposed, this is a simplified approximation.
        const canvasElement = document.getElementById('canvas');
        if (canvasElement) {
             canvasElement.addEventListener('mouseup', () => {
                 // Play a drawing sound effect after a mouse up (end of a stroke)
                 playSoundEffect('draw');
             });
        }
    };


    // --- Initialization on page load ---
    window.addEventListener('load', () => {
        // Delay to ensure Drawaria's scripts have loaded and elements are available
        // If the map doesn't appear, increase this delay.
        setTimeout(() => {
            createGTAMap(); // Create the map overlay
            playGTAMusic(); // Start the background music
            observeGameEvents(); // Set up event listeners for game actions

            // Add a "car horn" sound to the "Quick Play" button for interactive testing
            const quickPlayButton = document.getElementById('quickplay');
            if (quickPlayButton) {
                quickPlayButton.addEventListener('click', (e) => {
                    // Note: e.preventDefault() here would stop the original button action.
                    // If you want both, you'd need to manually trigger the original logic
                    // or attach to a later phase of the event. For this demo, just sound.
                    playSoundEffect('car_horn');
                });
            }

            // Apply GTA-style icons to loginbox stats for Score, Wins, Stars
            const loginStatsTable = document.querySelector('#login-midcol table');
            if (loginStatsTable) {
                const rows = loginStatsTable.querySelectorAll('tbody tr');
                rows.forEach(row => {
                    const labelCell = row.querySelector('td:first-child');
                    if (labelCell) {
                        let iconClass = '';
                        const text = labelCell.textContent.toLowerCase();
                        if (text.includes('score') || text.includes('очков') || text.includes('puntos')) {
                            iconClass = 'gta-icon-dollar';
                        } else if (text.includes('wins') || text.includes('матчей') || text.includes('victorias')) {
                            iconClass = 'gta-icon-star';
                        } else if (text.includes('stars') || text.includes('звезд') || text.includes('estrellas')) {
                            iconClass = 'gta-icon-star'; // Re-use star for stars
                        }
                        if (iconClass) {
                            // Insert the icon before the text
                            labelCell.innerHTML = `<i class="gta-icon ${iconClass}"></i>${labelCell.textContent}`;
                        }
                    }
                });
            }


            console.log('Drawaria.online GTA Enhancement script loaded.');
        }, 3000); // Adjust delay as needed to ensure Drawaria's elements are ready
    });

    // Optional: Stop music when navigating away from the page
    window.addEventListener('beforeunload', () => {
        stopGTAMusic();
    });

})();