La scena prende la finestra (Andrea: «come mai non sfruttiamo tutto lo schermo?»): la riserva sotto non è più 250 px fissi ma l'altezza vera del pannello, in unità --u legate alla larghezza del gioco (cqw, #app è un container); frase, verbi, inventario, risposte, HUD e fumetti scalano col gioco e non con la finestra, i verbi non si tagliano più; pannello alto sempre uguale (quattro risposte piene, da cinque il carattere cala) così la scena non si muove nei dialoghi; frase su una riga coi puntini; «Schermo intero» nella Pausa; la Pausa parte dall'alto se non ci sta. A 1040×590 la scena da 604 a 842 px. Prove nel browser a 1280×860 e 1040×590, con due dialoghi (Andrea)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiECd9RWGMKV7n3K1mdy8T
This commit is contained in:
2026-11-01 13:46:28 +01:00
co-authored by Claude Fable 5.1
parent 24e9b8c055
commit be099aa810
5 changed files with 91 additions and 17 deletions
+8
View File
@@ -302,6 +302,14 @@ versione nuova.
- **Test**: `make test` (29 test, compresa la soluzione completa da `src/story/solution.js`) e `make browser` - **Test**: `make test` (29 test, compresa la soluzione completa da `src/story/solution.js`) e `make browser`
(partita completa con clic veri in Chromium, ripresa del salvataggio, telefono). (partita completa con clic veri in Chromium, ripresa del salvataggio, telefono).
- **Impaginazione (1 novembre, Andrea: «come mai non sfruttiamo tutto lo schermo?»):** la scena prende la finestra. La
riserva sotto non è più 250 px fissi ma l'altezza vera del pannello, 13,3 unità `--u` (un centesimo della larghezza
del gioco, `cqw`, fra 8,75 e 12,8 px: `#app` è un container). Frase, verbi, inventario, risposte, HUD e fumetti scalano
col gioco, non con la finestra: i verbi non si tagliano più nelle finestre piccole. Il pannello è alto sempre uguale
(ci stanno quattro risposte; da cinque il carattere cala, da sette ancora) così la scena non si muove nei dialoghi;
la frase è una riga sola coi puntini. «Schermo intero» nella Pausa (dove il browser lo permette). A 1040×590 la scena
passa da 604 a 842 px di larghezza, a 1366×650 da 711 a 935; sui telefoni niente cambia. Prova in `browser.mjs`.
## Materiale ricevuto da Andrea (in `assets/raw/`, escluso da git) ## Materiale ricevuto da Andrea (in `assets/raw/`, escluso da git)
| File | Cosa | Uso previsto | | File | Cosa | Uso previsto |
+1
View File
@@ -86,6 +86,7 @@
<div class="buttons pause-buttons"> <div class="buttons pause-buttons">
<button id="resume">Riprendi</button> <button id="resume">Riprendi</button>
<button id="pause-map" class="secondary">Mappa</button> <button id="pause-map" class="secondary">Mappa</button>
<button id="fullscreen" class="secondary" title="Il gioco su tutto lo schermo; Esc per uscire">Schermo intero</button>
<button id="save-file" class="secondary" title="Scarica la partita in un file da aprire su un altro dispositivo">Salva su file</button> <button id="save-file" class="secondary" title="Scarica la partita in un file da aprire su un altro dispositivo">Salva su file</button>
<button class="open-credits secondary">Ringraziamenti</button> <button class="open-credits secondary">Ringraziamenti</button>
<button id="to-title" class="secondary">Torna al titolo</button> <button id="to-title" class="secondary">Torna al titolo</button>
+12
View File
@@ -399,6 +399,18 @@ addEventListener('pagehide', () => leave(true));
addEventListener('pageshow', () => leave(document.hidden)); addEventListener('pageshow', () => leave(document.hidden));
$('resume').addEventListener('click', closePause); $('resume').addEventListener('click', closePause);
$('pause-map').addEventListener('click', () => { closePause(); openMap(); }); $('pause-map').addEventListener('click', () => { closePause(); openMap(); });
// Schermo intero dalla Pausa, dove il browser lo permette (su iPhone no: il pulsante sparisce); Esc o il pulsante per uscire.
{
const full = $('fullscreen');
if (!document.fullscreenEnabled) full.hidden = true;
else {
full.addEventListener('click', () => {
if (document.fullscreenElement) document.exitFullscreen();
else document.documentElement.requestFullscreen({ navigationUI: 'hide' }).catch(() => {});
});
document.addEventListener('fullscreenchange', () => { full.textContent = document.fullscreenElement ? 'Esci da schermo intero' : 'Schermo intero'; });
}
}
$('to-title').addEventListener('click', () => { $('to-title').addEventListener('click', () => {
$('pause').hidden = true; $('pause').hidden = true;
music.hold = false; music.hold = false;
+38 -16
View File
@@ -20,7 +20,14 @@ html, body { margin: 0; height: 100%; background: var(--bg); color: var(--ink);
button, input { font-family: var(--font); } button, input { font-family: var(--font); }
body { line-height: 1.5; } body { line-height: 1.5; }
body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-highlight-color: transparent; } body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-highlight-color: transparent; }
#app { width: min(100vw, 1280px, calc((100dvh - 250px) * 16 / 9)); min-width: min(100vw, 320px); margin-block: auto; display: flex; flex-direction: column; padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom); } /* Il gioco è largo quanto può: tutta la finestra fino a 1280px, oppure quanto ci sta in altezza con il pannello sotto la
scena 16:9. Il pannello (frase, poi verbi e inventario o le risposte di un dialogo) è alto --side-h unità --u, dove --u
è un centesimo della larghezza del gioco (1cqw) fra 8,75px e 12,8px: tutto il pannello cresce e cala col gioco, non con
la finestra, e la riserva in altezza è esatta (prima erano 250px fissi, un centinaio di troppo, che in una finestra
1040×590 costavano un terzo della scena). I due calc sono i due regimi di --u: proporzionale, e al minimo. */
#app { --u: clamp(8.75px, 1cqw, 12.8px); --side-h: 13.3; container-type: inline-size;
width: min(100vw, 1280px, calc(100dvh / (9 / 16 + var(--side-h) / 100)), calc((100dvh - var(--side-h) * 8.75px) * 16 / 9));
min-width: min(100vw, 320px); margin-block: auto; display: flex; flex-direction: column; padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom); }
#stage { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; user-select: none; touch-action: manipulation; } #stage { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; user-select: none; touch-action: manipulation; }
#scene { width: 100%; height: 100%; display: block; image-rendering: pixelated; cursor: crosshair; } #scene { width: 100%; height: 100%; display: block; image-rendering: pixelated; cursor: crosshair; }
@@ -80,7 +87,7 @@ body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-h
/* max-content: vicino al bordo destro il fumetto non si stringe a colonna (lo spazio dopo `left` non conta). */ /* max-content: vicino al bordo destro il fumetto non si stringe a colonna (lo spazio dopo `left` non conta). */
#speech { position: absolute; left: 0; top: 0; width: max-content; max-width: 46%; transform: translate(-50%, -100%); pointer-events: none; #speech { position: absolute; left: 0; top: 0; width: max-content; max-width: 46%; transform: translate(-50%, -100%); pointer-events: none;
font-size: clamp(10.08px, 1.66vw, 18.72px); font-weight: bold; line-height: 1.2; text-align: center; font-size: calc(1.4625 * var(--u)); font-weight: bold; line-height: 1.2; text-align: center;
text-shadow: 2px 0 #000, -2px 0 #000, 0 2px #000, 0 -2px #000, 2px 2px #000, -2px -2px #000; } text-shadow: 2px 0 #000, -2px 0 #000, 0 2px #000, 0 -2px #000, 2px 2px #000, -2px -2px #000; }
#speech:empty { display: none; } #speech:empty { display: none; }
#speech { z-index: 3; } #speech { z-index: 3; }
@@ -89,7 +96,7 @@ body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-h
#muro { position: absolute; left: 0; top: 0; width: 1067px; height: 600px; border: 0; z-index: 1; background: #f7e1c0; #muro { position: absolute; left: 0; top: 0; width: 1067px; height: 600px; border: 0; z-index: 1; background: #f7e1c0;
transform: scale(var(--muro-scale, 0.6)); transform-origin: 0 0; } transform: scale(var(--muro-scale, 0.6)); transform-origin: 0 0; }
/* Il primo piano del libro si chiude da qui (o con Esc). */ /* Il primo piano del libro si chiude da qui (o con Esc). */
#closeup-close { position: absolute; right: 10px; bottom: 10px; z-index: 2; font: bold clamp(10px, 1.5vw, 15px) var(--font); #closeup-close { position: absolute; right: 10px; bottom: 10px; z-index: 2; font: bold calc(1.17 * var(--u)) var(--font);
padding: .45em 1.2em; border-radius: 6px; border: 0; cursor: pointer; color: var(--bg); background: var(--accent); } padding: .45em 1.2em; border-radius: 6px; border: 0; cursor: pointer; color: var(--bg); background: var(--accent); }
/* Al biliardo audio, pausa e uscita hanno spazio proprio: nessun comando copre biglie o punteggio. */ /* Al biliardo audio, pausa e uscita hanno spazio proprio: nessun comando copre biglie o punteggio. */
#stage.biliardo { padding-top: 44px; aspect-ratio: auto; background: #17110c; } #stage.biliardo { padding-top: 44px; aspect-ratio: auto; background: #17110c; }
@@ -104,24 +111,27 @@ body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-h
background: rgba(27, 20, 32, .62); border-radius: 18px; } background: rgba(27, 20, 32, .62); border-radius: 18px; }
#mute { width: 30px; height: 30px; padding: 0; font: bold 12.96px var(--font); color: var(--accent); background: none; border: 0; cursor: pointer; } #mute { width: 30px; height: 30px; padding: 0; font: bold 12.96px var(--font); color: var(--accent); background: none; border: 0; cursor: pointer; }
#mute[aria-pressed="true"] { color: var(--dim); text-decoration: line-through; } #mute[aria-pressed="true"] { color: var(--dim); text-decoration: line-through; }
#volume, #voice-volume { width: clamp(50px, 9vw, 100px); accent-color: var(--accent); cursor: pointer; } #volume, #voice-volume { width: calc(7.8 * var(--u)); accent-color: var(--accent); cursor: pointer; }
.sound-label { margin-left: 4px; font-size: 9px; color: var(--accent); } .sound-label { margin-left: 4px; font-size: 9px; color: var(--accent); }
#sentence { min-height: 1.8em; padding: .25em .6em; text-align: center; color: var(--blue); font-size: clamp(10.8px, 1.44vw, 15.84px); font-weight: bold; background: var(--panel); } /* La frase: una riga sola, alta sempre uguale (2,5 --u); una frase lunghissima finisce coi puntini invece di spostare la scena. */
#sentence { height: calc(2.5 * var(--u)); line-height: 1.5; padding: .25em .6em; text-align: center; color: var(--blue); font-size: calc(1.2375 * var(--u)); font-weight: bold; background: var(--panel);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#panel { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 8px; padding: 8px; background: var(--panel); } /* Verbi e inventario: alti sempre uguale (10,8 --u, dove stanno anche quattro risposte di dialogo: vedi #choices). */
#panel { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: calc(.625 * var(--u)); padding: calc(.625 * var(--u)); height: calc(10.8 * var(--u)); background: var(--panel); }
/* Otto verbi su due righe piene, quattro per riga. */ /* Otto verbi su due righe piene, quattro per riga. */
#verbs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; align-content: start; } #verbs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: calc(.3125 * var(--u)); align-content: stretch; }
#verbs button, #inventory button { font: inherit; color: var(--dim); background: #3a2d42; border: 2px solid #4b3a55; border-radius: 4px; #verbs button, #inventory button { font: inherit; color: var(--dim); background: #3a2d42; border: 2px solid #4b3a55; border-radius: 4px;
padding: .35em .2em; font-size: clamp(9.36px, 1.22vw, 13.68px); font-weight: bold; cursor: pointer; } padding: .35em .2em; font-size: calc(1.069 * var(--u)); font-weight: bold; cursor: pointer; }
#verbs button:hover, #inventory button:hover { color: var(--ink); } #verbs button:hover, #inventory button:hover { color: var(--ink); }
#verbs button[aria-pressed="true"] { color: var(--bg); background: var(--accent); border-color: var(--accent); } #verbs button[aria-pressed="true"] { color: var(--bg); background: var(--accent); border-color: var(--accent); }
#inventory { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: minmax(2.4em, auto); gap: 4px; align-content: start; #inventory { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: minmax(2.4em, auto); gap: calc(.3125 * var(--u)); align-content: start;
min-height: 5.4em; padding: 4px; background: #231a29; border: 2px solid #4b3a55; border-radius: 4px; min-height: 0; padding: calc(.3125 * var(--u)); background: #231a29; border: 2px solid #4b3a55; border-radius: 4px;
/* L'inventario non allunga mai il pannello: è alto quanto i verbi, e se gli oggetti sono tanti si scorre. */ /* L'inventario non allunga mai il pannello: è alto quanto i verbi, e se gli oggetti sono tanti si scorre. */
contain: size; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #6a557a transparent; } contain: size; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #6a557a transparent; }
#inventory:empty::before { content: 'Inventario'; grid-column: 1 / -1; align-self: center; text-align: center; color: var(--dim); opacity: .45; #inventory:empty::before { content: 'Inventario'; grid-column: 1 / -1; align-self: center; text-align: center; color: var(--dim); opacity: .45;
font-size: clamp(9.36px, 1.22vw, 13.68px); font-weight: bold; letter-spacing: .08em; } font-size: calc(1.069 * var(--u)); font-weight: bold; letter-spacing: .08em; }
#inventory button { display: flex; align-items: center; gap: .35em; justify-content: center; color: var(--ink); } #inventory button { display: flex; align-items: center; gap: .35em; justify-content: center; color: var(--ink); }
#inventory button { min-width: 0; overflow: hidden; } #inventory button { min-width: 0; overflow: hidden; }
#inventory button span.long { min-width: 0; font-size: .72em; line-height: 1.15; text-align: left; } #inventory button span.long { min-width: 0; font-size: .72em; line-height: 1.15; text-align: left; }
@@ -130,8 +140,14 @@ body { display: flex; justify-content: center; overflow-x: hidden; -webkit-tap-h
#inventory img { width: 1.9em; height: 1.9em; flex: none; image-rendering: pixelated; } #inventory img { width: 1.9em; height: 1.9em; flex: none; image-rendering: pixelated; }
/* Il carattere sta sull'elenco, non sulle righe: il rientro in em segue il carattere. «1. » nel carattere pixel è largo tre caratteri e mezzo. */ /* Il carattere sta sull'elenco, non sulle righe: il rientro in em segue il carattere. «1. » nel carattere pixel è largo tre caratteri e mezzo. */
#choices { margin: 0; padding: 10px 14px 12px 3.6em; background: var(--panel); min-height: 6.5em; font-size: clamp(10.8px, 1.44vw, 15.84px); } /* Le risposte: alte quanto il pannello dei verbi, così la scena non si muove quando parte un dialogo. Quattro risposte ci
#choices li { padding: .25em 0; font-weight: bold; color: var(--dim); cursor: pointer; } stanno al carattere pieno; da cinque in su il carattere cala (e se mai non bastasse, si scorre). */
#choices { margin: 0; padding: calc(.78 * var(--u)) calc(1.1 * var(--u)) calc(.94 * var(--u)) 3.6em; background: var(--panel); height: calc(10.8 * var(--u));
overflow-y: auto; scrollbar-width: thin; font-size: calc(1.2375 * var(--u)); line-height: 1.4; }
#choices li { padding: .2em 0; font-weight: bold; color: var(--dim); cursor: pointer; }
#choices:has(li:nth-child(5)) { font-size: calc(.96 * var(--u)); line-height: 1.25; }
#choices:has(li:nth-child(5)) li { padding: .15em 0; }
#choices:has(li:nth-child(7)) { font-size: calc(.72 * var(--u)); }
#choices li:hover, #choices li:focus { color: var(--accent); outline: none; } #choices li:hover, #choices li:focus { color: var(--accent); outline: none; }
.overlay { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .6em; .overlay { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .6em;
@@ -223,10 +239,11 @@ body.full #sound { position: fixed; z-index: 13; top: max(8px, env(safe-area-ins
/* Mappa: la pianta del paese a gradini, i posti visti come cartellini, le strade come linee. */ /* Mappa: la pianta del paese a gradini, i posti visti come cartellini, le strade come linee. */
/* In alto a sinistra: Pausa e Mappa. */ /* In alto a sinistra: Pausa e Mappa. */
#corner { position: absolute; top: 6px; left: 6px; z-index: 2; display: flex; gap: 6px; } #corner { position: absolute; top: 6px; left: 6px; z-index: 2; display: flex; gap: 6px; }
#map-button, #pause-button, #notebook-button, #spots-button, #back-button { font: bold clamp(8.64px, 1.15vw, 11.52px) var(--font); padding: .35em .8em; #map-button, #pause-button, #notebook-button, #spots-button, #back-button { font: bold calc(.9 * var(--u)) var(--font); padding: .35em .8em;
color: var(--accent); background: rgba(27, 20, 32, .62); border: 0; border-radius: 18px; cursor: pointer; } color: var(--accent); background: rgba(27, 20, 32, .62); border: 0; border-radius: 18px; cursor: pointer; }
#back-button { color: #1b1420; background: #9fd0ff; } #back-button { color: #1b1420; background: #9fd0ff; }
#pause { z-index: 3; background: rgba(15, 10, 20, .9); } /* Con molti pulsanti la Pausa può essere più alta della scena: «safe» la fa partire dall'alto e scorrere, invece di tagliare il titolo. */
#pause { z-index: 3; background: rgba(15, 10, 20, .9); justify-content: safe center; }
/* Il player dei video «Come si gioca»: sopra a tutto, il video grande quanto può. */ /* Il player dei video «Come si gioca»: sopra a tutto, il video grande quanto può. */
#tutorial { position: fixed; inset: 0; z-index: 20; background: rgba(10, 8, 14, .97); padding: 8px; } #tutorial { position: fixed; inset: 0; z-index: 20; background: rgba(10, 8, 14, .97); padding: 8px; }
#tutorial video { max-width: min(100%, 1100px); max-height: calc(100dvh - 9em); background: #000; } #tutorial video { max-width: min(100%, 1100px); max-height: calc(100dvh - 9em); background: #000; }
@@ -323,6 +340,9 @@ body.full #sound { position: fixed; z-index: 13; top: max(8px, env(safe-area-ins
#pause { position: fixed; inset: 0; z-index: 10; overflow-y: auto; background: rgba(15, 10, 20, .97); } } #pause { position: fixed; inset: 0; z-index: 10; overflow-y: auto; background: rgba(15, 10, 20, .97); } }
/* Schermi stretti: il carattere a pixel è largo, i verbi vanno sopra l'inventario e su due righe da quattro. */ /* Schermi stretti: il carattere a pixel è largo, i verbi vanno sopra l'inventario e su due righe da quattro. */
@media (max-width: 600px) { @media (max-width: 600px) {
#sentence { height: auto; min-height: 1.8em; white-space: normal; }
#panel, #choices { height: auto; }
#choices { min-height: 6.5em; }
#panel { grid-template-columns: 1fr; gap: 6px; padding: 6px; } #panel { grid-template-columns: 1fr; gap: 6px; padding: 6px; }
#verbs button { font-size: 9px; padding: .7em .1em; } #verbs button { font-size: 9px; padding: .7em .1em; }
#inventory { height: 7.6em; } #inventory { height: 7.6em; }
@@ -341,7 +361,8 @@ body.full #sound { position: fixed; z-index: 13; top: max(8px, env(safe-area-ins
.card-hand-many button, .card-buttons button { flex-basis: 40%; overflow-wrap: anywhere; } .card-hand-many button, .card-buttons button { flex-basis: 40%; overflow-wrap: anywhere; }
.card-hand { flex-direction: column; gap: 4px; } .card-hand { flex-direction: column; gap: 4px; }
#side { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--panel); } #side { flex: 1; min-width: 0; display: flex; flex-direction: column; background: var(--panel); }
#sentence { font-size: 10.08px; } #sentence { height: auto; min-height: 1.8em; white-space: normal; font-size: 10.08px; }
#panel, #choices { height: auto; }
/* Il fumetto: più largo e più piccolo, sennò una battuta lunga diventa una colonna alta quanto la scena. */ /* Il fumetto: più largo e più piccolo, sennò una battuta lunga diventa una colonna alta quanto la scena. */
#speech { max-width: 72%; font-size: 11px; } #speech { max-width: 72%; font-size: 11px; }
#panel { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); flex: 1; min-height: 0; gap: 6px; padding: 6px; } #panel { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); flex: 1; min-height: 0; gap: 6px; padding: 6px; }
@@ -358,6 +379,7 @@ body.full #sound { position: fixed; z-index: 13; top: max(8px, env(safe-area-ins
.buttons { gap: 6px; margin-top: .2em; } .buttons { gap: 6px; margin-top: .2em; }
.buttons button { font-size: 10px; padding: .45em 1em; } .buttons button { font-size: 10px; padding: .45em 1em; }
.pause-buttons { min-width: 12em; } .pause-buttons { min-width: 12em; }
#pause .hint { display: none; }
/* Mappa: schede più piccole, ne stanno cinque o sei per riga. */ /* Mappa: schede più piccole, ne stanno cinque o sei per riga. */
#map-board button { width: 68px; } #map-board button { width: 68px; }
#map-board button img { width: 60px; } #map-board button img { width: 60px; }
+32 -1
View File
@@ -248,9 +248,33 @@ async function minigiochi() {
} }
async function telefoni() { async function telefoni() {
// La finestra di Andrea (1040×590: Firefox con le sue barre su un portatile): la scena era larga 604 px con 100 px
// vuoti sotto, ora prende l'altezza; i verbi stanno nei pulsanti; un dialogo a quattro risposte e uno a cinque
// non muovono la scena e stanno nel pannello.
{
const win = await open({ width: 1040, height: 590 });
await win.click('#splash'); await win.click('#new-game'); await win.click('#rione [data-rione="campomigliaio"]'); await win.click('#skip-intro');
await settle(win);
const box = async () => { const r = await win.locator('#stage').boundingBox(); return [r.x, r.y, r.width, r.height].map(Math.round); };
const b0 = await box();
assert.ok(b0[2] >= 820 && b0[1] >= 0, `a 1040×590 la scena è larga ${b0[2]} px e comincia a ${b0[1]}`);
assert.ok(await win.evaluate(() => document.documentElement.scrollHeight <= innerHeight), 'la pagina scorre a 1040×590');
assert.ok(await win.evaluate(() => [...document.querySelectorAll('#verbs button')].every(b => b.scrollWidth <= b.clientWidth + 1 && b.scrollHeight <= b.clientHeight + 1)), 'verbi tagliati a 1040×590');
for (const id of ['giocatore_carte', 'arcu']) {
await win.evaluate(id => { game.dialogue = { id, node: 'start' }; }, id);
await win.waitForFunction(() => !document.getElementById('choices').hidden);
const n = await win.evaluate(() => game.options().length);
assert.deepEqual(await box(), b0, `la scena si muove col dialogo ${id}`);
assert.ok(await win.evaluate(() => { const c = document.getElementById('choices'); return c.scrollHeight <= c.clientHeight + 1; }), `le ${n} risposte di ${id} non stanno nel pannello`);
if (id === 'arcu') await win.screenshot({ path: `${OUT}/11-finestra-1040-dialogo.png` });
await win.evaluate(() => { game.dialogue = null; });
}
await win.screenshot({ path: `${OUT}/11-finestra-1040.png` });
await win.close();
}
const phone = await open({ width: 390, height: 844 }); const phone = await open({ width: 390, height: 844 });
await phone.click('#splash'); // un clic salta il cartello await phone.click('#splash'); // un clic salta il cartello
assert.ok(await phone.isVisible('#title'), 'il clic sul cartello porta al titolo'); assert.ok(await phone.waitForSelector('#title:not([hidden])', { timeout: 5000 }).then(() => true, () => false), 'il clic sul cartello porta al titolo');
await phone.click('#new-game'); await phone.click('#new-game');
await phone.click('#rione [data-rione="campomigliaio"]'); await phone.click('#rione [data-rione="campomigliaio"]');
await phone.click('#skip-intro'); await phone.click('#skip-intro');
@@ -476,10 +500,17 @@ try {
assert.deepEqual(inv, { inside: true, scrolls: true, lastVisible: true }, 'inventario pieno'); assert.deepEqual(inv, { inside: true, scrolls: true, lastVisible: true }, 'inventario pieno');
await page.screenshot({ path: `${OUT}/02d-inventario-pieno.png` }); await page.screenshot({ path: `${OUT}/02d-inventario-pieno.png` });
await page.evaluate(() => { game.state.inventory = game.state.inventory.filter(i => !i.startsWith('prova')); for (let i = 1; i <= 16; i++) delete game.story.items['prova' + i]; }); await page.evaluate(() => { game.state.inventory = game.state.inventory.filter(i => !i.startsWith('prova')); for (let i = 1; i <= 16; i++) delete game.story.items['prova' + i]; });
// Il gioco prende la finestra: a 1280×860 la scena non si ferma più ai 1084 px di una riserva fissa di 250 px sotto,
// la pagina non scorre e i verbi stanno interi nei pulsanti (la finestra di Andrea, 1040×590, è provata più sotto).
const stage0 = await page.locator('#stage').boundingBox();
assert.ok(stage0.width >= 1200, `a 1280×860 la scena è larga ${stage0.width} px`);
assert.ok(await page.evaluate(() => document.documentElement.scrollHeight <= innerHeight), 'la pagina scorre in verticale a 1280×860');
assert.ok(await page.evaluate(() => [...document.querySelectorAll('#verbs button')].every(b => b.scrollWidth <= b.clientWidth + 1 && b.scrollHeight <= b.clientHeight + 1)), 'verbi tagliati a 1280×860');
// Pausa: Esc o il pulsante fermano il gioco; Riprendi torna dov'eri; il titolo offre «Continua». // Pausa: Esc o il pulsante fermano il gioco; Riprendi torna dov'eri; il titolo offre «Continua».
await page.keyboard.press('Escape'); await page.keyboard.press('Escape');
await page.waitForSelector('#pause:not([hidden])'); await page.waitForSelector('#pause:not([hidden])');
await page.screenshot({ path: `${OUT}/02b-pausa.png` }); await page.screenshot({ path: `${OUT}/02b-pausa.png` });
assert.equal(await page.evaluate(() => document.getElementById('fullscreen').hidden), await page.evaluate(() => !document.fullscreenEnabled), 'il pulsante Schermo intero c\'è se il browser lo permette');
// Salva su file: si scarica la partita; dal titolo «Carica» la riapre anche senza il salvataggio del browser. // Salva su file: si scarica la partita; dal titolo «Carica» la riapre anche senza il salvataggio del browser.
const [download] = await Promise.all([page.waitForEvent('download'), page.click('#save-file')]); const [download] = await Promise.all([page.waitForEvent('download'), page.click('#save-file')]);
assert.match(download.suggestedFilename(), /^regolo-\d{4}-\d\d-\d\d-\d{4}\.json$/); assert.match(download.suggestedFilename(), /^regolo-\d{4}-\d\d-\d\d-\d{4}\.json$/);