Files
gamercomp/frontend/public/assets/backgrounds/themes/weather.js
T
2026-04-30 02:14:25 +00:00

477 lines
23 KiB
JavaScript

// 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);