477 lines
23 KiB
JavaScript
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);
|