Files
leggenda-del-regolo/src/engine/telefono.js
T
manzoloandClaude Opus 5.5 c5953f04c0 Si comincia svegliandosi sulla panchina dei vecchietti dopo una notte brava: gli occhi che si aprono a fatica, la piazza sfocata e doppia; e dal Barone il telefonino col sito di San Piero, che si apre a tutto schermo, e il Muro del Sanpierino vero coi messaggi del 2007, «Aggiorna» compreso (Andrea)
- Il risveglio (src/engine/risveglio.js): primo piano sopra la scena, con le battute mentre le palpebre si aprono e
  i vecchietti che danno la notizia del palio; «Salta» lo salta.
- Il telefonino del Barone (src/engine/telefono.js): sale dal basso come il libro, la home di sanpiero50037.it dalla
  Wayback Machine nello schermo, il mattone del muro apre il muro.
- Il muro (assets/muro, tools/muro.py): la pagina vera rifatta statica dal muro riesumato, coi 24 messaggi del dump
  (senza email né IP) e quelli del gioco, che arrivano con la storia; «Scrivi» spento.
- Il gioco sgancia i service worker di altre app rimasti sul suo indirizzo (la Bussola su localhost:8080).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiECd9RWGMKV7n3K1mdy8T
2026-10-31 09:52:34 +01:00

82 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Il telefonino del Barone (story.closeups, type 'telefono'; Andrea, 31 ottobre: «prenda il cellulare, nello stile
// di come si sfoglia la storia infinita, e quando si clicca viene a tutto schermo il sito di San Piero»).
// Come il libro: la scena si scurisce e le mani alzano il telefonino dal basso; nello schermo c'è il sito
// (`site`, un'immagine 16:9 della home di sanpiero50037.it). Un tocco: lo schermo si allarga a tutta la scena.
// Sul sito si tocca il link (`link`, in pixel dell'immagine del sito) e si apre il muro: una pagina vera
// (assets/muro, la mette main.js sopra la scena), che qui sotto lascia solo il fondo. A muro aperto partono le
// battute `comment`, e chiudendo `closing`. Unità: i pixel della scena 640×360.
export const RISE = 0.45, ZOOM = 0.55;
const W = 640, H = 360;
const clamp01 = k => Math.max(0, Math.min(1, k));
const ease = k => (1 - Math.cos(Math.PI * clamp01(k))) / 2;
export function newTelefono(id) { return { id, step: 'phone', t: 0, age: 0, aim: null }; }
// Il tempo: lo zoom finisce sul sito; a muro aperto, dopo un attimo, le battute (una alla volta, come il libro).
export function updateTelefono(c, dt) {
c.t += dt;
if (c.step === 'zoom' && c.t >= ZOOM) Object.assign(c, { step: 'site', t: 0 });
}
// Dove sta il link del muro sulla scena, a sito aperto: il riquadro `link` riportato da immagine a scena.
export function linkBox(def, site) {
const [x, y, w, h] = def.link, kx = W / site[0], ky = H / site[1];
return [x * kx, y * ky, w * kx, h * ky];
}
const inside = ([x, y, w, h], [px, py]) => px >= x && px <= x + w && py >= y && py <= y + h;
// Un tocco: sul telefonino apre il sito; sul sito, solo sul link, apre il muro. Torna true se è successo qualcosa.
export function tapTelefono(c, def) {
if (c.step === 'phone') { if (c.age < RISE) return false; Object.assign(c, { step: 'zoom', t: 0 }); return true; }
if (c.step === 'site' && c.aim && inside(linkBox(def, def.siteSize), c.aim)) { Object.assign(c, { step: 'wall', t: 0 }); return true; }
return false;
}
// Il riquadro dello schermo sulla scena, col telefonino che sale dal basso.
function phoneRect(def, img, age) {
const rise = 1 - clamp01(age / RISE), k = def.phoneScale ?? 1;
const w = img.width * k, h = img.height * k, x = Math.round((W - w) / 2), y = Math.round(H - h + 140 * rise * rise);
return { x, y, w, h, k };
}
export function drawTelefono(ctx, c, def, { phone, site }, time) {
ctx.save();
ctx.fillStyle = `rgba(8, 5, 12, ${Math.min(0.78, c.age / 0.35 * 0.78)})`;
ctx.fillRect(0, 0, W, H);
if (!phone || !site) { ctx.restore(); return; }
const p = phoneRect(def, phone, c.age), [sx, sy, sw, sh] = def.screen;
const screen = [p.x + sx * p.k, p.y + sy * p.k, sw * p.k, sh * p.k];
const z = c.step === 'phone' ? 0 : c.step === 'zoom' ? ease(c.t / ZOOM) : 1;
// Il telefonino, finché lo schermo non ha preso tutta la scena; poi sfuma.
if (z < 1) {
ctx.globalAlpha = 1 - z;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(phone, p.x, p.y, p.w, p.h);
ctx.globalAlpha = 1;
}
// Il sito: dentro lo schermo, poi sempre più grande fino a tutta la scena. Lo schermo ha le sue proporzioni:
// il sito ci sta intero, centrato, sul fondo del sito.
const r = screen.map((v, i) => v + ([0, 0, W, H][i] - v) * z);
const fit = Math.min(r[2] / site.width, r[3] / site.height), dw = site.width * fit, dh = site.height * fit;
ctx.fillStyle = def.paper ?? '#f7e0c0';
ctx.fillRect(r[0], r[1], r[2], r[3]);
ctx.imageSmoothingEnabled = true;
ctx.drawImage(site, r[0] + (r[2] - dw) / 2, r[1] + (r[3] - dh) / 2, dw, dh);
// Sotto il muro (la pagina vera) resta il fondo del sito.
if (c.step === 'wall') { ctx.fillStyle = def.paper ?? '#f7e0c0'; ctx.fillRect(0, 0, W, H); }
// Il link da toccare: un riquadro che pulsa intorno al mattone del muro.
if (c.step === 'site') {
const [x, y, w, h] = linkBox(def, def.siteSize), pulse = 0.5 + 0.5 * Math.sin(time / 260);
ctx.strokeStyle = `rgba(255, 210, 90, ${0.5 + 0.5 * pulse})`;
ctx.lineWidth = 2 + pulse;
ctx.strokeRect(Math.round(x) - 2, Math.round(y) - 2, Math.round(w) + 4, Math.round(h) + 4);
}
ctx.imageSmoothingEnabled = false;
ctx.restore();
}
// Il suggerimento in alto, finché c'è qualcosa da toccare.
export const telefonoHint = c => c.step === 'phone' && c.age > RISE ? 'Clic o tocco: guarda il sito'
: c.step === 'site' ? 'Clic o tocco sul Muro del Sanpierino' : null;