您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
High-quality elemental animations (Ocean, Magma, Poison, PurpleGoo) with solid bases, outlines, and thematic particles for Drawaria.online
// ==UserScript== // @name Drawaria Drawbot Elemental Animations // @namespace http://tampermonkey.net/ // @version 1.0 // @description High-quality elemental animations (Ocean, Magma, Poison, PurpleGoo) with solid bases, outlines, and thematic particles for Drawaria.online // @author YouTubeDrawaria // @match https://drawaria.online/* // @license MIT // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // @grant none // ==/UserScript== (() => { 'use_strict'; const EL = (sel) => document.querySelector(sel); const ELL = (sel) => document.querySelectorAll(sel); let drawing_active = false; let originalCanvas = null; let cw = 0; let ch = 0; function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function getRandomFloat(min, max) { return Math.random() * (max - min) + min; } function sendDrawCmd(socket, start, end, color, thickness, isEraser = false, algo = 0) { if (!socket || socket.readyState !== WebSocket.OPEN) { console.warn("Bot socket not open."); return false; } const p1x = Math.max(0, Math.min(1, start[0])), p1y = Math.max(0, Math.min(1, start[1])); const p2x = Math.max(0, Math.min(1, end[0])), p2y = Math.max(0, Math.min(1, end[1])); let numThickness = parseFloat(thickness); if (isNaN(numThickness)) { console.warn("Invalid thickness provided, defaulting to 10:", thickness); numThickness = 10; } const gT = isEraser ? numThickness : 0 - numThickness; socket.send(`42["drawcmd",0,[${p1x},${p1y},${p2x},${p2y},${isEraser},${gT},"${color}",0,0,{"2":${algo},"3":0.5,"4":0.5}]]`); return true; } async function animateElementalWave(type) { if (!window.___BOT || !window.___BOT.conn || !window.___BOT.conn.socket || window.___BOT.conn.socket.readyState !== WebSocket.OPEN) { alert("Bot not connected."); return; } if (drawing_active) { console.log("Animation already in progress."); alert("An animation is already running. Please wait for it to finish or clear the canvas."); return; } drawing_active = true; const socket = window.___BOT.conn.socket; console.log(`Starting ${type} Animation...`); let config; switch (type) { case 'Ocean': config = { baseFillColor: '#00008B', waveColors: ['#1E90FF', '#4682B4'], outlineColor: '#AFEEEE', particleColor: '#00FFFF', particleType: 'blob', particleChance: 0.15, particleSizeMin: 0.01, particleSizeMax: 0.03, particleThickness: 8, startHeight: 0.88, waveAmplitude: 0.06, waveFrequency: 2.5, waveSpeed: 0.02, outlineOffset: 0.005, outlineThicknessReduction: 3, mainThickness: 30, waveStyle: 'smooth', frames: 100, frameDelay: 50 }; break; case 'Magma': config = { baseFillColor: '#660000', waveColors: ['#FF2400', '#CC5500'], outlineColor: '#282828', particleColor: '#FFA500', particleType: 'ember', particleChance: 0.2, particleSizeMin: 0.008, particleSizeMax: 0.02, particleThickness: 4, startHeight: 0.86, waveAmplitude: 0.04, waveFrequency: 2, waveSpeed: 0.01, outlineOffset: 0.01, outlineThicknessReduction: 1, mainThickness: 28, waveStyle: 'jagged', frames: 100, frameDelay: 70 }; break; case 'Poison': config = { baseFillColor: '#556B2F', waveColors: ['#6B8E23', '#808000'], outlineColor: '#ADFF2F', particleColor: '#7FFF00', particleType: 'blob', particleChance: 0.25, particleSizeMin: 0.015, particleSizeMax: 0.035, particleThickness: 10, startHeight: 0.87, waveAmplitude: 0.055, waveFrequency: 1.8, waveSpeed: 0.018, outlineOffset: 0.006, outlineThicknessReduction: 2, mainThickness: 26, waveStyle: 'gloopy', frames: 100, frameDelay: 65 }; break; case 'PurpleGoo': config = { baseFillColor: '#4B0082', waveColors: ['#800080', '#9932CC'], outlineColor: '#FF00FF', particleColor: '#DA70D6', particleType: 'blob', particleChance: 0.2, particleSizeMin: 0.01, particleSizeMax: 0.03, particleThickness: 9, startHeight: 0.88, waveAmplitude: 0.05, waveFrequency: 2.2, waveSpeed: 0.022, outlineOffset: 0.007, outlineThicknessReduction: 2, mainThickness: 27, waveStyle: 'jagged', frames: 100, frameDelay: 55 }; break; default: drawing_active = false; console.warn(`Unknown animation type: ${type}`); return; } await botClearCanvas(); const segments = 30; const segmentWidth = 1 / segments; // --- Draw Solid Base Fill (Alternative approach) --- const fillToY = config.startHeight + config.waveAmplitude; // Lowest point of waves let currentYFill = 0.99; // Start near the bottom const fillStep = 0.05; // How much to move up for each fill line const fillThickness = 150; // Drawaria thickness for fill lines while (currentYFill > fillToY && drawing_active) { if (!sendDrawCmd(socket, [0, currentYFill], [1, currentYFill], config.baseFillColor, fillThickness)) { drawing_active = false; console.error("Failed to draw base segment."); break; } currentYFill -= fillStep; if (config.frameDelay > 0 && fillStep < 0.1) await delay(5); // Small delay only if steps are small } if (!drawing_active) { console.log("Animation stopped during base fill."); return; } await delay(50); for (let frame = 0; frame < config.frames && drawing_active; frame++) { let wavePoints = []; for (let i = 0; i <= segments; i++) { const x = i * segmentWidth; let yOffset = Math.sin((x * config.waveFrequency * Math.PI) + (frame * config.waveSpeed)) * config.waveAmplitude; if (config.waveStyle === 'gloopy') { yOffset += (Math.sin((x * config.waveFrequency * 1.7 * Math.PI) + (frame * config.waveSpeed * 1.3) + 0.5) * config.waveAmplitude * 0.4) * Math.sin(x * Math.PI); } else if (config.waveStyle === 'jagged') { yOffset += (Math.random() - 0.5) * config.waveAmplitude * 0.3; } const y = config.startHeight - yOffset; wavePoints.push([x, y]); } for (let i = 0; i < wavePoints.length - 1 && drawing_active; i++) { const p1 = wavePoints[i]; const p2 = wavePoints[i + 1]; const waveColor = config.waveColors[i % config.waveColors.length]; if (!sendDrawCmd(socket, p1, p2, waveColor, config.mainThickness)) { drawing_active = false; break; } const p1_outline = [p1[0], p1[1] - config.outlineOffset]; const p2_outline = [p2[0], p2[1] - config.outlineOffset]; const outlineThickness = Math.max(1, config.mainThickness - config.outlineThicknessReduction); if (!sendDrawCmd(socket, p1_outline, p2_outline, config.outlineColor, outlineThickness)) { drawing_active = false; break; } if (Math.random() < config.particleChance) { const particleX = (p1[0] + p2[0]) / 2 + (Math.random() - 0.5) * 0.05; const particleY = Math.min(p1[1], p2[1]) - 0.02 - Math.random() * 0.08; const particleSize = getRandomFloat(config.particleSizeMin, config.particleSizeMax); if (config.particleType === 'dot') { if (!sendDrawCmd(socket, [particleX, particleY], [particleX + 0.001, particleY + 0.001], config.particleColor, config.particleThickness * (particleSize / config.particleSizeMin))) { drawing_active = false; break; } } else if (config.particleType === 'blob') { let blobPx = particleX; let blobPy = particleY; const blobSegments = getRandomInt(3, 5); for (let k = 0; k < blobSegments && drawing_active; k++) { const nextBlobPx = blobPx + (Math.random() - 0.5) * particleSize; const nextBlobPy = blobPy + (Math.random() - 0.5) * particleSize; if (!sendDrawCmd(socket, [blobPx, blobPy], [nextBlobPx, nextBlobPy], config.particleColor, config.particleThickness)) { drawing_active = false; break; } blobPx = nextBlobPx; blobPy = nextBlobPy; } if (!drawing_active) break; if (blobSegments > 0 && Math.random() < 0.5 && drawing_active) { if (!sendDrawCmd(socket, [blobPx, blobPy], [particleX, particleY], config.particleColor, config.particleThickness)) { drawing_active = false; break; } } } else if (config.particleType === 'ember') { const emberEndX = particleX + (Math.random() - 0.5) * particleSize * 0.5; const emberEndY = particleY - particleSize; if (!sendDrawCmd(socket, [particleX, particleY], [emberEndX, emberEndY], config.particleColor, config.particleThickness)) { drawing_active = false; break; } } } if (!drawing_active) break; } if (!drawing_active) break; if (config.frameDelay > 0) await delay(config.frameDelay); } drawing_active = false; console.log(`${type} Animation finished.`); } async function botClearCanvas() { if (!window.___BOT || !window.___BOT.conn || !window.___BOT.conn.socket || window.___BOT.conn.socket.readyState !== WebSocket.OPEN) { alert("Bot not connected."); return; } sendDrawCmd(window.___BOT.conn.socket, [0.001, 0.5], [0.999, 0.5], "#FFFFFF", 2000, false); console.log("Canvas clear (whiteout) attempted."); await delay(200); } function addBoxIcons() { if (document.querySelector('link[href*="boxicons"]')) return; let b = document.createElement('link'); b.href = 'https://unpkg.com/[email protected]/css/boxicons.min.css'; b.rel = 'stylesheet'; document.head.appendChild(b); } function createStylesheet() { if (document.getElementById('drawaria-elemental-style')) return; let s = document.createElement('style'); s.id = 'drawaria-elemental-style'; s.innerHTML = ` #elemental-menu{position:fixed;top:70px;left:10px;width:360px;background-color:#333A44;color:#E0E0E0;border:1px solid #555E69;border-radius:8px;box-shadow:0 5px 15px rgba(0,0,0,0.3);z-index:10001;font-family:'Arial',sans-serif;font-size:14px;display:none} #elemental-menu-header{padding:10px 15px;background-color:#4A525E;color:#FFF;cursor:move;border-top-left-radius:7px;border-top-right-radius:7px;display:flex;justify-content:space-between;align-items:center} #elemental-menu-header h3{margin:0;font-size:16px;font-weight:bold} #elemental-menu-close{background:none;border:none;color:#FFF;font-size:24px;cursor:pointer;padding:0 5px;} #elemental-menu-body{padding:15px;max-height:calc(90vh - 50px);overflow-y:auto;background-color:#39414C} .elemental-section{margin-bottom:18px;padding-bottom:12px;border-bottom:1px solid #4A525E} .elemental-section:last-child{border-bottom:none;margin-bottom:0;} .elemental-section h4{margin-top:0;margin-bottom:10px;color:#A0D2EB;font-size:15px;border-bottom:1px solid #4A525E;padding-bottom:5px;} .elemental-button-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px} .elemental-button{padding:10px 12px;background-color:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer;text-align:center;font-size:14px;transition:background-color .2s ease, transform .1s ease; display:flex; align-items:center; justify-content:center;} .elemental-button i{margin-right:8px; font-size: 1.2em;} .elemental-button:hover{background-color:#45a049; transform: translateY(-1px);} .elemental-button:active{transform: translateY(0px);} .elemental-button.ocean{background-color:#1E90FF;} .elemental-button.ocean:hover{background-color:#187CDA;} .elemental-button.magma{background-color:#8B0000;} .elemental-button.magma:hover{background-color:#7A0000;} /* Magma icon is bxs-volcano */ .elemental-button.poison{background-color:#6B8E23;} .elemental-button.poison:hover{background-color:#5A7D13;} .elemental-button.purplegoo{background-color:#800080;} .elemental-button.purplegoo:hover{background-color:#700070;} #elemental-toggle-button{margin-left:5px; background-color: #6c757d; color:white;} #elemental-toggle-button.active{background-color: #5a6268; box-shadow: inset 0 1px 3px rgba(0,0,0,.2);} .elemental-clear-button{padding:10px 12px;background-color:#ff9800;color:white;border:none;border-radius:5px;cursor:pointer;text-align:center;font-size:14px;transition:background-color .2s ease, transform .1s ease;width:100%;margin-top:12px;display:flex; align-items:center; justify-content:center;} .elemental-clear-button i{margin-right:8px; font-size: 1.2em;} .elemental-clear-button:hover{background-color:#f57c00;transform: translateY(-1px);} .elemental-clear-button:active{transform: translateY(0px);}`; document.head.appendChild(s); } function makeDraggable(m, h) { let oX, oY, d = false; h.onmousedown = e => { if (e.target.closest('button,input,select,a')) return; d = true; oX = e.clientX - m.getBoundingClientRect().left; oY = e.clientY - m.getBoundingClientRect().top; m.style.userSelect = 'none'; document.body.style.cursor = 'grabbing'; }; document.onmousemove = e => { if (!d) return; m.style.left = `${e.clientX - oX}px`; m.style.top = `${e.clientY - oY}px`; }; document.onmouseup = () => { if (d) { d = false; m.style.userSelect = ''; document.body.style.cursor = ''; } }; h.style.cursor = 'move'; } function buildMenu() { addBoxIcons(); createStylesheet(); const M = document.createElement('div'); M.id = 'elemental-menu'; const H = document.createElement('div'); H.id = 'elemental-menu-header'; H.innerHTML = `<h3><i class='bx bxs-color-fill' style="margin-right:8px;"></i>Elemental Animations</h3><button id="elemental-menu-close" title="Close Menu"><i class='bx bx-x'></i></button>`; M.appendChild(H); const B = document.createElement('div'); B.id = 'elemental-menu-body'; B.innerHTML = ` <div class="elemental-section"> <h4><i class='bx bxs-magic-wand'></i> Animations</h4> <div class="elemental-button-grid"> <button id="effect-ocean" class="elemental-button ocean" title="Dynamic ocean waves with foamy crests."><i class='bx bxs-droplet'></i> Ocean</button> <button id="effect-magma" class="elemental-button magma" title="Thick, crusty magma flow with embers."><i class='bx bxs-volcano'></i> Magma</button> <button id="effect-poison" class="elemental-button poison" title="Gloopy toxic poison with bubbling blobs."><i class='bx bxs-skull'></i> Poison</button> <button id="effect-purplegoo" class="elemental-button purplegoo" title="Viscous purple goo with magenta highlights."><i class='bx bxs-vial'></i> Purple Goo</button> </div> </div> <div class="elemental-section"> <h4><i class='bx bx-eraser'></i> Utilities</h4> <button id="bot-clear-canvas" class="elemental-clear-button"><i class='bx bxs-eraser'></i> Bot Clear (Whiteout)</button> </div>`; M.appendChild(B); document.body.appendChild(M); makeDraggable(M, H); EL('#elemental-menu-close').onclick = () => M.style.display = 'none'; EL('#effect-ocean').onclick = () => animateElementalWave('Ocean'); EL('#effect-magma').onclick = () => animateElementalWave('Magma'); EL('#effect-poison').onclick = () => animateElementalWave('Poison'); EL('#effect-purplegoo').onclick = () => animateElementalWave('PurpleGoo'); EL('#bot-clear-canvas').onclick = botClearCanvas; const chatInputContainer = EL('#chatbox_textinput')?.parentElement; if (chatInputContainer && !EL('#elemental-toggle-button')) { let tB = document.createElement('button'); tB.id = 'elemental-toggle-button'; tB.className = 'btn btn-sm'; tB.innerHTML = `<i class='bx bx-palette bx-sm'></i>`; tB.title = "Toggle Elemental Animations Menu"; tB.style.marginLeft = "5px"; tB.onclick = e => { e.preventDefault(); const menu = EL('#elemental-menu'); menu.style.display = (menu.style.display === 'none' || menu.style.display === '') ? 'block' : 'none'; tB.classList.toggle('active', menu.style.display === 'block'); }; const sendButton = EL('#chatbox_form button[type="submit"], #chatbox_form button:not([id])'); // Try to find the send button if (sendButton && sendButton.parentElement) { sendButton.parentElement.insertBefore(tB, sendButton.nextSibling); // Insert after send button } else if (chatInputContainer.querySelector('.input-group-append')) { chatInputContainer.querySelector('.input-group-append').appendChild(tB); } else { chatInputContainer.appendChild(tB); } } } function initializeWhenReady() { originalCanvas = document.getElementById('canvas'); const cI = document.getElementById('chatbox_textinput'); if (!originalCanvas || !cI) { setTimeout(initializeWhenReady, 500); return; } if (originalCanvas) { cw = originalCanvas.width; ch = originalCanvas.height; console.log(`Canvas dimensions captured: ${cw}x${ch}`); } console.log("Drawaria Elemental Animations Enhanced V1.6 init..."); window['___ENGINE'] = { animateElementalWave, botClearCanvas }; buildMenu(); console.log("Drawaria Elemental Animations Enhanced V1.6 Loaded!"); } if (document.readyState === "complete" || document.readyState === "interactive") { initializeWhenReady(); } else { window.addEventListener('load', initializeWhenReady); } })();