Andrea vero: camminata, parlato, prendere e di spalle dal foglio sprite

tools/slice_sprites.py da Manzolo Rush e tools/prepare_character.py, che allinea
i fotogrammi sul busto e sui piedi; il motore sceglie il fotogramma da quello
che Andrea sta facendo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 01:00:36 +02:00
co-authored by Claude Opus 5.5
parent 7133c46891
commit f7f65a74fd
12 changed files with 803 additions and 19 deletions
+24 -4
View File
@@ -17,6 +17,7 @@ const DEFAULT_LINES = {
const SPEED = 95;
// Solo profilo, come i cicli di camminata: 'up' e 'down' lasciano il verso com'è.
const FACING = { left: -1, right: 1 };
const WALK = ['walk_1', 'walk_2', 'walk_3', 'walk_4'], STEP = 15, REACH = 0.45;
// Condizioni: 'flag', '!flag', 'has:oggetto', '!has:oggetto', oppure un elenco (tutte vere).
export function check(cond, state) {
@@ -57,12 +58,15 @@ export class Game {
this.dialogue = null;
this.walking = false;
this.time = 0;
this.stride = 0; // strada fatta, per scegliere il passo della camminata
this.reach = 0; // secondi di braccio teso quando prende qualcosa
this.back = false; // di spalle davanti a una porta o a una bacheca
}
get scene() { return this.story.scenes[this.state.scene]; }
// Occupato: c'è qualcosa in corso che il giocatore non può interrompere. La camminata verso un punto
// o verso un oggetto si può sempre cambiare con un altro clic: la coda che la seguiva viene scartata.
get busy() { return !!this.speech || (this.task ? !this.task.interruptible : this.queue.length > 0); }
get busy() { return !!this.speech || this.reach > 0 || (this.task ? !this.task.interruptible : this.queue.length > 0); }
get idle() { return !this.busy && !this.task && !this.dialogue; }
snapshot() {
@@ -151,6 +155,15 @@ export class Game {
startWalk(p) {
this.path = findPath(this.scene.walk, [this.state.x, this.state.y], p);
this.walking = this.path.length > 0;
this.back = false;
}
// Il fotogramma di Andrea in questo istante: un passo ogni STEP pixel percorsi (a grandezza piena).
pose() {
if (this.reach > 0) return 'take';
if (this.walking) return WALK[Math.floor(this.stride / STEP) % WALK.length];
if (this.speech?.who === 'andrea') return Math.floor(this.time / 0.2) % 2 ? 'talk' : 'stand';
return this.back ? 'back' : 'stand';
}
scaleAt(y) {
@@ -174,8 +187,14 @@ export class Game {
if (this.speech.left > 0) return;
this.speech = null;
}
if (this.reach > 0) {
this.reach -= dt;
if (this.reach > 0) return;
}
if (this.walking) {
let step = SPEED * this.scaleAt(this.state.y) * dt;
const scale = this.scaleAt(this.state.y);
let step = SPEED * scale * dt;
this.stride += step / scale;
while (step > 0 && this.path.length) {
const target = this.path[0], d = dist([this.state.x, this.state.y], target);
if (Math.abs(target[0] - this.state.x) > 0.5) this.state.facing = target[0] > this.state.x ? 1 : -1;
@@ -203,8 +222,8 @@ export class Game {
else if (e.walk) { this.task = e; this.startWalk(e.walk); }
// `go` prima di `face`: un cambio scena porta con sé il verso d'arrivo.
else if (e.go) this.go(e.go, e.at, e.face);
else if (e.face) s.facing = FACING[e.face] ?? s.facing;
else if (e.give) { if (!s.inventory.includes(e.give)) s.inventory.push(e.give); }
else if (e.face) { s.facing = FACING[e.face] ?? s.facing; this.back = e.face === 'up'; }
else if (e.give) { if (!s.inventory.includes(e.give)) s.inventory.push(e.give); this.reach = REACH; this.back = false; }
else if (e.drop) s.inventory = s.inventory.filter(i => i !== e.drop);
else if (e.set) s.flags[e.set] = true;
else if (e.unset) delete s.flags[e.unset];
@@ -223,6 +242,7 @@ export class Game {
this.state.facing = FACING[face] ?? this.state.facing;
this.path = [];
this.walking = false;
this.back = false;
this.queue.unshift(...(this.scene.enter ?? []));
this.onSave(this.snapshot());
}