Le nuvole che scorrono sono quelle disegnate da Codex nello stile degli sfondi (tools/ritaglia_sprite.py)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ApqNBvNbNCwXqR2RLo8QJh
This commit is contained in:
2026-10-24 23:56:07 +02:00
co-authored by Claude Opus 5.5
parent fd0fddae5d
commit a9528f7ed7
12 changed files with 57 additions and 32 deletions
+10 -29
View File
@@ -226,28 +226,7 @@ const scratch = () => {
if (!scratchCanvas) { scratchCanvas = document.createElement('canvas'); scratchCanvas.width = WIDTH; scratchCanvas.height = HEIGHT; }
return scratchCanvas;
};
// Una nuvola a pixel: qualche cerchio pieno uno accanto all'altro, la cima color panna e la pancia azzurrina.
const cloudSprites = new Map();
function cloudSprite(n) {
if (cloudSprites.has(n)) return cloudSprites.get(n);
const w = 34 + Math.floor(hash(n * 13 + 1) * 30), h = 18, c = document.createElement('canvas');
c.width = w; c.height = h;
const x = c.getContext('2d'), blobs = [];
const count = 3 + Math.floor(hash(n * 3) * 3);
for (let b = 0; b < count; b++) {
const r = 4 + hash(n * 17 + b) * 5, cx = 5 + (w - 10) * (b + 0.5) / count + (hash(n + b * 3) - 0.5) * 4;
blobs.push([cx, h - 4 - r * 0.8, r]);
}
for (let py = 0; py < h; py++) for (let px = 0; px < w; px++) {
const inside = blobs.some(([cx, cy, r]) => (px - cx) ** 2 + (py - cy) ** 2 <= r * r) && py <= h - 3;
if (!inside) continue;
const top = !blobs.some(([cx, cy, r]) => (px - cx) ** 2 + (py - 2 - cy) ** 2 <= r * r);
x.fillStyle = py >= h - 6 ? '#c7d4ec' : top ? '#ffffff' : py < h - 9 ? '#fbf3dc' : '#eae6dc';
x.fillRect(px, py, 1, 1);
}
cloudSprites.set(n, c);
return c;
}
const CLOUDS = Array.from({ length: 8 }, (_, i) => `assets/effects/nuvola-${i + 1}.png`);
// Un effetto o una luce vale adesso? `when` (condizione sui flag) e `phase` (una fase del giorno o un elenco).
function onNow(e, game) {
@@ -402,24 +381,26 @@ export const EFFECTS = {
ctx.restore();
}
},
// Nuvole a pixel che attraversano il cielo, lentissime, ognuna a una sua altezza e velocità; uscita da un lato
// rientra dall'altro. Si vedono solo dove lo sfondo è cielo (skyMask): passano dietro alberi, torri e tetti.
// Nuvole che attraversano il cielo, lentissime (disegnate da Codex nello stile degli sfondi, assets/effects/):
// ognuna a una sua altezza e velocità, uscita da un lato rientra dall'altro. Si vedono solo dove lo sfondo è
// cielo (skyMask): passano dietro alberi, torri e tetti. Le due grandi solo coi cieli larghi (`big`).
clouds(ctx, e, time, game) {
const bg = backgrounds.get(game.scene.background), mask = bg && skyMask(bg);
if (!mask) return;
const layer = scratch(), lc = layer.getContext('2d'), [ax, ay, aw, ah] = e.area;
const pool = e.big ? CLOUDS : CLOUDS.slice(2);
lc.clearRect(0, 0, WIDTH, HEIGHT);
for (let i = 0; i < (e.count ?? 3); i++) {
const sprite = cloudSprite(i + (e.seed ?? 0)), speed = (e.speed ?? 3) * (0.6 + hash(i * 7) * 0.8);
const span = aw + sprite.width + 20, x = ax - sprite.width - 10 + ((hash(i * 11) * span + time / 1000 * speed) % span);
lc.drawImage(sprite, Math.round(x), ay + Math.floor(hash(i * 5 + 2) * ah));
const src = pool[Math.floor(hash(i * 19 + (e.seed ?? 0) + game.state.scene.length) * pool.length)], img = backgrounds.get(src);
if (!img) { loadBackground(src); continue; }
const speed = (e.speed ?? 2.5) * (0.6 + hash(i * 7) * 0.8), span = aw + img.width + 20;
const x = ax - img.width - 10 + ((hash(i * 11) * span + time / 1000 * speed) % span);
lc.drawImage(img, Math.round(x), ay + Math.floor(hash(i * 5 + 2) * ah));
}
lc.globalCompositeOperation = 'destination-in';
lc.drawImage(mask, 0, 0);
lc.globalCompositeOperation = 'source-over';
ctx.globalAlpha = e.alpha ?? 0.92;
ctx.drawImage(layer, 0, 0);
ctx.globalAlpha = 1;
},
// Lampioni: un alone caldo che si accende (di solito solo di sera, con `phase`).
lamps(ctx, e, time) {