Effetti ambientali nelle altre 35 scene (lavoro di Codex): piccioni, merli, gatto, farfalle, libellula, falena, acqua che scorre, fontanella, tende, ombre delle fronde e delle nuvole
Controllato: test, partita completa e il giro di schermate degli effetti (npm run browser:effects). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApqNBvNbNCwXqR2RLo8QJh
This commit is contained in:
+39
-13
@@ -1,6 +1,7 @@
|
||||
// Disegno della scena su un canvas 640×360, ingrandito dal CSS senza sfocatura.
|
||||
// Le forme provvisorie (`art`) verranno sostituite da sfondi e sprite in pixel art.
|
||||
import { check } from './game.js';
|
||||
import { ambientEffects, ambientHash } from './ambient.js';
|
||||
export const WIDTH = 640, HEIGHT = 360;
|
||||
|
||||
function drawShape(ctx, s) {
|
||||
@@ -258,6 +259,7 @@ const hash = n => (((n | 0) * 2654435761) >>> 0) / 4294967296;
|
||||
|
||||
// Gli effetti delle scene (`effects`), ognuno col suo `type`: piccole animazioni disegnate sopra lo sfondo.
|
||||
export const EFFECTS = {
|
||||
...ambientEffects,
|
||||
// Lucine dei festoni: ognuna si accende e si spegne per conto suo, coi suoi colori.
|
||||
bulbs(ctx, e, time) {
|
||||
const colors = e.colors ?? ['#ff5a5a', '#ffd25a', '#5ad2ff', '#7dff6a'];
|
||||
@@ -345,6 +347,8 @@ export const EFFECTS = {
|
||||
// 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;
|
||||
ctx.globalAlpha = e.opacity ?? 1;
|
||||
ctx.globalCompositeOperation = e.blend ?? 'source-over';
|
||||
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)';
|
||||
@@ -355,9 +359,10 @@ export const EFFECTS = {
|
||||
},
|
||||
// Rondini: ogni pochi secondi un gruppetto attraversa il cielo in diagonale, a «V» con le ali che battono.
|
||||
birds(ctx, e, time) {
|
||||
const [ax, ay, aw, ah] = e.area, every = e.every ?? 6000, run = Math.floor(time / every), t = (time % every) / every;
|
||||
if (t > 0.6) return;
|
||||
const k = t / 0.6, rtl = hash(run * 13) < 0.5;
|
||||
const [ax, ay, aw, ah] = e.area, every = e.every ?? 6000, duration = e.duration ?? every * 0.6;
|
||||
const elapsed = (time + (e.offset ?? 0)) % every, run = Math.floor((time + (e.offset ?? 0)) / every);
|
||||
if (elapsed >= duration) return;
|
||||
const k = elapsed / duration, rtl = hash(run * 13) < 0.5;
|
||||
for (let i = 0; i < (e.count ?? 4); i++) {
|
||||
const lag = i * 0.05, kk = k - lag;
|
||||
if (kk < 0) continue;
|
||||
@@ -411,16 +416,23 @@ 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.
|
||||
// Ombre di foglie in piccoli gruppi irregolari. Il seme mescolato evita file di
|
||||
// ovali equidistanti (che sembravano sassi); niente rettangoli di sfondo deformati.
|
||||
leafshadow(ctx, e, time) {
|
||||
const [ax, ay, aw, ah] = e.area, t = time / 1000;
|
||||
ctx.globalAlpha = e.opacity ?? 1;
|
||||
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);
|
||||
const bx = ax + ambientHash(i * 7 + ax) * aw, by = ay + ambientHash(i * 13 + ay) * ah;
|
||||
const x = bx + Math.sin(t * 0.9 + i) * 2 + Math.sin(t * 0.37 + i * 2), y = by + Math.sin(t * 0.7 + i * 3);
|
||||
for (let leaf = 0; leaf < 4; leaf++) {
|
||||
const n = i * 31 + leaf * 7, dx = (ambientHash(n + 3) - 0.5) * 22, dy = (ambientHash(n + 9) - 0.5) * 9;
|
||||
const w = 3 + ambientHash(n + 21) * (e.size ?? 7), h = 1.5 + ambientHash(n + 4) * 2.5;
|
||||
const px = x + dx, py = y + dy;
|
||||
ctx.fillStyle = leaf === 3 ? 'rgba(255,235,170,0.10)' : 'rgba(30,29,40,0.15)';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(px - w, py); ctx.lineTo(px - w * 0.2, py - h);
|
||||
ctx.lineTo(px + w * 0.6, py - h * 0.5); ctx.lineTo(px + w, py + h * 0.3);
|
||||
ctx.lineTo(px, py + h); ctx.closePath(); ctx.fill();
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -468,10 +480,24 @@ export const EFFECTS = {
|
||||
ctx.restore();
|
||||
},
|
||||
};
|
||||
const BACKGROUND_EFFECTS = new Set(['sway', 'clouds', 'cloth', 'leafshadow']);
|
||||
const BACKGROUND_EFFECTS = new Set(['sway', 'clouds', 'cloth', 'leafshadow', ...Object.keys(ambientEffects)]);
|
||||
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);
|
||||
for (const e of game.scene.effects ?? []) {
|
||||
if (BACKGROUND_EFFECTS.has(e.type) !== background || !onNow(e, game)) continue;
|
||||
ctx.save();
|
||||
// Acqua solo nell'acqua, tende e sagome solo dietro il vetro; il ritaglio può
|
||||
// seguire una riva obliqua senza toccare il terreno o gli oggetti degli enigmi.
|
||||
if (e.clip) {
|
||||
ctx.beginPath();
|
||||
e.clip.forEach(([x, y], i) => i ? ctx.lineTo(x, y) : ctx.moveTo(x, y));
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
}
|
||||
// Gli eventi nuovi seguono il tempo giocato: pausa e scheda nascosta non li fanno saltare.
|
||||
const at = ambientEffects[e.type] ? (game.time ?? time / 1000) * 1000 : time;
|
||||
EFFECTS[e.type]?.(ctx, e, at, game);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// Le scene buie (`shade`: il velo scuro sopra tutto, Andrea compreso) e le fiammelle (`lights`: candele accese).
|
||||
|
||||
Reference in New Issue
Block a user