Bocce: il clic fissa la mira come al biliardo, così andando verso la forza o «Lancia» il puntatore non la sposta più (scoperto girando il video: la boccia partiva a due passi). Copioni dei video «Come si gioca» di scopa, tressette, bocce e biliardo, e video/sovrapposte.py per controllare che il narratore non parli sopra i personaggi

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
This commit is contained in:
2026-10-26 21:23:23 +01:00
co-authored by Claude Opus 5.5
parent 111b892916
commit 3019a6e32f
7 changed files with 376 additions and 4 deletions
+79
View File
@@ -0,0 +1,79 @@
// Copione del video «Come si gioca: la scopa» (video/SCALETTA-CARTE.md, clip 3).
// Giro: video/narra.py, poi rec.mjs come in video/SCALETTA.md, poi video/monta.py <cartella> --tutorial.
import { c, sleep, setup as base, regista } from "./tutorial-comune.mjs";
export const title = { it: "Come si gioca · la scopa", en: "Come si gioca · la scopa" };
export const cues = [
c("dove", "Allo stesso tavolo dell'oratorio si gioca anche a scopa, in due: basta chiedere «Una scopa?»"),
c("mazzo", "Mazzo e livello si scelgono come per la briscola"),
c("valori", "Quattro carte in tavola e tre in mano. L'Asso vale uno, le altre il loro numero, il gobbo otto, la donna nove e il regio dieci"),
c("presa", "Con una carta prendi quella dello stesso valore; se non c'è, un gruppo di carte che fa la stessa somma. Se puoi prendere, devi"),
c("scelta", "Quando le prese possibili sono più d'una, scegli tu: tocca una carta in tavola, oppure usa i pulsanti qui sotto"),
c("scopa", "Se con la tua carta pulisci tutta la tavola, è scopa: un punto in più"),
c("resto", "Finite le tre carte se ne danno altre tre, finché c'è mazzo. Quel che resta in tavola alla fine va all'ultimo che ha preso"),
c("conto", "A fine smazzata un punto a chi ha più carte, uno a chi ha più mattoni, uno per il settebello, il sette di mattoni, e uno per la primiera"),
c("primiera", "La primiera somma la carta migliore di ogni seme: il sette vale ventuno, il sei diciotto, l'Asso sedici"),
c("fine", "Si va agli undici punti, una smazzata dopo l'altra. Buona scopa!"),
];
export const setup = base;
export async function run(d) {
const p = d.page, r = regista(d, "tutorial-scopa");
await r.ascolta();
await r.vaiA("oratorio", [470, 300]);
await r.cue("dove");
await r.parlaCon("giocatore_carte");
await r.scegli("Una scopa?");
await r.cue("mazzo");
await r.scegli("Con le toscane.");
await r.scegli("Seria.");
// Una smazzata fissa: Andrea sceglie fra due prese, fa due scope e la vince 5 a 1.
await p.evaluate(async () => {
const { newScopa } = await import("/src/engine/scopa.js");
game.closeup = { id: "scopa", ...newScopa(73, { level: "seria", deck: "toscane" }) };
});
await sleep(800);
await p.waitForFunction(() => window.music?.want === "assets/audio/carte.mp3", null, { timeout: 10000 });
await r.cue("valori");
await r.muovi([320, 160], 1.0); await sleep(500); await r.muovi([320, 300], 1.0);
await r.cue("presa");
// Una mossa di Andrea, come la sceglierebbe un giocatore attento; la scelta fra più prese si fa col pulsante.
let k = 1, scelta = false, scopa = false;
for (let i = 0; i < 400; i++) {
const s = await p.evaluate(async () => {
const { canPlay } = await import("/src/engine/scopa.js");
return ["smazzata", "result"].includes(game.closeup.step) ? "fine" : canPlay(game.closeup) && !game.speech ? "tocca" : "aspetta";
});
if (s === "fine") break;
if (s === "aspetta") { await sleep(150); continue; }
const m = await p.evaluate(async (k) => {
const S = await import("/src/engine/scopa.js"), c = game.closeup, m = S.choose(S.observation(c, "player"), "male", k);
const card = c.hands.player[m.index], many = S.captures(c.table, card).length > 1;
const clean = !!m.captured && m.captured.length === c.table.length;
const { handX, HAND } = await import("/src/engine/scopa-render.js");
return { ...m, many, clean, at: [handX(c.hands.player.length, m.index) + 32, HAND.y + 46] };
}, k++);
if (m.many && !scelta) { scelta = true; await r.cue("scelta"); }
if (m.clean && !scopa) { scopa = true; await r.cue("scopa"); }
await r.clicca(m.at, 0.7);
if (m.many) {
await p.waitForFunction(() => game.closeup.step === "choose", null, { timeout: 5000 });
await sleep(900);
await d.click(p.locator(`#scopa-options [data-option="${m.option}"]`), 0.7);
await p.locator("#scene").scrollIntoViewIfNeeded();
}
if (k === 9) await r.cue("resto");
}
await p.waitForFunction(() => game.closeup.step === "smazzata", null, { timeout: 60000 });
await sleep(1200);
await r.cue("conto");
await r.cue("primiera");
await r.cue("fine");
await r.aspetta();
await d.click(p.locator("#closeup-close"), 0.6);
await r.fermi(800);
await r.fine();
await r.salvaAudio();
}