// Copione del video «Come si gioca: il biliardo» (video/SCALETTA-CARTE.md, clip 5). // Giro: video/narra.py, poi rec.mjs come in video/SCALETTA.md, poi video/monta.py --tutorial. import { c, sleep, setup as base, regista } from "./tutorial-comune.mjs"; export const title = { it: "Come si gioca · il biliardo", en: "Come si gioca · il biliardo" }; export const cues = [ c("dove", "Il biliardo è al Circolo 4 Maggio, in piazza. L'Ingegner Tozzi gioca ai cinque birilli, e si va a venti"), c("livello", "Anche qui si sceglie quanto deve giocare forte"), c("tavolo", "Il tavolo dall'alto: la tua biglia, quella dell'avversario, il pallino, e in mezzo il castello dei birilli"), c("scopo", "Si colpisce la propria biglia e la si manda su quella dell'avversario: i birilli che lei abbatte fanno punti a chi tira"), c("mira", "La stecca segue il mouse, e un clic fissa la mira. La linea tratteggiata è la strada della biglia; le frecce, dove vanno le due biglie dopo l'urto"), c("tira", "Con le frecce si gira la stecca, con Maiuscolo ancora più piano. Poi si regola la forza, e si preme Tira"), c("valori", "Il birillo bianco vale due, il rosso quattro, il rosso da solo dieci. Il pallino toccato dalla biglia avversaria, tre"), c("fallo", "Attenzione ai falli: mancare la biglia avversaria, toccare prima il pallino, o buttare giù birilli con la propria. I punti vanno all'avversario"), c("fine", "Un colpo a testa, finché qualcuno arriva a venti. Buona partita!"), ]; export const setup = base; export async function run(d) { const p = d.page, r = regista(d, "tutorial-biliardo"); await r.ascolta(); await r.vaiA("circolo_4_maggio", [470, 300]); await r.cue("dove"); await r.parlaCon("ing_tozzi"); await r.scegli("una partita ai birilli"); await r.cue("livello"); await r.scegli("Alla bona."); // Una partita fissa: un colpo da otto, un fallo voluto per farlo vedere, poi quattro e tre; vince Andrea 21 a 2. await p.evaluate(async () => { const { newBiliardo } = await import("/src/engine/biliardo.js"); game.closeup = { id: "biliardo", ...newBiliardo(2, { level: "bona" }) }; }); await sleep(800); await r.cue("tavolo"); await r.muovi([200, 200], 0.9); await sleep(300); await r.muovi([400, 180], 0.9); await r.cue("scopo"); // Il colpo: il migliore fra quelli provati a vuoto (o, la seconda volta, un fallo apposta). Si mira col clic sul // tavolo lungo la direzione scelta, poi forza col cursore e Tira. const colpo = (sbaglia) => p.evaluate(async (sbaglia) => { const L = await import("/src/engine/biliardo.js"), c = game.closeup, b = c.balls.player, o = c.balls.cpu; let top = null; if (sbaglia) for (let a = 0; a < Math.PI * 2 && !top; a += 0.05) { if (L.simulate(c, "player", a, 0.12).foul === "miss") top = { angle: a, power: 0.12 }; } else { const base = Math.atan2(o.y - b.y, o.x - b.x); for (let d = -0.35; d <= 0.35; d += 0.01) for (const power of [0.3, 0.4, 0.5, 0.6, 0.7, 0.8]) { const j = L.simulate(c, "player", base + d, power), gain = j.to === "player" ? j.points : -j.points; if (!top || gain > top.gain) top = { angle: base + d, power, gain }; } } const x = b.x + Math.cos(top.angle) * 45, y = b.y + Math.sin(top.angle) * 45; return { ...top, at: [L.VIEW.x + x * L.VIEW.k, L.VIEW.y + y * L.VIEW.k] }; }, sbaglia); const tocca = async () => { for (let i = 0; i < 900; i++) { const s = await p.evaluate(async () => { const { canThrow } = await import("/src/engine/biliardo.js"); return game.closeup.step === "result" ? "fine" : canThrow(game.closeup) && !game.speech ? "tocca" : "aspetta"; }); if (s !== "aspetta") return s === "tocca"; await sleep(150); } return false; }; let n = 0; while (await tocca()) { n++; if (n === 1) await r.cue("mira"); if (n === 2) await r.cue("fallo"); const m = await colpo(n === 2); await r.muovi(m.at, 0.9); await sleep(400); await r.clicca(m.at, 0.3); await p.evaluate(({ angle }) => { game.closeup.angle = angle; game.closeup.aimLocked = true; }, m); if (n === 1) await r.cue("tira"); await d.hover(p.locator("#bocce-power"), 0.6); await p.locator("#bocce-power").fill(String(Math.round(m.power * 100))); await p.evaluate((v) => { game.closeup.power = v; }, m.power); await sleep(500); await d.click(p.locator("#bocce-throw"), 0.6); await p.locator("#scene").scrollIntoViewIfNeeded(); if (n === 1) await r.cue("valori"); } await p.waitForFunction(() => game.closeup.step === "result", null, { timeout: 120000 }); await sleep(1500); 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(); }