Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb8e6a8885 | ||
|
|
81a1dd1bc3 | ||
|
|
0f1740a7ad | ||
|
|
3d61fc3954 | ||
|
|
6226de3795 | ||
|
|
17824963aa | ||
|
|
75cbe8250f | ||
|
|
6b17288962 | ||
|
|
262c774718 | ||
|
|
4a2c88f0e3 | ||
|
|
c395b4e890 | ||
|
|
818af5b36f | ||
|
|
a080c0ea56 | ||
|
|
fbeef76f01 | ||
|
|
45d310b9b4 | ||
|
|
481e4a9514 |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 452 KiB |
|
After Width: | Height: | Size: 419 KiB |
|
After Width: | Height: | Size: 464 KiB |
|
After Width: | Height: | Size: 352 KiB |
|
After Width: | Height: | Size: 447 KiB |
|
After Width: | Height: | Size: 454 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 320 KiB |
|
After Width: | Height: | Size: 331 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 290 KiB |
@@ -1603,9 +1603,9 @@
|
||||
"file": "andrea-a93c65234e.mp3",
|
||||
"sec": 4.28
|
||||
},
|
||||
"andrea|Di chi è quell'Audi nera?": {
|
||||
"file": "andrea-b9a784137e.mp3",
|
||||
"sec": 1.63
|
||||
"andrea|Di chi è quell'Audi lì?": {
|
||||
"file": "andrea-cc8a61a779.mp3",
|
||||
"sec": 1.4
|
||||
},
|
||||
"arcu|Let it be.": {
|
||||
"file": "arcu-6ba2f5285a.mp3",
|
||||
@@ -1731,6 +1731,154 @@
|
||||
"file": "andrea-2e64b2d5c2.mp3",
|
||||
"sec": 4.8
|
||||
},
|
||||
"andrea|Un piccolo stemma di pietra sul muro. Chissà di chi era, questa casa, un tempo.": {
|
||||
"file": "andrea-9317777d3a.mp3",
|
||||
"sec": 4.94
|
||||
},
|
||||
"andrea|Il lampione di ferro, attaccato al muro. La sera fa una luce gialla che pare d'altri tempi.": {
|
||||
"file": "andrea-e400e4ea1d.mp3",
|
||||
"sec": 6.11
|
||||
},
|
||||
"andrea|La palestra della scuola, col tetto a denti di sega.": {
|
||||
"file": "andrea-5ed0615726.mp3",
|
||||
"sec": 3.13
|
||||
},
|
||||
"andrea|Lassù Monte Cacioli, con le antenne in cima.": {
|
||||
"file": "andrea-c633ffc747.mp3",
|
||||
"sec": 3.12
|
||||
},
|
||||
"andrea|Il cancello della palestra. Per la scuola si passa dalla stradina più in là.": {
|
||||
"file": "andrea-f91d1262a4.mp3",
|
||||
"sec": 5.19
|
||||
},
|
||||
"andrea|Chiuso. Per la scuola si passa dalla stradina più in là.": {
|
||||
"file": "andrea-237197db3a.mp3",
|
||||
"sec": 4.32
|
||||
},
|
||||
"andrea|Una siepe alta come una casa, e l'edera che si mangia il muro.": {
|
||||
"file": "andrea-2544c4c379.mp3",
|
||||
"sec": 3.89
|
||||
},
|
||||
"andrea|Le villette col giardino e il muretto: qui è tutto in ordine.": {
|
||||
"file": "andrea-05ade48460.mp3",
|
||||
"sec": 4.36
|
||||
},
|
||||
"andrea|Una villa con gli archi dietro la siepe. Qui le siepi le tosano col righello.": {
|
||||
"file": "andrea-2e0acd9298.mp3",
|
||||
"sec": 5
|
||||
},
|
||||
"andrea|Un cipresso dritto come un fuso, in mezzo all'aiuola.": {
|
||||
"file": "andrea-33576f81c3.mp3",
|
||||
"sec": 3.8
|
||||
},
|
||||
"andrea|Una panchina all'ombra.": {
|
||||
"file": "andrea-ee2789a3a8.mp3",
|
||||
"sec": 1.38
|
||||
},
|
||||
"andrea|Sedersi ora? Prima il palio.": {
|
||||
"file": "andrea-aa636ddf94.mp3",
|
||||
"sec": 2.74
|
||||
},
|
||||
"andrea|Il palazzo di mattoni coi negozi sotto, di là dalla Provinciale.": {
|
||||
"file": "andrea-211109ca9d.mp3",
|
||||
"sec": 4.66
|
||||
},
|
||||
"andrea|I platani della Provinciale: d'estate fanno un'ombra che pare una galleria.": {
|
||||
"file": "andrea-2ce9db22c9.mp3",
|
||||
"sec": 5.8
|
||||
},
|
||||
"andrea|Le villette sotto il bosco, coi giardini curati.": {
|
||||
"file": "andrea-402f9ec788.mp3",
|
||||
"sec": 3.1
|
||||
},
|
||||
"andrea|La cascatella della Carza: quella che si vede anche dalla Luna.": {
|
||||
"file": "andrea-e9bb43952d.mp3",
|
||||
"sec": 3.42
|
||||
},
|
||||
"andrea|D'estate si veniva a rinfrescare i piedi qui sotto.": {
|
||||
"file": "andrea-2d8e58541c.mp3",
|
||||
"sec": 2.96
|
||||
},
|
||||
"andrea|Il greto pieno di sassi. Dighe, ponti, pesci immaginari: qui ci si passava l'estate.": {
|
||||
"file": "andrea-816bf4c405.mp3",
|
||||
"sec": 8.38
|
||||
},
|
||||
"andrea|Un lampione in mezzo al prato. Di sera la passeggiata la fanno le zanzare.": {
|
||||
"file": "andrea-74fe2ee88c.mp3",
|
||||
"sec": 5.16
|
||||
},
|
||||
"andrea|Gli orti con la rete: cavoli, insalata e i fagioli sulle canne.": {
|
||||
"file": "andrea-343e479437.mp3",
|
||||
"sec": 5.4
|
||||
},
|
||||
"andrea|Un cavolo dall'orto di qualcun altro? Poi chi lo sente.": {
|
||||
"file": "andrea-e842495cf5.mp3",
|
||||
"sec": 4.17
|
||||
},
|
||||
"andrea|Un ponticino di ferro sul fosso. Due passi e sei di là.": {
|
||||
"file": "andrea-145bc6ff82.mp3",
|
||||
"sec": 4.21
|
||||
},
|
||||
"andrea|Il greto bianco della Carza, con un filo d'acqua in mezzo ai sassi.": {
|
||||
"file": "andrea-f2902ed3cd.mp3",
|
||||
"sec": 3.72
|
||||
},
|
||||
"andrea|Una casa con la siepe rossa davanti. Da quel balcone si vede tutta la Carza.": {
|
||||
"file": "andrea-4d4c45e5de.mp3",
|
||||
"sec": 6.26
|
||||
},
|
||||
"andrea|La Carza sotto il muretto, con le canne dritte nell'acqua e il muro vecchio di sassi.": {
|
||||
"file": "andrea-bcce90bf03.mp3",
|
||||
"sec": 5.65
|
||||
},
|
||||
"andrea|Gli orti fatti coi bancali e le cassette, il bidone blu per l'acqua. Ognuno a modo suo.": {
|
||||
"file": "andrea-5c7774bb32.mp3",
|
||||
"sec": 7.15
|
||||
},
|
||||
"andrea|Qui la Carza ha un po' d'acqua chiara fra i sassi bianchi.": {
|
||||
"file": "andrea-0a363e408f.mp3",
|
||||
"sec": 3.63
|
||||
},
|
||||
"andrea|Il ponte delle poste, coi suoi tre archi di mattoni: da sotto pare più vecchio del paese.": {
|
||||
"file": "andrea-1e782b31f7.mp3",
|
||||
"sec": 5.87
|
||||
},
|
||||
"andrea|Altri orti, coi cavoli grossi come palloni.": {
|
||||
"file": "andrea-dc196f39fc.mp3",
|
||||
"sec": 3.41
|
||||
},
|
||||
"andrea|La Carza passa sotto gli archi e se ne va verso la Sieve.": {
|
||||
"file": "andrea-33a6033e4f.mp3",
|
||||
"sec": 3.16
|
||||
},
|
||||
"andrea|La casa dello zio Mario, quella gialla al sole.": {
|
||||
"file": "andrea-d641627592.mp3",
|
||||
"sec": 3.18
|
||||
},
|
||||
"andrea|Gli orti dello zio Mario: pomodori sulle canne, insalata, cavoli. Tutto in fila come soldati.": {
|
||||
"file": "andrea-9fd392ca78.mp3",
|
||||
"sec": 7.46
|
||||
},
|
||||
"andrea|Un pomodoro dell'orto dello zio? Se se ne accorge, e se ne accorge, son dolori.": {
|
||||
"file": "andrea-1f05d7a7da.mp3",
|
||||
"sec": 6.16
|
||||
},
|
||||
"andrea|Laggiù la passerella coperta sulla Carza.": {
|
||||
"file": "andrea-42e8b1e2b0.mp3",
|
||||
"sec": 2.81
|
||||
},
|
||||
"andrea|Di là ci passo un'altra volta: prima il palio.": {
|
||||
"file": "andrea-d38b6e49ef.mp3",
|
||||
"sec": 3.49
|
||||
},
|
||||
"andrea|Le case coi balconi pieni di vasi: qui ogni finestra è un giardino.": {
|
||||
"file": "andrea-ec81875c47.mp3",
|
||||
"sec": 5.22
|
||||
},
|
||||
"andrea|Il palazzo di mattoni e intonaco, coi balconi in fila.": {
|
||||
"file": "andrea-d32e1a2a87.mp3",
|
||||
"sec": 3.45
|
||||
},
|
||||
"andrea|Un pino a ombrello enorme. Alla ricreazione, tutti all'ombra lì sotto. Sembra ieri.": {
|
||||
"file": "andrea-0a073729fd.mp3",
|
||||
"sec": 7.3
|
||||
|
||||
@@ -18,6 +18,11 @@ Online su 50037.manzolo.it tutto fino agli effetti ambientali di Codex. Aperto,
|
||||
|
||||
## Dove siamo arrivati
|
||||
|
||||
- **Nuovi posti dalle foto di Andrea (29 ottobre, sfondi di Codex)**: Via della Compagnia (fra Via de' Medici, la
|
||||
Pieve e Villa Adami), Via Cafaggio in tre tratti fino alla rotonda, la Provinciale sotto i platani, la passeggiata
|
||||
sulla Carza (cascatella, orti, dietro le poste, sotto il ponte delle poste, gli orti dello zio Mario), la strada fra
|
||||
il bivio della Provinciale e la stazione. Le strade vecchie passano ora da questi posti. Battute provvisorie
|
||||
[DA ANDREA]; foto originali in `assets/raw/foto/`.
|
||||
- **La cerbottana (29 ottobre)**: in piazza, la bottega di Pierin di Frilli sotto la tenda rossa; si compra lo stucco
|
||||
verde e si tira al muro della piazza (`muro-cerbottana.png`, Codex). Motore `cerbottana.js`, disegno
|
||||
`cerbottana-render.js`: si mira, si tiene premuto per il fiato (giusto al 70%: poco cade sotto, troppo va sopra), il
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "leggenda-del-regolo",
|
||||
"version": "0.1.47",
|
||||
"version": "0.1.52",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -151,4 +151,117 @@ export const ambience = {
|
||||
{ type: 'cloudshadow', phase: day, area: [60, 220, 520, 140], offset: 23000,
|
||||
clip: [[0, 215], [400, 195], [640, 260], [640, 360], [0, 360]] },
|
||||
],
|
||||
// I posti nuovi del 29 ottobre (foto di Andrea): nuvole, uccelli, la Carza che scorre e luccica, fronde, ombre.
|
||||
via_compagnia: [
|
||||
{ type: 'clouds', area: [130, 0, 340, 80], count: 2 },
|
||||
{ type: 'lamps', phase: 'sera', points: [[115, 104]], glow: 10 },
|
||||
bird([250, 300, 70, 16], 9000),
|
||||
],
|
||||
via_cafaggio: [
|
||||
// Il panorama: Monte Cacioli e le colline, con le ombre delle nuvole che ci scorrono sopra.
|
||||
{ type: 'clouds', area: [0, 0, 640, 80], count: 4, big: true },
|
||||
{ type: 'cloudshadow', phase: day, area: [0, 80, 640, 90], offset: 6000, clip: rect([0, 80, 640, 90]) },
|
||||
{ type: 'cloudshadow', phase: day, area: [0, 80, 640, 90], offset: 32000, clip: rect([0, 80, 640, 90]) },
|
||||
{ type: 'birds', phase: day, area: [80, 20, 420, 70], count: 4, every: 21000, duration: 6000, offset: 3000 },
|
||||
{ type: 'sway', rects: [[560, 0, 80, 90]], amount: 2 },
|
||||
butterfly([40, 300, 160, 40], 15000),
|
||||
],
|
||||
via_cafaggio_fondo: [
|
||||
{ type: 'clouds', area: [0, 10, 380, 80], count: 3, big: true },
|
||||
{ type: 'birds', phase: day, area: [150, 20, 250, 60], count: 2, every: 26000, duration: 5000, offset: 8000 },
|
||||
{ type: 'sway', rects: [[390, 0, 150, 160]], amount: 1.5 },
|
||||
shade([300, 270, 220, 70], 12),
|
||||
{ type: 'lamps', phase: 'sera', points: [[252, 83], [319, 121]], glow: 9 },
|
||||
],
|
||||
via_cafaggio_avanti: [
|
||||
{ type: 'clouds', area: [150, 10, 400, 80], count: 3, big: true },
|
||||
{ type: 'sway', rects: [[400, 60, 200, 90]], amount: 1.5 },
|
||||
shade([180, 250, 260, 90], 12),
|
||||
bird([120, 300, 80, 16], 14000),
|
||||
],
|
||||
rotonda_cafaggio: [
|
||||
{ type: 'clouds', area: [200, 20, 420, 90], count: 4, big: true },
|
||||
{ type: 'birds', phase: day, area: [250, 20, 300, 60], count: 3, every: 23000, duration: 5500, offset: 5000 },
|
||||
{ type: 'sway', rects: [[0, 0, 140, 240], [350, 110, 200, 100], [560, 0, 80, 230]], amount: 2 },
|
||||
shade([0, 250, 200, 60], 12),
|
||||
],
|
||||
provinciale_luna: [
|
||||
{ type: 'clouds', area: [80, 20, 330, 80], count: 3, big: true },
|
||||
{ type: 'birds', phase: day, area: [100, 20, 280, 60], count: 3, every: 24000, duration: 5000, offset: 12000 },
|
||||
{ type: 'sway', rects: [[400, 0, 240, 170]], amount: 2 },
|
||||
shade([380, 230, 170, 100], 14),
|
||||
],
|
||||
passeggiata_carza: [
|
||||
// La cascatella che si vede dalla Luna: l'acqua che scende luccica, e la Carza scorre verso valle.
|
||||
{ type: 'ripples', area: [150, 200, 196, 55], count: 14, flow: [-10, 4],
|
||||
clip: [[150, 242], [210, 226], [290, 204], [346, 200], [346, 212], [300, 224], [225, 246], [160, 256]] },
|
||||
{ type: 'sparkle', area: [296, 184, 46, 18], count: 5 },
|
||||
{ type: 'sparkle', area: [170, 222, 120, 26], count: 3 },
|
||||
{ type: 'clouds', area: [380, 40, 140, 50], count: 2 },
|
||||
{ type: 'sway', rects: [[0, 0, 180, 250], [300, 0, 100, 200], [440, 60, 100, 120]], amount: 2 },
|
||||
shade([200, 240, 300, 60], 14),
|
||||
{ type: 'insect', kind: 'dragonfly', phase: day, area: [180, 214, 110, 30], every: 33000, duration: 9000, offset: 4000 },
|
||||
butterfly([380, 228, 120, 40], 17000),
|
||||
{ type: 'lamps', phase: 'sera', points: [[519, 77]], glow: 10 },
|
||||
],
|
||||
passeggiata_carza_avanti: [
|
||||
{ type: 'ripples', area: [480, 236, 160, 70], count: 10, flow: [10, 4],
|
||||
clip: [[480, 240], [560, 250], [640, 272], [640, 318], [560, 292], [500, 270]] },
|
||||
{ type: 'sparkle', area: [520, 252, 100, 40], count: 3 },
|
||||
{ type: 'clouds', area: [0, 0, 640, 90], count: 4, big: true },
|
||||
{ type: 'birds', phase: day, area: [150, 20, 350, 60], count: 3, every: 22000, duration: 5500, offset: 7000 },
|
||||
{ type: 'sway', rects: [[250, 10, 110, 170], [360, 40, 90, 120], [500, 60, 140, 120]], amount: 2 },
|
||||
butterfly([40, 200, 160, 50], 9000),
|
||||
],
|
||||
passeggiata_poste: [
|
||||
{ type: 'ripples', area: [470, 270, 170, 88], count: 12, flow: [10, 3],
|
||||
clip: [[470, 278], [560, 272], [640, 300], [640, 358], [560, 358], [480, 302]] },
|
||||
{ type: 'sparkle', area: [500, 290, 120, 50], count: 3 },
|
||||
{ type: 'clouds', area: [380, 0, 260, 90], count: 3, big: true },
|
||||
{ type: 'sway', rects: [[150, 50, 170, 150], [400, 120, 240, 90]], amount: 2 },
|
||||
shade([180, 250, 130, 60], 10),
|
||||
{ type: 'lamps', phase: 'sera', points: [[337, 144]], glow: 10 },
|
||||
],
|
||||
passeggiata_ponte: [
|
||||
{ type: 'ripples', area: [480, 250, 160, 108], count: 12, flow: [10, 4],
|
||||
clip: [[480, 256], [560, 250], [640, 272], [640, 358], [580, 358], [500, 300]] },
|
||||
{ type: 'sparkle', area: [500, 270, 120, 70], count: 4 },
|
||||
{ type: 'clouds', area: [240, 20, 300, 80], count: 3, big: true },
|
||||
{ type: 'birds', phase: day, area: [250, 30, 250, 50], count: 2, every: 26000, duration: 5000, offset: 16000 },
|
||||
{ type: 'sway', rects: [[0, 0, 250, 190], [270, 80, 370, 160]], amount: 2 },
|
||||
butterfly([60, 220, 100, 40], 11000),
|
||||
],
|
||||
ponte_stazione: [
|
||||
{ type: 'ripples', area: [300, 214, 340, 102], count: 18, flow: [12, 4],
|
||||
clip: [[300, 218], [380, 214], [520, 240], [640, 280], [640, 316], [560, 300], [420, 252], [320, 228]] },
|
||||
{ type: 'sparkle', area: [420, 240, 200, 60], count: 5 },
|
||||
{ type: 'clouds', area: [180, 10, 280, 80], count: 3, big: true },
|
||||
{ type: 'sway', rects: [[0, 0, 170, 190], [390, 0, 250, 210]], amount: 2 },
|
||||
butterfly([150, 240, 150, 50], 6000),
|
||||
{ type: 'insect', kind: 'dragonfly', phase: day, area: [440, 228, 110, 30], every: 31000, duration: 8500, offset: 15000 },
|
||||
],
|
||||
orti_zio_mario: [
|
||||
{ type: 'clouds', area: [80, 20, 500, 90], count: 3, big: true },
|
||||
{ type: 'birds', phase: day, area: [300, 30, 250, 60], count: 3, every: 24000, duration: 5000, offset: 2000 },
|
||||
{ type: 'sway', rects: [[180, 10, 140, 180], [430, 10, 210, 240]], amount: 2 },
|
||||
butterfly([10, 240, 140, 50], 8000),
|
||||
bird([280, 290, 80, 18], 13000, 'blackbird'),
|
||||
{ type: 'lamps', phase: 'sera', points: [[352, 110]], glow: 10 },
|
||||
],
|
||||
viale_stazione: [
|
||||
{ type: 'clouds', area: [230, 0, 380, 110], count: 4, big: true },
|
||||
{ type: 'birds', phase: day, area: [250, 20, 300, 60], count: 3, every: 22000, duration: 5500, offset: 9000 },
|
||||
{ type: 'sway', rects: [[270, 110, 90, 110], [330, 160, 140, 70]], amount: 1.5 },
|
||||
{ type: 'lamps', phase: 'sera', points: [[396, 117], [405, 155]], glow: 9 },
|
||||
bird([520, 260, 60, 16], 18000),
|
||||
],
|
||||
terrazza: [
|
||||
// Il panorama di casa: il paese e la Fortezza sotto un cielo che si muove, le ombre delle nuvole sulla collina.
|
||||
{ type: 'clouds', area: [0, 0, 640, 80], count: 5, big: true },
|
||||
{ type: 'cloudshadow', phase: day, area: [0, 40, 640, 200], offset: 4000, clip: rect([0, 40, 640, 200]) },
|
||||
{ type: 'cloudshadow', phase: day, area: [0, 40, 640, 200], offset: 30000, clip: rect([0, 40, 640, 200]) },
|
||||
{ type: 'birds', phase: day, area: [60, 20, 480, 80], count: 5, every: 19000, duration: 6500, offset: 1000 },
|
||||
{ type: 'sway', rects: [[54, 176, 136, 100]], amount: 1.5 },
|
||||
butterfly([500, 230, 100, 40], 10000),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -790,7 +790,7 @@ export const story = {
|
||||
id: 'verso_forno', name: 'a destra, verso il forno', box: [570, 176, 70, 76], at: [588, 250], exit: { scene: 'forno', at: [628, 300], face: 'left' },
|
||||
},
|
||||
{
|
||||
id: 'salita_villa_adami', name: 'salita verso Villa Adami', box: [440, 330, 200, 30], at: [600, 350], exit: { scene: 'villa_adami', at: [595, 268], face: 'left' },
|
||||
id: 'salita_villa_adami', name: 'su per Via della Compagnia, verso Villa Adami', box: [440, 330, 200, 30], at: [600, 350], exit: { scene: 'via_compagnia', at: [94, 316], face: 'right' },
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -978,7 +978,7 @@ export const story = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'su_villa_adami', name: 'su verso Villa Adami', box: [272, 186, 60, 34], at: [300, 216], exit: { scene: 'villa_adami', at: [50, 252], face: 'right' },
|
||||
id: 'su_villa_adami', name: 'su, verso Via della Compagnia', box: [272, 186, 60, 34], at: [300, 216], exit: { scene: 'via_compagnia', at: [300, 350], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'giu_piazzetta', name: 'giù verso Piazzetta Cambray Digny', box: [0, 334, 545, 26], at: [300, 354], exit: { scene: 'piazzetta', at: [20, 320], face: 'right' },
|
||||
@@ -1318,6 +1318,13 @@ export const story = {
|
||||
{
|
||||
id: 'alla_stazione', name: 'verso la stazione', box: [100, 110, 80, 130], at: [137, 240], exit: { scene: 'stazione', at: [628, 320], face: 'left' },
|
||||
},
|
||||
{
|
||||
// Dietro le poste passa la passeggiata sulla Carza (Andrea, 29 ottobre).
|
||||
id: 'alla_carza', name: 'dietro, la passeggiata sulla Carza', box: [560, 150, 80, 80], at: [600, 250], exit: { scene: 'passeggiata_poste', at: [280, 240], face: 'right' },
|
||||
},
|
||||
{
|
||||
id: 'alla_strada', name: 'indietro, sulla strada della stazione', box: [100, 336, 440, 24], at: [300, 352], exit: { scene: 'viale_stazione', at: [620, 280], face: 'left' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// Via Calimara: dalla Piazza scende alla farmacia, in Via Provinciale.
|
||||
@@ -1950,6 +1957,10 @@ export const story = {
|
||||
{
|
||||
id: 'alla_pieve', name: 'a destra, verso la Pieve', box: [590, 220, 50, 140], at: [628, 300], exit: { scene: 'pieve', at: [588, 250], face: 'left' },
|
||||
},
|
||||
{
|
||||
// Dietro, su per l'arco di Via della Compagnia (Andrea, 29 ottobre).
|
||||
id: 'all_arco', name: 'dietro, su per l\'arco di Via della Compagnia', box: [60, 334, 520, 26], at: [320, 350], exit: { scene: 'via_compagnia', at: [222, 296], face: 'up' },
|
||||
},
|
||||
],
|
||||
},
|
||||
stazione: {
|
||||
@@ -1977,11 +1988,15 @@ export const story = {
|
||||
id: 'al_ponticino', name: 'a sinistra, verso il ponticino della Carza', box: [0, 190, 56, 120], at: [14, 300], exit: { scene: 'ponticino', at: [474, 202], face: 'down' },
|
||||
},
|
||||
{
|
||||
id: 'al_bivio', name: 'indietro, verso la Provinciale', box: [60, 334, 520, 26], at: [320, 354], exit: { scene: 'bivio_provinciale', at: [580, 345], face: 'left' },
|
||||
id: 'al_bivio', name: 'indietro, verso la Provinciale', box: [60, 334, 520, 26], at: [320, 354], exit: { scene: 'viale_stazione', at: [340, 236], face: 'down' },
|
||||
},
|
||||
{
|
||||
id: 'ai_giardini', name: 'a destra, il campone delle poste', box: [590, 190, 50, 100], at: [628, 300], exit: { scene: 'giardini_poste', at: [137, 240], face: 'down' },
|
||||
},
|
||||
{
|
||||
// Dal ponte delle poste si scende sulla passeggiata della Carza (Andrea, 29 ottobre).
|
||||
id: 'giu_carza', name: 'giù dal ponte, la passeggiata sulla Carza', box: [590, 296, 50, 38], at: [620, 320], exit: { scene: 'ponte_stazione', at: [440, 252], face: 'left' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// Via Provinciale fra il forno e la stazione: qui si sbuca dalla scuola.
|
||||
@@ -2009,7 +2024,7 @@ export const story = {
|
||||
id: 'al_forno', name: 'laggiù, verso il forno', box: [280, 196, 80, 40], at: [318, 224], exit: { scene: 'forno', at: [12, 300], face: 'right' },
|
||||
},
|
||||
{
|
||||
id: 'alla_stazione', name: 'a destra, la stazione', box: [560, 250, 80, 110], at: [580, 345], exit: { scene: 'stazione', at: [320, 350], face: 'up' },
|
||||
id: 'alla_stazione', name: 'a destra, verso la stazione', box: [560, 250, 80, 110], at: [580, 345], exit: { scene: 'viale_stazione', at: [300, 352], face: 'up' },
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -2024,7 +2039,7 @@ export const story = {
|
||||
entry: [300, 350],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'verso_la_sieve', name: 'avanti, verso La Luna', box: [392, 220, 80, 22], at: [435, 236], exit: { scene: 'verso_luna', at: [150, 206], face: 'down' },
|
||||
id: 'verso_la_sieve', name: 'avanti, alla rotonda: Via Cafaggio e La Luna', box: [392, 220, 80, 22], at: [435, 236], exit: { scene: 'rotonda_cafaggio', at: [20, 245], face: 'right' },
|
||||
},
|
||||
{
|
||||
id: 'negozi', name: 'negozi', box: [20, 150, 200, 95], at: [150, 330], face: 'left',
|
||||
@@ -2090,7 +2105,7 @@ export const story = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'al_viale', name: 'indietro, verso il paese', box: [0, 182, 240, 26], at: [150, 200], exit: { scene: 'provinciale_viale', at: [435, 238], face: 'down' },
|
||||
id: 'al_viale', name: 'indietro, verso la rotonda di Via Cafaggio', box: [0, 182, 240, 26], at: [150, 200], exit: { scene: 'provinciale_luna', at: [200, 352], face: 'up' },
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -2456,14 +2471,15 @@ export const story = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'verso_piazza', name: 'Via de\' Medici, verso la Piazza', box: [20, 215, 50, 40], at: [45, 250], exit: { scene: 'via_de_medici', at: [300, 216], face: 'down' },
|
||||
},
|
||||
{
|
||||
id: 'verso_pieve', name: 'discesa verso la Pieve', box: [560, 236, 60, 38], at: [600, 266], exit: { scene: 'pieve', at: [600, 350], face: 'left' },
|
||||
id: 'verso_compagnia', name: 'a destra, giù per Via della Compagnia', box: [560, 236, 60, 38], at: [600, 266], exit: { scene: 'via_compagnia', at: [440, 258], face: 'down' },
|
||||
},
|
||||
{
|
||||
id: 'salita', name: 'salita verso la Fortezza', box: [100, 330, 400, 30], at: [300, 354], exit: { scene: 'salita_villa_adami', at: [340, 352], face: 'right' },
|
||||
},
|
||||
{
|
||||
// Oltre il muretto si scende alla scuola e a Via Cafaggio (Andrea, 29 ottobre).
|
||||
id: 'alla_scuola', name: 'giù, verso la scuola e Via Cafaggio', box: [502, 296, 138, 64], at: [498, 345], exit: { scene: 'via_cafaggio', at: [150, 352], face: 'up' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// Il Poggio, dalla salita del leone alla Fortezza: la stradina col muretto e tutto il Mugello sotto (foto di Andrea, 24 ottobre).
|
||||
@@ -2529,7 +2545,7 @@ export const story = {
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'verso_scuola', name: 'strada verso la scuola', box: [0, 286, 120, 44], at: [40, 336], face: 'left', exit: { scene: 'scuola', at: [128, 340], face: 'right' },
|
||||
id: 'verso_scuola', name: 'giù, verso la scuola e Via Cafaggio', box: [0, 286, 120, 44], at: [40, 336], face: 'left', exit: { scene: 'via_cafaggio', at: [300, 350], face: 'up' },
|
||||
actions: {
|
||||
look: [{ say: 'Di là si va alla scuola. Ci ho passato abbastanza anni, grazie.' }],
|
||||
},
|
||||
@@ -2755,11 +2771,11 @@ export const story = {
|
||||
},
|
||||
},
|
||||
{
|
||||
// «Di chi è quell'Audi nera?» «Let it be» (che si legge «L'è di Bi»: è di Bini). Andrea, 28 ottobre.
|
||||
// «Di chi è quell'Audi lì?» «Let it be» (che si legge «L'è di Bi»: è di Bini). Andrea, 28 ottobre.
|
||||
id: 'audi', name: 'Audi nera', box: [292, 198, 68, 40], at: [330, 254], face: 'up', default: 'look',
|
||||
actions: {
|
||||
look: [{ say: 'Di chi è quell\'Audi nera?' }, { say: 'Let it be.', who: 'arcu', pose: 'explain' }],
|
||||
talk: [{ say: 'Di chi è quell\'Audi nera?' }, { say: 'Let it be.', who: 'arcu', pose: 'explain' }],
|
||||
look: [{ say: 'Di chi è quell\'Audi lì?' }, { say: 'Let it be.', who: 'arcu', pose: 'explain' }],
|
||||
talk: [{ say: 'Di chi è quell\'Audi lì?' }, { say: 'Let it be.', who: 'arcu', pose: 'explain' }],
|
||||
use: [{ say: 'Non è mica mia. Mi garba, però, nera fiammante.' }],
|
||||
},
|
||||
},
|
||||
@@ -2793,7 +2809,7 @@ export const story = {
|
||||
},
|
||||
{
|
||||
// Il campo del Giannini è laggiù a destra, oltre la siepe (Andrea, 28 ottobre).
|
||||
id: 'al_giannini', name: 'verso il campo del Giannini', box: [404, 150, 58, 50], at: [520, 306], exit: { scene: 'campo_giannini', at: [60, 250], face: 'right' },
|
||||
id: 'al_giannini', name: 'verso il campo del Giannini', box: [404, 150, 160, 42], at: [520, 306], exit: { scene: 'campo_giannini', at: [60, 250], face: 'right' },
|
||||
},
|
||||
// In fondo all'elenco, così sta sopra all'uscita e alla siepe.
|
||||
{
|
||||
@@ -2917,6 +2933,395 @@ export const story = {
|
||||
},
|
||||
],
|
||||
},
|
||||
// Via della Compagnia (foto di Andrea, 29 ottobre): la strada che sale dalla Pieve verso la Fortezza. Alle spalle
|
||||
// Via de' Medici, a sinistra l'arco che scende alla Pieve, a destra si sale a Villa Adami. [DA ANDREA: le battute]
|
||||
via_compagnia: {
|
||||
name: 'Via della Compagnia',
|
||||
map: [34, 30],
|
||||
background: 'assets/scenes/via-della-compagnia.png',
|
||||
walk: [[0, 360], [0, 318], [88, 306], [200, 292], [250, 290], [330, 272], [420, 254], [446, 248], [460, 262], [540, 360]],
|
||||
depth: [[248, 0.55], [360, 1.3]],
|
||||
entry: [300, 350],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'stemma', name: 'stemma di pietra', box: [358, 160, 22, 18], at: [370, 266], face: 'up',
|
||||
actions: { look: [{ say: 'Un piccolo stemma di pietra sul muro. Chissà di chi era, questa casa, un tempo.' }] },
|
||||
},
|
||||
{
|
||||
id: 'lampione', name: 'lampione', box: [102, 90, 26, 36], at: [150, 306], face: 'up',
|
||||
actions: { look: [{ say: 'Il lampione di ferro, attaccato al muro. La sera fa una luce gialla che pare d\'altri tempi.' }] },
|
||||
},
|
||||
{
|
||||
// L'arco porta al forno; alla Pieve si scende fra le due case gialle (Andrea, 29 ottobre).
|
||||
id: 'arco_forno', name: 'sotto l\'arco, giù al forno', box: [198, 222, 50, 66], at: [222, 296], exit: { scene: 'forno', at: [320, 350], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'alla_pieve', name: 'fra le case gialle, giù alla Pieve', box: [80, 196, 40, 100], at: [94, 316], exit: { scene: 'pieve', at: [600, 350], face: 'left' },
|
||||
},
|
||||
{
|
||||
id: 'su_villa_adami', name: 'su verso Villa Adami', box: [420, 196, 42, 56], at: [440, 258], exit: { scene: 'villa_adami', at: [600, 266], face: 'left' },
|
||||
},
|
||||
{
|
||||
id: 'giu_medici', name: 'giù per Via de\' Medici', box: [0, 336, 540, 24], at: [300, 352], exit: { scene: 'via_de_medici', at: [300, 216], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// Via Cafaggio (foto di Andrea, 29 ottobre): scendendo dalla salita di Villa Adami (alle spalle), a sinistra il
|
||||
// cancello della scuola con la palestra dal tetto a denti, dritto Via Cafaggio, lassù Monte Cacioli con le antenne.
|
||||
// [DA ANDREA: le battute]
|
||||
via_cafaggio: {
|
||||
name: 'Via Cafaggio',
|
||||
map: [26, 17],
|
||||
background: 'assets/scenes/via-cafaggio.png',
|
||||
walk: [[0, 360], [0, 346], [160, 318], [300, 268], [345, 258], [400, 240], [470, 208], [500, 188], [515, 188], [530, 230], [555, 300], [565, 360]],
|
||||
depth: [[188, 0.3], [360, 1.4]],
|
||||
entry: [300, 350],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'palestra', name: 'palestra della scuola', box: [20, 165, 250, 60], at: [200, 324], face: 'up',
|
||||
actions: { look: [{ say: 'La palestra della scuola, col tetto a denti di sega.' }] },
|
||||
},
|
||||
{
|
||||
id: 'monte_cacioli', name: 'Monte Cacioli', box: [230, 86, 170, 56], at: [300, 300], face: 'up',
|
||||
actions: { look: [{ say: 'Lassù Monte Cacioli, con le antenne in cima.' }] },
|
||||
},
|
||||
{
|
||||
id: 'giu_cafaggio', name: 'dritto, giù per Via Cafaggio', box: [486, 166, 46, 36], at: [507, 192], exit: { scene: 'via_cafaggio_fondo', at: [300, 350], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'cancello', name: 'cancello della palestra', box: [298, 188, 52, 70], at: [322, 268], face: 'up',
|
||||
actions: { look: [{ say: 'Il cancello della palestra. Per la scuola si passa dalla stradina più in là.' }], open: [{ say: 'Chiuso. Per la scuola si passa dalla stradina più in là.' }] },
|
||||
},
|
||||
{
|
||||
// Alla scuola non si passa dal cancello ma dalla stradina subito dopo (Andrea, 29 ottobre).
|
||||
id: 'alla_scuola', name: 'la stradina della scuola', box: [380, 172, 50, 32], at: [410, 246], exit: { scene: 'scuola', at: [128, 340], face: 'right' },
|
||||
},
|
||||
{
|
||||
id: 'su_villa', name: 'su, verso Villa Adami', box: [0, 336, 280, 24], at: [150, 352], exit: { scene: 'villa_adami', at: [498, 345], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'su_salita', name: 'su per la salita, verso la Fortezza', box: [280, 336, 280, 24], at: [400, 352], exit: { scene: 'salita_villa_adami', at: [40, 336], face: 'right' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// Via Cafaggio più giù (foto di Andrea, 29 ottobre): dritta fra le case, verso la Provinciale. [DA ANDREA: battute]
|
||||
via_cafaggio_fondo: {
|
||||
name: 'Via Cafaggio',
|
||||
map: [44, 40],
|
||||
background: 'assets/scenes/via-cafaggio-fondo.png',
|
||||
walk: [[60, 360], [150, 300], [260, 240], [340, 196], [368, 176], [386, 176], [400, 200], [420, 262], [470, 282], [640, 318], [640, 360]],
|
||||
depth: [[176, 0.3], [360, 1.4]],
|
||||
entry: [300, 350],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'siepe', name: 'siepe', box: [400, 0, 240, 260], at: [430, 290], face: 'right',
|
||||
actions: { look: [{ say: 'Una siepe alta come una casa, e l\'edera che si mangia il muro.' }] },
|
||||
},
|
||||
{
|
||||
id: 'villette', name: 'villette', box: [0, 30, 200, 130], at: [200, 300], face: 'left',
|
||||
actions: { look: [{ say: 'Le villette col giardino e il muretto: qui è tutto in ordine.' }] },
|
||||
},
|
||||
{
|
||||
id: 'avanti_cafaggio', name: 'avanti per Via Cafaggio', box: [350, 150, 56, 34], at: [377, 180], exit: { scene: 'via_cafaggio_avanti', at: [300, 350], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'su_cafaggio', name: 'indietro, su per Via Cafaggio', box: [0, 336, 640, 24], at: [300, 352], exit: { scene: 'via_cafaggio', at: [507, 192], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// Via Cafaggio, ancora avanti (foto di Andrea, 29 ottobre: Codex ha ricostruito il pezzo coperto dal dito).
|
||||
via_cafaggio_avanti: {
|
||||
name: 'Via Cafaggio, verso la rotonda',
|
||||
map: [56, 58],
|
||||
background: 'assets/scenes/via-cafaggio-avanti.png',
|
||||
walk: [[0, 360], [0, 330], [110, 268], [230, 190], [248, 176], [272, 176], [300, 200], [360, 262], [440, 330], [460, 360]],
|
||||
depth: [[176, 0.3], [360, 1.4]],
|
||||
entry: [300, 350],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'villa_archi', name: 'villa con gli archi', box: [470, 40, 170, 90], at: [380, 300], face: 'right',
|
||||
actions: { look: [{ say: 'Una villa con gli archi dietro la siepe. Qui le siepi le tosano col righello.' }] },
|
||||
},
|
||||
{
|
||||
id: 'alla_rotonda', name: 'in fondo, alla rotonda della Provinciale', box: [236, 158, 50, 34], at: [260, 180], exit: { scene: 'rotonda_cafaggio', at: [390, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'su_cafaggio', name: 'indietro, su per Via Cafaggio', box: [0, 336, 460, 24], at: [300, 352], exit: { scene: 'via_cafaggio_fondo', at: [377, 180], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// In fondo a Via Cafaggio, la rotonda sulla Provinciale (foto di Andrea, 29 ottobre): a sinistra il viale
|
||||
// alberato, dritto verso La Luna, lungo la Carza.
|
||||
rotonda_cafaggio: {
|
||||
name: 'La rotonda in fondo a Via Cafaggio',
|
||||
map: [64, 66],
|
||||
background: 'assets/scenes/rotonda-cafaggio.png',
|
||||
walk: [[180, 360], [230, 320], [300, 290], [330, 262], [0, 252], [0, 238], [360, 236], [520, 224], [560, 224], [590, 300], [600, 360]],
|
||||
depth: [[224, 0.4], [360, 1.35]],
|
||||
entry: [390, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'cipresso', name: 'cipresso', box: [146, 0, 64, 250], at: [300, 292], face: 'left',
|
||||
actions: { look: [{ say: 'Un cipresso dritto come un fuso, in mezzo all\'aiuola.' }] },
|
||||
},
|
||||
{
|
||||
id: 'panchina', name: 'panchina', box: [68, 252, 110, 40], at: [260, 336], face: 'left',
|
||||
actions: { look: [{ say: 'Una panchina all\'ombra.' }], use: [{ say: 'Sedersi ora? Prima il palio.' }] },
|
||||
},
|
||||
{
|
||||
id: 'palazzo_rosso', name: 'palazzo di mattoni', box: [204, 136, 146, 88], at: [360, 240], face: 'up',
|
||||
actions: { look: [{ say: 'Il palazzo di mattoni coi negozi sotto, di là dalla Provinciale.' }] },
|
||||
},
|
||||
{
|
||||
id: 'al_viale', name: 'a sinistra, il viale alberato', box: [0, 222, 60, 40], at: [20, 245], exit: { scene: 'provinciale_viale', at: [435, 238], face: 'down' },
|
||||
},
|
||||
{
|
||||
id: 'verso_luna', name: 'dritto, lungo la Provinciale verso La Luna', box: [500, 204, 60, 30], at: [540, 226], exit: { scene: 'provinciale_luna', at: [330, 198], face: 'down' },
|
||||
},
|
||||
{
|
||||
id: 'su_cafaggio', name: 'indietro, su per Via Cafaggio', box: [180, 336, 420, 24], at: [390, 352], exit: { scene: 'via_cafaggio_avanti', at: [260, 180], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// La Provinciale fra La Luna (alle spalle) e la rotonda di Via Cafaggio (davanti); a destra, fra i platani, la
|
||||
// passeggiata sulla Carza (foto di Andrea, 29 ottobre). [DA ANDREA: le battute]
|
||||
provinciale_luna: {
|
||||
name: 'La Provinciale, sotto i platani',
|
||||
map: [65, 70],
|
||||
background: 'assets/scenes/provinciale-luna.png',
|
||||
walk: [[0, 360], [0, 262], [150, 222], [290, 200], [380, 192], [420, 192], [470, 200], [505, 240], [510, 268], [400, 268], [390, 360]],
|
||||
depth: [[190, 0.35], [360, 1.4]],
|
||||
entry: [200, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'platani', name: 'platani', box: [530, 0, 110, 360], at: [470, 262], face: 'right',
|
||||
actions: { look: [{ say: 'I platani della Provinciale: d\'estate fanno un\'ombra che pare una galleria.' }] },
|
||||
},
|
||||
{
|
||||
id: 'villette', name: 'villette', box: [0, 70, 250, 110], at: [150, 300], face: 'left',
|
||||
actions: { look: [{ say: 'Le villette sotto il bosco, coi giardini curati.' }] },
|
||||
},
|
||||
{
|
||||
id: 'alla_rotonda', name: 'avanti, alla rotonda di Via Cafaggio', box: [290, 176, 90, 24], at: [330, 198], exit: { scene: 'rotonda_cafaggio', at: [540, 226], face: 'left' },
|
||||
},
|
||||
{
|
||||
id: 'alla_passeggiata', name: 'a destra, la passeggiata sulla Carza', box: [462, 186, 48, 44], at: [480, 232], exit: { scene: 'passeggiata_carza', at: [330, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'alla_luna', name: 'indietro, verso La Luna', box: [0, 336, 390, 24], at: [200, 352], exit: { scene: 'verso_luna', at: [150, 206], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// La passeggiata sulla Carza (foto di Andrea, 29 ottobre): il prato, il greto e la cascatella che si vede dalla
|
||||
// Luna. [DA ANDREA: le battute]
|
||||
passeggiata_carza: {
|
||||
name: 'La passeggiata sulla Carza',
|
||||
map: [70, 76],
|
||||
background: 'assets/scenes/passeggiata-carza.png',
|
||||
walk: [[0, 360], [0, 300], [190, 300], [230, 270], [330, 235], [420, 200], [540, 195], [560, 260], [520, 300], [540, 360]],
|
||||
depth: [[195, 0.45], [360, 1.35]],
|
||||
entry: [330, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'cascata', name: 'la cascatella', box: [288, 172, 84, 36], at: [330, 246], face: 'left',
|
||||
actions: { look: [{ say: 'La cascatella della Carza: quella che si vede anche dalla Luna.' }, { say: 'D\'estate si veniva a rinfrescare i piedi qui sotto.' }] },
|
||||
},
|
||||
{
|
||||
id: 'greto', name: 'greto della Carza', box: [150, 186, 150, 70], at: [230, 280], face: 'left',
|
||||
actions: { look: [{ say: 'Il greto pieno di sassi. Dighe, ponti, pesci immaginari: qui ci si passava l\'estate.' }] },
|
||||
},
|
||||
{
|
||||
id: 'lampione', name: 'lampione', box: [504, 70, 26, 186], at: [480, 270], face: 'right',
|
||||
actions: { look: [{ say: 'Un lampione in mezzo al prato. Di sera la passeggiata la fanno le zanzare.' }] },
|
||||
},
|
||||
{
|
||||
// Il sentiero va avanti a sinistra, dietro il grande albero (Andrea, 29 ottobre).
|
||||
id: 'avanti_carza', name: 'avanti, lungo la Carza', box: [0, 200, 50, 130], at: [20, 330], exit: { scene: 'passeggiata_carza_avanti', at: [300, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'alla_provinciale', name: 'indietro, sulla Provinciale', box: [180, 336, 360, 24], at: [330, 352], exit: { scene: 'provinciale_luna', at: [480, 232], face: 'left' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// La passeggiata sulla Carza più avanti (foto di Andrea, 29 ottobre): il ponticino, gli orti, il greto.
|
||||
// [DA ANDREA: le battute]
|
||||
passeggiata_carza_avanti: {
|
||||
name: 'La passeggiata sulla Carza, gli orti',
|
||||
map: [74, 80],
|
||||
background: 'assets/scenes/passeggiata-carza-avanti.png',
|
||||
walk: [[0, 360], [0, 300], [180, 236], [262, 186], [300, 186], [350, 222], [470, 262], [560, 300], [600, 360]],
|
||||
depth: [[186, 0.4], [360, 1.4]],
|
||||
entry: [300, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'orti', name: 'orti', box: [0, 150, 220, 130], at: [150, 310], face: 'left',
|
||||
actions: { look: [{ say: 'Gli orti con la rete: cavoli, insalata e i fagioli sulle canne.' }], take: [{ say: 'Un cavolo dall\'orto di qualcun altro? Poi chi lo sente.' }] },
|
||||
},
|
||||
{
|
||||
id: 'ponticino', name: 'ponticino', box: [280, 182, 72, 40], at: [316, 232], face: 'up',
|
||||
actions: { look: [{ say: 'Un ponticino di ferro sul fosso. Due passi e sei di là.' }] },
|
||||
},
|
||||
{
|
||||
id: 'greto', name: 'greto della Carza', box: [470, 210, 170, 126], at: [520, 300], face: 'right',
|
||||
actions: { look: [{ say: 'Il greto bianco della Carza, con un filo d\'acqua in mezzo ai sassi.' }] },
|
||||
},
|
||||
{
|
||||
id: 'in_fondo', name: 'avanti, lungo la Carza', box: [252, 146, 56, 34], at: [282, 190], exit: { scene: 'passeggiata_poste', at: [250, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'indietro', name: 'indietro, verso la cascatella', box: [0, 336, 600, 24], at: [300, 352], exit: { scene: 'passeggiata_carza', at: [20, 330], face: 'right' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// La passeggiata sulla Carza, dietro le case (foto di Andrea, 29 ottobre): a sinistra si va al campone delle poste,
|
||||
// dritto si continua verso il ponte delle poste. [DA ANDREA: le battute]
|
||||
passeggiata_poste: {
|
||||
name: 'La passeggiata sulla Carza, dietro le poste',
|
||||
map: [16, 64],
|
||||
background: 'assets/scenes/passeggiata-carza-poste.png',
|
||||
walk: [[90, 360], [180, 300], [260, 250], [300, 224], [318, 224], [330, 250], [350, 300], [420, 360]],
|
||||
depth: [[224, 0.45], [360, 1.4]],
|
||||
entry: [250, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'casa', name: 'casa con la siepe', box: [0, 0, 150, 170], at: [150, 330], face: 'left',
|
||||
actions: { look: [{ say: 'Una casa con la siepe rossa davanti. Da quel balcone si vede tutta la Carza.' }] },
|
||||
},
|
||||
{
|
||||
id: 'carza', name: 'la Carza', box: [440, 230, 200, 130], at: [370, 320], face: 'right',
|
||||
actions: { look: [{ say: 'La Carza sotto il muretto, con le canne dritte nell\'acqua e il muro vecchio di sassi.' }] },
|
||||
},
|
||||
{
|
||||
id: 'al_campone', name: 'a sinistra, il campone delle poste', box: [226, 196, 48, 40], at: [280, 240], exit: { scene: 'giardini_poste', at: [600, 250], face: 'left' },
|
||||
},
|
||||
{
|
||||
id: 'avanti', name: 'avanti, verso il ponte delle poste', box: [286, 196, 52, 30], at: [310, 226], exit: { scene: 'passeggiata_ponte', at: [225, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'indietro', name: 'indietro, verso gli orti', box: [90, 336, 330, 24], at: [250, 352], exit: { scene: 'passeggiata_carza_avanti', at: [282, 190], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// L'ultimo pezzo della passeggiata sulla Carza (foto di Andrea, 29 ottobre): gli orti a sinistra, la Carza a destra,
|
||||
// in fondo il ponte che collega le poste alla stazione. [DA ANDREA: le battute]
|
||||
passeggiata_ponte: {
|
||||
name: 'La passeggiata sulla Carza, verso il ponte',
|
||||
map: [14, 70],
|
||||
background: 'assets/scenes/passeggiata-carza-ponte.png',
|
||||
walk: [[150, 360], [200, 290], [226, 200], [242, 200], [260, 280], [300, 360]],
|
||||
depth: [[200, 0.4], [360, 1.4]],
|
||||
entry: [225, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'orti', name: 'orti', box: [0, 140, 170, 180], at: [190, 320], face: 'left',
|
||||
actions: { look: [{ say: 'Gli orti fatti coi bancali e le cassette, il bidone blu per l\'acqua. Ognuno a modo suo.' }] },
|
||||
},
|
||||
{
|
||||
id: 'carza', name: 'la Carza', box: [450, 210, 190, 150], at: [270, 330], face: 'right',
|
||||
actions: { look: [{ say: 'Qui la Carza ha un po\' d\'acqua chiara fra i sassi bianchi.' }] },
|
||||
},
|
||||
{
|
||||
id: 'al_ponte', name: 'avanti, al ponte delle poste', box: [212, 168, 46, 36], at: [234, 204], exit: { scene: 'ponte_stazione', at: [300, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'indietro', name: 'indietro, dietro le poste', box: [150, 336, 150, 24], at: [225, 352], exit: { scene: 'passeggiata_poste', at: [310, 226], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// In fondo alla passeggiata sulla Carza, sotto il ponte ad archi che collega le poste alla stazione (foto di Andrea,
|
||||
// 29 ottobre): il viottolo a sinistra sale sul ponte. [DA ANDREA: le battute]
|
||||
ponte_stazione: {
|
||||
name: 'Sotto il ponte delle poste',
|
||||
map: [12, 75],
|
||||
background: 'assets/scenes/ponte-carza-stazione.png',
|
||||
walk: [[0, 360], [0, 320], [60, 290], [120, 240], [150, 190], [168, 190], [200, 240], [300, 232], [420, 236], [520, 280], [640, 300], [640, 360]],
|
||||
depth: [[190, 0.4], [360, 1.35]],
|
||||
entry: [300, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'ponte', name: 'il ponte ad archi', box: [190, 124, 200, 90], at: [300, 240], face: 'up',
|
||||
actions: { look: [{ say: 'Il ponte delle poste, coi suoi tre archi di mattoni: da sotto pare più vecchio del paese.' }] },
|
||||
},
|
||||
{
|
||||
id: 'orti', name: 'orti', box: [0, 180, 120, 90], at: [80, 300], face: 'left',
|
||||
actions: { look: [{ say: 'Altri orti, coi cavoli grossi come palloni.' }] },
|
||||
},
|
||||
{
|
||||
id: 'carza', name: 'la Carza', box: [440, 210, 200, 90], at: [460, 262], face: 'right',
|
||||
actions: { look: [{ say: 'La Carza passa sotto gli archi e se ne va verso la Sieve.' }] },
|
||||
},
|
||||
{
|
||||
// Il viottolo passa sotto l'arco e va avanti lungo la Carza; alla stazione si sale dalla parte destra del ponte
|
||||
// (Andrea, 29 ottobre).
|
||||
id: 'avanti_carza', name: 'avanti per il viottolo, sotto il ponte', box: [136, 158, 46, 34], at: [158, 192], exit: { scene: 'orti_zio_mario', at: [400, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'su_ponte', name: 'su sul ponte, alla stazione', box: [410, 86, 100, 60], at: [440, 252], exit: { scene: 'stazione', at: [620, 320], face: 'left' },
|
||||
},
|
||||
{
|
||||
id: 'indietro', name: 'indietro lungo la Carza', box: [0, 336, 640, 24], at: [300, 352], exit: { scene: 'passeggiata_ponte', at: [234, 204], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// Oltre il ponte delle poste, lungo la Carza (foto di Andrea, 29 ottobre): la casa gialla al sole è quella dello zio
|
||||
// Mario, e sotto ci sono i suoi orti. In fondo la passerella. [DA ANDREA: le battute]
|
||||
orti_zio_mario: {
|
||||
name: 'Gli orti dello zio Mario',
|
||||
map: [8, 82],
|
||||
background: 'assets/scenes/orti-zio-mario.png',
|
||||
walk: [[150, 360], [300, 250], [280, 236], [340, 236], [420, 248], [640, 330], [640, 360]],
|
||||
depth: [[236, 0.45], [360, 1.35]],
|
||||
entry: [400, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'casa_zio', name: 'casa dello zio Mario', box: [80, 90, 200, 90], at: [260, 290], face: 'left',
|
||||
actions: { look: [{ say: 'La casa dello zio Mario, quella gialla al sole.' }] },
|
||||
},
|
||||
{
|
||||
id: 'orti', name: 'gli orti dello zio Mario', box: [0, 180, 250, 170], at: [205, 330], face: 'left',
|
||||
actions: {
|
||||
look: [{ say: 'Gli orti dello zio Mario: pomodori sulle canne, insalata, cavoli. Tutto in fila come soldati.' }],
|
||||
take: [{ say: 'Un pomodoro dell\'orto dello zio? Se se ne accorge, e se ne accorge, son dolori.' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'passerella', name: 'la passerella sulla Carza', box: [384, 186, 70, 30], at: [410, 252], face: 'up',
|
||||
actions: { look: [{ say: 'Laggiù la passerella coperta sulla Carza.' }], walk: [{ say: 'Di là ci passo un\'altra volta: prima il palio.' }] },
|
||||
},
|
||||
{
|
||||
id: 'indietro', name: 'indietro, verso il ponte delle poste', box: [150, 336, 490, 24], at: [400, 352], exit: { scene: 'ponte_stazione', at: [158, 192], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// La strada fra il bivio della Provinciale (alle spalle) e la stazione (in fondo); a destra si va al campone delle
|
||||
// poste (foto di Andrea, 29 ottobre). [DA ANDREA: le battute]
|
||||
viale_stazione: {
|
||||
name: 'Verso la stazione',
|
||||
map: [20, 52],
|
||||
background: 'assets/scenes/viale-stazione.png',
|
||||
walk: [[30, 360], [230, 280], [320, 240], [330, 228], [360, 228], [400, 250], [440, 262], [600, 268], [640, 290], [640, 360]],
|
||||
depth: [[228, 0.35], [360, 1.4]],
|
||||
entry: [300, 352],
|
||||
hotspots: [
|
||||
{
|
||||
id: 'case', name: 'case coi balconi fioriti', box: [0, 0, 230, 270], at: [200, 320], face: 'left',
|
||||
actions: { look: [{ say: 'Le case coi balconi pieni di vasi: qui ogni finestra è un giardino.' }] },
|
||||
},
|
||||
{
|
||||
id: 'palazzo', name: 'palazzo di mattoni', box: [450, 20, 190, 220], at: [520, 280], face: 'right',
|
||||
actions: { look: [{ say: 'Il palazzo di mattoni e intonaco, coi balconi in fila.' }] },
|
||||
},
|
||||
{
|
||||
id: 'alla_stazione', name: 'avanti, la stazione', box: [320, 196, 50, 36], at: [340, 236], exit: { scene: 'stazione', at: [320, 350], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'al_campone', name: 'a destra, il campone delle poste', box: [590, 240, 50, 60], at: [620, 280], exit: { scene: 'giardini_poste', at: [300, 352], face: 'up' },
|
||||
},
|
||||
{
|
||||
id: 'al_bivio', name: 'indietro, al bivio della Provinciale', box: [30, 336, 610, 24], at: [300, 352], exit: { scene: 'bivio_provinciale', at: [580, 345], face: 'left' },
|
||||
},
|
||||
],
|
||||
},
|
||||
// L'ingresso della scuola, giù per la strada dalla salita di Villa Adami: dal cancello alle porte a vetri.
|
||||
scuola: {
|
||||
name: 'La scuola',
|
||||
@@ -2959,7 +3364,7 @@ export const story = {
|
||||
id: 'al_bivio', name: 'a destra, verso la Provinciale', box: [584, 220, 56, 120], at: [600, 320], exit: { scene: 'bivio_provinciale', at: [208, 226], face: 'right' },
|
||||
},
|
||||
{
|
||||
id: 'alla_salita', name: 'a sinistra, verso la salita', box: [0, 240, 130, 120], at: [128, 340], exit: { scene: 'salita_villa_adami', at: [40, 336], face: 'right' },
|
||||
id: 'alla_salita', name: 'a sinistra, dal cancello verso Via Cafaggio', box: [0, 240, 130, 120], at: [128, 340], exit: { scene: 'via_cafaggio', at: [410, 246], face: 'down' },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// La versione del gioco: la cambia scripts/bump.sh, insieme a package.json.
|
||||
export const VERSION = '0.1.47';
|
||||
export const VERSION = '0.1.52';
|
||||
|
||||
// Immagini, musiche e voci con ?v=<versione>: a ogni versione l'indirizzo cambia e il browser le riscarica,
|
||||
// mentre fra una versione e l'altra le tiene in cache a lungo (nginx.conf).
|
||||
|
||||
@@ -67,3 +67,18 @@ python3 video/monta.py ~/Workspaces/qemu/qemu-iso-lab/artifacts/tour/out/partita
|
||||
- **Il ritaglio** (`TAGLIO` in `monta.py`) va rimisurato quando cambia l'impaginazione: `convert -trim` su una
|
||||
schermata della prova a vuoto.
|
||||
- **Per Telegram** (50 MB al massimo): due passate x264 `-tune animation` a 470 kbit/s → circa 45 MB.
|
||||
|
||||
## Il trailer e la extended (29 ottobre 2026)
|
||||
|
||||
Copioni `video/trailer.mjs` (circa 1 min 30 s) e `video/trailer-extended.mjs` (circa 3 min 25 s, col giro completo
|
||||
del paese e i giochi giocati da soli). Niente clic: la scena è a tutto schermo, le scritte e gli stacchi al nero
|
||||
sono elementi aggiunti nella pagina, e la musica la sceglie il copione (`out/<clip>-audio.json`). Nel finale si
|
||||
cercano solo gli occhi gialli: il palio non si mostra (Andrea: si capirebbe dov'è).
|
||||
|
||||
- Prova locale, senza VM: `OUT=/tmp/prova COPIONE=./trailer.mjs node video/prova-locale.mjs` (una schermata per
|
||||
battuta, e i tempi).
|
||||
- Ripresa: come la partita completa, poi `python3 video/monta.py <cartella> --trailer`.
|
||||
- Il gioco va in pausa da solo dopo un minuto senza tocchi: il copione manda un finto `pointermove` ogni 10 s.
|
||||
- Una camminata lascia `game.task` in sospeso, e cambiando scena di colpo il gioco non si ferma più: si azzera.
|
||||
- Le battute saltate lasciano spezzoni di voce nel registro: prima di montare si tolgono quelli sotto il mezzo secondo
|
||||
(attenzione a «Let it be.», che dura un secondo ed è giusta).
|
||||
|
||||
@@ -4,6 +4,7 @@ copione -> video/out/<clip>.mp4.
|
||||
|
||||
python3 video/monta.py ~/Workspaces/qemu/qemu-iso-lab/artifacts/tour/out/demo-prototipo
|
||||
python3 video/monta.py ~/Workspaces/qemu/qemu-iso-lab/artifacts/tour/out/partita-completa
|
||||
python3 video/monta.py ~/Workspaces/qemu/qemu-iso-lab/artifacts/tour/out/trailer --trailer
|
||||
|
||||
- ritaglia lo schermo 1600x900 della VM sull'area del gioco e lascia sopra una fascia per le didascalie;
|
||||
- le didascalie (cues.json, solo «it») restano a schermo il tempo di leggerle, non fino alla successiva;
|
||||
@@ -31,6 +32,9 @@ VOLUME = 0.5 # musica della demo-prototipo (senza voci)
|
||||
VOLUME_SOTTO = 0.22 # musica sotto le voci: bassa (Andrea, 25 ottobre)
|
||||
VOLUME_VOCI = 1.0
|
||||
VOLUME_NARRATORE = 1.15
|
||||
# Il trailer (--trailer): tutto lo schermo della VM (la scena è a tutto schermo), niente fascia né didascalie (le
|
||||
# scritte sono nella pagina), la musica in primo piano.
|
||||
VOLUME_TRAILER = 0.6
|
||||
VOLUME_TUTORIAL = 0.07 # musica dei video «Come si gioca»: sottofondo # il narratore dei video «Come si gioca», sopra la musica bassa
|
||||
FADE = 1.2
|
||||
|
||||
@@ -49,6 +53,7 @@ def ts(s: float) -> str:
|
||||
def main() -> None:
|
||||
d = Path(sys.argv[1]).expanduser()
|
||||
tutorial = "--tutorial" in sys.argv[2:]
|
||||
trailer = "--trailer" in sys.argv[2:]
|
||||
taglio, fascia = (TAGLIO_TUTORIAL, FASCIA_TUTORIAL) if tutorial else (TAGLIO, FASCIA)
|
||||
meta = json.loads((d / "cues.json").read_text())
|
||||
nome = meta["clip"]
|
||||
@@ -71,6 +76,8 @@ def main() -> None:
|
||||
# «troppo alta la musica del gioco»).
|
||||
if (OUT / f"{nome}-narrazione.json").exists():
|
||||
volume = VOLUME_TUTORIAL
|
||||
if trailer:
|
||||
volume = VOLUME_TRAILER
|
||||
else:
|
||||
musica = json.loads((OUT / "musica.json").read_text())
|
||||
volume = VOLUME
|
||||
@@ -131,6 +138,8 @@ def main() -> None:
|
||||
mix = "".join(pezzi) + f"amix=inputs={len(pezzi)}:normalize=0:duration=longest[a]"
|
||||
stile = f"FontName=DejaVu Sans,FontSize=11,Bold=1,MarginV=4,Outline=1,Shadow=0,PrimaryColour=&H007AD2FF"
|
||||
video = f"[0:v]{taglio},pad=iw:ih+{fascia}:0:{fascia}:{FONDO},subtitles={srt}:force_style='{stile}'[v]"
|
||||
if trailer:
|
||||
video = "[0:v]scale=1600:900:flags=lanczos,fade=t=in:d=0.5[v]"
|
||||
finale = OUT / f"{nome}.mp4"
|
||||
subprocess.run(["ffmpeg", "-loglevel", "error", "-y", "-i", str(raw), *ingressi,
|
||||
"-filter_complex", ";".join([video, *catene, mix]), "-map", "[v]", "-map", "[a]",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// Prova locale di un copione del trailer, senza la VM: un finto «d» col solo necessario, una schermata per battuta.
|
||||
// OUT=/tmp/prova COPIONE=./trailer-extended.mjs node video/prova-locale.mjs
|
||||
import { chromium } from 'playwright';
|
||||
import { staticServer } from '../tests/helpers.mjs';
|
||||
const server = await staticServer();
|
||||
const browser = await chromium.launch({ args: ['--autoplay-policy=no-user-gesture-required', '--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
|
||||
const out = process.env.OUT;
|
||||
try {
|
||||
const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
|
||||
page.on('pageerror', e => console.log('ERR', e.message));
|
||||
const orig = page.goto.bind(page);
|
||||
page.goto = url => orig(url.replace('http://127.0.0.1:8000/', `http://127.0.0.1:${server.port}/`));
|
||||
const mod = await import(process.env.COPIONE ?? './trailer.mjs');
|
||||
const t0 = Date.now(); let n = 0;
|
||||
const d = { page, vm: () => {}, cue: async id => { console.log(((Date.now() - t0) / 1000).toFixed(1), id); setTimeout(() => page.screenshot({ path: `${out}/tr-${String(++n).padStart(2, '0')}-${id}.png` }), id === 'buio' ? 5000 : 3000); } };
|
||||
await mod.setup(d);
|
||||
await mod.run(d);
|
||||
console.log('fine', ((Date.now() - t0) / 1000).toFixed(1));
|
||||
} finally { await browser.close(); server.kill(); }
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copione del trailer lungo «La Leggenda del Regolo · extended» (scaletta approvata da Andrea il 29 ottobre): circa tre
|
||||
// minuti, come il trailer (video/trailer.mjs) ma col giro completo del paese e i giochi giocati per qualche secondo:
|
||||
// bocce, biliardo, GP di Fortezza, briscola, macchinine, cerbottana.
|
||||
// Si gira come la partita completa (video/SCALETTA.md), poi: python3 video/monta.py <cartella> --trailer .
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
|
||||
const c = (id, it) => ({ id, it, en: it, top: true });
|
||||
export const title = { it: "La Leggenda del Regolo · extended", en: "La Leggenda del Regolo · extended" };
|
||||
export const cues = [
|
||||
c("apertura", "San Piero a Sieve, maggio 2007"),
|
||||
c("notte", "La notte prima del Palio…"),
|
||||
c("sparito", "…il palio è sparito."),
|
||||
c("giro", "il giro del paese"),
|
||||
c("sfide", "Un paese pieno di sfide"),
|
||||
c("bocce", "Le bocce al pallaio"),
|
||||
c("biliardo", "Il biliardo del Circolo"),
|
||||
c("gara", "Il GP di Fortezza"),
|
||||
c("carte", "La briscola all'oratorio"),
|
||||
c("macchinine", "Le macchinine col gesso"),
|
||||
c("cerbottana", "La cerbottana"),
|
||||
c("amici", "…e di vecchie conoscenze"),
|
||||
c("buio", "la fessura"),
|
||||
c("titolo", "La Leggenda del Regolo"),
|
||||
];
|
||||
|
||||
const URL_GIOCO = "http://127.0.0.1:8000/";
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
export async function setup(d) {
|
||||
const p = d.page;
|
||||
await p.goto(URL_GIOCO);
|
||||
await p.evaluate(() => localStorage.clear());
|
||||
await p.reload();
|
||||
await sleep(500);
|
||||
if (await p.evaluate(() => document.getElementById("splash").classList.contains("wait"))) await p.click("#splash");
|
||||
await p.waitForSelector("#title:not([hidden])");
|
||||
// Si entra in partita dietro le quinte: rione, introduzione saltata.
|
||||
await p.click("#new-game");
|
||||
await p.click('#rione [data-rione="campomigliaio"]');
|
||||
await p.click("#skip-intro");
|
||||
// Il palcoscenico del trailer: solo la scena, a tutto schermo; sopra, il nero e le scritte.
|
||||
await p.addStyleTag({ content: `
|
||||
#side, #corner, #sound, #closeup-close, #bocce-controls, #briscola-controls, #tressette-controls, #scopa-controls,
|
||||
#map-button, #pause-button, #spots-button, #notebook-button, #sentence { display: none !important; }
|
||||
body { background: #000 !important; overflow: hidden; }
|
||||
#app { position: fixed !important; inset: 0 !important; width: 100vw !important; margin: 0 !important; }
|
||||
#stage, #stage.biliardo, #stage.briscola { width: 100vw !important; height: 100vh !important; }
|
||||
#scene { width: 100vw !important; height: 100vh !important; }
|
||||
#title .buttons, #title .version, #title .dedication { display: none !important; }
|
||||
#tr-nero { position: fixed; inset: 0; background: #000; opacity: 1; transition: opacity .7s; z-index: 50; pointer-events: none; }
|
||||
#tr-scritta { position: fixed; left: 0; right: 0; top: 50%; transform: translateY(-50%); text-align: center; z-index: 51;
|
||||
font-family: "Press Start 2P", monospace; font-size: 42px; line-height: 1.6; color: #ffe9b0; opacity: 0;
|
||||
transition: opacity .8s; text-shadow: 4px 4px 0 #2a1608, 0 0 24px rgba(0,0,0,.9); pointer-events: none; padding: 0 60px; }
|
||||
#tr-scritta.basso { top: auto; bottom: 70px; transform: none; font-size: 32px; }
|
||||
` });
|
||||
await p.evaluate(() => {
|
||||
for (const id of ["tr-nero", "tr-scritta"]) { const e = document.createElement("div"); e.id = id; document.body.appendChild(e); }
|
||||
});
|
||||
// Senza nessun tocco il gioco va in pausa da solo dopo un minuto: un finto movimento ogni tanto lo tiene sveglio.
|
||||
await p.evaluate(() => setInterval(() => dispatchEvent(new PointerEvent("pointermove")), 10000));
|
||||
// Le battute d'ingresso in piazza si saltano e la voce si ferma: il trailer parte in silenzio.
|
||||
await p.evaluate(async () => { const { settle } = await import('/tests/helpers.mjs'); settle(game); window.voices?.stop?.(); });
|
||||
// Il puntatore vero lontano, nell'angolo.
|
||||
d.vm("DISPLAY=:0 ~/lab/video/mv.py 1599 899 0.2");
|
||||
await sleep(1200);
|
||||
}
|
||||
|
||||
export async function run(d) {
|
||||
const p = d.page;
|
||||
// L'orologio dell'audio, come nella partita completa: le voci che partono; la musica la decide il copione.
|
||||
await p.evaluate(() => {
|
||||
window.__t0 = Date.now(); window.__audio = [];
|
||||
const t = () => (Date.now() - window.__t0) / 1000;
|
||||
const path = (src) => decodeURI(new URL(src, location.href).pathname.slice(1));
|
||||
let voice = null;
|
||||
window.__musica = (src) => window.__audio.push({ t: t(), music: src });
|
||||
const look = () => {
|
||||
const a = window.voices?.audio, now = a && !a.paused && !a.ended ? a : null;
|
||||
if (now !== voice) {
|
||||
if (voice) window.__audio.push({ t: t(), voiceEnd: true });
|
||||
if (now) window.__audio.push({ t: t(), voice: path(now.src) });
|
||||
voice = now;
|
||||
}
|
||||
requestAnimationFrame(look);
|
||||
};
|
||||
look();
|
||||
});
|
||||
const musica = (src) => p.evaluate((s) => window.__musica(s), src);
|
||||
const nero = (on) => p.evaluate((on) => { document.getElementById("tr-nero").style.opacity = on ? 1 : 0; }, on);
|
||||
const scritta = (testo, basso = false) => p.evaluate(([t, b]) => {
|
||||
const e = document.getElementById("tr-scritta");
|
||||
if (t) { e.textContent = t; e.classList.toggle("basso", b); }
|
||||
e.style.opacity = t ? 1 : 0;
|
||||
}, [testo, basso]);
|
||||
// In ogni scena: niente primo piano, niente battute in coda, flag di partenza; Andrea dove serve (o fuori campo).
|
||||
const vai = (scene, at, flags = {}) => `async () => {
|
||||
const { settle } = await import('/tests/helpers.mjs');
|
||||
game.closeup = null; game.queue.length = 0; game.speech = null; game.dialogue = null; game.path = []; game.walking = false; game.task = null;
|
||||
Object.assign(game.state.flags, ${JSON.stringify(flags)});
|
||||
game.go('${scene}'${at ? `, ${JSON.stringify(at)}` : ""}); settle(game);
|
||||
${at ? "" : "game.state.x = -400;"}
|
||||
}`;
|
||||
const prepara = (codice) => p.evaluate(`(${codice})()`);
|
||||
const fuoco = async (codice, veloce) => {
|
||||
await p.evaluate((v) => { document.getElementById("tr-nero").style.transition = `opacity ${v ? .18 : .7}s`; }, veloce);
|
||||
await nero(true); await sleep(veloce ? 200 : 750);
|
||||
await prepara(codice);
|
||||
await sleep(veloce ? 150 : 300);
|
||||
await nero(false); await sleep(veloce ? 200 : 700);
|
||||
};
|
||||
const dice = (who, testo, pose) => p.evaluate(([w, t, po]) => game.run(w ? { say: t, who: w, pose: po } : { say: t }), [who, testo, pose]);
|
||||
const finitoDiDire = () => p.waitForFunction(() => !game.speech, null, { timeout: 20000 });
|
||||
|
||||
// 1. Il nero, poi il panorama di Monte Cacioli con le nuvole.
|
||||
await musica("assets/audio/sera.mp3");
|
||||
await d.cue("apertura");
|
||||
await sleep(800);
|
||||
await p.evaluate(() => { document.getElementById("tr-scritta").style.whiteSpace = "pre-line"; });
|
||||
await scritta("San Piero a Sieve\nmaggio 2007");
|
||||
await sleep(2400);
|
||||
await scritta(null);
|
||||
await fuoco(vai("via_cafaggio"));
|
||||
await sleep(3200);
|
||||
|
||||
// 2. La Fortezza al tramonto, le impronte.
|
||||
await d.cue("notte");
|
||||
await fuoco(vai("fortezza", null, { indizio_regolo: true }));
|
||||
await scritta("La notte prima del Palio…", true);
|
||||
await sleep(3000);
|
||||
await scritta(null);
|
||||
|
||||
// 3. La scuola: il palio non c'è più.
|
||||
await d.cue("sparito");
|
||||
await fuoco(vai("scuola", [300, 330], { indizio_regolo: false }));
|
||||
await scritta("…il palio è sparito.", true);
|
||||
await sleep(3200);
|
||||
await scritta(null);
|
||||
|
||||
// 4. Il giro completo del paese: Andrea cammina di scena in scena, con la musica del paese.
|
||||
await d.cue("giro");
|
||||
await musica("assets/audio/paese.mp3");
|
||||
for (const [scene, da, a] of [["piazza", [120, 330], [300, 300]], ["via_de_medici", [300, 348], [300, 230]], ["via_compagnia", [300, 350], [430, 262]],
|
||||
["villa_adami", [300, 345], [560, 272]], ["fortezza", [175, 352], [400, 300]], ["pieve", [40, 330], [260, 300]], ["via_cafaggio", [150, 352], [420, 250]],
|
||||
["rotonda_cafaggio", [390, 352], [540, 230]], ["passeggiata_carza", [330, 352], [420, 250]], ["ponte_stazione", [300, 352], [200, 260]],
|
||||
["orti_zio_mario", [400, 352], [330, 260]], ["stazione", [320, 350], [450, 290]], ["cardetole", [300, 350], [470, 300]], ["terrazza", [300, 330], [420, 320]]]) {
|
||||
await fuoco(vai(scene, da), true);
|
||||
await p.evaluate((a) => game.walkTo(a), a);
|
||||
await sleep(3300);
|
||||
}
|
||||
|
||||
// 5. Le sfide, giocate davvero per qualche secondo: ogni gioco col suo nome in basso e la sua musica.
|
||||
await d.cue("sfide");
|
||||
await musica(null);
|
||||
await fuoco(`async () => { game.state.x = -400; }`);
|
||||
await scritta("Un paese pieno di sfide");
|
||||
await sleep(2000);
|
||||
await scritta(null);
|
||||
// Apre il primo piano, poi ogni 150 ms chiama `mossa` (il giocatore che gioca da solo) finché passano i secondi.
|
||||
const gioca = async (cue, nome, brano, closeup, level, modulo, prima, mossa, secondi) => {
|
||||
await d.cue(cue);
|
||||
await musica(brano);
|
||||
await fuoco(`async () => {
|
||||
const { settle } = await import('/tests/helpers.mjs');
|
||||
game.closeup = null; game.queue.length = 0; game.speech = null; game.dialogue = null; game.path = []; game.walking = false; game.task = null;
|
||||
game.run({ closeup: '${closeup}', level: '${level}' }); settle(game);
|
||||
window.__m = await import('${modulo}');
|
||||
const M = window.__m, c = game.closeup; ${prima}
|
||||
}`);
|
||||
await scritta(nome, true);
|
||||
const fine = Date.now() + secondi * 1000;
|
||||
while (Date.now() < fine) {
|
||||
await p.evaluate(`(() => { const M = window.__m, c = game.closeup; if (!c) return; ${mossa} })()`);
|
||||
if (Date.now() > fine - (secondi - 3) * 1000) await scritta(null);
|
||||
await sleep(150);
|
||||
}
|
||||
};
|
||||
await gioca("bocce", "Le bocce al pallaio", "assets/audio/bocce.mp3", "bocce", "seria", "/src/engine/bocce.js", "",
|
||||
"if (M.canThrow(c)) { c.aim = { u: c.jack.u, w: c.jack.w }; c.power = M.powerFor(Math.hypot(c.jack.u, c.jack.w) - 1.5); M.shoot(c); }", 14);
|
||||
await gioca("biliardo", "Il biliardo del Circolo", "assets/audio/biliardo.mp3", "biliardo", "seria", "/src/engine/biliardo.js", "",
|
||||
"if (c.step === 'aim' && c.current === 'player' && !c.holding) M.strike(c, 'player', Math.atan2(c.balls.cpu.y - c.balls.player.y, c.balls.cpu.x - c.balls.player.x) + 0.04, 0.75);", 14);
|
||||
await gioca("gara", "Il GP di Fortezza", "assets/audio/gara.mp3", "gara", "bona", "/src/engine/gara.js",
|
||||
"while (c.step === 'intro') M.press(c); M.release(c);", "c.holding = c.step === 'race' && !M.mustLift(c);", 16);
|
||||
await gioca("carte", "La briscola all'oratorio", "assets/audio/carte.mp3", "briscola", "seria", "/src/engine/briscola.js", "",
|
||||
"if (M.canPlay(c)) M.play(c, 0);", 12);
|
||||
await gioca("macchinine", "Le macchinine col gesso", "assets/audio/macchinine.mp3", "micro", "bona", "/src/engine/micro.js", "M.pick(c, 0);",
|
||||
"if (M.canThrow(c)) { const car = c.cars.player, i = (car.idx + Math.round(110 / c.track.step)) % c.track.n, [x, y] = c.track.pts[i]; M.strike(c, 'player', Math.atan2(y - car.y, x - car.x), M.powerFor(Math.hypot(x - car.x, y - car.y))); }", 12);
|
||||
await gioca("cerbottana", "La cerbottana", "assets/audio/gara.mp3", "cerbottana", "seria", "/src/engine/cerbottana.js", "",
|
||||
"if (M.canThrow(c)) { const k = c.cans.find(k => !k.down); c.aim = k && c.left % 2 ? { x: k.x, y: M.LEDGE - 8 } : { x: 320 + (c.left % 3 - 1) * 6, y: 118 }; c.power = M.BEST; M.shoot(c); }", 10);
|
||||
await musica("assets/audio/paese.mp3");
|
||||
|
||||
// 6. Le vecchie conoscenze, con le loro battute.
|
||||
await d.cue("amici");
|
||||
await fuoco(vai("via_liguri_magelli", [330, 300]));
|
||||
await scritta("…e di vecchie conoscenze", true);
|
||||
await dice(null, "Di chi è quell'Audi lì?");
|
||||
await finitoDiDire();
|
||||
await scritta(null);
|
||||
await dice("arcu", "Let it be.", "explain");
|
||||
await finitoDiDire();
|
||||
await sleep(300);
|
||||
await dice(null, "Zio, hai problemi?");
|
||||
await finitoDiDire();
|
||||
await dice("zio_mario", "I problemi io gli avevo quando andavo a scuola!", "laugh");
|
||||
await finitoDiDire();
|
||||
await fuoco(vai("cardetole", [470, 330]), true);
|
||||
await dice("chiaru", "Vorrai dire un giochino!", "laugh");
|
||||
await finitoDiDire();
|
||||
await sleep(400);
|
||||
|
||||
// 7. Il buio: la sera, la torcia nella fessura, gli occhi gialli.
|
||||
await d.cue("buio");
|
||||
// Sotto il buio una musica bassa di suspense (Andrea: «un po' di musichetta nel finale»).
|
||||
await musica("assets/audio/sera.mp3");
|
||||
await fuoco(vai("fortezza", [462, 310], { indizio_regolo: true, casetta_aperta: true }));
|
||||
await sleep(900);
|
||||
await p.evaluate(() => { game.state.inventory.push("torcia"); game.act("use", "fessura", "torcia"); });
|
||||
// «Accendo la torcia e la punto dentro…» si salta: il primo piano si apre subito.
|
||||
// Il palio non si vede (Andrea: si capirebbe che è in Fortezza): la luce parte da destra, nel buio, e i primi due
|
||||
// ritrovamenti si danno per fatti; si cercano solo gli occhi.
|
||||
for (let k = 0; k < 60 && await p.evaluate(() => game.closeup?.id !== "fessura"); k++) { await p.evaluate(() => game.skip()); await sleep(50); }
|
||||
await p.evaluate(() => { const c = game.closeup; c.found = 2; c.beam = [600, 300]; });
|
||||
await sleep(1600);
|
||||
const targets = await p.evaluate(() => game.story.closeups.fessura.targets.map((t) => t.at));
|
||||
for (let i = 2; i < targets.length; i++) {
|
||||
if (i === 2) await musica("assets/audio/cartello.mp3");
|
||||
// La luce va piano verso il punto, come una mano che cerca: prima in basso, poi su verso gli occhi.
|
||||
const from = await p.evaluate(() => game.closeup.beam);
|
||||
for (let k = 1; k <= 30; k++) {
|
||||
const x = from[0] + (targets[i][0] - from[0]) * k / 30 + Math.sin(k / 4) * 12, y = from[1] + (targets[i][1] - from[1]) * k / 30;
|
||||
await p.evaluate(([x, y]) => game.closeupAim([x, y]), [x, y]);
|
||||
await sleep(50);
|
||||
}
|
||||
// Le battute dei primi ritrovamenti si saltano (partono prima che il ritrovamento conti, e lo fermano); resta solo
|
||||
// l'ultima: «…sono due occhi gialli!».
|
||||
for (let k = 0; k < 300 && await p.evaluate((i) => !(game.closeup?.found > i), i); k++) {
|
||||
if (i < 2) await p.evaluate(() => game.skip());
|
||||
await sleep(100);
|
||||
}
|
||||
await p.waitForFunction(() => !game.speech && !game.closeup?.lines?.length, null, { timeout: 30000 });
|
||||
await sleep(i === 2 ? 1400 : 100);
|
||||
}
|
||||
await nero(true);
|
||||
await sleep(1400);
|
||||
|
||||
// 8. Il titolo.
|
||||
await d.cue("titolo");
|
||||
await musica("assets/audio/titolo.mp3");
|
||||
await p.evaluate(() => {
|
||||
game.closeup = null;
|
||||
document.getElementById("title").hidden = false;
|
||||
document.getElementById("tr-nero").style.transition = "opacity 1.6s";
|
||||
});
|
||||
await sleep(300);
|
||||
await nero(false);
|
||||
await sleep(7500);
|
||||
await nero(true);
|
||||
await sleep(1600);
|
||||
|
||||
const audio = await p.evaluate(() => window.__audio);
|
||||
const out = new URL("./out/", import.meta.url);
|
||||
mkdirSync(out, { recursive: true });
|
||||
writeFileSync(new URL("trailer-extended-audio.json", out), JSON.stringify({ eventi: audio }, null, 2));
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
// Copione del trailer «La Leggenda del Regolo» (scaletta approvata da Andrea il 29 ottobre): circa 75 secondi, la scena a
|
||||
// tutto schermo, scritte grandi e stacchi al nero fatti dentro la pagina, niente puntatore e niente clic.
|
||||
// Si gira come la partita completa (video/SCALETTA.md), poi: python3 video/monta.py <cartella> --trailer .
|
||||
// La musica non è quella che sceglierebbe il gioco: il copione annota i brani voluti, e le voci che partono, in
|
||||
// out/trailer-audio.json; monta.py rimonta la colonna sonora.
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
|
||||
const c = (id, it) => ({ id, it, en: it, top: true });
|
||||
export const title = { it: "La Leggenda del Regolo · trailer", en: "La Leggenda del Regolo · trailer" };
|
||||
export const cues = [
|
||||
c("apertura", "San Piero a Sieve, maggio 2007"),
|
||||
c("notte", "La notte prima del Palio…"),
|
||||
c("sparito", "…il palio è sparito."),
|
||||
c("paese", "il paese"),
|
||||
c("sfide", "Un paese pieno di sfide"),
|
||||
c("amici", "…e di vecchie conoscenze"),
|
||||
c("buio", "la fessura"),
|
||||
c("titolo", "La Leggenda del Regolo"),
|
||||
];
|
||||
|
||||
const URL_GIOCO = "http://127.0.0.1:8000/";
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
|
||||
export async function setup(d) {
|
||||
const p = d.page;
|
||||
await p.goto(URL_GIOCO);
|
||||
await p.evaluate(() => localStorage.clear());
|
||||
await p.reload();
|
||||
await sleep(500);
|
||||
if (await p.evaluate(() => document.getElementById("splash").classList.contains("wait"))) await p.click("#splash");
|
||||
await p.waitForSelector("#title:not([hidden])");
|
||||
// Si entra in partita dietro le quinte: rione, introduzione saltata.
|
||||
await p.click("#new-game");
|
||||
await p.click('#rione [data-rione="campomigliaio"]');
|
||||
await p.click("#skip-intro");
|
||||
// Il palcoscenico del trailer: solo la scena, a tutto schermo; sopra, il nero e le scritte.
|
||||
await p.addStyleTag({ content: `
|
||||
#side, #corner, #sound, #closeup-close, #bocce-controls, #briscola-controls, #tressette-controls, #scopa-controls,
|
||||
#map-button, #pause-button, #spots-button, #notebook-button, #sentence { display: none !important; }
|
||||
body { background: #000 !important; overflow: hidden; }
|
||||
#app { position: fixed !important; inset: 0 !important; width: 100vw !important; margin: 0 !important; }
|
||||
#stage, #stage.biliardo, #stage.briscola { width: 100vw !important; height: 100vh !important; }
|
||||
#scene { width: 100vw !important; height: 100vh !important; }
|
||||
#title .buttons, #title .version, #title .dedication { display: none !important; }
|
||||
#tr-nero { position: fixed; inset: 0; background: #000; opacity: 1; transition: opacity .7s; z-index: 50; pointer-events: none; }
|
||||
#tr-scritta { position: fixed; left: 0; right: 0; top: 50%; transform: translateY(-50%); text-align: center; z-index: 51;
|
||||
font-family: "Press Start 2P", monospace; font-size: 42px; line-height: 1.6; color: #ffe9b0; opacity: 0;
|
||||
transition: opacity .8s; text-shadow: 4px 4px 0 #2a1608, 0 0 24px rgba(0,0,0,.9); pointer-events: none; padding: 0 60px; }
|
||||
#tr-scritta.basso { top: auto; bottom: 70px; transform: none; font-size: 32px; }
|
||||
` });
|
||||
await p.evaluate(() => {
|
||||
for (const id of ["tr-nero", "tr-scritta"]) { const e = document.createElement("div"); e.id = id; document.body.appendChild(e); }
|
||||
});
|
||||
// Senza nessun tocco il gioco va in pausa da solo dopo un minuto: un finto movimento ogni tanto lo tiene sveglio.
|
||||
await p.evaluate(() => setInterval(() => dispatchEvent(new PointerEvent("pointermove")), 10000));
|
||||
// Le battute d'ingresso in piazza si saltano e la voce si ferma: il trailer parte in silenzio.
|
||||
await p.evaluate(async () => { const { settle } = await import('/tests/helpers.mjs'); settle(game); window.voices?.stop?.(); });
|
||||
// Il puntatore vero lontano, nell'angolo.
|
||||
d.vm("DISPLAY=:0 ~/lab/video/mv.py 1599 899 0.2");
|
||||
await sleep(1200);
|
||||
}
|
||||
|
||||
export async function run(d) {
|
||||
const p = d.page;
|
||||
// L'orologio dell'audio, come nella partita completa: le voci che partono; la musica la decide il copione.
|
||||
await p.evaluate(() => {
|
||||
window.__t0 = Date.now(); window.__audio = [];
|
||||
const t = () => (Date.now() - window.__t0) / 1000;
|
||||
const path = (src) => decodeURI(new URL(src, location.href).pathname.slice(1));
|
||||
let voice = null;
|
||||
window.__musica = (src) => window.__audio.push({ t: t(), music: src });
|
||||
const look = () => {
|
||||
const a = window.voices?.audio, now = a && !a.paused && !a.ended ? a : null;
|
||||
if (now !== voice) {
|
||||
if (voice) window.__audio.push({ t: t(), voiceEnd: true });
|
||||
if (now) window.__audio.push({ t: t(), voice: path(now.src) });
|
||||
voice = now;
|
||||
}
|
||||
requestAnimationFrame(look);
|
||||
};
|
||||
look();
|
||||
});
|
||||
const musica = (src) => p.evaluate((s) => window.__musica(s), src);
|
||||
const nero = (on) => p.evaluate((on) => { document.getElementById("tr-nero").style.opacity = on ? 1 : 0; }, on);
|
||||
const scritta = (testo, basso = false) => p.evaluate(([t, b]) => {
|
||||
const e = document.getElementById("tr-scritta");
|
||||
if (t) { e.textContent = t; e.classList.toggle("basso", b); }
|
||||
e.style.opacity = t ? 1 : 0;
|
||||
}, [testo, basso]);
|
||||
// In ogni scena: niente primo piano, niente battute in coda, flag di partenza; Andrea dove serve (o fuori campo).
|
||||
const vai = (scene, at, flags = {}) => `async () => {
|
||||
const { settle } = await import('/tests/helpers.mjs');
|
||||
game.closeup = null; game.queue.length = 0; game.speech = null; game.dialogue = null; game.path = []; game.walking = false; game.task = null;
|
||||
Object.assign(game.state.flags, ${JSON.stringify(flags)});
|
||||
game.go('${scene}'${at ? `, ${JSON.stringify(at)}` : ""}); settle(game);
|
||||
${at ? "" : "game.state.x = -400;"}
|
||||
}`;
|
||||
const prepara = (codice) => p.evaluate(`(${codice})()`);
|
||||
const fuoco = async (codice, veloce) => {
|
||||
await p.evaluate((v) => { document.getElementById("tr-nero").style.transition = `opacity ${v ? .18 : .7}s`; }, veloce);
|
||||
await nero(true); await sleep(veloce ? 200 : 750);
|
||||
await prepara(codice);
|
||||
await sleep(veloce ? 150 : 300);
|
||||
await nero(false); await sleep(veloce ? 200 : 700);
|
||||
};
|
||||
const dice = (who, testo, pose) => p.evaluate(([w, t, po]) => game.run(w ? { say: t, who: w, pose: po } : { say: t }), [who, testo, pose]);
|
||||
const finitoDiDire = () => p.waitForFunction(() => !game.speech, null, { timeout: 20000 });
|
||||
|
||||
// 1. Il nero, poi il panorama di Monte Cacioli con le nuvole.
|
||||
await musica("assets/audio/sera.mp3");
|
||||
await d.cue("apertura");
|
||||
await sleep(800);
|
||||
await p.evaluate(() => { document.getElementById("tr-scritta").style.whiteSpace = "pre-line"; });
|
||||
await scritta("San Piero a Sieve\nmaggio 2007");
|
||||
await sleep(2400);
|
||||
await scritta(null);
|
||||
await fuoco(vai("via_cafaggio"));
|
||||
await sleep(3200);
|
||||
|
||||
// 2. La Fortezza al tramonto, le impronte.
|
||||
await d.cue("notte");
|
||||
await fuoco(vai("fortezza", null, { indizio_regolo: true }));
|
||||
await scritta("La notte prima del Palio…", true);
|
||||
await sleep(3000);
|
||||
await scritta(null);
|
||||
|
||||
// 3. La scuola: il palio non c'è più.
|
||||
await d.cue("sparito");
|
||||
await fuoco(vai("scuola", [300, 330], { indizio_regolo: false }));
|
||||
await scritta("…il palio è sparito.", true);
|
||||
await sleep(3200);
|
||||
await scritta(null);
|
||||
|
||||
// 4. Il paese, a stacchi: Andrea cammina.
|
||||
await d.cue("paese");
|
||||
await musica("assets/audio/gara.mp3");
|
||||
for (const [scene, da, a] of [["piazza", [120, 330], [300, 300]], ["pieve", [40, 330], [260, 300]], ["passeggiata_carza", [330, 352], [420, 250]],
|
||||
["terrazza", [300, 330], [420, 320]], ["ponte_stazione", [300, 352], [200, 260]]]) {
|
||||
await fuoco(vai(scene, da), true);
|
||||
await p.evaluate((a) => game.walkTo(a), a);
|
||||
await sleep(2100);
|
||||
}
|
||||
|
||||
// 5. Le sfide: un lampo per minigioco.
|
||||
await d.cue("sfide");
|
||||
await scritta("Un paese pieno di sfide");
|
||||
await sleep(1600);
|
||||
await scritta(null);
|
||||
const sfida = async (closeup, level, dopo = "") => {
|
||||
await fuoco(`async () => {
|
||||
const { settle } = await import('/tests/helpers.mjs');
|
||||
game.closeup = null; game.queue.length = 0; game.speech = null; game.dialogue = null; game.path = []; game.walking = false; game.task = null;
|
||||
game.run({ closeup: '${closeup}', level: '${level}' }); settle(game);
|
||||
${dopo}
|
||||
}`, true);
|
||||
await sleep(1300);
|
||||
};
|
||||
await sfida("gara", "seria", "const g = await import('/src/engine/gara.js'); while (game.closeup.step === 'intro') g.press(game.closeup);");
|
||||
await sfida("biliardo", "seria", "const b = await import('/src/engine/biliardo.js'); const c = game.closeup; b.strike(c, 'player', Math.atan2(c.balls.cpu.y - c.balls.player.y, c.balls.cpu.x - c.balls.player.x), 0.8);");
|
||||
await sfida("rinquarto", "allenamento");
|
||||
await sfida("micro", "seria", "const m = await import('/src/engine/micro.js'); const c = game.closeup; m.pick(c, 0); m.strike(c, 'player', c.angle, 0.55);");
|
||||
await sfida("cerbottana", "seria", "const m = await import('/src/engine/cerbottana.js'); const c = game.closeup; c.aim = { x: 320, y: 118 }; c.power = m.BEST; m.shoot(c);");
|
||||
await sfida("briscola", "seria");
|
||||
await sfida("basket", "seria", "const b = await import('/src/engine/basket.js'); const c = game.closeup; c.power = 0.62; b.shoot(c);");
|
||||
|
||||
// 6. Le vecchie conoscenze, con le loro battute.
|
||||
await d.cue("amici");
|
||||
await fuoco(vai("via_liguri_magelli", [330, 300]));
|
||||
await scritta("…e di vecchie conoscenze", true);
|
||||
await dice(null, "Di chi è quell'Audi lì?");
|
||||
await finitoDiDire();
|
||||
await scritta(null);
|
||||
await dice("arcu", "Let it be.", "explain");
|
||||
await finitoDiDire();
|
||||
await sleep(300);
|
||||
await dice(null, "Zio, hai problemi?");
|
||||
await finitoDiDire();
|
||||
await dice("zio_mario", "I problemi io gli avevo quando andavo a scuola!", "laugh");
|
||||
await finitoDiDire();
|
||||
await fuoco(vai("cardetole", [470, 330]), true);
|
||||
await dice("chiaru", "Vorrai dire un giochino!", "laugh");
|
||||
await finitoDiDire();
|
||||
await sleep(400);
|
||||
|
||||
// 7. Il buio: la sera, la torcia nella fessura, gli occhi gialli.
|
||||
await d.cue("buio");
|
||||
// Sotto il buio una musica bassa di suspense (Andrea: «un po' di musichetta nel finale»).
|
||||
await musica("assets/audio/sera.mp3");
|
||||
await fuoco(vai("fortezza", [462, 310], { indizio_regolo: true, casetta_aperta: true }));
|
||||
await sleep(900);
|
||||
await p.evaluate(() => { game.state.inventory.push("torcia"); game.act("use", "fessura", "torcia"); });
|
||||
// «Accendo la torcia e la punto dentro…» si salta: il primo piano si apre subito.
|
||||
// Il palio non si vede (Andrea: si capirebbe che è in Fortezza): la luce parte da destra, nel buio, e i primi due
|
||||
// ritrovamenti si danno per fatti; si cercano solo gli occhi.
|
||||
for (let k = 0; k < 60 && await p.evaluate(() => game.closeup?.id !== "fessura"); k++) { await p.evaluate(() => game.skip()); await sleep(50); }
|
||||
await p.evaluate(() => { const c = game.closeup; c.found = 2; c.beam = [600, 300]; });
|
||||
await sleep(1600);
|
||||
const targets = await p.evaluate(() => game.story.closeups.fessura.targets.map((t) => t.at));
|
||||
for (let i = 2; i < targets.length; i++) {
|
||||
if (i === 2) await musica("assets/audio/cartello.mp3");
|
||||
// La luce va piano verso il punto, come una mano che cerca: prima in basso, poi su verso gli occhi.
|
||||
const from = await p.evaluate(() => game.closeup.beam);
|
||||
for (let k = 1; k <= 30; k++) {
|
||||
const x = from[0] + (targets[i][0] - from[0]) * k / 30 + Math.sin(k / 4) * 12, y = from[1] + (targets[i][1] - from[1]) * k / 30;
|
||||
await p.evaluate(([x, y]) => game.closeupAim([x, y]), [x, y]);
|
||||
await sleep(50);
|
||||
}
|
||||
// Le battute dei primi ritrovamenti si saltano (partono prima che il ritrovamento conti, e lo fermano); resta solo
|
||||
// l'ultima: «…sono due occhi gialli!».
|
||||
for (let k = 0; k < 300 && await p.evaluate((i) => !(game.closeup?.found > i), i); k++) {
|
||||
if (i < 2) await p.evaluate(() => game.skip());
|
||||
await sleep(100);
|
||||
}
|
||||
await p.waitForFunction(() => !game.speech && !game.closeup?.lines?.length, null, { timeout: 30000 });
|
||||
await sleep(i === 2 ? 1400 : 100);
|
||||
}
|
||||
await nero(true);
|
||||
await sleep(1400);
|
||||
|
||||
// 8. Il titolo.
|
||||
await d.cue("titolo");
|
||||
await musica("assets/audio/titolo.mp3");
|
||||
await p.evaluate(() => {
|
||||
game.closeup = null;
|
||||
document.getElementById("title").hidden = false;
|
||||
document.getElementById("tr-nero").style.transition = "opacity 1.6s";
|
||||
});
|
||||
await sleep(300);
|
||||
await nero(false);
|
||||
await sleep(7500);
|
||||
await nero(true);
|
||||
await sleep(1600);
|
||||
|
||||
const audio = await p.evaluate(() => window.__audio);
|
||||
const out = new URL("./out/", import.meta.url);
|
||||
mkdirSync(out, { recursive: true });
|
||||
writeFileSync(new URL("trailer-audio.json", out), JSON.stringify({ eventi: audio }, null, 2));
|
||||
}
|
||||