Compare commits

..
15 Commits
Author SHA1 Message Date
manzolo 447c150974 Versione 0.1.5 2026-10-25 18:26:34 +01:00
manzoloandClaude Opus 5.5 95e7777dbc La vecchietta: «Secondo me va in Fortezza solo per farsi le canne»
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 18:22:47 +01:00
manzoloandClaude Opus 5.5 26697cd9c2 Il vecchietto nuovo, dalla foto di Andrea: giacca blu, camicia azzurra e cravatta, stesse otto pose (Codex)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 18:21:42 +01:00
manzoloandClaude Opus 5.5 de303c5825 Suoni d'ambiente sintetizzati (vento, acqua, uccelli, grilli, campane) col loro volume in Pausa; la fessura in primo piano: si cerca col fascio della torcia il quadro e gli occhi (lavoro di Codex). La foto del palio leggibile da nginx, e un test che lo controlla per tutti i file serviti
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 18:17:01 +01:00
manzoloandClaude Opus 5.5 1fabb6e543 Video della partita completa (pomeriggio → sera) con voci e musiche rimontate dal registro audio del copione; inventario: nomi lunghi su due righe dentro il pulsante
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 17:36:06 +01:00
manzoloandClaude Opus 5.5 32a3d8a7c5 Risposte su misura ai tentativi sbagliati (squama, torcia, ghiacciolo, libro a vecchietti, leone, San Pietro, Tosca, torre, amici); il pettegolezzo che cambia con le scoperte (vecchietti, Arcu, Tosca); l'Arcu senza memoria; le altre idee di Codex in IDEE.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 14:56:02 +01:00
manzolo 4bda52d9ac Versione 0.1.4 2026-10-25 14:28:24 +01:00
manzoloandClaude Opus 5.5 ec008b5ea1 Salutando l'Arcu: «Ci si becca da i' Trappola, ai' Barrettino!»
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 14:24:53 +01:00
manzoloandClaude Opus 5.5 73f3418bab Il cancello dell'oratorio: «se chiudo gli occhi vedo ancora l'A. Bettini», letto «Abbettini»
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 14:14:37 +01:00
manzoloandClaude Opus 5.5 ce4e47a2ed Chiudendo il libro: il «Nulla» viaggia troppo velocemente nell'animo umano
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 14:13:00 +01:00
manzoloandClaude Opus 5.5 f6fd2fdfdc Il libro dell'oratorio in primo piano: copertina, frontespizio, una pagina che gira col fruscio e «FINE» (immagini di Codex, testi disegnati dal codice); l'Arcu a scelte (Borgo e la Casa dell'Armando); il cancello dell'oratorio e l'A. Bettini
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 14:06:12 +01:00
manzoloandClaude Opus 5.5 9d9dc81bcb Idee: le mutande del Barone e la racchetta del Tozzino
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 13:21:59 +01:00
manzolo bc19d6be53 Versione 0.1.3 2026-10-25 13:15:45 +01:00
manzoloandClaude Opus 5.5 c54023083a Il Tozzino con una voce da ragazzo (Xavier Hayasaka, più alta)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 12:46:01 +01:00
manzoloandClaude Opus 5.5 af6861350d Mappa: filtrando, i posti trovati si vedono (la mappa non resta lunga e scorsa in fondo); il Barone e le sue due donne e due jack; prove nel browser su una porta libera (con la 8080 giravano sul container)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
2026-10-25 12:40:48 +01:00
123 changed files with 1855 additions and 189 deletions
+1
View File
@@ -12,6 +12,7 @@ COPY assets/fonts /usr/share/nginx/html/assets/fonts
COPY assets/voices /usr/share/nginx/html/assets/voices
COPY assets/credits /usr/share/nginx/html/assets/credits
COPY assets/effects /usr/share/nginx/html/assets/effects
COPY assets/closeups /usr/share/nginx/html/assets/closeups
# Le immagini e i font citati da index.html e style.css prendono ?v=<versione>, come quelli caricati dal codice.
RUN cd /usr/share/nginx/html \
+7
View File
@@ -41,6 +41,12 @@ python3 -m http.server 8000 # poi http://localhost:8000
Clic = azione più sensata (parla, guarda, vai); clic destro o pressione lunga = guarda; un verbo scelto vale per il
clic successivo. Spazio salta la battuta, i numeri scelgono nel dialogo, Esc annulla il verbo.
Il libro dell'oratorio si apre e si sfoglia con clic o tocco; Esc lo chiude. Nella fessura della Fortezza,
la torcia segue mouse o dito (anche trascinando): si cercano i dettagli tenendo brevemente la luce su di essi.
Si possono usare anche le frecce, oppure Invio per cercare il prossimo dettaglio. «Torna fuori» permette di
riprovare; dopo il ritrovamento, «Continua» conclude il capitolo.
Il volume dei suoni ambientali e del fruscio delle pagine si regola in **Pausa → Ambiente ed effetti**.
## In Docker
```zsh
@@ -64,5 +70,6 @@ make down
npm test # logica, storia coerente, soluzione percorsa dall'inizio alla fine
npm install && npx playwright install chromium-headless-shell
npm run browser # partita completa con clic veri in Chromium, salvataggio, telefono
npm run browser -- --media-only # libro, torcia e audio: controllo mirato desktop/telefono
```
+25 -25
View File
@@ -5,65 +5,65 @@
"sit": {
"x": 0,
"y": 0,
"w": 76,
"w": 77,
"h": 96,
"ax": 31.4,
"ax": 32.9,
"ay": 96
},
"doze": {
"x": 78,
"x": 79,
"y": 0,
"w": 76,
"w": 77,
"h": 96,
"ax": 31.5,
"ax": 33.0,
"ay": 96
},
"talk": {
"x": 156,
"x": 158,
"y": 0,
"w": 67,
"w": 68,
"h": 96,
"ax": 25.1,
"ax": 26.3,
"ay": 96
},
"explain": {
"x": 225,
"x": 228,
"y": 0,
"w": 62,
"w": 68,
"h": 96,
"ax": 27.4,
"ax": 29.9,
"ay": 96
},
"point": {
"x": 289,
"x": 298,
"y": 0,
"w": 74,
"w": 78,
"h": 97,
"ax": 31.5,
"ax": 33.3,
"ay": 97
},
"laugh": {
"x": 365,
"x": 378,
"y": 0,
"w": 70,
"w": 71,
"h": 97,
"ax": 23.2,
"ax": 22.8,
"ay": 97
},
"cat": {
"x": 437,
"x": 451,
"y": 0,
"w": 67,
"h": 95,
"ax": 24.1,
"ay": 95
"w": 68,
"h": 96,
"ax": 26.1,
"ay": 96
},
"shrug": {
"x": 506,
"x": 521,
"y": 0,
"w": 81,
"w": 83,
"h": 97,
"ax": 38.9,
"ax": 40.2,
"ay": 97
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+314 -70
View File
@@ -91,6 +91,22 @@
"file": "andrea-af4d717814.mp3",
"sec": 6.44
},
"vecchietto|Dice che Andrea ha trovato un serpente lungo dodici metri.": {
"file": "vecchietto-cc1d61420b.mp3",
"sec": 3.64
},
"vecchietta|Ma quale serpente. Secondo me va in Fortezza solo per farsi le canne: torna sempre con gli occhi rossi!": {
"file": "vecchietta-c11979f5e8.mp3",
"sec": 6.73
},
"andrea|Sono qui, eh. Vi sento.": {
"file": "andrea-5debd43bf6.mp3",
"sec": 2.64
},
"vecchietta|Oh! Andrea! Si parlava d'un altro.": {
"file": "vecchietta-1cbc047e44.mp3",
"sec": 4.9
},
"andrea|Se li porto via, il paese resta senza notizie.": {
"file": "andrea-566475abe2.mp3",
"sec": 3.48
@@ -111,6 +127,26 @@
"file": "andrea-eeb5a2273c.mp3",
"sec": 1.3
},
"vecchietta|Al limone! Da ragazza me ne mangiavo tre di fila.": {
"file": "vecchietta-15dbd36313.mp3",
"sec": 3.24
},
"vecchietto|E poi tre giorni a letto.": {
"file": "vecchietto-6a0dcb4430.mp3",
"sec": 1.37
},
"andrea|Me lo tengo io, allora.": {
"file": "andrea-d5aeb501d8.mp3",
"sec": 1.16
},
"vecchietto|«La storia infinita»? Alla mia età, figliolo, meglio qualcosa di corto.": {
"file": "vecchietto-27843b8226.mp3",
"sec": 5.02
},
"andrea|Fidati: è più corto di quel che sembra.": {
"file": "andrea-20c5ab3680.mp3",
"sec": 2.41
},
"andrea|Qualcosa luccica, proprio sotto i piedi dei vecchietti.": {
"file": "andrea-9a8732a3aa.mp3",
"sec": 3.97
@@ -175,6 +211,10 @@
"file": "andrea-fd10e3f083.mp3",
"sec": 3.04
},
"andrea|Di giorno la torcia non serve: le impronte si vedono benissimo. È il buio della fessura, il problema.": {
"file": "andrea-36648fc8d5.mp3",
"sec": 7.66
},
"andrea|Ormai lo so: è passato di qui. E le impronte piegano verso il bastione.": {
"file": "andrea-5b0cf4f9cf.mp3",
"sec": 5.03
@@ -231,38 +271,14 @@
"file": "andrea-aa9b313a43.mp3",
"sec": 2.22
},
"andrea|Troppo buio: non vedo nemmeno dove lo infilo. Prima ci vuole una luce.": {
"file": "andrea-59df1ada58.mp3",
"sec": 4.78
},
"andrea|Accendo la torcia e la punto dentro...": {
"file": "andrea-68a845881b.mp3",
"sec": 1.94
},
"andrea|Laggiù, appoggiato al muro, c'è un angolo di tela dipinta: una strada bianca, e un omino con la maglia rossa e le braccia alzate.": {
"file": "andrea-6484641c58.mp3",
"sec": 8.67
},
"andrea|È il palio! Il quadro dei ragazzi delle medie!": {
"file": "andrea-51ec04c16e.mp3",
"sec": 3.54
},
"andrea|E quelli... sono due occhi gialli!": {
"file": "andrea-0f5f2a4b2f.mp3",
"sec": 2.63
},
"andrea|Spariti nel buio.": {
"file": "andrea-f34cc00733.mp3",
"sec": 1.17
},
"andrea|Il Regolo. E ha il nostro palio.": {
"file": "andrea-1b2601be96.mp3",
"sec": 2.77
},
"andrea|Però non l'ha portato via: l'ha messo proprio lassù, in Fortezza. Dove domani portiamo i mattoni col passamano.": {
"file": "andrea-abeac78e68.mp3",
"sec": 7.13
},
"andrea|Il palio è già pronto ad aspettarci. Torno giù a dirlo a tutti: la festa si fa!": {
"file": "andrea-28f672ef5f.mp3",
"sec": 5.42
},
"andrea|La Pieve di San Pietro. Il mio rione: noi bianchi.": {
"file": "andrea-43fd5c2b27.mp3",
"sec": 4.46
@@ -279,6 +295,14 @@
"file": "andrea-6012288000.mp3",
"sec": 7.97
},
"san_pietro|Figliolo, io tengo le chiavi, non le squame. Portala a chi l'ha persa.": {
"file": "san_pietro-28fac8029b.mp3",
"sec": 5.6
},
"san_pietro|Te l'ho data io: usala, non restituirla. Le chiavi servono alle porte.": {
"file": "san_pietro-72373bd589.mp3",
"sec": 6.13
},
"andrea|La statua di San Pietro, con le chiavi in mano. Lui il palio l'ha visto passare di sicuro.": {
"file": "andrea-0663f64ddf.mp3",
"sec": 5.97
@@ -427,13 +451,17 @@
"file": "andrea-42e270af3d.mp3",
"sec": 6.21
},
"andrea|Me lo prendo. Se è suo, il proprietario lo ritrova... prima o poi.": {
"file": "andrea-78a0a84b84.mp3",
"sec": 5.95
"andrea|Ricordo di aver visto il film da piccolo... era stupendo!": {
"file": "andrea-5528e4c7b0.mp3",
"sec": 4.06
},
"andrea|Il cancello di dietro.": {
"file": "andrea-a2f0124f4d.mp3",
"sec": 1.41
"andrea|Il cancello di dietro. Se chiudo gli occhi vedo ancora l'A. Bettini, il campo da calcio di quando eravamo piccoli: lassù ci sono ancora i riflettori.": {
"file": "andrea-bc60e0ce13.mp3",
"sec": 9.9
},
"andrea|Il primo vero campo del paese: inaugurato nel 1964 e intitolato ad Alberto Bettini, un giovane consigliere della società scomparso troppo presto.": {
"file": "andrea-f035c38acc.mp3",
"sec": 11.19
},
"andrea|Chiuso. Si apre solo quando c'è la festa.": {
"file": "andrea-5e1cd26d46.mp3",
@@ -583,6 +611,10 @@
"file": "andrea-a8dfddbe65.mp3",
"sec": 3.45
},
"andrea|Un lucchetto non si pesca. Ci vuole la sua chiave.": {
"file": "andrea-d193b44288.mp3",
"sec": 3.65
},
"andrea|Il tabernacolo prima del ponte. Chi passa per andare via dal paese, un saluto glielo fa sempre.": {
"file": "andrea-78a5ef65f0.mp3",
"sec": 7.84
@@ -623,13 +655,25 @@
"file": "andrea-e47b3cebb5.mp3",
"sec": 4.87
},
"barondeduke|Me la giochi? Ho un sette e un due, ma un sacco di fiducia.": {
"file": "barondeduke-52184c7c05.mp3",
"sec": 5.58
},
"andrea|No, Barone. A poker le squame non valgono.": {
"file": "andrea-40651c0ea4.mp3",
"sec": 4.39
},
"barondeduke|A questo giro non punto niente. Nemmeno un ghiacciolo.": {
"file": "barondeduke-eff8685095.mp3",
"sec": 4.28
},
"andrea|Il BaronDeDuke, col cappello da cowboy. Ha l'aria di uno che aspetta qualcuno, e che spera non lo veda nessuno.": {
"file": "andrea-0bd1b0f32e.mp3",
"sec": 8.7
},
"andrea|Baron! Che ci fai qui?": {
"file": "andrea-1b59141c98.mp3",
"sec": 2.59
"andrea|Barone! Che ci fai qui?": {
"file": "andrea-83abae1419.mp3",
"sec": 2.91
},
"barondeduke|Aspetto uno che mi deve riportare le mutande.": {
"file": "barondeduke-3ea60b7083.mp3",
@@ -643,9 +687,9 @@
"file": "barondeduke-5542a06624.mp3",
"sec": 1.65
},
"barondeduke|In mano avevo full. E ho detto: «Male male si divide».": {
"file": "barondeduke-944c3e57dc.mp3",
"sec": 5.03
"barondeduke|Avevo due donne e due jack. Entra la terza donna, e penso: «Male male si divide».": {
"file": "barondeduke-d4ff967179.mp3",
"sec": 7.64
},
"andrea|E invece?": {
"file": "andrea-435306c242.mp3",
@@ -799,18 +843,22 @@
"file": "andrea-bb70dea2a6.mp3",
"sec": 2.15
},
"arcu|Che è, la mancia? Una squama? Grazie, la metto in cassaforte.": {
"file": "arcu-8c7f1850a3.mp3",
"sec": 5.84
},
"arcu|Tienila te, che io di roba verde ne ho già abbastanza sul lavoro.": {
"file": "arcu-bb69d88654.mp3",
"sec": 3.23
},
"arcu|Un libro? A me? Lo sai che io leggo solo le etichette degli armadi.": {
"file": "arcu-1da81f6f98.mp3",
"sec": 6.44
},
"andrea|L'Arcu, il mio amico di sempre. Davanti al circolino, come se ci fosse nato.": {
"file": "andrea-8ddab2d024.mp3",
"sec": 5.79
},
"andrea|Arcu! Ma che è sempre là, Borgo?": {
"file": "andrea-aaeb40d873.mp3",
"sec": 3.42
},
"arcu|E chi lo sposta???": {
"file": "arcu-64735a5925.mp3",
"sec": 0.97
},
"andrea|Il ponte vecchio, coi suoi due archi. Chissà quante piene ha visto passare: io al primo temporale cerco riparo.": {
"file": "andrea-01ec1d03ec.mp3",
"sec": 7.67
@@ -843,6 +891,18 @@
"file": "andrea-455ac4031a.mp3",
"sec": 4.96
},
"andrea|Tosca, guarda cosa ho trovato.": {
"file": "andrea-f5ff3c0ef9.mp3",
"sec": 2.27
},
"tosca|Bella. Ma nella cedrata questa non ce la metto.": {
"file": "tosca-7451ea2ef5.mp3",
"sec": 3.81
},
"tosca|Un libro? Andrea, qui si legge solo il listino. E costa meno.": {
"file": "tosca-ffc7cea9af.mp3",
"sec": 6.46
},
"andrea|Il chiosco del tennis. D'estate qui c'è la Tosca coi ghiaccioli.": {
"file": "andrea-24214bad4e.mp3",
"sec": 5.6
@@ -963,6 +1023,26 @@
"file": "andrea-468fe91000.mp3",
"sec": 3.28
},
"leone|Ehi! Mi abbagli! Sono di terracotta, mica cieco.": {
"file": "leone-4addbac9c7.mp3",
"sec": 6
},
"andrea|Scusa, leone.": {
"file": "andrea-66a622cd5e.mp3",
"sec": 0.92
},
"leone|Quella tienila per il buio. Qui c'è il sole, se non l'hai notato.": {
"file": "leone-19d904d4e1.mp3",
"sec": 5.66
},
"leone|Una squama? Io di squame ne ho viste... mai. Io faccio la guardia alla salita.": {
"file": "leone-7bd7f55e53.mp3",
"sec": 8.73
},
"andrea|Lo so, lo so. Alla salita.": {
"file": "andrea-62c970e189.mp3",
"sec": 2.65
},
"andrea|Di là si va alla scuola. Ci ho passato abbastanza anni, grazie.": {
"file": "andrea-f6fa5d6d6d.mp3",
"sec": 4.03
@@ -979,6 +1059,14 @@
"file": "andrea-f5d55187e8.mp3",
"sec": 5.11
},
"tozzino|Grazie, ma prima della partita niente zuccheri. Federer non li tocca.": {
"file": "tozzino-cfc1b1fa67.mp3",
"sec": 4.8
},
"tozzino|Una torcia? Se Federer arriva di notte, magari. Per ora tienila te.": {
"file": "tozzino-18e6d6edff.mp3",
"sec": 5.58
},
"andrea|Il Tozzino, vestito di bianco da capo a piedi, con la racchetta. Guarda l'orologio ogni dieci secondi.": {
"file": "andrea-3c32888a15.mp3",
"sec": 8.1
@@ -988,20 +1076,20 @@
"sec": 3.1
},
"tozzino|Aspetto Federer. Oggi si fa una partita.": {
"file": "tozzino-a069d2c5e2.mp3",
"sec": 2.85
"file": "tozzino-c3898e9bdb.mp3",
"sec": 3.23
},
"andrea|Federer? Roger Federer?": {
"file": "andrea-70f150bdec.mp3",
"sec": 2.67
},
"tozzino|E chi sennò? Ha chiamato ora: è imbottigliato in Bolognese.": {
"file": "tozzino-5072201408.mp3",
"sec": 4.3
"file": "tozzino-d51159c364.mp3",
"sec": 4.08
},
"tozzino|Se non arriva, la vinco a tavolino.": {
"file": "tozzino-c1736e2d62.mp3",
"sec": 1.42
"file": "tozzino-c06cc01612.mp3",
"sec": 2.06
},
"andrea|La Carza. A maggio c'è poca acqua: d'estate si scendeva a fare le dighe coi sassi.": {
"file": "andrea-91be1de49b.mp3",
@@ -1139,6 +1227,14 @@
"file": "andrea-a1dab4073c.mp3",
"sec": 3.82
},
"castello|Luce? In seicento anni ne ho vista tanta. Tienila per il buio, che quello non manca mai.": {
"file": "castello-c2cc9224c5.mp3",
"sec": 9.1
},
"castello|Non a me. Portala vicino a chi l'ha persa: le cose parlano solo dove sono cadute.": {
"file": "castello-3267e20838.mp3",
"sec": 7.74
},
"andrea|La torre del Trebbio. Da ragazzo ci si rifugiò Amerigo Vespucci, per scappare dalla peste di Firenze.": {
"file": "andrea-14cbceece4.mp3",
"sec": 6.99
@@ -1183,22 +1279,6 @@
"file": "andrea-23e40a2495.mp3",
"sec": 5.74
},
"andrea|Vediamo da dove comincia...": {
"file": "andrea-239bf733a6.mp3",
"sec": 1.26
},
"andrea|C'è una pagina sola. Con scritto: «FINE».": {
"file": "andrea-1d80123a55.mp3",
"sec": 3.93
},
"andrea|Infinita, dicevano.": {
"file": "andrea-77f1348d3a.mp3",
"sec": 2.13
},
"andrea|Leggere adesso? Prima il palio. E poi, a quanto pare, si fa presto.": {
"file": "andrea-3a6af3897f.mp3",
"sec": 6.06
},
"andrea|Un ghiacciolo al limone. Si scioglie a guardarlo.": {
"file": "andrea-8c9713081a.mp3",
"sec": 3.44
@@ -1315,6 +1395,10 @@
"file": "andrea-2d0e3c759e.mp3",
"sec": 1.39
},
"andrea|Tosca, cosa si dice in giro?": {
"file": "andrea-be96b05f57.mp3",
"sec": 2.04
},
"andrea|Niente, grazie Tosca.": {
"file": "andrea-2637918e77.mp3",
"sec": 1.32
@@ -1339,6 +1423,14 @@
"file": "tosca-376fbb9f17.mp3",
"sec": 2.41
},
"tosca|Che sei andato sotto la Fortezza da solo. Il paese parla, Andrea.": {
"file": "tosca-ad26d2c8dc.mp3",
"sec": 5.16
},
"tosca|Prenditi una cedrata, che le chiacchiere mettono sete.": {
"file": "tosca-940ffeb62c.mp3",
"sec": 2.98
},
"tosca|Quando ti viene sete, sai dove trovarmi.": {
"file": "tosca-db2da31cef.mp3",
"sec": 2.31
@@ -1403,6 +1495,18 @@
"file": "andrea-379131645c.mp3",
"sec": 1.98
},
"andrea|Ho trovato una squama, sotto la panchina.": {
"file": "andrea-5a36e0de20.mp3",
"sec": 2.57
},
"andrea|Sotto la Fortezza ci sono impronte, e vanno verso il bastione.": {
"file": "andrea-dc9addb491.mp3",
"sec": 3.56
},
"andrea|Ho una torcia. Stasera salgo alla Fortezza.": {
"file": "andrea-c458e32a56.mp3",
"sec": 4.28
},
"andrea|Ci vediamo dopo.": {
"file": "andrea-7e9841c23a.mp3",
"sec": 0.95
@@ -1443,6 +1547,42 @@
"file": "vecchietta-fe3c07afa2.mp3",
"sec": 5.11
},
"vecchietta|L'avevo detto io, che luccicava!": {
"file": "vecchietta-8bcd369812.mp3",
"sec": 2.3
},
"vecchietto|Una squama a maggio? Allora il Regolo fa la muta, come le bisce.": {
"file": "vecchietto-33b2aadc49.mp3",
"sec": 4.64
},
"vecchietta|O l'ha persa uno della sfilata, col costume. Che ne sai.": {
"file": "vecchietta-51d10531a0.mp3",
"sec": 4.34
},
"vecchietto|Lo sapevo. Stanotte ho sentito raspare, dalla parte della Fortezza.": {
"file": "vecchietto-7204d5b851.mp3",
"sec": 5.29
},
"vecchietta|Tu stanotte dormivi. L'hai detto tu.": {
"file": "vecchietta-8f618fae8c.mp3",
"sec": 2.75
},
"vecchietto|Si può dormire e sentire. Alla mia età si fa tutto insieme.": {
"file": "vecchietto-320caa6792.mp3",
"sec": 4.92
},
"vecchietto|Di sera? Lassù? Allora salutaci il Regolo.": {
"file": "vecchietto-9d2e14790f.mp3",
"sec": 4.08
},
"vecchietta|E se ti mangia, almeno la festa ha un argomento.": {
"file": "vecchietta-7baa7686cd.mp3",
"sec": 2.94
},
"andrea|Grazie dell'incoraggiamento.": {
"file": "andrea-f845779a10.mp3",
"sec": 1.57
},
"vecchietta|Salutaci i tuoi!": {
"file": "vecchietta-814e558af5.mp3",
"sec": 1.47
@@ -1451,6 +1591,74 @@
"file": "vecchietto-c8eae2e59e.mp3",
"sec": 4.88
},
"andrea|Ma che è sempre là, Borgo?": {
"file": "andrea-73c12c835b.mp3",
"sec": 2.34
},
"andrea|Come va alla Casa dell'Armando?": {
"file": "andrea-413cf7673b.mp3",
"sec": 1.56
},
"andrea|Ho trovato una squama del Regolo.": {
"file": "andrea-51314c3413.mp3",
"sec": 1.69
},
"andrea|Sotto la Fortezza ci sono le sue impronte.": {
"file": "andrea-898cf87dd0.mp3",
"sec": 2.49
},
"andrea|Ho la torcia. Stasera entro nella fessura.": {
"file": "andrea-343fb5c806.mp3",
"sec": 3.33
},
"andrea|Cosa stavamo dicendo???": {
"file": "andrea-06f5050907.mp3",
"sec": 1.24
},
"andrea|Ci si becca da i' Trappola, ai' Barrettino!": {
"file": "andrea-fca6f59a70.mp3",
"sec": 3.41
},
"arcu|E chi lo sposta???": {
"file": "arcu-64735a5925.mp3",
"sec": 0.97
},
"arcu|Mah... un si fa una sega...": {
"file": "arcu-5d07c15776.mp3",
"sec": 2.89
},
"arcu|Fammi vedere... Verde. Come la Panda di mi' zio.": {
"file": "arcu-af7afda339.mp3",
"sec": 4.18
},
"arcu|Verde! Allora il Regolo tifa Cardetole. Tradimento.": {
"file": "arcu-85c7d0b8f6.mp3",
"sec": 5.57
},
"arcu|E te ci vai da solo? Io ti aspetto qui.": {
"file": "arcu-e157a4b70b.mp3",
"sec": 2.73
},
"arcu|Qualcuno deve raccontarlo, dopo.": {
"file": "arcu-5d1fa73d32.mp3",
"sec": 1.77
},
"arcu|Fai una cosa: se vedi due occhi gialli, un sono i fari della Provinciale.": {
"file": "arcu-fdd1ab5f5b.mp3",
"sec": 4.37
},
"andrea|Grazie, Arcu. Molto utile.": {
"file": "andrea-2caf0f40f2.mp3",
"sec": 2.73
},
"andrea|Ma ti si è bruciata la memoria, fratello???!?": {
"file": "andrea-bf8a51895b.mp3",
"sec": 2.98
},
"arcu|...I pompieri per cosa?": {
"file": "arcu-5148dffe36.mp3",
"sec": 1.7
},
"andrea|Sono bloccato. Mi dai un consiglio?": {
"file": "andrea-42b02eb9ea.mp3",
"sec": 3.9
@@ -1527,6 +1735,42 @@
"file": "castello-dc8a873d86.mp3",
"sec": 4.36
},
"andrea|Laggiù, appoggiato al muro, c'è un angolo di tela dipinta: una strada bianca, e un omino con la maglia rossa e le braccia alzate.": {
"file": "andrea-6484641c58.mp3",
"sec": 8.67
},
"andrea|È il palio! Il quadro dei ragazzi delle medie!": {
"file": "andrea-51ec04c16e.mp3",
"sec": 3.54
},
"andrea|E quelli... sono due occhi gialli!": {
"file": "andrea-0f5f2a4b2f.mp3",
"sec": 2.63
},
"andrea|Spariti nel buio.": {
"file": "andrea-f34cc00733.mp3",
"sec": 1.17
},
"andrea|Il Regolo. E ha il nostro palio.": {
"file": "andrea-1b2601be96.mp3",
"sec": 2.77
},
"andrea|Però non l'ha portato via: l'ha messo proprio lassù, in Fortezza. Dove domani portiamo i mattoni col passamano.": {
"file": "andrea-abeac78e68.mp3",
"sec": 7.13
},
"andrea|Il palio è già pronto ad aspettarci. Torno giù a dirlo a tutti: la festa si fa!": {
"file": "andrea-28f672ef5f.mp3",
"sec": 5.42
},
"andrea|Mi ricordavo un finale diverso.": {
"file": "andrea-d9ff79ed54.mp3",
"sec": 1.73
},
"andrea|Il «Nulla» sta viaggiando troppo velocemente nell'animo umano, devo darmi una mossa!": {
"file": "andrea-71dc4628cc.mp3",
"sec": 4.81
},
"andrea|Niente di speciale.": {
"file": "andrea-6ebcf8e5de.mp3",
"sec": 1.04
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+15
View File
@@ -90,3 +90,18 @@ si segna qui con la data e il punto di `src/story/story.js` dove sta; le persone
gioco, come i nove bastioni.
- **Regole perché resti divertente**: le chiavi importanti non si buttano, una porta aperta resta aperta, al Trebbio
c'è sempre l'aiuto.
- **Le mutande del Barone** (Andrea, 25 ottobre; ci pensa ancora): si ritrovano e gli si riportano, e lui in cambio
dà qualcosa che serve alla storia. Proposta: appese come trofeo all'insegna del circolino di Campomigliaio
(l'Arcu ci scherza), e in cambio due pile per la torcia della casetta, scarica.
- **La racchetta del Tozzino** (Andrea, 25 ottobre): non la trova, Federer sta arrivando, ha paura di perdere e chiede
aiuto ad Andrea. Si potrebbe legare al poker del Barone (persa la stessa sera?). Serve un foglio sprite del Tozzino
senza racchetta, più una posa con la racchetta ritrovata.
- **Consigli di Codex (25 ottobre), da valutare dopo 5 e 1 (risposte ai tentativi sbagliati e pettegolezzo che
cambia, fatti):**
- *Colpo di scena a metà*: la squama somiglia al Regolo della sfilata... ma allora chi ha lasciato le impronte?
Solo con dialoghi e una riga nel taccuino, senza toccare la catena degli enigmi.
- *Un enigma con l'Arcu*: tiene qualcosa, distrae qualcuno o fraintende un'istruzione (gag). Uno solo: tocca
soluzione, test, voci e la sua posizione in ogni scena dove compare (le pose ci sono).
- *Regia alla fessura*: col meccanismo del primo piano (`closeup`) il buio, la torcia, un istante di silenzio,
poi gli occhi. Due immagini di Codex.
- *Segreti facoltativi del paese* con pagine illustrate nel taccuino: funzione nuova, dopo la storia principale.
+4
View File
@@ -1,5 +1,9 @@
# Da dove vengono le immagini
- `assets/closeups/palio-2007.jpg`: copia della foto `assets/raw/palio-2007-foto.jpg` fornita da Andrea
il 24 ottobre 2026. Usata nel primo piano della fessura; il renderer mostra soltanto la tela tramite
le coordinate `story.closeups.fessura.crop`, senza modificare la foto originale.
Da controllare **prima del primo push**: quello che è nella storia di git, una volta pubblicato, resta pubblico
anche se poi lo si cancella. Le foto originali (`assets/raw/`) e i prompt (`prompts/`) sono esclusi da git e non sono
mai stati committati (verificato il 2026-10-23); qui si parla degli sfondi ridisegnati in `assets/scenes/`, che invece
+17
View File
@@ -18,6 +18,23 @@ Online su 50037.manzolo.it tutto fino agli effetti ambientali di Codex. Aperto,
## Dove siamo arrivati
- **Multimedia (25 ottobre, modifiche locali)**: il libro sfogliabile già presente conserva immagini, battute e
riapertura dall'inventario; il fruscio ora segue il volume degli effetti, non quello delle voci.
Nuovi paesaggi sonori sintetizzati con Web Audio (`src/engine/soundscape.js`, profili in
`src/story/soundscapes.js`): vento, acqua, uccelli di giorno e grilli di sera, campane, bicchieri,
gocce e scricchiolii distanziati. In **Pausa → Ambiente ed effetti** si regola il volume, salvato nel browser.
Muto, pausa e pagina nascosta fermano anche i suoni già avviati.
La **torcia nella fessura** apre un primo piano: mouse, tocco/trascinamento o frecce spostano la luce;
Invio aiuta a cercare il prossimo dettaglio. Pietra → palio → occhi; la musica si abbassa durante la ricerca.
«Torna fuori»/Esc permette di riprovare; soltanto dopo gli occhi «Continua» avvia la conclusione.
Nessun progresso parziale viene salvato. La tela usa la foto vera del 2007, ritagliata durante il disegno.
Controllo mirato: `npm run browser -- --media-only`; il collaudo normale include anche queste prove.
Verificati `npm test` e `npm run browser`: soluzione completa, salvataggi, libro su desktop/telefono,
torcia con mouse/tastiera/tocco/trascinamento, pausa e muto, segnale Web Audio senza saturazione.
Schermate in `/tmp/regolo-shots/media-*.png` e `/tmp/regolo-shots/05b-fessura-*.png`.
I copioni video sono aggiornati ai gesti richiesti dalla torcia; il montaggio audio da file continua a
includere solo musica e voci (gli effetti Web Audio non vengono esportati dal copione).
- **Ambienti animati (25 ottobre)**: alle prove già presenti (bandiere del campeggio, fascio di luce nella Pieve,
ombre al Bosco ai Frati) si aggiungono 51 dettagli nelle altre 35 scene. Configurazione in
`src/story/ambience.js`, richiamata da `story.js`; nuovi disegni in `src/engine/ambient.js`:
+5 -1
View File
@@ -11,6 +11,7 @@
<div id="stage">
<canvas id="scene" width="640" height="360" aria-label="La scena dell'avventura"></canvas>
<div id="speech" aria-live="polite"></div>
<button id="closeup-close" hidden title="Chiudi (Esc)">Chiudi</button>
<div id="corner">
<button id="pause-button" hidden title="Pausa (Esc)">Pausa</button>
<button id="map-button" hidden title="Mappa (P)">Mappa</button>
@@ -18,7 +19,7 @@
<button id="spots-button" hidden title="Mostra cosa si può toccare (H)">?</button>
</div>
<div id="sound">
<button id="mute" aria-label="Musica: togli l'audio" title="Musica (M)">♪</button>
<button id="mute" aria-label="Togli l'audio" title="Audio (M)">♪</button>
<input id="volume" type="range" min="0" max="100" step="5" aria-label="Volume della musica" title="Volume della musica">
<label for="voice-volume" class="sound-label" title="Volume delle voci">bla</label>
<input id="voice-volume" type="range" min="0" max="100" step="5" aria-label="Volume delle voci" title="Volume delle voci">
@@ -72,6 +73,9 @@
</section>
<section id="pause" class="overlay" hidden aria-label="Pausa">
<h2>Pausa</h2>
<label class="effects-control" for="effects-volume">Ambiente ed effetti
<input id="effects-volume" type="range" min="0" max="100" step="5" aria-label="Volume ambiente ed effetti">
</label>
<div class="buttons pause-buttons">
<button id="resume">Riprendi</button>
<button id="pause-map" class="secondary">Mappa</button>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "leggenda-del-regolo",
"version": "0.1.2",
"version": "0.1.5",
"private": true,
"type": "module",
"scripts": {
+80 -3
View File
@@ -22,6 +22,9 @@ const SPEED = 95;
// Solo profilo, come i cicli di camminata: 'up' e 'down' lasciano il verso com'è.
const FACING = { left: -1, right: 1 };
const WALK = ['walk_1', 'walk_2', 'walk_3', 'walk_4'], STEP = 15, REACH = 0.45;
// Il primo piano di un libro (story.closeups, effetto `closeup`): copertina → frontespizio → una pagina girata →
// l'ultima pagina, poi dopo una pausa il commento. Tempi in secondi.
export const PAGE_TURN = 0.7, CLOSEUP_PAUSE = 1.4;
// Condizioni: 'flag', '!flag', 'has:oggetto', '!has:oggetto', oppure un elenco (tutte vere).
export function check(cond, state) {
@@ -115,15 +118,16 @@ export class Game {
this.hurry = false; // doppio clic: si cammina al doppio della velocità
this.reach = 0; // secondi di braccio teso quando prende qualcosa
this.back = false; // di spalle davanti a una porta o a una bacheca
this.closeup = null; // il primo piano aperto: { id, step: 'cover' | 'title' | 'turn' | 'end', t, age }
}
get scene() { return this.story.scenes[this.state.scene]; }
// Occupato: c'è qualcosa in corso che il giocatore non può interrompere. La camminata verso un punto
// o verso un oggetto si può sempre cambiare con un altro clic: la coda che la seguiva viene scartata.
get busy() { return !!this.speech || this.reach > 0 || (this.task ? !this.task.interruptible : this.queue.length > 0); }
get busy() { return !!this.speech || !!this.closeup || this.reach > 0 || (this.task ? !this.task.interruptible : this.queue.length > 0); }
get idle() { return !this.busy && !this.task && !this.dialogue; }
// Fermo: nessuna battuta, nessun effetto in coda, nessuna camminata che fa parte di una sequenza.
get steady() { return !this.speech && this.reach <= 0 && !this.queue.length && !(this.task && !this.task.interruptible); }
get steady() { return !this.speech && !this.closeup && this.reach <= 0 && !this.queue.length && !(this.task && !this.task.interruptible); }
// Lo stato da salvare adesso: quello attuale se è fermo, altrimenti l'ultimo punto fermo.
savable() { return this.steady ? this.snapshot() : this.checkpoint; }
// A sequenza finita, se qualcosa è cambiato, nuovo punto fermo e salvataggio.
@@ -221,6 +225,63 @@ export class Game {
return false;
}
// Il primo piano: un tocco sfoglia (copertina → frontespizio → si gira la pagina); all'ultima pagina non fa niente.
closeupTap() {
const c = this.closeup;
if (!c || this.speech) return false;
if (this.story.closeups[c.id].type === 'torch') return false;
if (c.step === 'cover') Object.assign(c, { step: 'title', t: 0 });
else if (c.step === 'title') Object.assign(c, { step: 'turn', t: 0 });
else return false;
return true;
}
// Chiuso il libro si torna alla scena, e gli effetti che seguivano il primo piano ripartono.
closeupClose() {
const c = this.closeup;
if (!c) return false;
this.closeup = null;
this.speech = null;
const def = this.story.closeups?.[c.id];
if (def?.type === 'torch') {
if (c.step === 'done') this.queue.unshift(...def.complete);
return true;
}
// Chi è arrivato all'ultima pagina, chiudendo, la commenta (`closing`).
if (c.step === 'end') this.queue.unshift(...(this.story.closeups?.[c.id]?.closing ?? []));
return true;
}
// Solo luce, nessun clic sugli oggetti dietro al primo piano. Il progresso è transitorio:
// uscire o ricaricare durante la ricerca permette di ripeterla, senza anticipare il finale.
closeupAim([x, y]) {
const c = this.closeup;
if (!c || this.story.closeups[c.id].type !== 'torch' || !Number.isFinite(x) || !Number.isFinite(y)) return false;
c.beam = [Math.max(70, Math.min(570, x)), Math.max(65, Math.min(295, y))];
return true;
}
updateTorch(dt) {
const c = this.closeup, def = this.story.closeups[c.id];
c.t += dt;
if (c.lines?.length) { this.run(c.lines.shift()); return; }
if (c.step === 'reveal') {
if (c.t < 1.4) return;
c.found++;
c.step = c.found === def.targets.length ? 'done' : 'search';
c.t = c.dwell = 0;
return;
}
const target = def.targets[c.found];
if (c.step !== 'search' || !target) return;
c.dwell = Math.hypot(c.beam[0] - target.at[0], c.beam[1] - target.at[1]) < target.radius ? c.dwell + dt : 0;
if (c.dwell >= 0.45) {
c.step = 'reveal';
c.t = 0;
c.lines = [...(target.lines ?? [])];
}
}
options() {
if (!this.dialogue || this.busy) return [];
const node = this.story.dialogues[this.dialogue.id].nodes[this.dialogue.node];
@@ -271,6 +332,8 @@ export class Game {
}
speakerPoint(who) {
// Nel primo piano Andrea parla da sopra il libro, non dalla scena oscurata dietro.
if (this.closeup && (!who || who === 'andrea')) return [320, 34];
if (!who || who === 'andrea') return [this.state.x, this.state.y - 62 * this.scaleAt(this.state.y)];
if (this.scene.speakers?.[who]) return this.scene.speakers[who];
const h = this.hotspot(who) ?? (this.scene.hotspots ?? []).find(x => x.id === who);
@@ -280,6 +343,7 @@ export class Game {
update(dt) {
this.time += dt;
if (this.closeup) this.closeup.age += dt;
if (this.speech) {
this.speech.left -= dt;
if (this.speech.left > 0) return;
@@ -310,8 +374,18 @@ export class Game {
this.walking = false;
this.task = null;
}
// Nel primo piano il tempo scorre solo per la pagina che gira e per la pausa prima del commento.
const c = this.closeup;
if (c) {
if (this.story.closeups[c.id].type === 'torch') { this.updateTorch(dt); return; }
c.t += dt;
if (c.step === 'turn' && c.t >= PAGE_TURN) Object.assign(c, { step: 'end', t: 0 });
if (c.step === 'end' && !c.lines && c.t >= CLOSEUP_PAUSE) c.lines = [...(this.story.closeups?.[c.id]?.comment ?? [])];
if (c.lines?.length) this.run(c.lines.shift());
return;
}
// Esegue gli effetti istantanei di fila e si ferma al primo che richiede tempo.
while (!this.speech && !this.walking && this.queue.length) this.run(this.queue.shift());
while (!this.speech && !this.walking && !this.closeup && this.queue.length) this.run(this.queue.shift());
this.commit();
}
@@ -335,6 +409,9 @@ export class Game {
else if (e.node) this.dialogue.node = e.node;
else if (e.endDialogue) this.dialogue = null;
else if (e.end) s.ended = true;
else if (e.closeup) this.closeup = this.story.closeups[e.closeup].type === 'torch'
? { id: e.closeup, step: 'search', t: 0, age: 0, found: 0, dwell: 0, beam: [320, 180] }
: { id: e.closeup, step: 'cover', t: 0, age: 0 };
else throw new Error('Effetto sconosciuto: ' + JSON.stringify(e));
}
+86 -1
View File
@@ -1,7 +1,8 @@
// 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 { check, PAGE_TURN } from './game.js';
import { ambientEffects, ambientHash } from './ambient.js';
import { drawTorch } from './torch.js';
import { versioned } from '../version.js';
export const WIDTH = 640, HEIGHT = 360;
@@ -170,6 +171,7 @@ export function render(ctx, game, { hover, spots, debug, time = performance.now(
drawLights(ctx, game, time);
for (const shape of art) if (shape.top) drawShape(ctx, shape);
if (spots) drawSpots(ctx, game);
if (game.closeup) drawCloseup(ctx, game, time);
if (debug) drawDebug(ctx, game, debug);
if (hover) {
ctx.strokeStyle = 'rgba(255,255,255,0.35)';
@@ -644,3 +646,86 @@ function drawDebug(ctx, game, { mouse, points = [], help }) {
label(`${game.state.scene} ${where} ${help ?? ''}`, 2, HEIGHT - 3, '#fff');
ctx.restore();
}
// --- 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
// sulla piega; sotto c'è già l'ultima pagina.
const ink = '#3a1f12', FONT = '"Press Start 2P", monospace';
function pageText(ctx, lines, [x, y, w, h], size, at = 0.5, color = ink) {
ctx.font = `${size}px ${FONT}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = color;
const gap = size * 1.7, top = y + h * at - (lines.length - 1) * gap / 2;
lines.forEach((line, i) => ctx.fillText(line, Math.round(x + w / 2), Math.round(top + i * gap)));
}
const twoLines = title => { const i = title.lastIndexOf(' '); return i > 0 ? [title.slice(0, i), title.slice(i + 1)] : [title]; };
function drawCloseup(ctx, game, time) {
const c = game.closeup, def = game.story.closeups?.[c.id];
if (!def) return;
if (def.type === 'torch') {
if (!backgrounds.has(def.picture)) loadBackground(def.picture);
drawTorch(ctx, c, def, backgrounds.get(def.picture));
return;
}
ctx.save();
ctx.fillStyle = `rgba(8, 5, 12, ${Math.min(0.78, c.age / 0.35 * 0.78)})`;
ctx.fillRect(0, 0, WIDTH, HEIGHT);
const src = c.step === 'cover' ? def.closed : def.open, img = backgrounds.get(src);
if (!img) { loadBackground(src); loadBackground(c.step === 'cover' ? def.open : def.closed); ctx.restore(); return; }
// Il libro sale dal basso (solo all'inizio), con le braccia che entrano dal bordo.
const rise = 1 - Math.min(1, c.age / 0.45), ox = Math.round((WIDTH - img.width) / 2);
const oy = Math.round(HEIGHT - img.height + 140 * rise * rise);
ctx.imageSmoothingEnabled = false;
ctx.drawImage(img, ox, oy);
ctx.translate(ox, oy);
const lines = twoLines(def.title), { left, right, spine } = def.pages;
if (c.step === 'cover') pageText(ctx, lines, def.label, 8);
else {
const flip = c.step === 'turn' ? Math.min(1, c.t / PAGE_TURN) : c.step === 'end' ? 1 : 0;
// Sotto la pagina che gira c'è già l'ultima: «FINE», e nient'altro.
if (flip > 0) pageText(ctx, [def.last], right, 20, 0.46);
else pageText(ctx, lines, right, 8, 0.32);
if (flip > 0 && flip < 1) {
// La pagina che gira: si stringe sulla piega e passa a sinistra, col dorso un po' più scuro.
const e = (1 - Math.cos(Math.PI * flip)) / 2, k = Math.cos(Math.PI * e);
const lift = Math.round(Math.sin(Math.PI * e) * 10), [, y, , h] = right, pw = right[0] + right[2] - spine;
ctx.save();
ctx.translate(spine, 0);
ctx.scale(k || 0.001, 1);
ctx.fillStyle = k > 0 ? def.paper : '#e6cc9c';
ctx.beginPath();
ctx.moveTo(0, y); ctx.lineTo(pw, y - lift); ctx.lineTo(pw, y + h - lift); ctx.lineTo(0, y + h);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = 'rgba(58, 31, 18, 0.55)';
ctx.lineWidth = 1 / Math.max(0.2, Math.abs(k));
ctx.stroke();
if (k > 0) pageText(ctx, lines, [right[0] - spine, y - lift, right[2], h], 8, 0.32);
ctx.restore();
}
// Dove toccare per sfogliare: l'angolo della pagina che pulsa.
if (c.step === 'title' && !game.speech) {
const pulse = 0.5 + 0.5 * Math.sin(time / 260), cx = right[0] + right[2] - 2, cy = right[1] + right[3] - 2, d = 12 + 3 * pulse;
ctx.fillStyle = `rgba(232, 194, 90, ${0.55 + 0.4 * pulse})`;
ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx - d, cy); ctx.lineTo(cx, cy - d); ctx.closePath(); ctx.fill();
}
}
ctx.restore();
// Il suggerimento in alto, finché c'è qualcosa da sfogliare.
const hint = c.step === 'cover' ? 'Clic o tocco: apri il libro' : c.step === 'title' ? 'Clic o tocco: gira la pagina' : null;
if (hint && !game.speech && c.age > 0.5) {
ctx.save();
ctx.globalAlpha = 0.65 + 0.35 * Math.sin(time / 300);
ctx.font = `7px ${FONT}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
const tw = ctx.measureText(hint).width;
ctx.fillRect(WIDTH / 2 - tw / 2 - 6, 8, tw + 12, 14);
ctx.fillStyle = '#ffd27a';
ctx.fillText(hint, WIDTH / 2, 15);
ctx.restore();
}
}

Some files were not shown because too many files have changed in this diff Show More