您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Professional recreation of Sky Sanctuary Zone with detailed elements
// ==UserScript== // @name Drawaria Sky Sanctuary Zone // @namespace http://tampermonkey.net/ // @version 3.0 // @description Professional recreation of Sky Sanctuary Zone with detailed elements // @author YouTubeDrawaria // @match https://drawaria.online/* // @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; function waitForCanvas() { if (document.querySelector('canvas')) { initSkySanctuary(); } else { setTimeout(waitForCanvas, 1000); } } function initSkySanctuary() { const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d'); const colors = { skyTop: '#4080c0', // Darker, more realistic sky skyBottom: '#80b0e0', // Lighter blue clouds: '#ffffff', cloudShadow: '#e0e0e0', // Grayer shadow pillarLight: '#f0e68c', // Khaki pillarMid: '#d4c4a8', // Light Brown pillarShade: '#a08060', // Darker Brown pillarDark: '#604020', // Very Dark Brown grass: '#30a040', // Darker, richer green grassShade: '#208030', // Even darker green stone: '#c0c0c0', // Silver stoneDark: '#808080', // Gray decorGold: '#ffd700', ruins: '#b0a080', // More muted ruins vineGreen: '#2e8b57', // Sea Green vineDark: '#196127', // Darker Sea Green castleLight: 'rgba(200, 190, 170, 0.4)', // Slightly more opaque castleDark: 'rgba(150, 140, 120, 0.3)', // Darker and more opaque leaves: '#388e3c', // Darker Green flowerGold: '#ffd700', flowerWhite: '#ffffff', particle: 'rgba(255, 255, 255, 0.6)' // White with some opacity }; // Utility function for creating gradients function createVerticalGradient(ctx, topColor, bottomColor, startY, endY) { const gradient = ctx.createLinearGradient(0, startY, 0, endY); gradient.addColorStop(0, topColor); gradient.addColorStop(1, bottomColor); return gradient; } class ParallaxElement { constructor(x, y, speed, depth) { this.x = x; this.y = y; this.speed = speed; this.depth = depth; this.originalX = x; // Store original X for resetting this.originalY = y; // Store original Y for parallax relative to the canvas bottom. } update(deltaTime, cameraX = 0) { // Parallax scrolling. this.x = this.originalX - cameraX * this.depth; // Wrap around when going off-screen. if (this.x + 1000 < cameraX * this.depth ) { this.x = this.originalX + canvas.width; // Reset at the far right. this.originalX = this.x; // *Important*: Update originalX } } reset() { this.x = this.originalX; this.y = this.originalY; } } class DetailedPillar extends ParallaxElement { constructor(x, y, width, height, depth) { super(x, y, 0.04, depth); // Increased speed slightly this.width = width; this.height = height; this.decorations = this.generateDecorations(); this.cracks = this.generateCracks(); // Add cracks } generateDecorations() { const decorations = []; const numDecorations = Math.floor(5 + Math.random() * 5); // More decorations for (let i = 0; i < numDecorations; i++) { decorations.push({ x: Math.random() * this.width, y: Math.random() * this.height, size: 7 + Math.random() * 8, // Slightly larger sizes type: Math.random() < 0.7 ? 'circle' : 'square' // Different shapes }); } return decorations; } generateCracks() { const cracks = []; const numCracks = Math.floor(2 + Math.random() * 4); // Add a few cracks for (let i = 0; i < numCracks; i++) { cracks.push({ startX: this.width * (0.2 + Math.random() * 0.6), // Random within pillar startY: this.height * (0.1 + Math.random() * 0.8), length: 20 + Math.random() * 40, angle: (Math.random() - 0.5) * Math.PI / 4 // +/- 45 degrees }); } return cracks; } draw(ctx) { const perspective = 0.4 * this.depth; // Increased perspective const topWidth = this.width * (1 - perspective); this.drawPillarBody(ctx, topWidth); this.drawPillarDetails(ctx, topWidth); this.drawDecorations(ctx); this.drawCracks(ctx); // Draw cracks this.drawCapital(ctx, topWidth); } drawPillarBody(ctx, topWidth) { // console.log(`Pillar: x=${this.x}, y=${this.y}, width=${this.width}, height=${this.height}`); ctx.beginPath(); ctx.moveTo(this.x, this.y); // Start from the top-left ctx.lineTo(this.x + this.width, this.y); // Top-right ctx.lineTo(this.x + this.width/2 + topWidth/2, this.y - this.height); // Top perspective point ctx.lineTo(this.x + this.width/2 - topWidth/2, this.y- this.height); // Bottom perspective point ctx.closePath(); const gradient = ctx.createLinearGradient(this.x, this.y - this.height, this.x, this.y); gradient.addColorStop(0, colors.pillarLight); // Lighter at the top gradient.addColorStop(0.5, colors.pillarMid); gradient.addColorStop(1, colors.pillarShade); // Darker at the bottom ctx.fillStyle = gradient; ctx.globalAlpha = 0.8; // Slightly more opaque ctx.fill(); ctx.globalAlpha = 1; // Add shadow ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'; // Dark, semi-transparent shadow ctx.beginPath(); ctx.moveTo(this.x + this.width, this.y); ctx.lineTo(this.x + this.width * 1.1, this.y - 20); // Shadow extends slightly ctx.lineTo(this.x + this.width/2 + topWidth/2 + 20, this.y - this.height - 10); ctx.lineTo(this.x + this.width/2 + topWidth/2, this.y - this.height); ctx.closePath(); ctx.fill(); } drawPillarDetails(ctx, topWidth) { for (let i = 0.2; i <= 0.8; i += 0.2) { ctx.fillStyle = colors.pillarShade; ctx.beginPath(); ctx.moveTo(this.x + this.width * i, this.y); ctx.lineTo(this.x + this.width * (i + 0.05), this.y); ctx.lineTo(this.x + this.width/2 + topWidth * (i - 0.5) + topWidth * 0.025, this.y-this.height); ctx.lineTo(this.x + this.width/2 + topWidth * (i - 0.5), this.y-this.height); ctx.closePath(); ctx.fill(); } } drawDecorations(ctx) { this.decorations.forEach(dec => { ctx.fillStyle = colors.decorGold; if (dec.type === 'circle') { ctx.beginPath(); ctx.arc(this.x + dec.x, this.y - this.height + dec.y, dec.size, 0, Math.PI * 2); ctx.fill(); } else { // Square ctx.fillRect(this.x + dec.x - dec.size / 2, this.y - this.height + dec.y - dec.size / 2, dec.size, dec.size); } }); } drawCracks(ctx) { ctx.strokeStyle = colors.pillarDark; ctx.lineWidth = 2; this.cracks.forEach(crack => { ctx.beginPath(); ctx.moveTo(this.x + crack.startX, this.y - this.height + crack.startY); ctx.lineTo(this.x + crack.startX + Math.cos(crack.angle) * crack.length, this.y - this.height + crack.startY + Math.sin(crack.angle) * crack.length); ctx.stroke(); }); } drawCapital(ctx, topWidth) { const capitalHeight = this.height * 0.15; // Larger capital ctx.fillStyle = colors.stone; ctx.beginPath(); ctx.moveTo(this.x - this.width * 0.3, this.y - this.height); // Wider ctx.lineTo(this.x + this.width * 1.3, this.y - this.height); // Wider ctx.lineTo(this.x + this.width / 2 + topWidth *1.2, this.y - this.height - capitalHeight); ctx.lineTo(this.x + this.width / 2 - topWidth*1.2, this.y - this.height - capitalHeight); ctx.closePath(); ctx.fill(); // Add detailed carvings to the capital ctx.fillStyle = colors.decorGold; for (let i = 0; i < 7; i++) { // More carvings const carvingX = this.x + this.width * (-0.2 + i * 0.2); const carvingY = this.y - this.height - capitalHeight / 2; ctx.beginPath(); if(i % 2 === 0){ ctx.arc(carvingX, carvingY, capitalHeight / 5, 0, Math.PI * 2); } else { ctx.moveTo(carvingX - capitalHeight/6, carvingY); ctx.lineTo(carvingX, carvingY - capitalHeight/4); ctx.lineTo(carvingX + capitalHeight/6, carvingY); ctx.closePath(); } ctx.fill(); } } } class Cloud extends ParallaxElement { constructor(x, y, size, depth) { super(x, y, 0.015, depth); // Slightly faster clouds this.size = size; this.segments = this.generateSegments(); } generateSegments() { const segments = []; const numSegments = Math.floor(4 + Math.random() * 3); // Varying number of segments for (let i = 0; i < numSegments; i++) { segments.push({ xOffset: (Math.random() - 0.5) * this.size * 2.5, // Wider spread yOffset: (Math.random() - 0.5) * this.size * 1.5, // More vertical variation size: this.size * (0.6 + Math.random() * 0.4) // Slightly larger segments }); } return segments; } draw(ctx) { // Shadow first (darker and offset) ctx.fillStyle = colors.cloudShadow; this.segments.forEach(seg => { ctx.beginPath(); ctx.arc(this.x + seg.xOffset + 10, this.y + seg.yOffset + 15, // Shadow offset seg.size * 1.1, 0, Math.PI * 2); // Slightly larger shadow ctx.fill(); }); // Then the cloud itself ctx.fillStyle = colors.clouds; this.segments.forEach(seg => { ctx.beginPath(); ctx.arc(this.x + seg.xOffset, this.y + seg.yOffset, seg.size, 0, Math.PI * 2); ctx.fill(); }); } } class BackgroundCastle extends ParallaxElement { constructor(x, y, width, height, depth) { super(x, y, 0.008, depth); //Slightly increased speed this.width = width; this.height = height; this.towers = this.generateTowers(); // Generate towers this.windows = this.generateWindows(); // Generate windows } generateTowers() { const towers = []; const numTowers = Math.floor(4 + Math.random() * 3); // 4-6 towers for (let i = 0; i < numTowers; i++) { towers.push({ x: this.width * (0.1 + i * 0.2 + (Math.random() - 0.5) * 0.1), // Some x variation height: this.height * (0.4 + Math.random() * 0.3), // Varying heights width: 20 + Math.random() * 20, // Varying widths roofType: Math.random() < 0.6 ? 'pointed' : 'flat' // Different roof types }); } return towers; } generateWindows(){ const windows = []; const numWindows = Math.floor(10 + Math.random() * 15); for(let i = 0; i < numWindows; i++){ windows.push({ x: this.width * Math.random(), y: this.height * (0.2 + Math.random() * 0.6), size: 4 + Math.random() * 4 }); } return windows; } draw(ctx) { this.drawMainStructure(ctx); this.drawTowers(ctx); this.drawDetails(ctx); this.drawWindows(ctx); } drawMainStructure(ctx) { ctx.fillStyle = colors.castleLight; ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.lineTo(this.x + this.width, this.y); ctx.lineTo(this.x + this.width, this.y - this.height * 0.7); // Increased height ctx.lineTo(this.x + this.width * 0.8, this.y - this.height * 0.8); ctx.lineTo(this.x + this.width * 0.6, this.y - this.height); //Main point ctx.lineTo(this.x + this.width * 0.4, this.y - this.height * 0.9); ctx.lineTo(this.x + this.width * 0.2, this.y - this.height * 0.8); ctx.lineTo(this.x, this.y - this.height * 0.7); ctx.closePath(); ctx.fill(); // Add a subtle gradient for depth const gradient = ctx.createLinearGradient(this.x, this.y - this.height, this.x, this.y); gradient.addColorStop(0, 'rgba(255, 255, 255, 0.2)'); // Lighter at the top gradient.addColorStop(1, 'rgba(0, 0, 0, 0.1)'); // Darker at the bottom ctx.fillStyle = gradient; ctx.fill(); } drawTowers(ctx) { ctx.fillStyle = colors.castleLight; this.towers.forEach(tower => { const towerX = this.x + tower.x; ctx.beginPath(); ctx.moveTo(towerX, this.y); ctx.lineTo(towerX + tower.width, this.y); ctx.lineTo(towerX + tower.width, this.y - tower.height); if (tower.roofType === 'pointed') { ctx.lineTo(towerX + tower.width / 2, this.y - tower.height * 1.2); // Pointed roof ctx.lineTo(towerX, this.y - tower.height); } else { ctx.lineTo(towerX, this.y - tower.height); // Flat roof // Add battlements for flat roofs for (let i = 0; i < tower.width; i += 10) { ctx.fillRect(towerX + i, this.y - tower.height - 5, 5, 5); } } ctx.closePath(); ctx.fill(); }); } drawDetails(ctx) { ctx.fillStyle = colors.castleDark; for (let i = 0; i < this.width; i += 40) { // More details ctx.fillRect(this.x + i, this.y - this.height * 0.4, 30, this.height * 0.3); } } drawWindows(ctx){ ctx.fillStyle = 'rgba(255, 255, 0, 0.4)'; // Yellowish glow this.windows.forEach(window => { ctx.fillRect(this.x + window.x, this.y - window.y, window.size, window.size); }); } } class Vegetation { constructor(x, y, type) { this.x = x; this.y = y; this.type = type; this.swayOffset = Math.random() * Math.PI * 2; this.leafDetails = this.generateLeafDetails(); // For vines } generateLeafDetails() { if (this.type !== 'vine') return []; return Array(5).fill().map(() => ({ offset: Math.random() * 20 - 10, // x offset for each leaf size: 4 + Math.random() * 4 // Leaf size variation })); } draw(ctx, time) { switch(this.type) { case 'vine': this.drawVine(ctx, time); break; case 'flower': this.drawFlower(ctx); break; case 'bush': this.drawBush(ctx); break; } } drawVine(ctx, time) { const sway = Math.sin(time * 0.0015 + this.swayOffset) * 8; // Increased sway ctx.strokeStyle = colors.vineGreen; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(this.x, this.y); // More realistic vine curves for (let i = 0; i < 5; i++) { const curveX = this.x + sway * (i % 2 ? 1.2 : -0.8); // More variation in sway const curveY = this.y - i * 25; // Taller vines ctx.quadraticCurveTo( curveX, curveY, this.x + sway * (i % 2 ? 0.5 : -0.3), this.y - (i + 1) * 25 // Vary next point ); } ctx.stroke(); // Leaves with more detail for (let i = 1; i < 5; i++) { ctx.fillStyle = colors.leaves; const leafX = this.x + sway * (i % 2 ? 1 : -1) + this.leafDetails[i-1].offset; const leafY = this.y - i * 25; ctx.beginPath(); ctx.ellipse(leafX, leafY, 8, this.leafDetails[i-1].size * 2, // Use generated size Math.PI / 4 * (i % 2 ? 1 : -1), 0, Math.PI * 2); ctx.fill(); // Add a small line for leaf vein ctx.strokeStyle = colors.vineDark; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(leafX, leafY); ctx.lineTo(leafX + 4 * (i % 2 ? 1 : -1), leafY - 4); ctx.stroke(); } } drawFlower(ctx) { // More detailed flower center ctx.fillStyle = colors.flowerGold; ctx.beginPath(); ctx.arc(this.x, this.y, 6, 0, Math.PI * 2); // Larger center ctx.fill(); // More detailed petals ctx.fillStyle = colors.flowerWhite; for (let i = 0; i < 8; i++) { // More petals const angle = (i / 8) * Math.PI * 2; // Evenly spaced ctx.beginPath(); ctx.ellipse( this.x + Math.cos(angle) * 10, // Larger petals this.y + Math.sin(angle) * 10, 8, 5, // More elongated petals angle, 0, Math.PI * 2 ); ctx.fill(); // Add a small line for petal detail ctx.strokeStyle = colors.vineDark; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(this.x + Math.cos(angle) * 10, this.y + Math.sin(angle) * 10); ctx.lineTo(this.x + Math.cos(angle) * 15, this.y + Math.sin(angle) * 15); ctx.stroke(); } } drawBush(ctx) { ctx.fillStyle = colors.grass; for (let i = 0; i < 7; i++) { // More bush segments ctx.beginPath(); ctx.arc( this.x + (i - 3) * 12, // Wider bush this.y - Math.abs(i - 3) * 6, // Taller 12, // Larger segments 0, Math.PI * 2 ); ctx.fill(); } // Add darker spots for depth ctx.fillStyle = colors.grassShade; for (let i = 0; i < 3; i++) { ctx.beginPath(); ctx.arc( this.x + (Math.random() - 0.5) * 30, this.y - Math.random() * 10, 5 + Math.random() * 5, // Random size and position 0, Math.PI * 2 ); ctx.fill(); } } } class Particle { constructor(x, y, size, speed, depth) { this.x = x; this.y = y; this.size = size; this.speed = speed; this.depth = depth; this.alpha = 0.2 + Math.random() * 0.6; // Random initial opacity. } update(deltaTime, cameraX) { this.x -= this.speed * deltaTime * this.depth; this.y += Math.sin(this.x * 0.01) * 0.5; // Slight wavy movement. this.alpha -= 0.005 * deltaTime; // Fade out over time. if (this.x < cameraX - this.size || this.alpha <= 0) { this.reset(cameraX); } } reset(cameraX) { this.x = cameraX + canvas.width + Math.random() * 200; // Reset beyond the right edge. this.y = Math.random() * canvas.height; this.size = 2 + Math.random() * 3; this.speed = 0.01 + Math.random() * 0.02; this.alpha = 0.2 + Math.random() * 0.6; this.depth = 0.5 + Math.random() * 0.8; } draw(ctx) { ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`; // Use dynamic alpha. ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); } } const pillars = []; const clouds = []; const castles = []; const vegetation = []; const particles = []; let lastTime = 0; let cameraX = 0; // Camera position for parallax function initializeScene() { // Clear existing elements (important for reset) pillars.length = 0; clouds.length = 0; castles.length = 0; vegetation.length = 0; particles.length = 0; for (let i = 0; i < 8; i++) { const depth = 0.6 + Math.random() * 0.4; // Deeper pillars pillars.push(new DetailedPillar( 200 + i * 500, // Wider spacing canvas.height - 150, // Pillars anchored to a consistent y-position 150, // Wider pillars 400 - depth * 50, depth )); } for (let i = 0; i < 15; i++) { // More clouds const depth = 0.3 + Math.random() * 0.4; // Wider depth range clouds.push(new Cloud( Math.random() * canvas.width, 50 + Math.random() * 250, // Taller cloud area 40 + Math.random() * 30, // Larger clouds depth )); } for (let i = 0; i < 3; i++) { const depth = 0.2 + (i * 0.15); // Increased depth difference castles.push(new BackgroundCastle( 300 + i * 1200, // Increased spacing canvas.height - 150, // Consistent y-position for the castle base 600, // Much wider castles 450, // Taller castles depth )); } for (let i = 0; i < 40; i++) { // More vegetation vegetation.push(new Vegetation( Math.random() * canvas.width, canvas.height - 100 + Math.random() * 30, // Consistent y-range ['vine', 'flower', 'bush'][Math.floor(Math.random() * 3)] )); } for (let i = 0; i < 50; i++) { // Add particles particles.push(new Particle( Math.random() * canvas.width, Math.random() * canvas.height, 2 + Math.random() * 3, 0.01 + Math.random() * 0.02, 0.5 + Math.random()* 0.8 )); } // Reset camera cameraX = 0; } function drawBackground(ctx) { const gradient = createVerticalGradient(ctx, colors.skyTop, colors.skyBottom, 0, canvas.height); ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); } function drawGround(ctx) { const platformHeight = 120; // Slightly taller const gradient = createVerticalGradient(ctx, colors.stone, colors.stoneDark, canvas.height - platformHeight, canvas.height); ctx.fillStyle = gradient; ctx.fillRect(0, canvas.height - platformHeight, canvas.width, platformHeight); // Ground details (more detailed and varied) for (let i = 0; i < canvas.width; i += 25) { ctx.fillStyle = colors.grass; ctx.beginPath(); ctx.moveTo(i, canvas.height - platformHeight); ctx.lineTo(i + 15 + Math.random() * 10, canvas.height - platformHeight); // Varying widths ctx.lineTo(i + 7.5 + Math.random() * 5, canvas.height - platformHeight - (20 + Math.random() * 10)); // Varying heights ctx.closePath(); ctx.fill(); } // Add some cracks in the stone ctx.strokeStyle = colors.stoneDark; ctx.lineWidth = 2; for (let i = 0; i < canvas.width; i += 50 + Math.random() * 50) { ctx.beginPath(); ctx.moveTo(i, canvas.height - platformHeight + 5); ctx.lineTo(i + (Math.random() - 0.5) * 20, canvas.height - platformHeight + 20 + Math.random() * 10); ctx.stroke(); } } function drawScene(timestamp) { const deltaTime = timestamp - lastTime; lastTime = timestamp; // Camera movement (smooth) cameraX += deltaTime * 0.05; ctx.clearRect(0, 0, canvas.width, canvas.height); // Background and elements, with parallax drawBackground(ctx); castles.forEach(castle => { castle.update(deltaTime, cameraX); castle.draw(ctx); }); clouds.forEach(cloud => { cloud.update(deltaTime, cameraX); cloud.draw(ctx); }); pillars.sort((a, b) => b.depth - a.depth); // Depth sorting pillars.forEach(pillar => { pillar.update(deltaTime, cameraX); // Pass cameraX for parallax. pillar.draw(ctx); }); drawGround(ctx); vegetation.forEach(plant => { plant.x = plant.x - cameraX * 0.1; plant.draw(ctx, timestamp); plant.x = plant.x + cameraX * 0.1; }); particles.forEach(particle => { particle.update(deltaTime, cameraX); particle.draw(ctx); }); requestAnimationFrame(drawScene); } // UI controls function createControls() { const controlPanel = document.createElement('div'); controlPanel.style.cssText = ` position: fixed; top: 10px; right: 10px; z-index: 9999; background: rgba(0, 0, 0, 0.7); padding: 10px; border-radius: 5px; color: white; font-family: sans-serif; `; const startButton = document.createElement('button'); startButton.textContent = 'Draw Sky Sanctuary'; startButton.style.cssText = 'padding: 8px 12px; margin-right: 5px; cursor: pointer;'; startButton.onclick = () => { initializeScene(); requestAnimationFrame(drawScene); }; const resetButton = document.createElement('button'); resetButton.textContent = 'Reset Scene'; resetButton.style.cssText = 'padding: 8px 12px; cursor: pointer;'; resetButton.onclick = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); initializeScene(); // Re-initialize, don't just clear arrays. requestAnimationFrame(drawScene); // start drawing again }; controlPanel.appendChild(startButton); controlPanel.appendChild(resetButton); document.body.appendChild(controlPanel); } createControls(); } waitForCanvas(); })();