Il risveglio leggero per davvero: palpebre disegnate su una tela piccola e allargate con l'interpolazione (bordo morbido senza filtri né passate), tutto sulla tela principale, una versione sola; l'arretrato grafico dopo il risveglio passa da 24 secondi a un decimo. «Fanfara» si legge senza accenti (Andrea)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECd9RWGMKV7n3K1mdy8T
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1984,8 +1984,8 @@
|
|||||||
"sec": 2.79
|
"sec": 2.79
|
||||||
},
|
},
|
||||||
"andrea|La fanfara degli alpini. Quando sfilavano per il paese suonando, s'affacciava tutto San Piero.": {
|
"andrea|La fanfara degli alpini. Quando sfilavano per il paese suonando, s'affacciava tutto San Piero.": {
|
||||||
"file": "andrea-9904de279c.mp3",
|
"file": "andrea-a372a71849.mp3",
|
||||||
"sec": 6.69
|
"sec": 6.3
|
||||||
},
|
},
|
||||||
"fanfara|Un'altra? Questa la sanno anche i sassi!": {
|
"fanfara|Un'altra? Questa la sanno anche i sassi!": {
|
||||||
"file": "fanfara-d1179f98fa.mp3",
|
"file": "fanfara-d1179f98fa.mp3",
|
||||||
@@ -3676,8 +3676,8 @@
|
|||||||
"sec": 5.94
|
"sec": 5.94
|
||||||
},
|
},
|
||||||
"andrea|La Smarronata del 1993! Gli alpini, sotto il ponte: le bruciate, la polenta, il vino, la fanfara...": {
|
"andrea|La Smarronata del 1993! Gli alpini, sotto il ponte: le bruciate, la polenta, il vino, la fanfara...": {
|
||||||
"file": "andrea-2c7369689a.mp3",
|
"file": "andrea-4266372068.mp3",
|
||||||
"sec": 10.99
|
"sec": 10.39
|
||||||
},
|
},
|
||||||
"andrea|La macchina del tempo è sempre lì che aspetta.": {
|
"andrea|La macchina del tempo è sempre lì che aspetta.": {
|
||||||
"file": "andrea-81cad76b69.mp3",
|
"file": "andrea-81cad76b69.mp3",
|
||||||
|
|||||||
+1
-19
@@ -182,7 +182,6 @@ export function render(ctx, game, { hover, spots, debug, time = performance.now(
|
|||||||
: drawAndrea(ctx, s.x, s.y, game.scaleAt(s.y), s.facing, game.walking ? game.stride / 6 : null);
|
: drawAndrea(ctx, s.x, s.y, game.scaleAt(s.y), s.facing, game.walking ? game.stride / 6 : null);
|
||||||
// Durante il risveglio si guarda dagli occhi di Andrea: lui non si vede.
|
// Durante il risveglio si guarda dagli occhi di Andrea: lui non si vede.
|
||||||
const pov = game.closeup && game.story.closeups?.[game.closeup.id]?.type === 'risveglio';
|
const pov = game.closeup && game.story.closeups?.[game.closeup.id]?.type === 'risveglio';
|
||||||
if (!pov) risveglioLayer(ctx, false);
|
|
||||||
const queue = pov ? [] : [{ y: s.y, draw: drawAndreaNow }];
|
const queue = pov ? [] : [{ y: s.y, draw: drawAndreaNow }];
|
||||||
for (const p of game.people()) {
|
for (const p of game.people()) {
|
||||||
const sheet = sprites.get(p.who);
|
const sheet = sprites.get(p.who);
|
||||||
@@ -912,18 +911,6 @@ function drawDebug(ctx, game, { mouse, points = [], help }) {
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// La tela del risveglio (risveglio.js): una tela a parte sopra la scena, che c'è solo finché il risveglio dura.
|
|
||||||
function risveglioLayer(ctx, on = true) {
|
|
||||||
if (typeof document === 'undefined' || !ctx.canvas?.after) return null;
|
|
||||||
let layer = document.getElementById('risveglio');
|
|
||||||
if (!on) { layer?.remove(); return null; }
|
|
||||||
if (!layer) {
|
|
||||||
layer = Object.assign(document.createElement('canvas'), { id: 'risveglio', width: WIDTH, height: HEIGHT });
|
|
||||||
ctx.canvas.after(layer);
|
|
||||||
}
|
|
||||||
return layer.getContext('2d');
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Il primo piano di un libro (story.closeups, game.closeup): la scena si oscura, le mani alzano il libro dal basso.
|
// --- Il primo piano di un libro (story.closeups, game.closeup): la scena si oscura, le mani alzano il libro dal basso.
|
||||||
// Le immagini hanno etichetta e pagine vuote: i testi si scrivono qui, nitidi. Una pagina sola gira, a fisarmonica
|
// Le immagini hanno etichetta e pagine vuote: i testi si scrivono qui, nitidi. Una pagina sola gira, a fisarmonica
|
||||||
// sulla piega; sotto c'è già l'ultima pagina.
|
// sulla piega; sotto c'è già l'ultima pagina.
|
||||||
@@ -962,12 +949,7 @@ function drawCloseup(ctx, game, time) {
|
|||||||
// L'incantesimo va sopra la scena (con Andrea dentro), già disegnata.
|
// L'incantesimo va sopra la scena (con Andrea dentro), già disegnata.
|
||||||
if (def.type === 'incanto') { drawIncanto(ctx, c); return; }
|
if (def.type === 'incanto') { drawIncanto(ctx, c); return; }
|
||||||
// Il risveglio, visto da dentro gli occhi: anche lui sopra la scena già disegnata.
|
// Il risveglio, visto da dentro gli occhi: anche lui sopra la scena già disegnata.
|
||||||
// Sul telefono (schermo touch) la versione leggera, senza filtri; sul computer quella con la sfocatura vera.
|
if (def.type === 'risveglio') { drawRisveglio(ctx, c, backgrounds.get(game.backdrop())); return; }
|
||||||
if (def.type === 'risveglio') {
|
|
||||||
const layer = risveglioLayer(ctx), light = matchMedia('(pointer: coarse)').matches;
|
|
||||||
if (layer) drawRisveglio(layer, c, backgrounds.get(game.backdrop()), { light });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Il telefonino del Barone: sale come il libro, e il sito nello schermo si allarga a tutta la scena.
|
// Il telefonino del Barone: sale come il libro, e il sito nello schermo si allarga a tutta la scena.
|
||||||
if (def.type === 'telefono') {
|
if (def.type === 'telefono') {
|
||||||
const pic = src => { if (!backgrounds.has(src)) loadBackground(src); return backgrounds.get(src); };
|
const pic = src => { if (!backgrounds.has(src)) loadBackground(src); return backgrounds.get(src); };
|
||||||
|
|||||||
+29
-34
@@ -30,12 +30,12 @@ export function updateRisveglio(c, def, dt) {
|
|||||||
return { due, end: c.t >= END && c.fired >= lines.length };
|
return { due, end: c.t >= END && c.fired >= lines.length };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Il risveglio si disegna su una tela sua, appoggiata sopra la scena solo finché dura (render.js, `#risveglio`).
|
// Niente filtri e niente passate a tutto schermo (ctx.filter, il filter CSS e sedici palpebre trasparenti a ogni
|
||||||
// Due versioni (Andrea, 31 ottobre): sul computer quella bella, con la sfocatura vera (ctx.filter); sul telefono
|
// fotogramma lasciavano alla grafica un arretrato che si pagava dopo, a risveglio finito: il gioco si inceppava).
|
||||||
// (`light`) una leggera senza filtri, perché senza una scheda video la sfocatura a ogni fotogramma pesa: la nebbia è
|
// La nebbia è lo sfondo rimpicciolito e riallargato morbido; le palpebre si disegnano nette su una tela piccola e
|
||||||
// lo sfondo rimpicciolito e riallargato morbido, il bordo delle palpebre tante passate trasparenti.
|
// si allargano di cinque volte con l'interpolazione, che sfuma il bordo da sé. Una versione sola, computer e telefono.
|
||||||
const SMALL = [80, 45];
|
const SMALL = [80, 45], LIDS = [128, 72];
|
||||||
let small = null, smallOf = null;
|
let small = null, smallOf = null, lids = null;
|
||||||
function blurred(bg) {
|
function blurred(bg) {
|
||||||
if (smallOf !== bg) {
|
if (smallOf !== bg) {
|
||||||
small ??= Object.assign(document.createElement('canvas'), { width: SMALL[0], height: SMALL[1] });
|
small ??= Object.assign(document.createElement('canvas'), { width: SMALL[0], height: SMALL[1] });
|
||||||
@@ -47,23 +47,19 @@ function blurred(bg) {
|
|||||||
return small;
|
return small;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drawRisveglio(ctx, c, bg, { light = false } = {}) {
|
export function drawRisveglio(ctx, c, bg) {
|
||||||
const t = c.t, h = haze(t), open = openness(t), W = 640, H = 360;
|
const t = c.t, h = haze(t), open = openness(t), W = 640, H = 360;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.clearRect(0, 0, W, H);
|
// La scena annebbiata (lo sfondo sfocato sopra) e la vista doppia (una seconda copia, spostata, che ondeggia).
|
||||||
// La scena annebbiata: lo sfondo sfocato sopra a quello nitido, più una seconda copia spostata che ondeggia (la
|
if (h > 0.01 && bg && typeof document !== 'undefined') {
|
||||||
// vista doppia); tutto si dirada col passare del tempo.
|
const fog = blurred(bg), sway = 14 * h, dx = Math.sin(c.age * 1.3) * sway, dy = Math.sin(c.age * 0.9 + 1) * sway * 0.35;
|
||||||
if (h > 0.01 && bg) {
|
|
||||||
const sway = 14 * h, dx = Math.sin(c.age * 1.3) * sway, dy = Math.sin(c.age * 0.9 + 1) * sway * 0.35;
|
|
||||||
const fog = light ? blurred(bg) : bg;
|
|
||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
if (!light) ctx.filter = `blur(${(5 * h).toFixed(2)}px)`;
|
ctx.globalAlpha = 0.85 * h;
|
||||||
ctx.globalAlpha = h;
|
|
||||||
ctx.drawImage(fog, 0, 0, W, H);
|
ctx.drawImage(fog, 0, 0, W, H);
|
||||||
ctx.globalAlpha = 0.45 * h;
|
ctx.globalAlpha = 0.4 * h;
|
||||||
ctx.drawImage(fog, dx, dy, W, H);
|
ctx.drawImage(fog, dx, dy, W, H);
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.filter = 'none';
|
ctx.imageSmoothingEnabled = false;
|
||||||
// La luce del mattino che abbaglia.
|
// La luce del mattino che abbaglia.
|
||||||
ctx.fillStyle = `rgba(255,244,214,${0.22 * h})`;
|
ctx.fillStyle = `rgba(255,244,214,${0.22 * h})`;
|
||||||
ctx.fillRect(0, 0, W, H);
|
ctx.fillRect(0, 0, W, H);
|
||||||
@@ -74,23 +70,22 @@ export function drawRisveglio(ctx, c, bg, { light = false } = {}) {
|
|||||||
v.addColorStop(1, `rgba(10,2,4,${0.85 * (1 - open * 0.75) * Math.max(h, 0.3)})`);
|
v.addColorStop(1, `rgba(10,2,4,${0.85 * (1 - open * 0.75) * Math.max(h, 0.3)})`);
|
||||||
ctx.fillStyle = v;
|
ctx.fillStyle = v;
|
||||||
ctx.fillRect(0, 0, W, H);
|
ctx.fillRect(0, 0, W, H);
|
||||||
// Le palpebre: sopra e sotto, col bordo curvo e sfumato; chiuse, si vede il rosso scuro della luce che passa.
|
// Le palpebre: sopra e sotto, col bordo curvo; chiuse, si vede il rosso scuro della luce che passa.
|
||||||
if (open < 0.999) {
|
if (open < 0.999 && typeof document !== 'undefined') {
|
||||||
const mid = H / 2, bulge = 70 + 60 * open, lid = (gap, sign) => {
|
lids ??= Object.assign(document.createElement('canvas'), { width: LIDS[0], height: LIDS[1] });
|
||||||
const edge = mid + sign * gap, side = edge - sign * bulge, far = sign < 0 ? -20 : H + 20;
|
const l = lids.getContext('2d'), k = LIDS[0] / W, mid = H / 2 * k, gap = 200 * open * k, bulge = (70 + 60 * open) * k;
|
||||||
ctx.beginPath();
|
l.clearRect(0, 0, ...LIDS);
|
||||||
ctx.moveTo(-20, far); ctx.lineTo(W + 20, far); ctx.lineTo(W + 20, side);
|
l.fillStyle = 'rgb(22,4,6)';
|
||||||
ctx.quadraticCurveTo(W / 2, edge * 2 - side, -20, side);
|
for (const sign of [-1, 1]) {
|
||||||
ctx.closePath(); ctx.fill();
|
const edge = mid + sign * gap, side = edge - sign * bulge, far = sign < 0 ? -4 : LIDS[1] + 4;
|
||||||
};
|
l.beginPath();
|
||||||
if (light) {
|
l.moveTo(-4, far); l.lineTo(LIDS[0] + 4, far); l.lineTo(LIDS[0] + 4, side);
|
||||||
// Il bordo morbido: passate trasparenti che sporgono verso l'occhio aperto, un pixel di più l'una.
|
l.quadraticCurveTo(LIDS[0] / 2, edge * 2 - side, -4, side);
|
||||||
ctx.fillStyle = 'rgba(22,4,6,0.11)';
|
l.closePath(); l.fill();
|
||||||
for (let i = 16; i > 0; i--) { lid(200 * open - i, -1); lid(200 * open - i, 1); }
|
}
|
||||||
} else ctx.filter = 'blur(6px)';
|
ctx.imageSmoothingEnabled = true;
|
||||||
ctx.fillStyle = 'rgb(22,4,6)';
|
ctx.drawImage(lids, 0, 0, W, H);
|
||||||
lid(200 * open, -1); lid(200 * open, 1);
|
ctx.imageSmoothingEnabled = false;
|
||||||
ctx.filter = 'none';
|
|
||||||
}
|
}
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,6 @@ body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-h
|
|||||||
text-shadow: 2px 0 #000, -2px 0 #000, 0 2px #000, 0 -2px #000, 2px 2px #000, -2px -2px #000; }
|
text-shadow: 2px 0 #000, -2px 0 #000, 0 2px #000, 0 -2px #000, 2px 2px #000, -2px -2px #000; }
|
||||||
#speech:empty { display: none; }
|
#speech:empty { display: none; }
|
||||||
#speech { z-index: 3; }
|
#speech { z-index: 3; }
|
||||||
/* La tela del risveglio, sopra la scena solo finché dura (render.js). */
|
|
||||||
#risveglio { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1; }
|
|
||||||
/* Il Muro del Sanpierino sul telefonino del Barone: la pagina vera (assets/muro) larga 1067 pixel come la home del
|
/* Il Muro del Sanpierino sul telefonino del Barone: la pagina vera (assets/muro) larga 1067 pixel come la home del
|
||||||
sito, rimpicciolita sulla scena da main.js (--muro-scale). */
|
sito, rimpicciolita sulla scena da main.js (--muro-scale). */
|
||||||
#muro { position: absolute; left: 0; top: 0; width: 1067px; height: 600px; border: 0; z-index: 1; background: #f7e1c0;
|
#muro { position: absolute; left: 0; top: 0; width: 1067px; height: 600px; border: 0; z-index: 1; background: #f7e1c0;
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ try {
|
|||||||
}
|
}
|
||||||
await page.waitForFunction(() => !game.closeup && game.state.flags.intro, null, { timeout: 15000 });
|
await page.waitForFunction(() => !game.closeup && game.state.flags.intro, null, { timeout: 15000 });
|
||||||
await page.screenshot({ path: `${out}/${tag}-5-vecchietti.png` });
|
await page.screenshot({ path: `${out}/${tag}-5-vecchietti.png` });
|
||||||
assert.equal(await page.locator('#risveglio').count(), 0, 'finito il risveglio, la sua tela se ne va');
|
|
||||||
}
|
}
|
||||||
assert.deepEqual(errors, []);
|
assert.deepEqual(errors, []);
|
||||||
console.log(`ok: schermate in ${out}`);
|
console.log(`ok: schermate in ${out}`);
|
||||||
|
|||||||
@@ -21,6 +21,5 @@
|
|||||||
"Libici": "Lìbici",
|
"Libici": "Lìbici",
|
||||||
"mancano": "màncano",
|
"mancano": "màncano",
|
||||||
"console": "consòl",
|
"console": "consòl",
|
||||||
"Mahhh... è l'INGORGOOOO!!!": "Maaaa, è l'Ingorgo! L'Ingorgooo!",
|
"Mahhh... è l'INGORGOOOO!!!": "Maaaa, è l'Ingorgo! L'Ingorgooo!"
|
||||||
"fanfara": "fanfàra"
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user