Giochi di carte dopo la revisione di Codex sui tre insieme; player dei video «Come si gioca» e primo copione
Esito che vale e si salva appena deciso, per tutti i minigiochi (anche tornando al titolo dalla schermata del risultato); briscola: uscire durante il conteggio non perde più la vittoria; tressette: si esce ai 21 col primo evento che ci porta, accusa compresa (prima vinceva sempre Andrea se ci arrivavano tutte e due), il carico che fa vincere si dà anche se è il 2, accuse che non escono dal tavolo, convenzioni scritte nell'aiuto; scopa: primiera valutata per quel che migliora, finale esatto a mazzo finito, presa sulla tavola al rilascio con scelta e conferma, frecce e Invio sempre sulla presa scelta. Tastiera: Invio e Spazio giocano sempre la carta scelta. Tavola descritta a parole. Classi CSS comuni ai tre giochi. Video: player nel gioco (pulsante «Guarda il video» nel «Come si gioca», compare quando il video esiste), video/narra.py per la voce del narratore, pezzi comuni dei copioni e il copione della briscola; scaletta in video/SCALETTA-CARTE.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
This commit is contained in:
+62
-13
@@ -289,6 +289,38 @@ function closePause() {
|
||||
running = true;
|
||||
}
|
||||
$('pause-button').addEventListener('click', openPause);
|
||||
// I video «Come si gioca» (story.tutorials): il gioco si ferma come in pausa, la musica tace, il video ha il suo audio.
|
||||
function openTutorial(kind) {
|
||||
const t = story.tutorials?.[kind];
|
||||
if (!t || !running) return;
|
||||
running = false;
|
||||
game.closeupCancel();
|
||||
music.hold = true;
|
||||
voices.pause();
|
||||
ambience.configure({ active: false });
|
||||
$('tutorial-title').textContent = t.title;
|
||||
const video = $('tutorial-video');
|
||||
video.src = versioned(t.src);
|
||||
if (t.poster) video.poster = versioned(t.poster);
|
||||
video.muted = music.muted;
|
||||
$('tutorial').hidden = false;
|
||||
video.play().catch(() => {});
|
||||
$('tutorial-close').focus();
|
||||
}
|
||||
function closeTutorial() {
|
||||
const video = $('tutorial-video');
|
||||
video.pause(); video.removeAttribute('src'); video.load();
|
||||
$('tutorial').hidden = true;
|
||||
music.hold = false;
|
||||
voices.resume();
|
||||
idleSince = performance.now();
|
||||
running = true;
|
||||
}
|
||||
for (const button of document.querySelectorAll('.tutorial-open')) {
|
||||
button.hidden = !story.tutorials?.[button.dataset.tutorial];
|
||||
button.addEventListener('click', () => openTutorial(button.dataset.tutorial));
|
||||
}
|
||||
$('tutorial-close').addEventListener('click', closeTutorial);
|
||||
// Sul telefono, passando a un'altra app (o cambiando scheda), si ferma tutto: musica, voce, musichetta del cartello;
|
||||
// la partita va in pausa e aspetta. Tornando riparte la musica; la voce solo se non c'è la pausa (riparte con Riprendi).
|
||||
function leave(hidden) {
|
||||
@@ -410,11 +442,10 @@ canvas.addEventListener('pointerdown', e => {
|
||||
if (game.skip()) return;
|
||||
// Tressette e scopa: fra una smazzata e l'altra un tocco va avanti.
|
||||
if (MINI[type].advance?.(game.closeup)) return;
|
||||
// Scopa, più prese possibili: si tocca una carta in tavola; se sta in una presa sola, si prende quella.
|
||||
// Scopa, più prese possibili: si tocca una carta in tavola (si decide al rilascio, vedi pointerup).
|
||||
if (type === 'scopa' && game.closeup.step === 'choose') {
|
||||
const c = game.closeup, card = c.table[scopaTableAt(c, scenePoint(e))];
|
||||
const options = c.choice.options.flatMap((o, k) => o.includes(card) ? [k] : []);
|
||||
if (options.length === 1) scopa.take(c, options[0]); else if (options.length) scopa.select(c, options[0]);
|
||||
press = { table: scopaTableAt(game.closeup, scenePoint(e)), state: game.closeup };
|
||||
canvas.setPointerCapture(e.pointerId);
|
||||
return;
|
||||
}
|
||||
press = { card: deck(game.closeup, scenePoint(e)), state: game.closeup, at: deck };
|
||||
@@ -437,6 +468,16 @@ canvas.addEventListener('pointerdown', e => {
|
||||
});
|
||||
canvas.addEventListener('pointerup', e => {
|
||||
if (!press) return;
|
||||
// Scopa: la carta toccata e lasciata sullo stesso punto. Se sta nella presa già scelta, si prende quella; se no
|
||||
// si sceglie la prima presa che la contiene (un altro tocco conferma). Trascinando via non succede niente.
|
||||
if (press.table !== undefined) {
|
||||
const p = press; press = null;
|
||||
const c = game.closeup;
|
||||
if (!running || c !== p.state || c.step !== 'choose' || p.table < 0 || scopaTableAt(c, scenePoint(e)) !== p.table) return;
|
||||
const card = c.table[p.table], options = c.choice.options.flatMap((o, k) => o.includes(card) ? [k] : []);
|
||||
if (options.includes(c.choice.selected)) scopa.take(c); else if (options.length) scopa.select(c, options[0]);
|
||||
return;
|
||||
}
|
||||
if (press.card !== undefined) {
|
||||
const p = press; press = null;
|
||||
if (running && game.closeup === p.state && p.card >= 0 && p.at(game.closeup, scenePoint(e)) === p.card) game.closeupPlay(p.card);
|
||||
@@ -454,7 +495,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 = null; game?.closeupCancel(); } });
|
||||
canvas.addEventListener('lostpointercapture', () => { if (press?.closeup || 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 });
|
||||
@@ -489,6 +530,7 @@ for (let i = 0; i < 3; i++) {
|
||||
button.addEventListener('click', () => game?.closeupPlay(i));
|
||||
button.addEventListener('focus', () => { if (game?.closeup?.id === 'scopa' && game.closeup.step === 'play') scopa.select(game.closeup, i); });
|
||||
}
|
||||
$('scopa-options').addEventListener('focusin', e => { const k = e.target.dataset?.option; if (k !== undefined && game?.closeup?.step === 'choose') scopa.select(game.closeup, Number(k)); });
|
||||
$('scopa-options').addEventListener('click', e => { const k = e.target.dataset?.option; if (k !== undefined && game?.closeup?.id === 'scopa') scopa.take(game.closeup, Number(k)); });
|
||||
$('scopa-next').addEventListener('click', () => { if (game?.closeup?.id === 'scopa') scopa.advance(game.closeup); });
|
||||
for (let i = 0; i < 10; i++) {
|
||||
@@ -586,6 +628,7 @@ window.addEventListener('keydown', e => {
|
||||
return;
|
||||
}
|
||||
if (debugKey(e)) return;
|
||||
if (!$('tutorial').hidden) { if (e.key === 'Escape') closeTutorial(); return; }
|
||||
if (!$('credits').hidden) { if (e.key === 'Escape') { if (!$('face').hidden) $('face').hidden = true; else $('credits').hidden = true; } return; }
|
||||
if (!$('pause').hidden) { if (e.key === 'Escape') closePause(); return; }
|
||||
if (!$('notebook').hidden) { if (['Escape', 'n', 'N'].includes(e.key)) $('notebook').hidden = true; return; }
|
||||
@@ -595,7 +638,9 @@ window.addEventListener('keydown', e => {
|
||||
if (story.closeups[game.closeup.id].type === 'scopa') {
|
||||
const inControls = e.target.closest?.('#scopa-controls'), c = game.closeup, key = e.key.toLowerCase();
|
||||
if (e.repeat && ['enter', ' '].includes(key)) { e.preventDefault(); return; }
|
||||
if (inControls && ['BUTTON', 'SUMMARY'].includes(e.target.tagName) && ['enter', ' '].includes(key)) return;
|
||||
// Sui pulsanti delle carte e delle prese Invio e Spazio valgono per quella scelta (una sola fonte: lo stato).
|
||||
const choice = e.target.id?.startsWith('scopa-card-') || e.target.dataset?.option !== undefined;
|
||||
if (inControls && ['BUTTON', 'SUMMARY'].includes(e.target.tagName) && ['enter', ' '].includes(key) && !choice) return;
|
||||
if (['arrowleft', 'arrowright', 'arrowup', 'arrowdown', 'enter', ' '].includes(key) || /^[1-9]$/.test(key)) {
|
||||
e.preventDefault();
|
||||
if (e.repeat || game.skip()) return;
|
||||
@@ -603,9 +648,10 @@ window.addEventListener('keydown', e => {
|
||||
else if (key.startsWith('arrow')) scopa.nudge(c, ['arrowleft', 'arrowup'].includes(key) ? -1 : 1);
|
||||
else if (c.step === 'result') game.closeupClose();
|
||||
else if (c.step === 'choose') scopa.take(c);
|
||||
else if (!scopa.advance(c) && key === 'enter') game.closeupPlay();
|
||||
// Il fuoco segue la carta scelta: Invio su un pulsante gioca sempre quella che si vede scelta.
|
||||
if (inControls && scopa.canPlay(c)) $('scopa-card-' + c.selected).focus();
|
||||
else if (!scopa.advance(c)) game.closeupPlay();
|
||||
// Il fuoco segue la scelta, così quel che si vede scelto è quel che parte.
|
||||
if (inControls && c.step === 'choose') $('scopa-options').querySelector(`[data-option="${c.choice.selected}"]`)?.focus();
|
||||
else if (inControls && scopa.canPlay(c)) $('scopa-card-' + c.selected).focus();
|
||||
return;
|
||||
}
|
||||
// Esc: nella scelta della presa si torna alla mano, altrimenti si lascia il tavolo.
|
||||
@@ -616,7 +662,10 @@ window.addEventListener('keydown', e => {
|
||||
if (story.closeups[game.closeup.id].type === 'tressette') {
|
||||
const inControls = e.target.closest?.('#tressette-controls'), c = game.closeup;
|
||||
if (e.repeat && ['Enter', ' '].includes(e.key)) { e.preventDefault(); return; }
|
||||
if (inControls && ['BUTTON', 'SUMMARY'].includes(e.target.tagName) && ['Enter', ' '].includes(e.key)) return;
|
||||
// Sui pulsanti della mano Invio e Spazio giocano la carta scelta (anche se se ne è scelta una che non si
|
||||
// può giocare: allora si legge perché), mai quella che aveva il fuoco prima.
|
||||
const card = e.target.id?.startsWith('tressette-card-');
|
||||
if (inControls && ['BUTTON', 'SUMMARY'].includes(e.target.tagName) && ['Enter', ' '].includes(e.key) && !card) return;
|
||||
const key = e.key.toLowerCase(), signal = { b: 'busso', s: 'striscio', v: 'volo' }[key];
|
||||
if (['arrowleft', 'arrowright', 'arrowup', 'arrowdown', 'enter', ' '].includes(key) || /^[0-9]$/.test(key) || signal) {
|
||||
e.preventDefault();
|
||||
@@ -625,7 +674,7 @@ window.addEventListener('keydown', e => {
|
||||
else if (/^[0-9]$/.test(key)) tressette.select(c, (Number(key) + 9) % 10);
|
||||
else if (key.startsWith('arrow')) tressette.nudge(c, ['arrowleft', 'arrowup'].includes(key) ? -1 : 1);
|
||||
else if (c.step === 'result') game.closeupClose();
|
||||
else if (!tressette.advance(c) && key === 'enter') game.closeupPlay();
|
||||
else if (!tressette.advance(c)) game.closeupPlay(); // Invio o Spazio, come negli altri due giochi
|
||||
// Il fuoco segue la carta scelta: Invio su un pulsante gioca sempre quella che si vede scelta.
|
||||
if (inControls && tressette.canPlay(c) && !signal) $('tressette-card-' + c.selected).focus();
|
||||
return;
|
||||
@@ -638,7 +687,7 @@ window.addEventListener('keydown', e => {
|
||||
const inControls = e.target.closest?.('#briscola-controls');
|
||||
if (e.repeat && ['Enter', ' '].includes(e.key)) { e.preventDefault(); return; }
|
||||
if (inControls && e.target.tagName === 'SUMMARY' && e.key === 'Enter') return;
|
||||
if (inControls && e.target.tagName === 'BUTTON' && !e.target.disabled && e.key === 'Enter' && briscola.canPlay(game.closeup)) return;
|
||||
if (inControls && e.target.tagName === 'BUTTON' && !e.target.disabled && ['Enter', ' '].includes(e.key) && briscola.canPlay(game.closeup)) return;
|
||||
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', '1', '2', '3', 'Enter', ' '].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
if (e.repeat || game.skip()) return;
|
||||
@@ -646,7 +695,7 @@ window.addEventListener('keydown', e => {
|
||||
if (/^[123]$/.test(e.key)) briscola.select(c, Number(e.key) - 1);
|
||||
else if (e.key.startsWith('Arrow')) briscola.nudge(c, ['ArrowLeft', 'ArrowUp'].includes(e.key) ? -1 : 1);
|
||||
else if (c.step === 'result') game.closeupClose();
|
||||
else if (e.key === 'Enter') game.closeupPlay();
|
||||
else game.closeupPlay(); // Invio o Spazio, come negli altri due giochi
|
||||
if (inControls && briscola.canPlay(c)) $('briscola-card-' + c.selected).focus();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user