Lucciole, finestre che si accendono di sera, rondini, cipressi al vento e dissolvenza fra le scene

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:37:23 +02:00
co-authored by Claude Opus 5.5
parent f64051b5df
commit 6a96611a1b
4 changed files with 76 additions and 6 deletions
+58 -2
View File
@@ -145,6 +145,8 @@ export function render(ctx, game, { hover, spots, debug, time = performance.now(
ctx.imageSmoothingEnabled = false;
ctx.clearRect(0, 0, WIDTH, HEIGHT);
if (bg) ctx.drawImage(bg, 0, 0, WIDTH, HEIGHT);
// Gli effetti che muovono pezzi di sfondo (i cipressi al vento) vanno subito sopra lo sfondo, sotto tutto il resto.
drawEffects(ctx, game, time, true);
for (const shape of scene.art ?? []) drawShape(ctx, shape);
// Una forma può avere la sua condizione (`when`): la chiavetta nella griglia sparisce quando la si prende.
// I primi piani (`top`) invece vanno sopra a tutto, anche al buio e alle candele: si disegnano dopo.
@@ -290,6 +292,58 @@ export const EFFECTS = {
ctx.fillStyle = e.fill;
for (const [x, y, w, h] of e.rects) closed ? ctx.fillRect(x, y + Math.floor(h / 2), w, 1) : ctx.fillRect(x, y, w, h);
},
// Lucciole di sera: puntini gialli che vagano piano e si accendono e si spengono.
fireflies(ctx, e, time) {
const [ax, ay, aw, ah] = e.area;
for (let i = 0; i < (e.count ?? 8); i++) {
const t = time / 1000, glow = Math.sin(t * (1.3 + hash(i) * 1.2) + i * 2);
if (glow < 0.15) continue;
const x = ax + aw * (0.5 + 0.45 * Math.sin(t * 0.21 + i * 2.3) * Math.cos(t * 0.13 + i)), y = ay + ah * (0.5 + 0.45 * Math.sin(t * 0.17 + i * 1.7));
ctx.fillStyle = `rgba(255, 240, 120, ${0.25 * glow})`;
ctx.fillRect(Math.round(x) - 1, Math.round(y) - 1, 3, 3);
ctx.fillStyle = `rgba(230, 255, 140, ${glow})`;
ctx.fillRect(Math.round(x), Math.round(y), 1, 1);
}
},
// Le finestre che si accendono di sera, una dopo l'altra, appena si entra nella scena.
windows(ctx, e, time, game) {
const since = ((game.time ?? 0) - (game.enteredAt ?? 0)) * 1000;
e.rects.forEach(([x, y, w, h], i) => {
if (since < 400 + hash(i * 7 + 3) * (e.over ?? 3500)) return;
ctx.fillStyle = 'rgba(255, 196, 100, 0.72)';
ctx.fillRect(x, y, w, h);
ctx.fillStyle = 'rgba(255, 236, 170, 0.5)';
ctx.fillRect(x + 1, y + 1, Math.max(1, w - 2), Math.max(1, Math.floor(h / 3)));
});
},
// Rondini: ogni tanto un gruppetto attraversa il cielo, da destra a sinistra.
birds(ctx, e, time) {
const [ax, ay, aw, ah] = e.area, every = e.every ?? 9000, t = (time % every) / every, run = Math.floor(time / every);
if (t > 0.45) return;
const k = t / 0.45;
for (let i = 0; i < (e.count ?? 3); i++) {
const x = Math.round(ax + aw - k * (aw + 40) + i * 9 + hash(run * 5 + i) * 10);
const y = Math.round(ay + hash(run * 3 + i) * ah + Math.sin(k * 12 + i) * 3);
const up = Math.floor(time / 120 + i) % 2;
ctx.fillStyle = '#1e1a24';
ctx.fillRect(x, y, 2, 1);
ctx.fillRect(x - 2, y - up, 2, 1);
ctx.fillRect(x + 2, y - up, 2, 1);
}
},
// Alberi al vento (i cipressi): un pezzo di sfondo ridisegnato inclinato appena, più in cima che alla base.
sway(ctx, e, time, game) {
const bg = backgrounds.get(game.scene.background);
if (!bg) return;
const k = bg.width / WIDTH;
for (const [i, [x, y, w, h]] of e.rects.entries()) {
const a = Math.sin(time / (e.speed ?? 900) + i * 1.3) * (e.amount ?? 1.6);
for (let r = 0; r < h; r += 2) {
const dx = Math.round(a * (1 - r / h));
if (dx) ctx.drawImage(bg, x * k, (y + r) * k, w * k, 2 * k, x + dx, y + r, w, 2);
}
}
},
// Lampioni: un alone caldo che si accende (di solito solo di sera, con `phase`).
lamps(ctx, e, time) {
ctx.save();
@@ -305,8 +359,10 @@ export const EFFECTS = {
ctx.restore();
},
};
function drawEffects(ctx, game, time) {
for (const e of game.scene.effects ?? []) if (onNow(e, game)) EFFECTS[e.type]?.(ctx, e, time);
const BACKGROUND_EFFECTS = new Set(['sway']);
function drawEffects(ctx, game, time, background = false) {
for (const e of game.scene.effects ?? [])
if (BACKGROUND_EFFECTS.has(e.type) === background && onNow(e, game)) EFFECTS[e.type]?.(ctx, e, time, game);
}
// Le scene buie (`shade`: il velo scuro sopra tutto, Andrea compreso) e le fiammelle (`lights`: candele accese).