L'Ingorgo Sonoro: dal manifesto in piazza, ingrandito e toccato, «Quanto darei per essere all'Ingorgo stasera?!?» e l'effetto speciale; il centro di notte pieno zeppo di gente (sfondi di Codex), le console di Andrea (Suno) che si mescolano camminando, Andrea più grande alla festa, uscendo si torna a maggio. Baldoria al campo sportivo col bandierone della Sampdoria (Codex) (Andrea)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECd9Quo9nEdXyjXYF8ma
This commit is contained in:
+43
-3
@@ -13,6 +13,7 @@ import * as gara from './gara.js';
|
||||
import * as micro from './micro.js';
|
||||
import * as cerbottana from './cerbottana.js';
|
||||
import { newCorsa, updateCorsa } from './corsa.js';
|
||||
import { newIncanto, updateIncanto } from './incanto.js';
|
||||
import * as condor from './condor.js';
|
||||
import * as campana from './campana.js';
|
||||
import * as poker from './poker.js';
|
||||
@@ -148,6 +149,9 @@ export function restoreState(story, data) {
|
||||
return { scene: data.scene, x, y, facing: data.facing === -1 ? -1 : 1, inventory, flags: pick(data.flags), used: pick(data.used), ended: data.ended === true };
|
||||
}
|
||||
|
||||
// Il manifesto ingrandito si apre con uno zoom: un tocco prima che sia aperto non conta.
|
||||
export const POSTER_READY = 0.35;
|
||||
|
||||
export class Game {
|
||||
// voiceTime(who, text): secondi della voce registrata per la battuta, se c'è (il fumetto non finisce prima).
|
||||
constructor(story, { state, onSave, voiceTime } = {}) {
|
||||
@@ -295,6 +299,13 @@ export class Game {
|
||||
closeupTap() {
|
||||
const c = this.closeup;
|
||||
if (!c || this.speech) return false;
|
||||
// Un manifesto ingrandito (type 'poster'): toccarlo lo chiude e fa partire `tap` (il desiderio dell'Ingorgo).
|
||||
if (this.story.closeups[c.id].type === 'poster') {
|
||||
if (c.t < POSTER_READY) return false;
|
||||
this.closeup = null;
|
||||
this.queue.unshift(...(this.story.closeups[c.id].tap ?? []));
|
||||
return true;
|
||||
}
|
||||
if (this.story.closeups[c.id].type) return false; // torcia e minigiochi non si sfogliano
|
||||
if (c.step === 'cover') Object.assign(c, { step: 'title', t: 0 });
|
||||
else if (c.step === 'title') Object.assign(c, { step: 'turn', t: 0 });
|
||||
@@ -329,6 +340,12 @@ export class Game {
|
||||
}
|
||||
// Il viaggio si chiude da solo alla fine (o uscendo prima): si arriva comunque.
|
||||
if (def?.type === 'viaggio' || def?.type === 'corsa') { this.queue.unshift(...def.complete); return true; }
|
||||
// L'incantesimo, chiuso prima del lampo, fa comunque il suo cambio.
|
||||
if (def?.type === 'incanto') {
|
||||
if (!c.swapped) for (const e of def.swap ?? []) this.run(e);
|
||||
this.queue.unshift(...(def.complete ?? []));
|
||||
return true;
|
||||
}
|
||||
// I minigiochi: a partita finita (risultato deciso, anche se il conteggio si sta ancora mostrando) la battuta
|
||||
// di chi ha vinto; uscendo prima, quella di chi lascia.
|
||||
if (MINIGAMES[def?.type]) {
|
||||
@@ -430,15 +447,23 @@ export class Game {
|
||||
const talk = p.talk ?? ['talk'];
|
||||
return talk[Math.floor(this.time / 0.3) % talk.length];
|
||||
}
|
||||
// `flourish`: ogni tanto un gesto lungo a più fotogrammi (Baldoria che sventola il bandierone).
|
||||
const f = p.flourish;
|
||||
if (f) {
|
||||
const ft = (this.time + (p.offset ?? 0)) % f.every;
|
||||
if (ft < f.for) return f.poses[Math.floor(ft / (f.step ?? 0.35)) % f.poses.length];
|
||||
}
|
||||
const every = p.every ?? 4, t = (this.time + (p.offset ?? 0)) % every;
|
||||
return p.blink && t > every - 0.25 ? p.blink : idle;
|
||||
}
|
||||
|
||||
// La grandezza a quella altezza della scena; una fase può ingrandire tutti (`scale`: alla notte dell'Ingorgo la folla
|
||||
// dipinta è grande, e Andrea ci si perdeva in mezzo).
|
||||
scaleAt(y) {
|
||||
const d = this.scene.depth;
|
||||
if (!d) return 1;
|
||||
const d = this.scene.depth, k = this.phase()?.scale ?? 1;
|
||||
if (!d) return k;
|
||||
const [[y0, s0], [y1, s1]] = d, t = Math.max(0, Math.min(1, (y - y0) / (y1 - y0)));
|
||||
return s0 + (s1 - s0) * t;
|
||||
return (s0 + (s1 - s0) * t) * k;
|
||||
}
|
||||
|
||||
speakerPoint(who) {
|
||||
@@ -490,6 +515,12 @@ export class Game {
|
||||
if (this.story.closeups[c.id].type === 'torch') { this.updateTorch(dt); return; }
|
||||
if (this.story.closeups[c.id].type === 'viaggio') { this.updateTrip(dt); return; }
|
||||
if (this.story.closeups[c.id].type === 'corsa') { if (updateCorsa(c, this.story.closeups[c.id], dt)) this.closeupClose(); return; }
|
||||
if (this.story.closeups[c.id].type === 'incanto') {
|
||||
const now = updateIncanto(c, dt);
|
||||
if (now === 'swap') for (const e of this.story.closeups[c.id].swap ?? []) this.run(e);
|
||||
if (now === 'end') this.closeupClose();
|
||||
return;
|
||||
}
|
||||
const m = this.minigame();
|
||||
if (m) {
|
||||
m.update(c, dt);
|
||||
@@ -533,6 +564,8 @@ export class Game {
|
||||
this.closeup = type === 'torch' ? { id: e.closeup, step: 'search', t: 0, age: 0, found: 0, dwell: 0, beam: [320, 180] }
|
||||
: type === 'viaggio' ? { id: e.closeup, t: 0, age: 0, events: [], typed: 0 }
|
||||
: type === 'corsa' ? newCorsa(e.closeup)
|
||||
: type === 'incanto' ? newIncanto(e.closeup)
|
||||
: type === 'poster' ? { id: e.closeup, step: 'poster', t: 0, age: 0 }
|
||||
// `level`: «Alla bona», «Seria» o «A fassi male», scelto nel dialogo con l'avversario.
|
||||
// `decks`: il mazzo da usare quando il suo flag è acceso (briscola: le napoletane al posto delle toscane).
|
||||
: MINIGAMES[type] ? { id: e.closeup, ...MINIGAMES[type].create(Math.floor(Math.random() * 4294967296), e.level,
|
||||
@@ -586,6 +619,13 @@ export class Game {
|
||||
this.back = false;
|
||||
this.state.flags['visto:' + scene] = true; // per la mappa
|
||||
this.queue.unshift(...(this.scene.enter ?? []));
|
||||
// Le zone (story.zones): un flag che vale solo in certi posti, come la festa dell'Ingorgo. Uscendo (a piedi o
|
||||
// con la mappa) il flag si spegne e partono le battute del risveglio.
|
||||
for (const z of this.story.zones ?? []) {
|
||||
if (!this.state.flags[z.flag] || z.scenes.includes(scene)) continue;
|
||||
delete this.state.flags[z.flag];
|
||||
this.queue.unshift(...(z.leave ?? []));
|
||||
}
|
||||
this.dirty = true; // si salva quando l'ingresso nella scena è finito
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user