Ombre delle fronde al Bosco ai Frati, bandiere del campeggio al vento, fascio di luce nella Pieve; nel viale semi e foglioline verdi invece delle foglie d'autunno

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-25 00:38:08 +02:00
co-authored by Claude Opus 5.5
parent d6de63d344
commit 55bb814cc0
2 changed files with 47 additions and 2 deletions
+43 -1
View File
@@ -411,6 +411,48 @@ export const EFFECTS = {
lc.globalCompositeOperation = 'source-over';
ctx.drawImage(layer, 0, 0);
},
// Le ombre delle fronde sul terreno: macchie scure e chiare che ondeggiano piano col vento, come sotto un albero.
leafshadow(ctx, e, time) {
const [ax, ay, aw, ah] = e.area, t = time / 1000;
for (let i = 0; i < (e.count ?? 14); i++) {
const bx = ax + hash(i * 7) * aw, by = ay + hash(i * 13) * ah, r = 3 + hash(i * 3) * (e.size ?? 6);
const x = bx + Math.sin(t * 0.9 + i) * 2 + Math.sin(t * 0.37 + i * 2) * 2, y = by + Math.sin(t * 0.7 + i * 3) * 1;
ctx.fillStyle = i % 3 ? 'rgba(30, 20, 45, 0.24)' : 'rgba(255, 235, 170, 0.18)';
for (let dy = -r; dy <= r; dy += 1) {
const half = Math.round(Math.sqrt(r * r - dy * dy) * 1.6);
ctx.fillRect(Math.round(x - half), Math.round(y + dy / 2), half * 2, 1);
}
}
},
// Una bandiera dipinta nello sfondo che sventola: a strisce verticali che salgono e scendono, ferme dalla parte
// dell'asta (`pole`: 'left' o 'right') e sempre più mosse verso il lembo libero.
cloth(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()) {
for (let c = 0; c < w; c++) {
const free = e.pole === 'left' ? c / w : 1 - c / w;
const dy = Math.sin(time / 260 - free * 5 + i) * free * (e.amount ?? 2.2);
ctx.drawImage(bg, (x + c) * k, y * k, k, h * k, x + c, y + dy, 1, h);
}
}
},
// Un fascio di luce da una finestra: un cono chiaro che sfuma fino al pavimento, appena pulsante.
beam(ctx, e, time) {
const [[x1, y1], [x2, y2], [x3, y3], [x4, y4]] = e.poly, pulse = 0.85 + 0.15 * Math.sin(time / 2300);
const g = ctx.createLinearGradient(x1, y1, (x3 + x4) / 2, (y3 + y4) / 2);
g.addColorStop(0, `rgba(255, 236, 180, ${0.42 * pulse})`);
g.addColorStop(1, 'rgba(255, 236, 180, 0)');
ctx.save();
ctx.globalCompositeOperation = 'lighter';
ctx.fillStyle = g;
ctx.beginPath();
ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.lineTo(x3, y3); ctx.lineTo(x4, y4);
ctx.closePath();
ctx.fill();
ctx.restore();
},
// Lampioni: un alone caldo che si accende (di solito solo di sera, con `phase`).
lamps(ctx, e, time) {
ctx.save();
@@ -426,7 +468,7 @@ export const EFFECTS = {
ctx.restore();
},
};
const BACKGROUND_EFFECTS = new Set(['sway', 'clouds']);
const BACKGROUND_EFFECTS = new Set(['sway', 'clouds', 'cloth', 'leafshadow']);
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);