Initial commit

This commit is contained in:
Allen
2026-04-30 02:14:25 +00:00
commit 7090e6f01d
243 changed files with 115122 additions and 0 deletions
@@ -0,0 +1,360 @@
// nature.js — Nature theme for procedural background system
// 8 variants: forest, jungle, desert, mountains, ocean, underwater, waterfall, meadow
(function(engine) {
if (!engine) return;
var D = engine.Draw, H = engine.helpers;
engine.registerTheme('nature', {
forest: {
meta: { id: 'nature_forest', mood: 'peaceful', colors: ['#1A3320', '#2D5A27', '#4A7A3F', '#8FB86A'], tags: ['trees', 'green', 'calm'], description: 'A dense forest with layered canopy and dappled light', compatibleMusicMoods: ['calm', 'ambient', 'dreamy'], recommendedFor: ['story', 'rpg', 'puzzle'] },
colors: ['#1A3320', '#2D5A27', '#4A7A3F', '#8FB86A'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, ['#5588AA', '#88BBCC', c[1]], 90);
Draw.clouds(ctx, w, h, h * 0.08, 5, '#FFFFFF', 110);
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Background tree layer
Draw.hills(ctx, w, h, h * 0.4, 8, H.darken(c[0], 0.3), 111);
Draw.trees(ctx, w, h, h * 0.5, 25, '#3D2B1F', c[0], 112);
// Mid tree layer
Draw.hills(ctx, w, h, h * 0.6, 6, c[1], 113);
Draw.trees(ctx, w, h, h * 0.68, 18, '#5C3A1E', c[2], 114);
// Foreground
Draw.hills(ctx, w, h, h * 0.8, 5, c[2], 115);
Draw.trees(ctx, w, h, h * 0.85, 10, '#6B4226', c[3], 116);
// Forest floor
Draw.rect(ctx, 0, h * 0.88, w, h * 0.12, H.darken(c[0], 0.4));
// Light rays
var rng = H.seededRandom(117);
ctx.save();
ctx.globalAlpha = 0.04 + Math.sin(t * 0.5) * 0.02;
for (var i = 0; i < 5; i++) {
var rx = rng() * w;
ctx.fillStyle = '#FFFFCC';
ctx.beginPath();
ctx.moveTo(rx, 0);
ctx.lineTo(rx - 30, h * 0.7);
ctx.lineTo(rx + 30, h * 0.7);
ctx.closePath();
ctx.fill();
}
ctx.restore();
},
particles: { type: 'fireflies', count: 20, color: '#CCFF66' }
},
jungle: {
meta: { id: 'nature_jungle', mood: 'adventurous', colors: ['#0D2B0D', '#1A4D1A', '#2D7A2D', '#55AA33'], tags: ['tropical', 'dense', 'lush'], description: 'Thick tropical jungle with hanging vines and humid air', compatibleMusicMoods: ['adventurous', 'tense', 'ambient'], recommendedFor: ['action', 'rpg', 'pet'] },
colors: ['#0D2B0D', '#1A4D1A', '#2D7A2D', '#55AA33'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, ['#2A4420', c[0], H.darken(c[0], 0.3)], 90);
},
renderMid: function(ctx, w, h, c, Draw, t) {
Draw.hills(ctx, w, h, h * 0.35, 10, c[1], 210);
Draw.trees(ctx, w, h, h * 0.5, 30, '#3B2510', c[1], 211);
// Vines
var rng = H.seededRandom(212);
ctx.strokeStyle = H.rgba(c[2], 0.6);
ctx.lineWidth = 2;
for (var i = 0; i < 12; i++) {
var vx = rng() * w;
var sway = Math.sin(t * 0.8 + i * 1.3) * 8;
ctx.beginPath();
ctx.moveTo(vx, 0);
ctx.quadraticCurveTo(vx + sway, h * 0.2 + rng() * h * 0.15, vx + sway * 0.5, h * 0.3 + rng() * h * 0.2);
ctx.stroke();
}
// Large canopy leaves at top
Draw.hills(ctx, w, h * 0.15, h * 0.02, 12, H.rgba(c[2], 0.5), 213);
// Mid layer dense foliage
Draw.trees(ctx, w, h, h * 0.7, 20, '#4B3520', c[2], 214);
// Ground ferns
Draw.hills(ctx, w, h, h * 0.85, 15, c[3], 215);
Draw.rect(ctx, 0, h * 0.9, w, h * 0.1, H.darken(c[0], 0.5));
},
particles: { type: 'fireflies', count: 25, color: '#AAEE44' }
},
desert: {
meta: { id: 'nature_desert', mood: 'warm', colors: ['#F4A460', '#DAA06D', '#C2955A', '#8B6914'], tags: ['sand', 'hot', 'arid'], description: 'Rolling sand dunes under a blazing sun', compatibleMusicMoods: ['calm', 'epic', 'ambient'], recommendedFor: ['racing', 'action', 'rpg'] },
colors: ['#F4A460', '#DAA06D', '#C2955A', '#8B6914'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, ['#FFCC66', '#FF9933', c[0]], 90);
// Sun
var sunPulse = 1 + Math.sin(t * 0.3) * 0.03;
Draw.glow(ctx, w * 0.75, h * 0.12, 100 * sunPulse, H.rgba('#FFEE88', 0.4));
Draw.circle(ctx, w * 0.75, h * 0.12, 35, '#FFDD44');
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Far dunes
Draw.hills(ctx, w, h, h * 0.45, 5, H.lighten(c[0], 0.15), 310);
// Mid dunes
Draw.hills(ctx, w, h, h * 0.6, 4, c[1], 311);
// Near dunes
Draw.hills(ctx, w, h, h * 0.75, 3, c[2], 312);
// Ground
Draw.rect(ctx, 0, h * 0.82, w, h * 0.18, c[3]);
// Heat shimmer
var rng = H.seededRandom(313);
ctx.save();
ctx.globalAlpha = 0.03 + Math.sin(t * 2) * 0.02;
for (var i = 0; i < 8; i++) {
var sy = h * 0.45 + rng() * h * 0.15;
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 1;
ctx.beginPath();
for (var x = 0; x < w; x += 6) {
var yy = sy + Math.sin(x * 0.04 + t * 3 + i) * 3;
x === 0 ? ctx.moveTo(x, yy) : ctx.lineTo(x, yy);
}
ctx.stroke();
}
ctx.restore();
// Cacti
for (var j = 0; j < 4; j++) {
var cx = rng() * w;
var cy = h * 0.72 + rng() * h * 0.06;
var ch = 15 + rng() * 25;
Draw.rect(ctx, cx - 3, cy - ch, 6, ch, '#2D5A27');
Draw.rect(ctx, cx + 3, cy - ch * 0.7, 10, 4, '#2D5A27');
Draw.rect(ctx, cx + 9, cy - ch * 0.7, 4, -10, '#2D5A27');
Draw.rect(ctx, cx - 13, cy - ch * 0.5, 10, 4, '#2D5A27');
Draw.rect(ctx, cx - 13, cy - ch * 0.5, 4, -8, '#2D5A27');
}
},
particles: { type: 'dust', count: 20, color: '#DDCC99' }
},
mountains: {
meta: { id: 'nature_mountains', mood: 'majestic', colors: ['#2C3E50', '#4A6B5A', '#7A9B6B', '#FFFFFF'], tags: ['peaks', 'alpine', 'grand'], description: 'Snow-capped mountain peaks under sweeping skies', compatibleMusicMoods: ['epic', 'calm', 'dreamy'], recommendedFor: ['rpg', 'sports', 'story'] },
colors: ['#2C3E50', '#4A6B5A', '#7A9B6B', '#FFFFFF'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, ['#6699CC', '#AAC4DD', '#DDEEFF'], 90);
Draw.clouds(ctx, w, h, h * 0.1, 8, '#FFFFFF', 410);
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Far mountains
Draw.mountains(ctx, w, h, h * 0.45, 6, H.rgba(c[0], 0.6), 411);
// Snow caps
Draw.mountains(ctx, w, h, h * 0.42, 6, H.rgba(c[3], 0.25), 411);
// Mid mountains
Draw.mountains(ctx, w, h, h * 0.55, 5, c[0], 412);
Draw.mountains(ctx, w, h, h * 0.52, 5, H.rgba(c[3], 0.35), 412);
// Foothills
Draw.hills(ctx, w, h, h * 0.7, 8, c[1], 413);
Draw.hills(ctx, w, h, h * 0.8, 6, c[2], 414);
// Valley floor
Draw.rect(ctx, 0, h * 0.85, w, h * 0.15, H.darken(c[2], 0.2));
// River
ctx.strokeStyle = H.rgba('#5588BB', 0.5);
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(w * 0.3, h * 0.85);
ctx.quadraticCurveTo(w * 0.5, h * 0.88, w * 0.7, h * 0.92);
ctx.quadraticCurveTo(w * 0.85, h * 0.95, w, h * 0.93);
ctx.stroke();
},
particles: { type: 'fog', count: 8, color: '#CCCCCC' }
},
ocean: {
meta: { id: 'nature_ocean', mood: 'serene', colors: ['#1A5276', '#2E86C1', '#5DADE2', '#AED6F1'], tags: ['water', 'waves', 'calm'], description: 'Vast ocean stretching to the horizon at sunset', compatibleMusicMoods: ['calm', 'dreamy', 'ambient'], recommendedFor: ['puzzle', 'story', 'creative'] },
colors: ['#1A5276', '#2E86C1', '#5DADE2', '#AED6F1'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, ['#FF8844', '#FFAA66', '#FFCC88', c[3]], 90);
// Setting sun
var sunY = h * 0.35 + Math.sin(t * 0.1) * 2;
Draw.glow(ctx, w * 0.5, sunY, 120, H.rgba('#FFAA44', 0.4));
Draw.circle(ctx, w * 0.5, sunY, 30, '#FFDD66');
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Horizon line
Draw.rect(ctx, 0, h * 0.45, w, 2, H.rgba('#FFEECC', 0.3));
// Ocean
Draw.water(ctx, w, h, h * 0.45, c[1], 3 + Math.sin(t) * 1, 510);
// Sun reflection on water
var rng = H.seededRandom(511);
ctx.save();
ctx.globalAlpha = 0.08 + Math.sin(t * 0.5) * 0.04;
for (var i = 0; i < 15; i++) {
var ry = h * 0.47 + i * ((h * 0.53) / 15);
var rw = 20 + rng() * 40 - i * 1.5;
var rx = w * 0.5 - rw / 2 + Math.sin(t * 1.5 + i * 0.8) * 10;
Draw.rect(ctx, rx, ry, rw, 2, '#FFDDAA');
}
ctx.restore();
// Distant clouds
Draw.clouds(ctx, w, h, h * 0.15, 4, '#FFDDBB', 512);
},
particles: { type: 'fog', count: 6, color: '#AACCDD' }
},
underwater: {
meta: { id: 'nature_underwater', mood: 'mystical', colors: ['#0A2342', '#1A4B6E', '#2E86C1', '#48D1CC'], tags: ['deep', 'aquatic', 'blue'], description: 'Deep beneath the waves with shafts of sunlight', compatibleMusicMoods: ['dreamy', 'ambient', 'calm'], recommendedFor: ['puzzle', 'pet', 'creative'] },
colors: ['#0A2342', '#1A4B6E', '#2E86C1', '#48D1CC'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, [c[3], c[2], c[1], c[0]], 90);
// Light rays from surface
var rng = H.seededRandom(610);
ctx.save();
for (var i = 0; i < 6; i++) {
var rx = rng() * w;
var sway = Math.sin(t * 0.4 + i * 1.5) * 15;
ctx.fillStyle = H.rgba('#AADDFF', 0.03 + Math.sin(t * 0.3 + i) * 0.01);
ctx.beginPath();
ctx.moveTo(rx + sway, 0);
ctx.lineTo(rx - 40 + sway * 0.5, h);
ctx.lineTo(rx + 40 + sway * 0.5, h);
ctx.closePath();
ctx.fill();
}
ctx.restore();
},
renderMid: function(ctx, w, h, c, Draw, t) {
var rng = H.seededRandom(611);
// Seaweed
for (var i = 0; i < 10; i++) {
var sx = rng() * w;
var sh = 40 + rng() * 80;
var sway = Math.sin(t * 0.8 + i * 2) * 10;
ctx.strokeStyle = H.rgba('#228844', 0.5 + rng() * 0.3);
ctx.lineWidth = 3 + rng() * 3;
ctx.beginPath();
ctx.moveTo(sx, h);
ctx.quadraticCurveTo(sx + sway, h - sh * 0.5, sx + sway * 1.3, h - sh);
ctx.stroke();
}
// Coral mounds
for (var j = 0; j < 6; j++) {
var cx = rng() * w;
var cr = 15 + rng() * 30;
var cc = H.pick(['#CC4466', '#FF7744', '#FFAA33', '#AA55CC']);
Draw.circle(ctx, cx, h - cr * 0.5, cr, H.rgba(cc, 0.5));
Draw.circle(ctx, cx + cr * 0.4, h - cr * 0.3, cr * 0.6, H.rgba(cc, 0.35));
}
// Fish silhouettes
for (var k = 0; k < 8; k++) {
var fx = (rng() * w + t * (10 + rng() * 30)) % (w + 60) - 30;
var fy = h * 0.2 + rng() * h * 0.5;
var fs = 4 + rng() * 8;
ctx.fillStyle = H.rgba(c[3], 0.3 + rng() * 0.2);
ctx.beginPath();
ctx.ellipse(fx, fy, fs * 2, fs, 0, 0, Math.PI * 2);
ctx.fill();
// Tail
ctx.beginPath();
ctx.moveTo(fx - fs * 2, fy);
ctx.lineTo(fx - fs * 3, fy - fs * 0.8);
ctx.lineTo(fx - fs * 3, fy + fs * 0.8);
ctx.closePath();
ctx.fill();
}
// Sandy bottom
Draw.rect(ctx, 0, h * 0.92, w, h * 0.08, H.rgba('#C2B280', 0.25));
},
particles: { type: 'bubbles', count: 30, color: '#88CCEE' }
},
waterfall: {
meta: { id: 'nature_waterfall', mood: 'refreshing', colors: ['#1B4332', '#2D6A4F', '#52B788', '#B7E4C7'], tags: ['water', 'lush', 'dynamic'], description: 'A cascading waterfall in a lush green cliff', compatibleMusicMoods: ['calm', 'ambient', 'dreamy'], recommendedFor: ['puzzle', 'creative', 'story'] },
colors: ['#1B4332', '#2D6A4F', '#52B788', '#B7E4C7'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, ['#77AABB', '#99CCDD', c[2]], 90);
Draw.clouds(ctx, w, h, h * 0.05, 3, '#FFFFFF', 710);
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Cliff walls
Draw.rect(ctx, 0, h * 0.15, w * 0.35, h * 0.85, c[0]);
Draw.rect(ctx, w * 0.65, h * 0.15, w * 0.35, h * 0.85, c[0]);
// Cliff vegetation
Draw.hills(ctx, w * 0.35, h * 0.2, h * 0.15, 5, c[1], 711);
ctx.save(); ctx.translate(w * 0.65, 0);
Draw.hills(ctx, w * 0.35, h * 0.2, h * 0.15, 5, c[1], 712);
ctx.restore();
// Waterfall
var rng = H.seededRandom(713);
var fallX = w * 0.38, fallW = w * 0.24;
for (var i = 0; i < 20; i++) {
var fx = fallX + rng() * fallW;
var fy = h * 0.15 + rng() * h * 0.65;
var offset = (t * 80 + rng() * 200) % (h * 0.7);
var wy = h * 0.15 + offset;
ctx.strokeStyle = H.rgba('#FFFFFF', 0.1 + rng() * 0.2);
ctx.lineWidth = 1 + rng() * 2;
ctx.beginPath();
ctx.moveTo(fx, wy);
ctx.lineTo(fx + (rng() - 0.5) * 4, wy + 10 + rng() * 20);
ctx.stroke();
}
// Base glow
Draw.fillGradient(ctx, w, h, [H.rgba(c[3], 0), H.rgba(c[3], 0), H.rgba('#FFFFFF', 0.15)], 90);
// Pool at bottom
Draw.water(ctx, w, h, h * 0.82, H.rgba('#3388AA', 0.6), 2, 714);
// Mist
Draw.glow(ctx, w * 0.5, h * 0.8, 100, H.rgba('#FFFFFF', 0.12 + Math.sin(t) * 0.04));
// Foreground plants
Draw.trees(ctx, w, h, h * 0.9, 6, '#3D2B1F', c[2], 715);
},
particles: { type: 'fog', count: 12, color: '#DDEEFF' }
},
meadow: {
meta: { id: 'nature_meadow', mood: 'joyful', colors: ['#4CAF50', '#81C784', '#A5D6A7', '#FFF59D'], tags: ['flowers', 'sunny', 'cheerful'], description: 'A sunny meadow with wildflowers and gentle breeze', compatibleMusicMoods: ['happy', 'calm', 'dreamy'], recommendedFor: ['pet', 'creative', 'story', 'puzzle'] },
colors: ['#4CAF50', '#81C784', '#A5D6A7', '#FFF59D'],
renderBase: function(ctx, w, h, c, Draw, t) {
Draw.fillGradient(ctx, w, h, ['#55AADD', '#88CCEE', '#AADDEE'], 90);
// Sun
var pulse = 1 + Math.sin(t * 0.4) * 0.02;
Draw.glow(ctx, w * 0.8, h * 0.1, 80 * pulse, H.rgba('#FFEE66', 0.4));
Draw.circle(ctx, w * 0.8, h * 0.1, 28, '#FFEE44');
Draw.clouds(ctx, w, h, h * 0.08, 6, '#FFFFFF', 810);
},
renderMid: function(ctx, w, h, c, Draw, t) {
// Distant hills
Draw.hills(ctx, w, h, h * 0.55, 5, H.darken(c[0], 0.15), 811);
// Main meadow
Draw.hills(ctx, w, h, h * 0.65, 8, c[0], 812);
Draw.hills(ctx, w, h, h * 0.72, 6, c[1], 813);
// Ground
Draw.rect(ctx, 0, h * 0.78, w, h * 0.22, c[2]);
// Flowers
var rng = H.seededRandom(814);
var flowerColors = ['#FF6B6B', '#FFD93D', '#C084FC', '#FF8FA3', '#FFFFFF'];
for (var i = 0; i < 40; i++) {
var fx = rng() * w;
var fy = h * 0.68 + rng() * h * 0.25;
var fc = flowerColors[Math.floor(rng() * flowerColors.length)];
var sway = Math.sin(t * 1.2 + i * 0.8) * 2;
// Stem
ctx.strokeStyle = H.rgba('#338833', 0.5);
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(fx, fy + 8);
ctx.lineTo(fx + sway, fy);
ctx.stroke();
// Flower
Draw.circle(ctx, fx + sway, fy, 2 + rng() * 2, fc);
}
// Butterflies
for (var j = 0; j < 3; j++) {
var bx = (rng() * w + Math.sin(t * 0.5 + j * 3) * 40);
var by = h * 0.4 + rng() * h * 0.3 + Math.sin(t * 1.2 + j * 2) * 15;
var wingFlap = Math.abs(Math.sin(t * 5 + j * 2)) * 4;
var bc = H.pick(['#FFD700', '#FF69B4', '#87CEEB']);
ctx.fillStyle = H.rgba(bc, 0.7);
ctx.beginPath();
ctx.ellipse(bx - 3, by, wingFlap, 3, -0.3, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.ellipse(bx + 3, by, wingFlap, 3, 0.3, 0, Math.PI * 2);
ctx.fill();
Draw.circle(ctx, bx, by, 1, '#333333');
}
},
particles: { type: 'leaves', count: 10, color: '#AEDD77' }
}
});
})(window.BackgroundEngine);