您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Advanced drawing animation library for multiplayer drawing games
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.org/scripts/546216/1643828/Drawaria%20Animation%20Library.js
// ==UserScript== // @name Drawaria Animation Library // @namespace drawaria-animations // @version 3.0 // @description Complete animation library for Drawaria with all effects // @author DrawArtist // @license MIT // @match https://drawaria.online/* // @grant none // ==/UserScript== (function() { 'use strict'; // Verificar que getGameSocket esté disponible function getGameSocket() { return window.getGameSocket ? window.getGameSocket() : null; } // Core drawing animation functions const DRAWING_FUNCTIONS = { // Utility functions _delay: function(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }, _getRandomColor: function(saturation = 70, lightness = 50) { const hue = Math.floor(Math.random() * 360); return `hsl(${hue}, ${saturation}%, ${lightness}%)`; }, _sendDrawCmd: function(startPoint, endPoint, color, thickness) { try { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { console.warn('WebSocket not available or not open'); return false; } const cmd = `42["drawcmd",0,[${startPoint.toFixed(4)},${startPoint[1].toFixed(4)},${endPoint.toFixed(4)},${endPoint[1].toFixed(4)},false,${0-thickness},"${color}",0,0,{"2":0,"3":0.5,"4":0.5}]]`; socket.send(cmd); return true; } catch (error) { console.error('Error sending draw command:', error); return false; } }, _drawPixel: async function(x, y, size, color, delay = 0) { const endX = x + size * 0.0001; const endY = y + size * 0.0001; const thickness = size * 1000; const result = this._sendDrawCmd([x, y], [endX, endY], color, thickness); if (delay > 0) await this._delay(delay); return result; }, notify: function(type, message) { console.log(`[DRAWARIA-ANIMATIONS][${type.toUpperCase()}] ${message}`); }, // State management _drawingActive: false, _globalFrameCount: 0, // Pixel font for text rendering _pixelFont: { 'A': ["0110", "1001", "1111", "1001", "1001"], 'B': ["1110", "1001", "1110", "1001", "1110"], 'C': ["0110", "1000", "1000", "1000", "0110"], 'D': ["1110", "1001", "1001", "1001", "1110"], 'E': ["1111", "1000", "1110", "1000", "1111"], 'G': ["0110", "1000", "1011", "1001", "0111"], 'H': ["1001", "1001", "1111", "1001", "1001"], 'I': ["111", "010", "010", "010", "111"], 'K': ["1001", "1010", "1100", "1010", "1001"], 'L': ["1000", "1000", "1000", "1000", "1111"], 'M': ["10001", "11011", "10101", "10001", "10001"], 'N': ["1001", "1101", "1011", "1001", "1001"], 'O': ["0110", "1001", "1001", "1001", "0110"], 'P': ["1110", "1001", "1110", "1000", "1000"], 'R': ["1110", "1001", "1110", "1010", "1001"], 'S': ["0111", "1000", "0110", "0001", "1110"], 'U': ["1001", "1001", "1001", "1001", "0110"], 'V': ["10001", "10001", "01010", "01010", "00100"], ' ': ["000", "000", "000", "000", "000"] }, _charHeight: 5, async _drawPixelText(text, startX, startY, charPixelSize, color, textPixelDelay, letterSpacingFactor = 0.8) { let currentX = startX; text = text.toUpperCase(); for (const char of text) { if (!this._drawingActive) return; const charData = this._pixelFont[char]; if (charData) { let charWidth = 0; for (let y = 0; y < this._charHeight; y++) { if (!this._drawingActive) return; const row = charData[y]; charWidth = Math.max(charWidth, row.length); for (let x = 0; x < row.length; x++) { if (!this._drawingActive) return; if (row[x] === '1') { const dX = currentX + x * charPixelSize; const dY = startY + y * charPixelSize; if (!await this._drawPixel(dX, dY, charPixelSize, color, textPixelDelay)) return; } } } currentX += (charWidth + letterSpacingFactor) * charPixelSize; } else { currentX += (3 + letterSpacingFactor) * charPixelSize; } } }, // COMPLETE ANIMATION IMPLEMENTATIONS async pixelArtCharacters() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Pixel Art Characters..."); try { const Q_TOP_LEFT = { xMin: 0.0, yMin: 0.0, xMax: 0.5, yMax: 0.5 }; const Q_TOP_RIGHT = { xMin: 0.5, yMin: 0.0, xMax: 1.0, yMax: 0.5 }; const Q_BOTTOM_LEFT = { xMin: 0.0, yMin: 0.5, xMax: 0.5, yMax: 1.0 }; const Q_BOTTOM_RIGHT = { xMin: 0.5, yMin: 0.5, xMax: 1.0, yMax: 1.0 }; const marioSprite = { name: "MARIO", nameColor: "#FF0000", width: 12, data: ["____RRRRR___", "___RRRRRRR__", "___NNNYNY___", "__NSSYSYYN__", "__NSSYSYYYNN", "__NYYYYYYYYN", "____BBBB____", "__RBBBRBBR__", "_RBBRRRBBRR_", "RBBBBBRBBBB_", "BBBBBBRBBBBB", "BBBB__BBBB__", "NNN____NNN__", "_NN____NN___"], colors: { R: "#E60000", N: "#7A3D03", Y: "#FBD000", S: "#FFCC99", B: "#0040FF" }, quadrant: Q_TOP_LEFT, textOffsetY: -0.08 }; const pikachuSprite = { name: "PIKACHU", nameColor: "#FFA500", width: 13, data: ["____PPPPP____", "___PKKKPKK___", "__PKKPKPKKK__", "_PKKPKKPKPKK_", "_PKKPOKPKPOKK", "PPKPKKKPKPKPP", "PPKPK_KPKPKPP", "_PKPKKKPKPKP_", "__PKKKKKPKP__", "___PPPPPPP___", "____PP_PP____"], colors: { P: "#FFDE38", K: "#000000", O: "#FF4444", W: "#FFFFFF" }, quadrant: Q_TOP_RIGHT, textOffsetY: -0.08 }; const characters = [marioSprite, pikachuSprite]; const pixelDrawDelay = 3; const textPixelDelay = 2; const textCharPixelSize = 0.008; // Draw dividing lines const lineThickness = 8; if (!this._sendDrawCmd([0, 0.5], [1, 0.5], marioSprite.colors.R, lineThickness)) { this._drawingActive = false; return; } if (!this._sendDrawCmd([0.5, 0], [0.5, 1], pikachuSprite.colors.O, lineThickness)) { this._drawingActive = false; return; } await this._delay(100); // Draw "VS" text if (this._drawingActive) { const vsTextSize = 0.02; const vsColor = "#000000"; await this._drawPixelText("VS", 0.5 - (vsTextSize * 5) / 2, 0.5 - (vsTextSize * this._charHeight) / 2, vsTextSize, vsColor, textPixelDelay); } await this._delay(100); // Draw characters for (const char of characters) { if (!this._drawingActive) break; const charHeightPx = char.data.length; const charWidthPx = char.width; const quadW = char.quadrant.xMax - char.quadrant.xMin; const quadH = char.quadrant.yMax - char.quadrant.yMin; const scaleFactor = 0.65; const pixelSizeX = (quadW * scaleFactor) / charWidthPx; const pixelSizeY = (quadH * scaleFactor) / charHeightPx; const finalPixelSize = Math.min(pixelSizeX, pixelSizeY); const totalSpriteW = charWidthPx * finalPixelSize; const totalSpriteH = charHeightPx * finalPixelSize; const startX = char.quadrant.xMin + (quadW - totalSpriteW) / 2; const startY = char.quadrant.yMin + (quadH - totalSpriteH) / 2; // Draw character name const nameLenEst = char.name.length * 3 * textCharPixelSize; const textStartX = char.quadrant.xMin + (quadW - nameLenEst) / 2; let textStartY = startY + char.textOffsetY - (this._charHeight * textCharPixelSize); textStartY = Math.max(char.quadrant.yMin + 0.01, Math.min(char.quadrant.yMax - 0.01 - (this._charHeight * textCharPixelSize), textStartY)); if (this._drawingActive) { await this._drawPixelText(char.name, textStartX, textStartY, textCharPixelSize, char.nameColor, textPixelDelay); } await this._delay(50); // Draw sprite pixels for (let y = 0; y < charHeightPx; y++) { if (!this._drawingActive) break; for (let x = 0; x < charWidthPx; x++) { if (!this._drawingActive) break; const colorChar = char.data[y][x]; if (colorChar !== "_" && char.colors[colorChar]) { const dX = startX + x * finalPixelSize; const dY = startY + y * finalPixelSize; if (!await this._drawPixel(dX, dY, finalPixelSize, char.colors[colorChar], pixelDrawDelay)) { this._drawingActive = false; break; } } } } if (!this._drawingActive) break; await this._delay(200); } this._drawingActive = false; this.notify("success", "Pixel Art Characters finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, async pulsatingStainedGlass() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Pulsating Stained Glass..."); try { const gridX = 5 + Math.floor(Math.random() * 4); const gridY = 4 + Math.floor(Math.random() * 3); const cellW = 1 / gridX; const cellH = 1 / gridY; const animSteps = 150; const globalDelay = 50; const lineThickness = 3; const lineColor = "rgb(40,40,40)"; let cells = []; // Create grid cells for (let r = 0; r < gridY; r++) { for (let c = 0; c < gridX; c++) { const cellType = Math.random(); let points = []; const x = c * cellW, y = r * cellH, w = cellW, h = cellH; if (cellType < 0.33) { points = [[x, y], [x + w, y], [x + w, y + h], [x, y + h]]; } else if (cellType < 0.66) { if (Math.random() < 0.5) { points = [[x, y], [x + w, y], [x + w, y + h], [x, y], [x, y + h], [x + w, y + h]]; } else { points = [[x, y], [x + w, y], [x, y + h], [x + w, y], [x + w, y + h], [x, y + h]]; } } else { const cx = x + w / 2, cy = y + h / 2; points = [[x, y], [x + w, y], [cx, cy], [x + w, y], [x + w, y + h], [cx, cy], [x + w, y + h], [x, y + h], [cx, cy], [x, y + h], [x, y], [cx, cy]]; } cells.push({ basePoints: points, hue: Math.random() * 360, lightPhase: Math.random() * Math.PI * 2, lightSpeed: 0.05 + Math.random() * 0.1 }); } } // Draw cell outlines for (const cell of cells) { if (!this._drawingActive) break; for (let i = 0; i < cell.basePoints.length; i += 3) { if (!this._drawingActive || i + 2 >= cell.basePoints.length) break; const p1 = cell.basePoints[i], p2 = cell.basePoints[i + 1], p3 = cell.basePoints[i + 2]; if (!this._sendDrawCmd(p1, p2, lineColor, lineThickness)) { this._drawingActive = false; break; } if (!this._sendDrawCmd(p2, p3, lineColor, lineThickness)) { this._drawingActive = false; break; } if (!this._sendDrawCmd(p3, p1, lineColor, lineThickness)) { this._drawingActive = false; break; } await this._delay(5); } } // Animate pulsating colors for (let frame = 0; frame < animSteps && this._drawingActive; frame++) { for (const cell of cells) { if (!this._drawingActive) break; const currentLightness = 40 + 20 * Math.sin(cell.lightPhase + frame * cell.lightSpeed); const color = `hsl(${cell.hue},80%,${currentLightness}%)`; for (let i = 0; i < cell.basePoints.length; i += 3) { if (!this._drawingActive || i + 2 >= cell.basePoints.length) break; const p1 = cell.basePoints[i], p2 = cell.basePoints[i + 1], p3 = cell.basePoints[i + 2]; const m12 = [(p1 + p2) / 2, (p1[1] + p2[1]) / 2]; const m23 = [(p2 + p3) / 2, (p2[1] + p3[1]) / 2]; if (!this._sendDrawCmd(m12, p3, color, lineThickness * 3 + 2)) { this._drawingActive = false; break; } if (!this._sendDrawCmd(m23, p1, color, lineThickness * 3 + 2)) { this._drawingActive = false; break; } } } if (!this._drawingActive) break; await this._delay(globalDelay); } this._drawingActive = false; this.notify("success", "Pulsating Stained Glass finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, async celestialBallet() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Celestial Ballet..."); try { const numDots = 8 + Math.floor(Math.random() * 5); const steps = 150; const thickness = 3; const baseDelay = 25; let dots = []; // Initialize dancing dots for (let i = 0; i < numDots; i++) { dots.push({ x: 0.5, y: 0.5, vx: (Math.random() - 0.5) * 0.02, vy: (Math.random() - 0.5) * 0.02, orbitCenterX: 0.5 + (Math.random() - 0.5) * 0.4, orbitCenterY: 0.5 + (Math.random() - 0.5) * 0.4, orbitSpeed: (Math.random() * 0.05 + 0.02) * (Math.random() < 0.5 ? 1 : -1), hue: Math.random() * 360, lastX: 0.5, lastY: 0.5 }); } // Animate the celestial ballet for (let step = 0; step < steps && this._drawingActive; step++) { for (const dot of dots) { if (!this._drawingActive) break; // Store last position dot.lastX = dot.x; dot.lastY = dot.y; // Calculate orbital motion const angleToOrbit = Math.atan2(dot.y - dot.orbitCenterY, dot.x - dot.orbitCenterX); dot.vx += Math.cos(angleToOrbit + Math.PI / 2) * dot.orbitSpeed * 0.1; dot.vy += Math.sin(angleToOrbit + Math.PI / 2) * dot.orbitSpeed * 0.1; // Add center attraction dot.vx += (0.5 - dot.x) * 0.0005; dot.vy += (0.5 - dot.y) * 0.0005; // Apply damping dot.vx *= 0.97; dot.vy *= 0.97; // Update position dot.x += dot.vx; dot.y += dot.vy; // Boundary collision if (dot.x < 0.01 || dot.x > 0.99) dot.vx *= -0.8; if (dot.y < 0.01 || dot.y > 0.99) dot.vy *= -0.8; // Keep in bounds dot.x = Math.max(0.01, Math.min(0.99, dot.x)); dot.y = Math.max(0.01, Math.min(0.99, dot.y)); // Update color dot.hue = (dot.hue + 0.5) % 360; const color = `hsl(${dot.hue},100%,70%)`; // Draw trail if (!this._sendDrawCmd([dot.lastX, dot.lastY], [dot.x, dot.y], color, thickness)) { this._drawingActive = false; break; } } if (baseDelay > 0 && this._drawingActive) await this._delay(baseDelay); } this._drawingActive = false; this.notify("success", "Celestial Ballet finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, async recursiveStarPolygonNova() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Recursive Star Polygon Nova..."); try { const centerX = 0.5, centerY = 0.5; const initialRadius = 0.25; const maxDepth = 3 + Math.floor(Math.random() * 1); const numPoints = 5 + Math.floor(Math.random() * 2) * 2; const skipFactor = 2 + Math.floor(Math.random() * 1); const recursiveScaleFactor = 0.4; const stepDelay = 15; const baseHue = Math.random() * 360; let globalRotation = this._globalFrameCount * 0.01; const drawStar = async (cx, cy, radius, points, skip, depth, currentHue, currentThickness, parentAngle) => { if (!this._drawingActive || depth > maxDepth || radius < 0.005) return; const starCorners = []; for (let i = 0; i < points; i++) { const angle = (i / points) * 2 * Math.PI + parentAngle + globalRotation; starCorners.push({ x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) }); } const color = `hsl(${(currentHue + depth * 30) % 360},95%,${65 - depth * 10}%)`; const thickness = Math.max(1, currentThickness); // Draw star lines for (let i = 0; i < points && this._drawingActive; i++) { const p1 = starCorners[i]; const p2 = starCorners[(i + skip) % points]; if (!this._sendDrawCmd([p1.x, p1.y], [p2.x, p2.y], color, thickness)) { this._drawingActive = false; return; } if (stepDelay > 0 && this._drawingActive) await this._delay(stepDelay); } // Recursive calls for (let i = 0; i < points && this._drawingActive; i++) { const newAngle = (i / points) * 2 * Math.PI + parentAngle + Math.PI / points; await drawStar(starCorners[i].x, starCorners[i].y, radius * recursiveScaleFactor, points, skip, depth + 1, currentHue, thickness * 0.7, newAngle); } }; await drawStar(centerX, centerY, initialRadius, numPoints, skipFactor, 1, baseHue, 6, 0); this._globalFrameCount++; this._drawingActive = false; this.notify("success", "Recursive Star Polygon Nova finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, async fractalBloomMandala() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Fractal Bloom Mandala..."); try { const centerX = 0.5, centerY = 0.5; const maxDepth = 4; const initialBranches = 6 + Math.floor(Math.random() * 3); const initialLength = 0.15; const lengthRatio = 0.65; const angleStep = Math.PI / (3 + Math.random() * 2); const delay = 20; const baseHue = Math.random() * 360; let globalRotation = this._globalFrameCount * 0.01; const drawBranch = async (cx, cy, angle, length, depth, currentHue, branchThickness) => { if (!this._drawingActive || depth > maxDepth || length < 0.005) return; const x2 = cx + length * Math.cos(angle); const y2 = cy + length * Math.sin(angle); const thickness = Math.max(1, branchThickness * Math.pow(lengthRatio, depth - 1) * 2); const color = `hsl(${(currentHue + depth * 20) % 360},${80 - depth * 10}%,${60 - depth * 8}%)`; if (!this._sendDrawCmd([cx, cy], [x2, y2], color, thickness)) { this._drawingActive = false; return; } if (delay > 0) await this._delay(delay); if (!this._drawingActive) return; // Branch left and right await drawBranch(x2, y2, angle - angleStep, length * lengthRatio, depth + 1, currentHue, branchThickness); if (!this._drawingActive) return; await drawBranch(x2, y2, angle + angleStep, length * lengthRatio, depth + 1, currentHue, branchThickness); // Sometimes add a middle branch if (depth < maxDepth - 1 && Math.random() < 0.4) { if (!this._drawingActive) return; await drawBranch(x2, y2, angle, length * lengthRatio * 0.8, depth + 1, currentHue, branchThickness); } }; // Draw mandala branches for (let i = 0; i < initialBranches && this._drawingActive; i++) { const angle = (i / initialBranches) * 2 * Math.PI + globalRotation; await drawBranch(centerX, centerY, angle, initialLength, 1, (baseHue + i * (360 / initialBranches)) % 360, 10); if (delay > 0 && this._drawingActive) await this._delay(delay * 3); } this._globalFrameCount++; this._drawingActive = false; this.notify("success", "Fractal Bloom Mandala finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, async directionalHueBlast() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Directional Hue Blast..."); try { const directions = [ { s: [0.5, 0], e: [0.5, 1] }, // Top to bottom { s: [0.5, 1], e: [0.5, 0] }, // Bottom to top { s: [0, 0.5], e: [1, 0.5] }, // Left to right { s: [1, 0.5], e: [0, 0.5] } // Right to left ]; for (let i = 0; i < 100 && this._drawingActive; i++) { for (let dir of directions) { if (!this._drawingActive) break; let t = i / 100; let x = dir.s + (dir.e - dir.s) * t; let y = dir.s[1] + (dir.e[1] - dir.s[1]) * t; if (!this._sendDrawCmd([x, y], [x + 0.001, y + 0.001], `hsl(${i * 3.6},100%,50%)`, 10 + i * 0.5)) { this._drawingActive = false; break; } } if (!this._drawingActive) break; await this._delay(40); } this._drawingActive = false; this.notify("success", "Directional Hue Blast finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, async colorFestival() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Color Festival..."); try { const numShapes = 120; const frameDelay = 60; const shapeDelay = 10; for (let i = 0; i < numShapes && this._drawingActive; i++) { let x = Math.random() * 0.8 + 0.1; let y = Math.random() * 0.8 + 0.1; let size = Math.random() * 0.08 + 0.03; let color = this._getRandomColor(90, 55); let thickness = Math.floor(Math.random() * 10) + 4; let shapeType = Math.floor(Math.random() * 4); let success = true; if (shapeType === 0) { // Rectangle lines for (let j = 0; j < size * 100 && success && this._drawingActive; j += thickness / 2) { let lineY = y - size / 2 + (j / 100); if (lineY > y + size / 2) break; success = this._sendDrawCmd([x - size / 2, lineY], [x + size / 2, lineY], color, thickness); if (shapeDelay > 0 && success) await this._delay(shapeDelay); } } else if (shapeType === 1) { // Triangle const s = size; success = this._sendDrawCmd([x, y - s / 2], [x + s / 2, y + s / 2], color, thickness); if (success && this._drawingActive && shapeDelay > 0) await this._delay(shapeDelay); if (!success || !this._drawingActive) break; success = this._sendDrawCmd([x + s / 2, y + s / 2], [x - s / 2, y + s / 2], color, thickness); if (success && this._drawingActive && shapeDelay > 0) await this._delay(shapeDelay); if (!success || !this._drawingActive) break; success = this._sendDrawCmd([x - s / 2, y + s / 2], [x, y - s / 2], color, thickness); } else if (shapeType === 2) { // Star pattern const s = size * 0.7; for (let k = 0; k < 8 && success && this._drawingActive; k++) { const angle = (k / 8) * 2 * Math.PI; success = this._sendDrawCmd([x, y], [x + s * Math.cos(angle), y + s * Math.sin(angle)], color, thickness); if (shapeDelay > 0 && success) await this._delay(shapeDelay); } } else { // Spiral let lastX = x, lastY = y; for (let k = 0; k <= 20 && success && this._drawingActive; k++) { const angle = (k / 20) * 4 * Math.PI; const radius = (k / 20) * size; const currentX = x + radius * Math.cos(angle); const currentY = y + radius * Math.sin(angle); if (k > 0) success = this._sendDrawCmd([lastX, lastY], [currentX, currentY], color, thickness); lastX = currentX; lastY = currentY; if (shapeDelay > 0 && success) await this._delay(shapeDelay); } } if (!success || !this._drawingActive) break; if (frameDelay > 0) await this._delay(frameDelay); } this._drawingActive = false; this.notify("success", "Color Festival finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, async fireworks() { const socket = getGameSocket(); if (!socket || socket.readyState !== WebSocket.OPEN) { this.notify("error", "Not connected to game. Please be in a room."); return; } this._drawingActive = true; this.notify("info", "Starting Fireworks..."); try { const numFireworks = 8; const fireworkDelay = 600; for (let i = 0; i < numFireworks && this._drawingActive; i++) { // Launch trail let startX = Math.random() * 0.6 + 0.2; let startY = 0.95; let peakX = startX + (Math.random() - 0.5) * 0.3; let peakY = Math.random() * 0.4 + 0.05; let launchColor = this._getRandomColor(100, 70); let launchThickness = 6; let particleCount = 40 + Math.floor(Math.random() * 40); let particleThickness = 4 + Math.floor(Math.random() * 4); let launchSteps = 25; let launchStepDelay = 4; let explosionParticleDelay = 8; let success = true; // Draw launch trail for (let step = 0; step < launchSteps && success && this._drawingActive; step++) { let progress = step / launchSteps; let nextProgress = (step + 1) / launchSteps; let currentX = startX + (peakX - startX) * progress; let currentY = startY + (peakY - startY) * progress; let nextX = startX + (peakX - startX) * nextProgress; let nextY = startY + (peakY - startY) * nextProgress; success = this._sendDrawCmd([currentX, currentY], [nextX, nextY], launchColor, launchThickness); if (launchStepDelay > 0 && success) await this._delay(launchStepDelay); } if (!success || !this._drawingActive) break; // Explosion const explosionHue = Math.random() * 360; for (let j = 0; j < particleCount && success && this._drawingActive; j++) { const angle = Math.random() * 2 * Math.PI; const distance = Math.random() * 0.20 + 0.05; const endX = peakX + distance * Math.cos(angle); const endY = peakY + distance * Math.sin(angle); const particleHue = (explosionHue + (Math.random() - 0.5) * 60 + 360) % 360; success = this._sendDrawCmd([peakX, peakY], [endX, endY], `hsl(${particleHue},100%,60%)`, particleThickness); if (explosionParticleDelay > 0 && success) await this._delay(explosionParticleDelay); } if (!success || !this._drawingActive) break; if (fireworkDelay > 0) await this._delay(fireworkDelay); } this._drawingActive = false; this.notify("success", "Fireworks finished."); } catch (error) { this._drawingActive = false; this.notify("error", "Animation failed: " + error.message); } }, stopDrawing() { this._drawingActive = false; this.notify("info", "Drawing stopped by user."); } }; // Library interface window.DRAWARIA_ANIMATIONS = { getAnimations: function() { try { return Object.keys(DRAWING_FUNCTIONS).filter(key => typeof DRAWING_FUNCTIONS[key] === 'function' && !key.startsWith('_') && key !== 'notify' && key !== 'stopDrawing' ); } catch (error) { console.error('Error getting animations:', error); return []; } }, runAnimation: function(animationName) { try { if (typeof DRAWING_FUNCTIONS[animationName] === 'function') { return DRAWING_FUNCTIONS[animationName].call(DRAWING_FUNCTIONS); } else { console.error('Animation not found:', animationName); return Promise.reject('Animation not found'); } } catch (error) { console.error('Error running animation:', error); return Promise.reject(error); } }, getRandomAnimation: function() { try { const animations = this.getAnimations(); if (animations.length === 0) return null; return animations[Math.floor(Math.random() * animations.length)]; } catch (error) { console.error('Error getting random animation:', error); return null; } }, stop: function() { try { DRAWING_FUNCTIONS.stopDrawing(); } catch (error) { console.error('Error stopping animation:', error); } }, isDrawing: function() { return DRAWING_FUNCTIONS._drawingActive; }, animations: DRAWING_FUNCTIONS }; // Backward compatibility window.DRAWING_FUNCTIONS = DRAWING_FUNCTIONS; console.log('🎨 Drawaria Animation Library loaded successfully'); console.log('Available animations:', window.DRAWARIA_ANIMATIONS.getAnimations()); })();