Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECd9Quo9nEdXyjXYF8ma
166 lines
8.5 KiB
JavaScript
166 lines
8.5 KiB
JavaScript
// Copione del video breve dell'Ingorgo Sonoro (Andrea, 30 ottobre: «45 secondi, che parta come... ci sono cose che non
|
|
// tornano..... forse..., e fai vedere il manifesto dell'Ingorgo e girare tra la musica»). Come i trailer
|
|
// (video/trailer.mjs): la scena a tutto schermo, scritte e stacchi al nero nella pagina, niente clic; la musica la
|
|
// sceglie il copione, una console per posto (out/ingorgo-sonoro-audio.json).
|
|
// Prova: OUT=/tmp/prova COPIONE=./ingorgo-sonoro.mjs node video/prova-locale.mjs
|
|
// Ripresa 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 · l'Ingorgo Sonoro", en: "La Leggenda del Regolo · l'Ingorgo Sonoro" };
|
|
export const cues = [
|
|
c("dubbio", "Ci sono cose che non tornano…"),
|
|
c("manifesto", "il manifesto"),
|
|
c("incanto", "l'incantesimo"),
|
|
c("festa", "la festa"),
|
|
c("titolo", "La Leggenda del Regolo"),
|
|
];
|
|
|
|
const URL_GIOCO = "http://127.0.0.1:8000/";
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
const H = "assets/audio/ingorgo-house.mp3", L = "assets/audio/ingorgo-latino.mp3", U = "assets/audio/ingorgo-underground.mp3",
|
|
T = "assets/audio/ingorgo-techno.mp3";
|
|
|
|
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])");
|
|
await p.click("#new-game");
|
|
await p.click('#rione [data-rione="campomigliaio"]');
|
|
await p.click("#skip-intro");
|
|
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; }
|
|
#title::before { background-image: url(assets/scenes/ingorgo-piazza.png) !important; }
|
|
#title::after { background: linear-gradient(rgba(20, 12, 30, .6), rgba(20, 12, 30, .1) 45%, rgba(20, 12, 30, .75)),
|
|
url(assets/scenes/ingorgo-piazza.png) center / contain no-repeat !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: 40px; line-height: 1.7; color: #ffe9b0; opacity: 0; white-space: pre-line;
|
|
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: 30px; }
|
|
` });
|
|
await p.evaluate(() => {
|
|
for (const id of ["tr-nero", "tr-scritta"]) { const e = document.createElement("div"); e.id = id; document.body.appendChild(e); }
|
|
});
|
|
await p.evaluate(() => setInterval(() => dispatchEvent(new PointerEvent("pointermove")), 10000));
|
|
await p.evaluate(async () => { const { settle } = await import('/tests/helpers.mjs'); settle(game); window.voices?.stop?.(); });
|
|
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 nei trailer: 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, s = 0.7) => p.evaluate(([on, s]) => { const e = document.getElementById("tr-nero"); e.style.transition = `opacity ${s}s`; e.style.opacity = on ? 1 : 0; }, [on, s]);
|
|
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]);
|
|
const vai = (scene, at, flags = {}) => p.evaluate(async ([scene, at, flags]) => {
|
|
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, flags);
|
|
game.go(scene, at); settle(game);
|
|
}, [scene, at, flags]);
|
|
const cammina = (a) => p.evaluate((a) => game.walkTo(a), a);
|
|
|
|
// 1. Il nero e il dubbio.
|
|
await musica("assets/audio/sera.mp3");
|
|
await d.cue("dubbio");
|
|
await sleep(500);
|
|
await scritta("Ci sono cose\nche non tornano…");
|
|
await sleep(2400);
|
|
await scritta(null);
|
|
await sleep(600);
|
|
await scritta("…forse…");
|
|
await sleep(1500);
|
|
await scritta(null);
|
|
await sleep(400);
|
|
|
|
// 2. La piazza di giorno: Andrea va al manifesto, che si apre ingrandito.
|
|
await d.cue("manifesto");
|
|
await musica("assets/audio/paese.mp3");
|
|
await vai("piazza", [380, 320]);
|
|
await nero(false);
|
|
await sleep(700);
|
|
await p.evaluate(() => game.act("look", "manifesto_ingorgo"));
|
|
await p.waitForFunction(() => game.closeup?.id === "manifesto_ingorgo" && game.closeup.t > 0.5, null, { timeout: 15000 });
|
|
await sleep(2000);
|
|
// Il tocco sul manifesto: la battuta sul manifesto si salta, resta il desiderio, a voce.
|
|
await p.evaluate(() => game.closeupTap());
|
|
for (let k = 0; k < 200 && !(await p.evaluate(() => game.speech?.text.startsWith("Quanto darei"))); k++) {
|
|
await p.evaluate(() => { if (game.speech) game.skip(); });
|
|
await sleep(40);
|
|
}
|
|
|
|
// 3. L'incantesimo: nel lampo parte la house della piazza.
|
|
await d.cue("incanto");
|
|
await p.waitForFunction(() => game.closeup?.id === "ingorgo", null, { timeout: 20000 });
|
|
await p.waitForFunction(() => game.closeup?.swapped, null, { timeout: 10000 });
|
|
await musica(H);
|
|
// 4. Il giro della festa: Andrea si accorge della festa camminando fra la gente (la seconda battuta si salta), poi una
|
|
// console per posto, a stacchi veloci.
|
|
await p.waitForFunction(() => !game.closeup && game.speech?.text.toLowerCase().includes("ingorgo"), null, { timeout: 10000 });
|
|
await d.cue("festa");
|
|
await p.evaluate(() => { game.queue.length = 0; game.walkTo([200, 300]); });
|
|
await p.waitForFunction(() => !game.speech?.text.toLowerCase().includes("ingorgo"), null, { timeout: 20000 });
|
|
await p.evaluate(() => { game.queue.length = 0; game.speech = null; });
|
|
await sleep(300);
|
|
for (const [scene, da, a, brano] of [["via_antica_posta", [330, 345], [300, 290], H], ["piazzetta_comune", [260, 350], [300, 300], L],
|
|
["antica_posta_alta", [240, 350], [380, 290], U], ["piazzetta", [330, 352], [420, 290], T]]) {
|
|
await nero(true, 0.2); await sleep(220);
|
|
await musica(brano);
|
|
await vai(scene, da);
|
|
await nero(false, 0.2);
|
|
await cammina(a);
|
|
await sleep(3200);
|
|
}
|
|
await sleep(300);
|
|
|
|
// 5. Il titolo sopra la piazza della festa.
|
|
await d.cue("titolo");
|
|
await nero(true, 0.9); await sleep(1000);
|
|
await p.evaluate(() => { game.closeup = null; document.getElementById("title").hidden = false; });
|
|
await nero(false, 1.2);
|
|
await sleep(1200);
|
|
await scritta("l'Ingorgo Sonoro", true);
|
|
await sleep(3000);
|
|
await scritta(null);
|
|
await nero(true, 1.4);
|
|
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("ingorgo-sonoro-audio.json", out), JSON.stringify({ eventi: audio }, null, 2));
|
|
}
|