Il GP di Fortezza: gara dei cinquantini attorno alle mura (pista di Codex dalla planimetria, sterzo, acceleratori, miscela, buche, presentazione dei piloti coi motorini dalle foto); Via Liguri Magelli col Trappola, la targa della Ghiacciomobile e il Betino in cima alla salita; l'Arcu lancia la sfida dal Trappola e ha la voce nuova (Andrea)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECdCPptNRwbVzjzHbohi
This commit is contained in:
+51
-8
@@ -12,6 +12,7 @@ import * as bocce from './engine/bocce.js';
|
||||
import * as biliardo from './engine/biliardo.js';
|
||||
import * as basket from './engine/basket.js';
|
||||
import * as pesca from './engine/pesca.js';
|
||||
import * as gara from './engine/gara.js';
|
||||
import * as briscola from './engine/briscola.js';
|
||||
import { cardAt } from './engine/briscola-render.js';
|
||||
import * as tressette from './engine/tressette.js';
|
||||
@@ -497,6 +498,14 @@ canvas.addEventListener('pointerdown', e => {
|
||||
canvas.setPointerCapture(e.pointerId);
|
||||
return;
|
||||
}
|
||||
// Il GP di Fortezza: un dito solo sulla scena dà gas e sterza trascinando; gli altri tocchi non contano
|
||||
// (sterzo e freno hanno i loro pulsanti, che si tengono insieme al gas).
|
||||
if (type === 'gara') {
|
||||
if (press || game.skip()) return;
|
||||
gara.aimScreen(game.closeup, scenePoint(e), e.pointerId);
|
||||
if (gara.press(game.closeup, e.pointerId)) { press = { gara: e.pointerId }; canvas.setPointerCapture(e.pointerId); }
|
||||
return;
|
||||
}
|
||||
const aimed = game.closeupAim(scenePoint(e));
|
||||
if (['biliardo', 'basket'].includes(story.closeups[game.closeup.id].type)) {
|
||||
if (!aimed && !game.speech) return; // dove non si può mirare (dietro Andrea) non si carica nemmeno
|
||||
@@ -529,6 +538,10 @@ canvas.addEventListener('pointerup', e => {
|
||||
if (running && game.closeup === p.state && p.card >= 0 && p.at(game.closeup, scenePoint(e)) === p.card) game.closeupPlay(p.card);
|
||||
return;
|
||||
}
|
||||
if (press.gara !== undefined) {
|
||||
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; }
|
||||
clearTimeout(press.timer);
|
||||
// Il secondo clic vicino al primo, entro poco, è un doppio clic: stesso comando, al doppio della velocità.
|
||||
@@ -541,7 +554,7 @@ 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?.card !== undefined || press?.table !== undefined) { 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('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
if (running && !game.closeup) command.clickScene(scenePoint(e), { look: true });
|
||||
@@ -552,6 +565,10 @@ canvas.addEventListener('pointermove', e => {
|
||||
// Al biliardo e alle bocce la mira segue il mouse finché un clic (o una regolazione fine) non la fissa: dopo,
|
||||
// attraversare il campo per raggiungere la forza, «Tira» o «Lancia» non cambia il colpo. Al tiro dopo torna libera.
|
||||
// (Bocce: scoperto girando il video «Come si gioca», la mira finiva a due passi andando verso il cursore.)
|
||||
if (game.closeup && story.closeups[game.closeup.id].type === 'gara') {
|
||||
if (press?.gara === e.pointerId) gara.aimScreen(game.closeup, scenePoint(e), e.pointerId);
|
||||
return;
|
||||
}
|
||||
const aiming = game.closeup && ['biliardo', 'bocce', 'basket', 'pesca'].includes(story.closeups[game.closeup.id].type);
|
||||
if (aiming) { if (e.pointerType === 'mouse' && !press && !game.closeup.aimLocked) game.closeupAim(scenePoint(e)); }
|
||||
else if (e.pointerType === 'mouse' || press?.closeup) game.closeupAim(scenePoint(e));
|
||||
@@ -566,6 +583,8 @@ const MINI = {
|
||||
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.' },
|
||||
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 (mouse, dito, Spazio o ↑) per il gas, sterza con ← → o trascinando di lato; ↓ frena.' },
|
||||
pesca: { ...pesca, back: 'Torna in Fortezza', label: 'La pesca: tieni premuto per lanciare, premi quando il galleggiante va sotto, tieni premuto per recuperare.' },
|
||||
};
|
||||
const miniState = () => running && game?.closeup && MINI[story.closeups[game.closeup.id].type] ? game.closeup : null;
|
||||
@@ -594,8 +613,15 @@ for (let i = 0; i < 3; i++) {
|
||||
button.addEventListener('click', () => game?.closeupPlay(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]])
|
||||
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; });
|
||||
// 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); });
|
||||
}
|
||||
// Il freno del GP, tenuto premuto (sul telefono, insieme a Gas e Sterza).
|
||||
$('gara-brake').addEventListener('pointerdown', () => { const c = miniState(); if (c) mini().brakeButton?.(c, true); });
|
||||
for (const ev of ['pointerup', 'pointerleave', 'pointercancel']) $('gara-brake').addEventListener(ev, () => { const c = miniState(); if (c) mini().brakeButton?.(c, false); });
|
||||
addEventListener('blur', () => { if (press?.card !== undefined) press = null; game?.closeupCancel(); });
|
||||
|
||||
// --- Verbi.
|
||||
@@ -757,7 +783,12 @@ window.addEventListener('keydown', e => {
|
||||
return;
|
||||
}
|
||||
if (miniState()) {
|
||||
if (e.target.closest?.('#bocce-controls') && ['BUTTON', 'SUMMARY'].includes(e.target.tagName)) return;
|
||||
// Esc esce anche col fuoco sui pulsanti del pannello.
|
||||
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;
|
||||
const c = game.closeup, m = mini();
|
||||
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
@@ -801,7 +832,11 @@ window.addEventListener('keydown', e => {
|
||||
});
|
||||
|
||||
// Alle bocce e al biliardo, lasciando spazio o Invio parte il tiro.
|
||||
window.addEventListener('keyup', e => { if (running && [' ', 'Enter'].includes(e.key)) game?.closeupRelease(); });
|
||||
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 (running && [' ', 'Enter'].includes(e.key)) game?.closeupRelease();
|
||||
});
|
||||
|
||||
// --- Il primo piano: si chiude col pulsante; la pagina che gira fruscia (rumore filtrato, niente file audio).
|
||||
$('closeup-close').addEventListener('click', () => game?.closeupClose());
|
||||
@@ -908,19 +943,26 @@ function updateUi() {
|
||||
}
|
||||
}
|
||||
if (mg && !cards) {
|
||||
$('bocce-controls').setAttribute('aria-label', { biliardo: 'Comandi del biliardo', basket: 'Comandi del basket', pesca: 'Comandi della pesca' }[def.type] ?? 'Comandi delle bocce');
|
||||
$('bocce-controls').setAttribute('aria-label', { biliardo: 'Comandi del biliardo', basket: 'Comandi del basket', pesca: 'Comandi della pesca', gara: 'Comandi della gara' }[def.type] ?? 'Comandi delle bocce');
|
||||
$('panel').hidden = $('choices').hidden = true;
|
||||
const line = mg.status(c, def.names);
|
||||
if ($('bocce-status').textContent !== line) $('bocce-status').textContent = line;
|
||||
$('bocce-power').value = Math.round(c.power * 100);
|
||||
$('bocce-power-value').value = `${Math.round(c.power * 100)}%`;
|
||||
for (const id of ['bocce-power', 'bocce-left', 'bocce-right']) $(id).disabled = !(mg.canAdjust?.(c) ?? mg.canThrow(c)) || c.holding || !!game.speech;
|
||||
$('bocce-throw').disabled = !mg.canThrow(c) || c.holding || !!game.speech;
|
||||
for (const id of ['bocce-power', 'bocce-left', 'bocce-right']) $(id).disabled = !(mg.canAdjust?.(c) ?? mg.canThrow(c)) || (c.holding && !mg.toggle) || !!game.speech;
|
||||
$('bocce-throw').disabled = !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).
|
||||
$('bocce-power').closest('label').hidden = def.type === 'gara';
|
||||
$('bocce-left').textContent = def.type === 'gara' ? '← Sterza' : '← Mira';
|
||||
$('bocce-right').textContent = def.type === 'gara' ? 'Sterza →' : 'Mira →';
|
||||
$('gara-brake').hidden = def.type !== 'gara';
|
||||
$('gara-brake').disabled = !(mg.canAdjust?.(c) ?? false) || !!game.speech;
|
||||
$('bocce-throw').textContent = mg.button?.(c) ?? (c.step === 'result' ? 'Finita' : c.step === 'rolling' ? 'In movimento…' : c.current === 'cpu' ? `Tira ${def.names.cpu}…` : def.type === 'bocce' ? 'Lancia' : 'Tira');
|
||||
$('bocce-help').hidden = def.type !== 'bocce';
|
||||
$('biliardo-help').hidden = def.type !== 'biliardo';
|
||||
$('basket-help').hidden = def.type !== 'basket';
|
||||
$('pesca-help').hidden = def.type !== 'pesca';
|
||||
$('gara-help').hidden = def.type !== 'gara';
|
||||
if (mg.aimFree?.(c) ?? !mg.canThrow(c)) c.aimLocked = false; // a ogni nuovo tiro la mira torna libera
|
||||
}
|
||||
if (mg) for (const event of c.events.splice(0)) ambience.play(event.kind, { pan: 0, strength: event.power });
|
||||
@@ -934,7 +976,8 @@ function updateUi() {
|
||||
$('panel').inert = !!c;
|
||||
canvas.classList.toggle('torch', !!torch);
|
||||
canvas.classList.toggle('bocce', !!mg);
|
||||
$('stage').classList.toggle('biliardo', !!c && story.closeups[c.id].type === 'biliardo');
|
||||
// Biliardo e GP di Fortezza: audio, pausa e uscita fuori dal quadro (al GP la pista passa negli angoli).
|
||||
$('stage').classList.toggle('biliardo', !!c && ['biliardo', 'gara'].includes(story.closeups[c.id].type));
|
||||
$('stage').classList.toggle('briscola', !!cards);
|
||||
canvas.setAttribute('aria-label', torch ? 'Fessura: sposta la luce con mouse, dito o frecce. Invio cerca il prossimo dettaglio; Esc esce.' : mg ? mg.label : "La scena dell'avventura");
|
||||
// La pagina che comincia a girare fa il suo fruscio.
|
||||
|
||||
Reference in New Issue
Block a user