您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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(); }); })();