// seasonal.js — Seasonal background theme (8 variants) // Depends on backgroundEngine.js being loaded first (function(engine) { if (!engine) return; var D = engine.Draw, H = engine.helpers; engine.registerTheme('seasonal', { springMeadow: { meta: { id: 'seasonal_springMeadow', mood: 'cheerful', colors: ['#87CEEB', '#98FB98', '#FFD700', '#FF69B4'], tags: ['spring', 'flowers', 'meadow', 'bright'], description: 'A sunny spring meadow with wildflowers and gentle hills', compatibleMusicMoods: ['happy', 'upbeat', 'peaceful'], recommendedFor: ['puzzle', 'creative', 'pet'] }, colors: ['#87CEEB', '#98FB98', '#FFD700', '#FF69B4'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], H.lighten(c[0], 0.3)], 90); Draw.hills(ctx, w, h, h * 0.6, 6, c[1], 10); Draw.hills(ctx, w, h, h * 0.7, 8, H.darken(c[1], 0.15), 20); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(42); // Sun Draw.glow(ctx, w * 0.8, h * 0.15, h * 0.12, c[2]); Draw.circle(ctx, w * 0.8, h * 0.15, h * 0.04, c[2]); // Wildflowers across the meadow for (var i = 0; i < 40; i++) { var fx = rng() * w; var fy = h * 0.62 + rng() * h * 0.25; var fs = 3 + rng() * 5; var sway = Math.sin(t * 1.5 + i * 0.7) * 2; // Stem ctx.strokeStyle = H.darken(c[1], 0.2); ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(fx, fy); ctx.lineTo(fx + sway, fy - fs * 3); ctx.stroke(); // Petal var flowerColor = rng() > 0.5 ? c[3] : c[2]; Draw.circle(ctx, fx + sway, fy - fs * 3, fs, flowerColor); Draw.circle(ctx, fx + sway, fy - fs * 3, fs * 0.4, '#FFFFFF'); } // Butterflies for (var b = 0; b < 5; b++) { var bx = (rng() * w + t * 20 * (b + 1)) % (w + 40) - 20; var by = h * 0.3 + rng() * h * 0.3 + Math.sin(t * 2 + b) * 15; var wingFlap = Math.abs(Math.sin(t * 6 + b * 2)) * 6; ctx.fillStyle = H.rgba(c[3], 0.7); ctx.beginPath(); ctx.ellipse(bx - wingFlap, by, wingFlap, 3, -0.3, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(bx + wingFlap, by, wingFlap, 3, 0.3, 0, Math.PI * 2); ctx.fill(); } }, particles: { type: 'leaves', count: 10, color: '#FFB7C5' } }, autumnLeaves: { meta: { id: 'seasonal_autumnLeaves', mood: 'warm', colors: ['#2C1810', '#CC5500', '#DD8833', '#FFD700'], tags: ['autumn', 'fall', 'leaves', 'warm'], description: 'A warm autumn landscape with falling leaves and golden light', compatibleMusicMoods: ['mellow', 'peaceful', 'nostalgic'], recommendedFor: ['puzzle', 'story', 'rpg'] }, colors: ['#2C1810', '#CC5500', '#DD8833', '#FFD700'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, ['#D4956B', '#E8A050', '#CC7733'], 90); Draw.hills(ctx, w, h, h * 0.55, 5, '#8B6914', 15); Draw.hills(ctx, w, h, h * 0.65, 7, '#6B4E0A', 25); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(77); // Autumn trees for (var i = 0; i < 12; i++) { var tx = rng() * w; var ty = h * 0.5 + rng() * h * 0.2; var th = 40 + rng() * 50; var tw = 4 + rng() * 4; ctx.fillStyle = c[0]; ctx.fillRect(tx - tw / 2, ty - th * 0.4, tw, th * 0.5); var leafCol = [c[1], c[2], c[3]][Math.floor(rng() * 3)]; var canopyR = th * 0.35 + Math.sin(t + i) * 2; Draw.circle(ctx, tx, ty - th * 0.55, canopyR, leafCol); Draw.circle(ctx, tx - canopyR * 0.5, ty - th * 0.45, canopyR * 0.7, H.darken(leafCol, 0.15)); Draw.circle(ctx, tx + canopyR * 0.4, ty - th * 0.5, canopyR * 0.6, H.lighten(leafCol, 0.1)); } // Pumpkins on the ground for (var p = 0; p < 6; p++) { var px = rng() * w; var py = h * 0.72 + rng() * h * 0.15; var ps = 5 + rng() * 7; Draw.circle(ctx, px, py, ps, '#E86800'); Draw.circle(ctx, px, py, ps * 0.9, '#FF7700'); ctx.fillStyle = '#336600'; ctx.fillRect(px - 1, py - ps - 3, 2, 4); } }, particles: { type: 'leaves', count: 30, color: '#DD6622' } }, winterWonderland: { meta: { id: 'seasonal_winterWonderland', mood: 'calm', colors: ['#1A2744', '#4A7CB5', '#C8E0F4', '#FFFFFF'], tags: ['winter', 'snow', 'cold', 'peaceful'], description: 'A serene winter landscape with snow-covered hills and frosty air', compatibleMusicMoods: ['peaceful', 'calm', 'ambient'], recommendedFor: ['puzzle', 'story', 'creative'] }, colors: ['#1A2744', '#4A7CB5', '#C8E0F4', '#FFFFFF'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], c[1], c[2]], 90); Draw.hills(ctx, w, h, h * 0.6, 5, c[2], 30); Draw.hills(ctx, w, h, h * 0.7, 7, c[3], 40); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(55); // Snow-covered evergreen trees for (var i = 0; i < 15; i++) { var tx = rng() * w; var ty = h * 0.55 + rng() * h * 0.25; var th = 25 + rng() * 45; // Trunk ctx.fillStyle = '#4A3728'; ctx.fillRect(tx - 2, ty - 5, 4, 10); // Tree layers for (var layer = 0; layer < 3; layer++) { var lw = th * 0.4 * (1 - layer * 0.25); var ly = ty - 5 - layer * th * 0.25; ctx.fillStyle = '#1B4332'; ctx.beginPath(); ctx.moveTo(tx - lw, ly); ctx.lineTo(tx, ly - th * 0.3); ctx.lineTo(tx + lw, ly); ctx.closePath(); ctx.fill(); // Snow caps ctx.fillStyle = H.rgba(c[3], 0.8); ctx.beginPath(); ctx.moveTo(tx - lw * 0.7, ly - th * 0.08); ctx.lineTo(tx, ly - th * 0.3); ctx.lineTo(tx + lw * 0.7, ly - th * 0.08); ctx.closePath(); ctx.fill(); } } // Distant moon glow var moonY = h * 0.12 + Math.sin(t * 0.1) * 3; Draw.glow(ctx, w * 0.2, moonY, h * 0.08, c[3]); Draw.circle(ctx, w * 0.2, moonY, h * 0.03, c[3]); }, particles: { type: 'snow', count: 80, color: '#FFFFFF' } }, summerBeach: { meta: { id: 'seasonal_summerBeach', mood: 'energetic', colors: ['#0077BE', '#00BFFF', '#F4D03F', '#FF6347'], tags: ['summer', 'beach', 'ocean', 'sunny'], description: 'A vibrant summer beach scene with rolling waves and warm sand', compatibleMusicMoods: ['happy', 'upbeat', 'energetic'], recommendedFor: ['action', 'sports', 'racing'] }, colors: ['#0077BE', '#00BFFF', '#F4D03F', '#FF6347'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90); // Sand Draw.rect(ctx, 0, h * 0.65, w, h * 0.35, c[2]); // Shore wave line ctx.strokeStyle = H.rgba('#FFFFFF', 0.4); ctx.lineWidth = 2; ctx.beginPath(); for (var x = 0; x < w; x += 3) { var wy = h * 0.65 + Math.sin(x * 0.015 + t * 2) * 5 + Math.sin(x * 0.03 + t * 3) * 2; x === 0 ? ctx.moveTo(x, wy) : ctx.lineTo(x, wy); } ctx.stroke(); Draw.water(ctx, w, h, h * 0.45, H.rgba(c[0], 0.4), 4, 50); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(88); // Sun Draw.glow(ctx, w * 0.75, h * 0.1, h * 0.15, '#FFD700'); Draw.circle(ctx, w * 0.75, h * 0.1, h * 0.05, '#FFE44D'); // Palm trees for (var i = 0; i < 4; i++) { var px = w * 0.1 + rng() * w * 0.35; var py = h * 0.65; var ph = 60 + rng() * 40; var lean = (rng() - 0.5) * 0.3; // Curved trunk ctx.strokeStyle = '#8B6914'; ctx.lineWidth = 6; ctx.beginPath(); ctx.moveTo(px, py); ctx.quadraticCurveTo(px + lean * ph, py - ph * 0.5, px + lean * ph * 1.5, py - ph); ctx.stroke(); // Fronds var topX = px + lean * ph * 1.5, topY = py - ph; for (var f = 0; f < 6; f++) { var angle = (f / 6) * Math.PI * 2 + Math.sin(t * 0.8 + f) * 0.1; var fLen = 25 + rng() * 20; ctx.strokeStyle = '#228B22'; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(topX, topY); ctx.quadraticCurveTo(topX + Math.cos(angle) * fLen * 0.6, topY + Math.sin(angle) * fLen * 0.3, topX + Math.cos(angle) * fLen, topY + Math.abs(Math.sin(angle)) * fLen * 0.5 + 8); ctx.stroke(); } } // Beach umbrella var ux = w * 0.6, uy = h * 0.58; ctx.fillStyle = '#8B6914'; ctx.fillRect(ux - 1.5, uy, 3, h * 0.12); ctx.fillStyle = c[3]; ctx.beginPath(); ctx.arc(ux, uy, 25, Math.PI, 0); ctx.fill(); ctx.fillStyle = '#FFFFFF'; ctx.beginPath(); ctx.arc(ux, uy, 25, Math.PI, Math.PI + Math.PI / 4); ctx.lineTo(ux, uy); ctx.fill(); ctx.beginPath(); ctx.arc(ux, uy, 25, Math.PI + Math.PI / 2, Math.PI + Math.PI * 3 / 4); ctx.lineTo(ux, uy); ctx.fill(); }, particles: { type: 'sparkles', count: 20, color: '#FFE44D' } }, harvest: { meta: { id: 'seasonal_harvest', mood: 'warm', colors: ['#5C3317', '#D4A017', '#8B4513', '#FFD700'], tags: ['harvest', 'farm', 'autumn', 'golden'], description: 'Golden harvest fields with wheat rows and a warm sunset', compatibleMusicMoods: ['mellow', 'peaceful', 'warm'], recommendedFor: ['puzzle', 'story', 'creative'] }, colors: ['#5C3317', '#D4A017', '#8B4513', '#FFD700'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, ['#E8913A', '#F4C54D', c[1]], 90); // Field rows Draw.rect(ctx, 0, h * 0.55, w, h * 0.45, '#B8860B'); for (var r = 0; r < 8; r++) { var ry = h * 0.58 + r * h * 0.05; ctx.fillStyle = H.rgba(H.darken(c[1], 0.1 + r * 0.03), 0.6); ctx.fillRect(0, ry, w, 2); } }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(99); // Wheat stalks for (var i = 0; i < 60; i++) { var wx = rng() * w; var wy = h * 0.56 + rng() * h * 0.35; var wh = 15 + rng() * 25; var sway = Math.sin(t * 1.2 + i * 0.5) * 3; ctx.strokeStyle = H.rgba(c[1], 0.7); ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(wx, wy); ctx.lineTo(wx + sway, wy - wh); ctx.stroke(); // Wheat head ctx.fillStyle = c[3]; ctx.beginPath(); ctx.ellipse(wx + sway, wy - wh - 3, 2, 5, 0, 0, Math.PI * 2); ctx.fill(); } // Sunset glow Draw.glow(ctx, w * 0.5, h * 0.12, h * 0.18, '#FF8C00'); Draw.circle(ctx, w * 0.5, h * 0.12, h * 0.06, '#FFD700'); // Barn silhouette var bx = w * 0.78, by = h * 0.48, bw = 50, bh = 35; ctx.fillStyle = c[2]; ctx.fillRect(bx, by, bw, bh); ctx.beginPath(); ctx.moveTo(bx - 5, by); ctx.lineTo(bx + bw / 2, by - 25); ctx.lineTo(bx + bw + 5, by); ctx.closePath(); ctx.fillStyle = H.darken(c[2], 0.2); ctx.fill(); // Silo ctx.fillStyle = c[2]; ctx.fillRect(bx + bw + 8, by - 10, 12, bh + 10); ctx.beginPath(); ctx.arc(bx + bw + 14, by - 10, 6, 0, Math.PI * 2); ctx.fill(); }, particles: { type: 'dust', count: 20, color: '#DDCC88' } }, blossom: { meta: { id: 'seasonal_blossom', mood: 'peaceful', colors: ['#FFB7C5', '#FF69B4', '#FFF0F5', '#98FB98'], tags: ['spring', 'cherry', 'blossom', 'pink'], description: 'Gentle cherry blossom petals drifting under pink-tinted skies', compatibleMusicMoods: ['peaceful', 'calm', 'romantic'], recommendedFor: ['puzzle', 'story', 'creative', 'music'] }, colors: ['#FFB7C5', '#FF69B4', '#FFF0F5', '#98FB98'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[2], c[0], H.lighten(c[3], 0.3)], 90); Draw.hills(ctx, w, h, h * 0.7, 6, c[3], 12); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(33); // Cherry blossom trees for (var i = 0; i < 5; i++) { var tx = w * 0.1 + rng() * w * 0.8; var ty = h * 0.65 + rng() * h * 0.1; var th = 50 + rng() * 40; // Trunk and branches ctx.strokeStyle = '#5C3A1E'; ctx.lineWidth = 4 + rng() * 3; ctx.beginPath(); ctx.moveTo(tx, ty); ctx.quadraticCurveTo(tx + rng() * 10 - 5, ty - th * 0.5, tx, ty - th); ctx.stroke(); // Branch clusters for (var b = 0; b < 4; b++) { var bAngle = (rng() - 0.5) * Math.PI * 0.8; var bLen = 15 + rng() * 25; var bStartY = ty - th * (0.5 + rng() * 0.4); ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(tx, bStartY); ctx.lineTo(tx + Math.cos(bAngle) * bLen, bStartY + Math.sin(bAngle) * bLen - 10); ctx.stroke(); } // Blossom clouds for (var bc = 0; bc < 6; bc++) { var bx = tx + (rng() - 0.5) * th * 0.8; var by = ty - th * (0.4 + rng() * 0.5); var br = 8 + rng() * 15; var pulseR = br + Math.sin(t * 0.8 + i + bc) * 1.5; Draw.circle(ctx, bx, by, pulseR, H.rgba(c[0], 0.5 + rng() * 0.3)); Draw.circle(ctx, bx + br * 0.3, by - br * 0.2, pulseR * 0.7, H.rgba(c[1], 0.3)); } } // Soft light Draw.glow(ctx, w * 0.5, h * 0.08, h * 0.12, '#FFF5EE'); }, particles: { type: 'leaves', count: 25, color: '#FFB7C5' } }, snowfall: { meta: { id: 'seasonal_snowfall', mood: 'calm', colors: ['#0D1B2A', '#1B2838', '#415A77', '#E0E1DD'], tags: ['winter', 'night', 'snow', 'dark'], description: 'Heavy snowfall over a dark winter night with distant lights', compatibleMusicMoods: ['calm', 'ambient', 'mysterious'], recommendedFor: ['puzzle', 'story', 'rpg'] }, colors: ['#0D1B2A', '#1B2838', '#415A77', '#E0E1DD'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [c[0], c[1]], 90); Draw.mountains(ctx, w, h, h * 0.5, 6, c[2], 60); Draw.hills(ctx, w, h, h * 0.7, 5, H.lighten(c[2], 0.2), 70); // Snow ground Draw.rect(ctx, 0, h * 0.78, w, h * 0.22, H.rgba(c[3], 0.3)); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(44); // Distant village lights for (var i = 0; i < 8; i++) { var lx = rng() * w; var ly = h * 0.55 + rng() * h * 0.15; var twinkle = 0.3 + Math.sin(t * 2 + i * 1.5) * 0.2; Draw.glow(ctx, lx, ly, 8, H.rgba('#FFD700', twinkle)); Draw.circle(ctx, lx, ly, 1.5, H.rgba('#FFE088', twinkle + 0.3)); } // Bare tree silhouettes for (var tr = 0; tr < 6; tr++) { var tx = rng() * w; var ty = h * 0.75 + rng() * h * 0.05; var th = 30 + rng() * 40; ctx.strokeStyle = H.rgba(c[0], 0.6); ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(tx, ty); ctx.lineTo(tx, ty - th); ctx.stroke(); // Branches for (var br = 0; br < 4; br++) { var brY = ty - th * (0.3 + br * 0.18); var brDir = br % 2 === 0 ? 1 : -1; var brLen = 8 + rng() * 15; ctx.beginPath(); ctx.moveTo(tx, brY); ctx.lineTo(tx + brDir * brLen, brY - brLen * 0.6); ctx.stroke(); } } // Snow on ground — gentle undulation ctx.fillStyle = H.rgba(c[3], 0.15); ctx.beginPath(); ctx.moveTo(0, h); for (var x = 0; x <= w; x += 20) { ctx.lineTo(x, h * 0.78 + Math.sin(x * 0.03 + t * 0.3) * 4); } ctx.lineTo(w, h); ctx.closePath(); ctx.fill(); }, particles: { type: 'snow', count: 120, color: '#E0E1DD' } }, tropical: { meta: { id: 'seasonal_tropical', mood: 'energetic', colors: ['#00CED1', '#FF6347', '#228B22', '#FFD700'], tags: ['summer', 'tropical', 'jungle', 'vibrant'], description: 'A lush tropical scene with dense foliage and vivid colors', compatibleMusicMoods: ['energetic', 'upbeat', 'happy'], recommendedFor: ['action', 'racing', 'sports', 'pet'] }, colors: ['#00CED1', '#FF6347', '#228B22', '#FFD700'], renderBase: function(ctx, w, h, c, Draw, t) { Draw.fillGradient(ctx, w, h, [H.lighten(c[0], 0.2), c[0]], 90); Draw.hills(ctx, w, h, h * 0.5, 4, c[2], 35); Draw.hills(ctx, w, h, h * 0.6, 6, H.darken(c[2], 0.2), 45); // Jungle floor Draw.rect(ctx, 0, h * 0.75, w, h * 0.25, H.darken(c[2], 0.35)); }, renderMid: function(ctx, w, h, c, Draw, t) { var rng = H.seededRandom(66); // Tropical flowers for (var i = 0; i < 20; i++) { var fx = rng() * w; var fy = h * 0.55 + rng() * h * 0.3; var fs = 4 + rng() * 6; var bloom = fs + Math.sin(t + i * 0.8) * 1; var flColor = rng() > 0.5 ? c[1] : c[3]; // Petals ctx.save(); ctx.translate(fx, fy); for (var p = 0; p < 5; p++) { ctx.rotate(Math.PI * 2 / 5); ctx.fillStyle = H.rgba(flColor, 0.7); ctx.beginPath(); ctx.ellipse(0, -bloom, bloom * 0.4, bloom, 0, 0, Math.PI * 2); ctx.fill(); } ctx.restore(); Draw.circle(ctx, fx, fy, bloom * 0.3, '#FFFFFF'); } // Large palm fronds at edges for (var f = 0; f < 3; f++) { var side = f % 2 === 0 ? 0 : w; var dir = f % 2 === 0 ? 1 : -1; var fy2 = h * 0.05 + f * h * 0.15; var sway = Math.sin(t * 0.5 + f) * 8; ctx.strokeStyle = c[2]; ctx.lineWidth = 4; for (var fr = 0; fr < 4; fr++) { var angle = (0.2 + fr * 0.3) * dir; ctx.beginPath(); ctx.moveTo(side, fy2 + fr * 20); ctx.quadraticCurveTo(side + dir * (60 + sway), fy2 + fr * 20 + 30, side + dir * (100 + sway), fy2 + fr * 20 + 60); ctx.stroke(); } } // Parrots for (var b = 0; b < 3; b++) { var bx = (rng() * w + t * 15 * (b + 1)) % (w + 30) - 15; var by = h * 0.15 + rng() * h * 0.2 + Math.sin(t * 1.5 + b * 2) * 10; var wingF = Math.abs(Math.sin(t * 5 + b * 3)) * 5; ctx.fillStyle = [c[1], c[3], '#00AA44'][b % 3]; ctx.beginPath(); ctx.ellipse(bx, by, 4, 2.5, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = H.rgba('#000000', 0.4); ctx.beginPath(); ctx.ellipse(bx - wingF * 0.8, by - 1, wingF, 2, -0.4, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(bx + wingF * 0.8, by - 1, wingF, 2, 0.4, 0, Math.PI * 2); ctx.fill(); } }, particles: { type: 'fireflies', count: 15, color: '#CCFF44' } } }); })(window.BackgroundEngine);