// weather.js — Weather and atmospheric background themes // Variants: rain, snow, storm, sunset, sunrise, fog, lightning, aurora (function(engine) { if (!engine) return; var D = engine.Draw, H = engine.helpers; engine.registerTheme('weather', { rain: { meta: { id: 'weather_rain', mood: 'melancholy', colors: ['#3a4a5a', '#2a3a4a', '#5a6a7a', '#8899aa'], tags: ['rain', 'wet', 'gray', 'calm'], description: 'Overcast sky with steady rainfall, puddles, and muted city outlines', compatibleMusicMoods: ['melancholy', 'calm', 'ambient'], recommendedFor: ['story', 'puzzle', 'rpg'] }, colors: ['#3a4a5a', '#2a3a4a', '#5a6a7a', '#8899aa'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90); // Low heavy clouds var rng = H.seededRandom(111); for (var i = 0; i < 8; i++) { var cx = rng() * w * 1.2 - w * 0.1; var cy = h * 0.05 + rng() * h * 0.2; var cw = 100 + rng() * 200; var drift = Math.sin(t * 0.1 + i) * 5; ctx.fillStyle = H.rgba(c[2], 0.2 + rng() * 0.15); ctx.beginPath(); ctx.ellipse(cx + drift, cy, cw, 30 + rng() * 20, 0, 0, Math.PI * 2); ctx.fill(); } }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(112); // Distant buildings silhouette Draw.buildings(ctx, w, h, h * 0.75, 12, [H.darken(c[1], 0.2), H.darken(c[0], 0.25)], 113); // Ground with puddles Draw.rect(ctx, 0, h * 0.75, w, h * 0.25, H.darken(c[1], 0.35)); for (var p = 0; p < 6; p++) { var px = rng() * w; var pw = 30 + rng() * 60; var ripple = Math.sin(t * 3 + p * 2) * 0.08; ctx.fillStyle = H.rgba(c[3], 0.1 + ripple); ctx.beginPath(); ctx.ellipse(px, h * 0.82 + rng() * h * 0.1, pw, 6, 0, 0, Math.PI * 2); ctx.fill(); // Ripple rings var ringR = 5 + (t * 20 + p * 30) % 20; ctx.strokeStyle = H.rgba('#FFFFFF', 0.08 * (1 - ringR / 25)); ctx.lineWidth = 0.5; ctx.beginPath(); ctx.arc(px, h * 0.82 + rng() * h * 0.1, ringR, 0, Math.PI * 2); ctx.stroke(); } // Mist layer near ground for (var m = 0; m < 5; m++) { ctx.fillStyle = H.rgba(c[3], 0.04); ctx.beginPath(); ctx.ellipse(rng() * w, h * 0.73 + rng() * h * 0.05, 120 + rng() * 100, 15, 0, 0, Math.PI * 2); ctx.fill(); } }, particles: { type: 'rain', count: 150, color: '#99aabb' } }, snow: { meta: { id: 'weather_snow', mood: 'peaceful', colors: ['#c8d8e8', '#a0b8cc', '#e8eef4', '#FFFFFF'], tags: ['snow', 'winter', 'cold', 'peaceful'], description: 'Gentle snowfall over a soft winter landscape with frosted trees', compatibleMusicMoods: ['peaceful', 'calm', 'dreamy'], recommendedFor: ['puzzle', 'pet', 'creative', 'story'] }, colors: ['#c8d8e8', '#a0b8cc', '#e8eef4', '#FFFFFF'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[1], c[0]], 90); // Snow-covered ground Draw.hills(ctx, w, h, h * 0.7, 6, c[2], 221); Draw.rect(ctx, 0, h * 0.75, w, h * 0.25, c[2]); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(222); // Frosted trees for (var i = 0; i < 10; i++) { var tx = rng() * w; var ty = h * 0.55 + rng() * h * 0.18; var th = 40 + rng() * 60; var tw = th * 0.4; // Trunk Draw.rect(ctx, tx - 3, ty, 6, th * 0.3, '#665544'); // Snow-covered canopy layers for (var l = 0; l < 3; l++) { var ly = ty - th * (0.1 + l * 0.25); var lw = tw * (1 - l * 0.25); ctx.fillStyle = H.blendColor('#4a7a4a', c[2], 0.4 + l * 0.2); ctx.beginPath(); ctx.moveTo(tx - lw, ly + 15); ctx.lineTo(tx, ly); ctx.lineTo(tx + lw, ly + 15); ctx.closePath(); ctx.fill(); // Snow caps ctx.fillStyle = H.rgba(c[3], 0.7); ctx.beginPath(); ctx.moveTo(tx - lw * 0.6, ly + 8); ctx.lineTo(tx, ly - 2); ctx.lineTo(tx + lw * 0.6, ly + 8); ctx.closePath(); ctx.fill(); } } // Snow drifts for (var d = 0; d < 5; d++) { var dx = rng() * w; ctx.fillStyle = H.rgba(c[3], 0.3); ctx.beginPath(); ctx.ellipse(dx, h * 0.73 + rng() * h * 0.05, 40 + rng() * 60, 8, 0, 0, Math.PI * 2); ctx.fill(); } // Distant cabin var cabX = w * 0.7; Draw.rect(ctx, cabX, h * 0.56, 50, 35, '#5a3a2a'); ctx.fillStyle = '#6a4a3a'; ctx.beginPath(); ctx.moveTo(cabX - 5, h * 0.56); ctx.lineTo(cabX + 25, h * 0.48); ctx.lineTo(cabX + 55, h * 0.56); ctx.closePath(); ctx.fill(); // Snow on roof ctx.fillStyle = c[2]; ctx.beginPath(); ctx.moveTo(cabX - 5, h * 0.56); ctx.lineTo(cabX + 25, h * 0.475); ctx.lineTo(cabX + 55, h * 0.56); ctx.closePath(); ctx.fill(); // Chimney smoke var smokeAlpha = 0.06 + Math.sin(t * 0.5) * 0.02; for (var s = 0; s < 4; s++) { var sx = cabX + 40 + Math.sin(t * 0.4 + s) * 6; var sy = h * 0.42 - s * 15; ctx.fillStyle = H.rgba('#CCCCCC', smokeAlpha * (1 - s * 0.2)); ctx.beginPath(); ctx.ellipse(sx, sy, 8 + s * 5, 5 + s * 3, 0, 0, Math.PI * 2); ctx.fill(); } // Window glow Draw.rect(ctx, cabX + 15, h * 0.57, 10, 12, H.rgba('#ffcc44', 0.6)); Draw.glow(ctx, cabX + 20, h * 0.63, 20, '#ffcc44'); }, particles: { type: 'snow', count: 100, color: '#FFFFFF' } }, storm: { meta: { id: 'weather_storm', mood: 'intense', colors: ['#1a1a25', '#252535', '#3a3a55', '#667788'], tags: ['storm', 'dark', 'wind', 'dramatic'], description: 'Dark storm clouds with heavy rain, wind-bent trees, and rolling thunder', compatibleMusicMoods: ['intense', 'dramatic', 'tense'], recommendedFor: ['action', 'rpg', 'racing'] }, colors: ['#1a1a25', '#252535', '#3a3a55', '#667788'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90); // Turbulent clouds var rng = H.seededRandom(331); for (var i = 0; i < 12; i++) { var cx = rng() * w * 1.3 - w * 0.15; var cy = rng() * h * 0.35; var cw = 80 + rng() * 200; var ch = 25 + rng() * 40; var drift = Math.sin(t * 0.3 + i * 0.7) * 10; ctx.fillStyle = H.rgba(c[2], 0.2 + rng() * 0.2); ctx.beginPath(); ctx.ellipse(cx + drift, cy, cw, ch, 0, 0, Math.PI * 2); ctx.fill(); } }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(332); // Dark ground Draw.rect(ctx, 0, h * 0.78, w, h * 0.22, H.darken(c[0], 0.4)); Draw.hills(ctx, w, h, h * 0.78, 5, H.darken(c[1], 0.3), 333); // Wind-bent trees for (var i = 0; i < 6; i++) { var tx = rng() * w; var sway = Math.sin(t * 2 + i) * 8; ctx.strokeStyle = '#3a2a1a'; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(tx, h * 0.78); ctx.quadraticCurveTo(tx + sway * 0.5, h * 0.65, tx + sway, h * 0.58); ctx.stroke(); // Branches blown sideways for (var b = 0; b < 3; b++) { var by = h * 0.62 + b * 0.05 * h; var bSway = sway * (1.2 - b * 0.2); ctx.strokeStyle = '#3a2a1a'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(tx + sway * (0.5 + b * 0.1), by); ctx.lineTo(tx + bSway + 15 + rng() * 10, by - 5 - rng() * 8); ctx.stroke(); } } // Distant lightning flash (periodic) var flashCycle = (t * 0.7) % 6; if (flashCycle < 0.1) { ctx.fillStyle = H.rgba('#FFFFFF', 0.08); ctx.fillRect(0, 0, w, h); ctx.fillStyle = H.rgba('#ccddff', 0.15); ctx.fillRect(0, 0, w, h * 0.4); } // Power lines ctx.strokeStyle = H.rgba('#444', 0.5); ctx.lineWidth = 1; for (var pl = 0; pl < 3; pl++) { ctx.beginPath(); ctx.moveTo(0, h * 0.5 + pl * 10); for (var px = 0; px < w; px += 5) { ctx.lineTo(px, h * 0.5 + pl * 10 + Math.sin(px * 0.008 + t * 0.5) * 4); } ctx.stroke(); } // Utility pole Draw.rect(ctx, w * 0.3, h * 0.35, 4, h * 0.43, '#4a3a2a'); Draw.rect(ctx, w * 0.27, h * 0.38, 30, 3, '#4a3a2a'); Draw.rect(ctx, w * 0.7, h * 0.35, 4, h * 0.43, '#4a3a2a'); Draw.rect(ctx, w * 0.67, h * 0.38, 30, 3, '#4a3a2a'); }, particles: { type: 'rain', count: 250, color: '#7788aa' } }, sunset: { meta: { id: 'weather_sunset', mood: 'warm', colors: ['#1a0a2e', '#cc4400', '#ff8833', '#ffd166'], tags: ['sunset', 'golden', 'warm', 'beautiful'], description: 'Vibrant sunset with orange-purple gradient, silhouette landscape, and warm glow', compatibleMusicMoods: ['warm', 'romantic', 'calm'], recommendedFor: ['story', 'creative', 'pet', 'music'] }, colors: ['#1a0a2e', '#cc4400', '#ff8833', '#ffd166'], renderBase: function(ctx, w, h, c, Draw, t) { var pulse = Math.sin(t * 0.15) * 0.05; ctx.fillStyle = Draw.linearGradient(ctx, 0, 0, 0, h, [ [0, c[0]], [0.3, H.blendColor(c[0], c[1], 0.5 + pulse)], [0.5, c[1]], [0.65, c[2]], [0.8, c[3]], [1, H.lighten(c[3], 0.3)] ]); ctx.fillRect(0, 0, w, h); // Sun var sunY = h * 0.52 + Math.sin(t * 0.05) * 3; Draw.glow(ctx, w * 0.5, sunY, 120, c[3]); Draw.glow(ctx, w * 0.5, sunY, 60, '#FFFFFF'); Draw.circle(ctx, w * 0.5, sunY, 30, c[3]); Draw.circle(ctx, w * 0.5, sunY, 22, H.lighten(c[3], 0.4)); }, renderMid: function(ctx, w, h, c, Draw, t) { // Silhouette mountains Draw.mountains(ctx, w, h, h * 0.65, 8, '#1a0a15', 441); Draw.mountains(ctx, w, h, h * 0.72, 6, '#0d050a', 442); // Silhouette trees on ridge var rng = H.seededRandom(443); for (var i = 0; i < 15; i++) { var tx = rng() * w; var ty = h * 0.65 + rng() * h * 0.08; var th = 15 + rng() * 30; ctx.fillStyle = '#0d050a'; ctx.beginPath(); ctx.moveTo(tx - th * 0.3, ty); ctx.lineTo(tx, ty - th); ctx.lineTo(tx + th * 0.3, ty); ctx.closePath(); ctx.fill(); } // Clouds catching light Draw.clouds(ctx, w, h, h * 0.15, 5, '#ff6633', 444); // Water reflection Draw.rect(ctx, 0, h * 0.78, w, h * 0.22, H.rgba(c[1], 0.3)); for (var r = 0; r < 8; r++) { var ry = h * 0.8 + r * h * 0.025; ctx.strokeStyle = H.rgba(c[3], 0.06 - r * 0.005); ctx.lineWidth = 2; ctx.beginPath(); for (var x = 0; x < w; x += 4) { ctx.lineTo(x, ry + Math.sin(x * 0.015 + t * 0.5 + r) * 2); } ctx.stroke(); } // Birds silhouettes for (var b = 0; b < 5; b++) { var bx = w * 0.2 + rng() * w * 0.6 + Math.sin(t * 0.3 + b) * 10; var by = h * 0.15 + rng() * h * 0.15; ctx.strokeStyle = '#1a0a15'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.arc(bx - 4, by, 5, -0.8, 0.1); ctx.stroke(); ctx.beginPath(); ctx.arc(bx + 4, by, 5, Math.PI - 0.1, Math.PI + 0.8); ctx.stroke(); } }, particles: { type: 'sparkles', count: 15, color: '#FFD166' } }, sunrise: { meta: { id: 'weather_sunrise', mood: 'hopeful', colors: ['#2a1a40', '#884466', '#ee8855', '#ffe4a0'], tags: ['sunrise', 'morning', 'fresh', 'bright'], description: 'Early morning sunrise with pink-orange sky, mist, and dewy fields', compatibleMusicMoods: ['hopeful', 'upbeat', 'calm'], recommendedFor: ['puzzle', 'pet', 'creative', 'sports'] }, colors: ['#2a1a40', '#884466', '#ee8855', '#ffe4a0'], renderBase: function(ctx, w, h, c, Draw, t) { var shift = Math.sin(t * 0.1) * 0.03; ctx.fillStyle = Draw.linearGradient(ctx, 0, 0, 0, h, [ [0, c[0]], [0.25, c[1]], [0.5, c[2]], [0.7, H.lighten(c[3], shift)], [1, H.lighten(c[3], 0.3)] ]); ctx.fillRect(0, 0, w, h); // Rising sun at horizon var sunY = h * 0.58 - Math.sin(t * 0.02) * 2; Draw.glow(ctx, w * 0.35, sunY, 150, c[3]); Draw.glow(ctx, w * 0.35, sunY, 70, '#FFFFFF'); Draw.circle(ctx, w * 0.35, sunY, 35, '#fff8e0'); }, renderMid: function(ctx, w, h, c, Draw, t) { // Rolling hills Draw.hills(ctx, w, h, h * 0.65, 5, '#3a6a3a', 551); Draw.hills(ctx, w, h, h * 0.72, 7, '#2a5a2a', 552); Draw.rect(ctx, 0, h * 0.78, w, h * 0.22, '#2a4a2a'); // Morning mist layers for (var m = 0; m < 6; m++) { var my = h * 0.6 + m * h * 0.04; var drift = Math.sin(t * 0.15 + m * 0.8) * 15; ctx.fillStyle = H.rgba('#ffe8cc', 0.04 - m * 0.004); ctx.beginPath(); ctx.ellipse(w * 0.3 + drift + m * 40, my, 200 + m * 30, 15, 0, 0, Math.PI * 2); ctx.fill(); } // Dew sparkles on grass var rng = H.seededRandom(553); for (var d = 0; d < 20; d++) { var dx = rng() * w; var dy = h * 0.73 + rng() * h * 0.18; var sparkle = Math.sin(t * 3 + d * 1.5) > 0.7 ? 0.6 : 0.1; Draw.circle(ctx, dx, dy, 1.5, H.rgba('#FFFFFF', sparkle)); } // Fence posts for (var f = 0; f < 8; f++) { var fx = w * 0.1 + f * w * 0.11; Draw.rect(ctx, fx, h * 0.68, 3, h * 0.08, '#5a4a3a'); } ctx.strokeStyle = '#5a4a3a'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(w * 0.1, h * 0.71); ctx.lineTo(w * 0.87, h * 0.71); ctx.stroke(); ctx.beginPath(); ctx.moveTo(w * 0.1, h * 0.74); ctx.lineTo(w * 0.87, h * 0.74); ctx.stroke(); // Light rays from sun for (var r = 0; r < 6; r++) { var angle = -0.4 + r * 0.16; var rayAlpha = 0.02 + Math.sin(t * 0.3 + r) * 0.01; ctx.fillStyle = H.rgba(c[3], rayAlpha); ctx.beginPath(); ctx.moveTo(w * 0.35, h * 0.58); ctx.lineTo(w * 0.35 + Math.cos(angle) * w, h * 0.58 + Math.sin(angle) * h); ctx.lineTo(w * 0.35 + Math.cos(angle + 0.05) * w, h * 0.58 + Math.sin(angle + 0.05) * h); ctx.closePath(); ctx.fill(); } }, particles: { type: 'dust', count: 15, color: '#FFE8AA' } }, fog: { meta: { id: 'weather_fog', mood: 'eerie', colors: ['#8a8a8a', '#666666', '#aaaaaa', '#cccccc'], tags: ['fog', 'mist', 'mysterious', 'gray'], description: 'Thick fog with barely visible shapes, faded landmarks, and diffused light', compatibleMusicMoods: ['eerie', 'mysterious', 'ambient'], recommendedFor: ['story', 'rpg', 'puzzle'] }, colors: ['#8a8a8a', '#666666', '#aaaaaa', '#cccccc'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[1], c[0], c[2]], 90); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(661); // Ghost buildings barely visible for (var b = 0; b < 5; b++) { var bx = rng() * w; var bw = 30 + rng() * 50; var bh = 60 + rng() * 100; ctx.fillStyle = H.rgba(c[1], 0.08); ctx.fillRect(bx, h * 0.5 - bh, bw, bh + h * 0.5); } // Ground fade Draw.rect(ctx, 0, h * 0.7, w, h * 0.3, H.rgba(c[1], 0.2)); // Faded streetlamps for (var l = 0; l < 3; l++) { var lx = w * 0.2 + l * w * 0.3; Draw.rect(ctx, lx, h * 0.35, 3, h * 0.35, H.rgba('#555', 0.3)); var lampGlow = 0.15 + Math.sin(t * 0.4 + l) * 0.05; Draw.circle(ctx, lx + 1, h * 0.34, 6, H.rgba('#ffdd88', lampGlow)); Draw.glow(ctx, lx + 1, h * 0.34, 60, H.rgba('#ffdd88', lampGlow * 0.6)); } // Rolling fog layers for (var f = 0; f < 10; f++) { var fx = (rng() * w * 1.5 - w * 0.25) + Math.sin(t * 0.08 + f * 0.6) * 30; var fy = h * 0.2 + rng() * h * 0.6; var fs = 100 + rng() * 200; ctx.fillStyle = H.rgba(c[2], 0.06 + rng() * 0.04); ctx.beginPath(); ctx.ellipse(fx, fy, fs, fs * 0.3, 0, 0, Math.PI * 2); ctx.fill(); } // Road disappearing into fog ctx.fillStyle = H.rgba('#555', 0.1); ctx.beginPath(); ctx.moveTo(w * 0.35, h); ctx.lineTo(w * 0.48, h * 0.4); ctx.lineTo(w * 0.52, h * 0.4); ctx.lineTo(w * 0.65, h); ctx.closePath(); ctx.fill(); // Dashed center line ctx.strokeStyle = H.rgba('#ddcc44', 0.06); ctx.lineWidth = 2; ctx.setLineDash([10, 15]); ctx.beginPath(); ctx.moveTo(w * 0.5, h); ctx.lineTo(w * 0.5, h * 0.4); ctx.stroke(); ctx.setLineDash([]); }, particles: { type: 'fog', count: 18, color: '#BBBBBB' } }, lightning: { meta: { id: 'weather_lightning', mood: 'dramatic', colors: ['#0a0a18', '#1a1a30', '#3344aa', '#eeeeff'], tags: ['lightning', 'thunder', 'electric', 'dark'], description: 'Pitch-dark sky lit by dramatic lightning bolts and electric-blue highlights', compatibleMusicMoods: ['dramatic', 'intense', 'electronic'], recommendedFor: ['action', 'rpg', 'racing'] }, colors: ['#0a0a18', '#1a1a30', '#3344aa', '#eeeeff'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90); // Cloud mass var rng = H.seededRandom(771); for (var i = 0; i < 10; i++) { var cx = rng() * w; var cy = rng() * h * 0.3; ctx.fillStyle = H.rgba(c[2], 0.08 + rng() * 0.06); ctx.beginPath(); ctx.ellipse(cx, cy, 80 + rng() * 150, 25 + rng() * 30, 0, 0, Math.PI * 2); ctx.fill(); } }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(772); // Dark landscape Draw.mountains(ctx, w, h, h * 0.75, 6, H.darken(c[0], 0.3), 773); Draw.rect(ctx, 0, h * 0.82, w, h * 0.18, H.darken(c[0], 0.4)); // Lightning bolt (procedural, periodic) var boltPhase = (t * 0.6) % 5; if (boltPhase < 0.15 || (boltPhase > 2 && boltPhase < 2.08)) { var boltRng = H.seededRandom(Math.floor(t * 0.6) * 100); var bx = w * 0.2 + boltRng() * w * 0.6; var segments = 8 + Math.floor(boltRng() * 5); ctx.strokeStyle = c[3]; ctx.lineWidth = 3; ctx.shadowColor = c[2]; ctx.shadowBlur = 20; ctx.beginPath(); ctx.moveTo(bx, 0); var curX = bx, curY = 0; for (var s = 0; s < segments; s++) { curX += (boltRng() - 0.5) * 60; curY += h * 0.7 / segments + boltRng() * 20; ctx.lineTo(curX, curY); // Branch if (boltRng() > 0.6) { var branchLen = 20 + boltRng() * 40; ctx.moveTo(curX, curY); ctx.lineTo(curX + (boltRng() - 0.5) * branchLen, curY + branchLen * 0.5); ctx.moveTo(curX, curY); } } ctx.stroke(); ctx.shadowBlur = 0; // Sky flash ctx.fillStyle = H.rgba(c[2], 0.06); ctx.fillRect(0, 0, w, h); // Cloud underlight for (var cl = 0; cl < 4; cl++) { Draw.glow(ctx, w * 0.2 + boltRng() * w * 0.6, h * 0.1 + boltRng() * h * 0.15, 80, H.rgba(c[2], 0.15)); } } // Rain streaks for (var r = 0; r < 30; r++) { var rx = rng() * w; var ry = rng() * h; ctx.strokeStyle = H.rgba('#8899bb', 0.15); ctx.lineWidth = 0.5; ctx.beginPath(); ctx.moveTo(rx, ry); ctx.lineTo(rx - 3, ry + 15); ctx.stroke(); } }, particles: { type: 'rain', count: 180, color: '#8899cc' } }, aurora: { meta: { id: 'weather_aurora', mood: 'magical', colors: ['#0a0a1a', '#0d1b2a', '#22cc88', '#8844dd'], tags: ['aurora', 'northern-lights', 'magical', 'night'], description: 'Dark arctic sky with shimmering aurora borealis in green and purple', compatibleMusicMoods: ['magical', 'dreamy', 'ambient', 'mysterious'], recommendedFor: ['creative', 'rpg', 'story', 'music'] }, colors: ['#0a0a1a', '#0d1b2a', '#22cc88', '#8844dd'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90); // Stars Draw.stars(ctx, w, h * 0.5, 80, '#FFFFFF', 881, [0.5, 2]); }, renderMid: function(ctx, w, h, c, Draw, t) { // Aurora bands for (var band = 0; band < 5; band++) { var bandY = h * 0.15 + band * h * 0.08; var bandColor = band % 2 === 0 ? c[2] : c[3]; ctx.beginPath(); ctx.moveTo(0, bandY); for (var x = 0; x <= w; x += 3) { var wave1 = Math.sin(x * 0.005 + t * 0.3 + band * 0.8) * 25; var wave2 = Math.sin(x * 0.012 + t * 0.15 + band * 1.2) * 15; var wave3 = Math.sin(x * 0.002 + t * 0.08) * 35; ctx.lineTo(x, bandY + wave1 + wave2 + wave3); } ctx.lineTo(w, bandY + h * 0.12); ctx.lineTo(w, bandY); for (var x2 = w; x2 >= 0; x2 -= 3) { var w1 = Math.sin(x2 * 0.005 + t * 0.3 + band * 0.8) * 25; var w2 = Math.sin(x2 * 0.012 + t * 0.15 + band * 1.2) * 15; var w3 = Math.sin(x2 * 0.002 + t * 0.08) * 35; ctx.lineTo(x2, bandY + w1 + w2 + w3 + h * 0.06); } ctx.closePath(); var alpha = 0.06 + Math.sin(t * 0.4 + band) * 0.03; ctx.fillStyle = H.rgba(bandColor, alpha); ctx.fill(); } // Vertical aurora curtains for (var cur = 0; cur < 8; cur++) { var cx = w * 0.1 + cur * w * 0.1 + Math.sin(t * 0.2 + cur) * 20; var curtainAlpha = 0.03 + Math.sin(t * 0.5 + cur * 1.3) * 0.02; var cColor = cur % 3 === 0 ? c[2] : (cur % 3 === 1 ? c[3] : H.blendColor(c[2], c[3], 0.5)); ctx.fillStyle = H.rgba(cColor, curtainAlpha); ctx.fillRect(cx, h * 0.05, 15 + Math.sin(t * 0.3 + cur) * 5, h * 0.45); } // Snow-covered ground Draw.hills(ctx, w, h, h * 0.8, 5, '#1a2a3a', 882); Draw.rect(ctx, 0, h * 0.85, w, h * 0.15, '#0d1520'); // Reflection on ground for (var ref = 0; ref < 3; ref++) { var refX = w * 0.2 + ref * w * 0.25; var refColor = ref % 2 === 0 ? c[2] : c[3]; var refAlpha = 0.03 + Math.sin(t * 0.4 + ref) * 0.015; Draw.glow(ctx, refX, h * 0.88, 60, H.rgba(refColor, refAlpha)); } // Bright star var starPulse = 0.6 + Math.sin(t * 1.5) * 0.3; Draw.circle(ctx, w * 0.8, h * 0.08, 2, H.rgba('#FFFFFF', starPulse)); Draw.glow(ctx, w * 0.8, h * 0.08, 10, '#FFFFFF'); }, particles: { type: 'stars', count: 60, color: '#FFFFFF' } } }); })(window.BackgroundEngine);