I rinquarti di Bertovo: gara a punti (Bertovo tira dopo Andrea, preciso secondo il livello), allenamento, posizioni misurate, traiettoria con le sponde e Prossima, comandi sicuri col dito (revisione di Codex); la Brauda in gara; voce di Bertovo (Andrea)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiECdCPptNRwbVzjzHbohi
This commit is contained in:
2026-10-28 23:55:47 +01:00
co-authored by Claude Opus 5.5
parent bf22dc2511
commit 086e4682d4
24 changed files with 506 additions and 422 deletions
+33 -11
View File
@@ -507,15 +507,16 @@ canvas.addEventListener('pointerdown', e => {
if (gara.press(game.closeup, e.pointerId)) { press = { gara: e.pointerId }; canvas.setPointerCapture(e.pointerId); }
return;
}
if (press?.closeup) return;
const aimed = game.closeupAim(scenePoint(e));
if (['biliardo', 'rinquarto', 'basket'].includes(story.closeups[game.closeup.id].type)) {
if (!aimed && !game.speech) return; // dove non si può mirare (dietro Andrea) non si carica nemmeno
if (aimed) game.closeup.aimLocked = true; // il clic fissa la mira
}
if (['bocce', 'basket', 'pesca'].includes(story.closeups[game.closeup.id].type) && aimed) game.closeup.aimLocked = true;
press = { closeup: true };
press = { closeup: true, pointerId: e.pointerId, state: game.closeup };
// Alle bocce si tiene premuto per caricare la forza (la battuta in corso, un tocco la salta).
if (!game.skip()) game.closeupPress();
if (!game.skip()) game.closeupPress(e.pointerId);
canvas.setPointerCapture(e.pointerId);
return;
}
@@ -543,7 +544,12 @@ canvas.addEventListener('pointerup', e => {
if (e.pointerId === press.gara) { if (game?.closeup) gara.release(game.closeup, e.pointerId); press = null; }
return;
}
if (press.closeup) { press = null; if (running && !game.closeupRelease() && !game.skip()) game.closeupTap(); return; }
if (press.closeup) {
if (e.pointerId !== press.pointerId) return;
const state = press.state; press = null;
if (running && game.closeup === state && !game.closeupRelease(e.pointerId) && !game.skip()) game.closeupTap();
return;
}
clearTimeout(press.timer);
// Il secondo clic vicino al primo, entro poco, è un doppio clic: stesso comando, al doppio della velocità.
if (!press.long) {
@@ -554,8 +560,13 @@ canvas.addEventListener('pointerup', e => {
}
press = null;
});
canvas.addEventListener('pointercancel', () => { if (press) clearTimeout(press.timer); press = null; game?.closeupCancel(); });
canvas.addEventListener('lostpointercapture', () => { if (press?.closeup || press?.gara !== undefined || press?.card !== undefined || press?.table !== undefined) { press = null; game?.closeupCancel(); } });
canvas.addEventListener('pointercancel', e => {
if (press?.closeup && e.pointerId !== press.pointerId) return;
if (press) clearTimeout(press.timer);
if (!press?.closeup || game?.closeup === press.state) game?.closeupCancel(e.pointerId);
press = null;
});
canvas.addEventListener('lostpointercapture', e => { if (press?.closeup && e.pointerId !== press.pointerId) return; if (press?.closeup || press?.gara !== undefined || press?.card !== undefined || press?.table !== undefined) { press = null; game?.closeupCancel(e.pointerId); } });
canvas.addEventListener('contextmenu', e => {
e.preventDefault();
if (running && !game.closeup) command.clickScene(scenePoint(e), { look: true });
@@ -590,7 +601,7 @@ const MINI = {
briscola: { ...briscola, back: 'Torna all’oratorio', label: 'Briscola: tocca una carta per giocarla. Frecce o 1, 2, 3 scelgono; Invio gioca. Esc esce.' },
bocce: { ...bocce, nudge: (c, dir) => bocce.aim(c, c.aim.u, c.aim.w + dir * 0.3), back: 'Torna al pallaio', label: 'Bocce: tocca il campo o il dettaglio per mirare. Regola la forza e premi Lancia.' },
biliardo: { ...biliardo, back: 'Torna al bar', label: 'Biliardo: clic o tocco fissa la mira. Frecce per affinarla, Maiusc per passi più piccoli. Regola la forza e premi Tira.' },
rinquarto: { ...rinquarto, back: 'Torna da Bertovo', label: 'I rinquarti di Bertovo: clic o tocco fissa la mira. Frecce per affinarla, Maiusc per passi più piccoli. Regola la forza e premi Tira.' },
rinquarto: { ...rinquarto, back: 'Torna da Bertovo', label: 'I rinquarti di Bertovo: clic o tocco fissa la mira, frecce o pulsanti Mira per girare la stecca (Maiusc per i passi fini), Tira per tirare. Dopo l’esito, Prossima.' },
basket: { ...basket, back: 'Torna in strada', label: 'Fai due tiri: muovi il mouse o tocca per mirare, tieni premuto per caricare la forza, lascia per tirare.' },
// La gara: niente mira né forza, il pulsante alterna gas e freno (chi non può tenere premuto).
gara: { ...gara, toggle: true, back: 'Torna dal Trappola', label: 'GP di Fortezza: tieni premuto sulla scena per il gas e trascina verso la direzione in cui vuoi andare sullo schermo; alza il dito per mollare. Anche col mouse. Oppure Spazio, Invio o ↑ per il gas, ← → per sterzare, ↓ per frenare; disponibili anche i pulsanti Gas, Sterza e Frena.' },
@@ -645,7 +656,16 @@ for (let i = 0; i < 3; i++) {
button.addEventListener('focus', () => { if (game?.closeup?.id === 'briscola') briscola.select(game.closeup, i); });
}
for (const [id, dir] of [['bocce-left', -1], ['bocce-right', 1]]) {
$(id).addEventListener('click', () => { const c = miniState(); if (c && mini().nudge(c, dir)) c.aimLocked = true; });
$(id).addEventListener('click', e => { const c = miniState(); if (!c) return;
if (mini().turn) { if (!e.detail) { mini().turn(c, dir, 'click'); mini().stopTurn(c, 'click'); } }
else if (mini().nudge(c, dir)) c.aimLocked = true;
});
$(id).addEventListener('pointerdown', e => { const c = miniState();
if (c && mini().turn?.(c, dir, e.pointerId)) $(id).setPointerCapture(e.pointerId);
});
for (const ev of ['pointerup', 'pointercancel', 'lostpointercapture']) $(id).addEventListener(ev, e => {
const c = miniState(); if (c) mini().stopTurn?.(c, e.pointerId);
});
// Al GP di Fortezza si sterza finché il pulsante resta premuto (anche col dito, insieme a Gas).
$(id).addEventListener('pointerdown', () => { const c = miniState(); if (c) mini().steerButton?.(c, dir); });
for (const ev of ['pointerup', 'pointerleave', 'pointercancel']) $(id).addEventListener(ev, () => { const c = miniState(); if (c) mini().steerButton?.(c, 0); });
@@ -821,8 +841,8 @@ window.addEventListener('keydown', e => {
if (e.key === 'Escape') { game.closeupClose(); return; }
const onButton = ['BUTTON', 'SUMMARY'].includes(e.target.tagName), onControl = onButton && e.target.closest?.('#bocce-controls');
// Il GP di Fortezza: frecce, Spazio e Invio finché si tengono giù (su un pulsante Spazio e Invio lo premono).
if (!(onButton && [' ', 'Enter'].includes(e.key)) && mini().key?.(game.closeup, e.key, true)) { e.preventDefault(); return; }
if (onControl) return;
if (!(onButton && [' ', 'Enter'].includes(e.key)) && mini().key?.(game.closeup, e.key, true, e.shiftKey)) { e.preventDefault(); return; }
if (onControl && [' ', 'Enter'].includes(e.key)) return;
const c = game.closeup, m = mini();
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
e.preventDefault();
@@ -868,7 +888,7 @@ window.addEventListener('keydown', e => {
// Alle bocce e al biliardo, lasciando spazio o Invio parte il tiro.
window.addEventListener('keyup', e => {
// Il GP di Fortezza tiene conto di ogni tasto per conto suo: lasciarne uno non toglie il gas del dito o degli altri.
if (miniState() && mini().key) { mini().key(game.closeup, e.key, false); return; }
if (miniState() && mini().key?.(game.closeup, e.key, false)) return;
if (running && [' ', 'Enter'].includes(e.key)) game?.closeupRelease();
});
@@ -985,10 +1005,12 @@ function updateUi() {
setValue($('bocce-power'), Math.round(c.power * 100));
setValue($('bocce-power-value'), `${Math.round(c.power * 100)}%`);
for (const id of ['bocce-power', 'bocce-left', 'bocce-right']) setProp($(id), 'disabled', !(mg.canAdjust?.(c) ?? mg.canThrow(c)) || (c.holding && !mg.toggle) || !!game.speech);
setProp($('bocce-throw'), 'disabled', !mg.canThrow(c) || (c.holding && !mg.toggle) || !!game.speech);
setProp($('bocce-throw'), 'disabled', !(mg.canPress?.(c) ?? mg.canThrow(c)) || (c.holding && !mg.toggle) || !!game.speech);
// Il GP di Fortezza: niente forza, e i due pulsanti di mira diventano lo sterzo (si tengono premuti).
// Nel GP di Fortezza il cursore della forza diventa la sensibilità dello sterzo col dito (ricordata).
const powerLabel = $('bocce-power').closest('label');
// Ai rinquarti la forza è sempre piena: il cursore non serve.
setProp(powerLabel, 'hidden', def.type === 'rinquarto');
const powerName = def.type === 'gara' ? 'Sensibilità ' : 'Forza ';
if (powerLabel.firstChild.nodeValue !== powerName) powerLabel.firstChild.nodeValue = powerName;
setAttr($('bocce-power'), 'aria-label', def.type === 'gara' ? 'Sensibilità dello sterzo col dito' : 'Forza del lancio');