Suoni d'ambiente sintetizzati (vento, acqua, uccelli, grilli, campane) col loro volume in Pausa; la fessura in primo piano: si cerca col fascio della torcia il quadro e gli occhi (lavoro di Codex). La foto del palio leggibile da nginx, e un test che lo controlla per tutti i file serviti
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018ks2K99uqCZjAxvy7Rdj1R
This commit is contained in:
+24
-5
@@ -6,12 +6,16 @@ import { solution } from '../src/story/solution.js';
|
||||
import { Game, VERBS, checkSave, introText, shuffledCredits, matchPlace } from '../src/engine/game.js';
|
||||
import { inside } from '../src/engine/geometry.js';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { settle } from './helpers.mjs';
|
||||
import { settle, exploreTorch } from './helpers.mjs';
|
||||
|
||||
const EFFECTS = ['say', 'walk', 'face', 'give', 'drop', 'set', 'unset', 'if', 'dialogue', 'node', 'endDialogue', 'go', 'end', 'closeup'];
|
||||
|
||||
// Visita ogni elenco di effetti della storia, dentro scene, oggetti, dialoghi e rami `if`.
|
||||
function* effectLists() {
|
||||
for (const [id, c] of Object.entries(story.closeups ?? {})) {
|
||||
for (const k of ['comment', 'closing', 'complete']) yield [`${id}.${k}`, c[k] ?? []];
|
||||
for (const t of c.targets ?? []) yield [`${id}.target`, t.lines ?? []];
|
||||
}
|
||||
for (const [id, scene] of Object.entries(story.scenes)) {
|
||||
yield [`${id}.enter`, scene.enter ?? []];
|
||||
for (const h of scene.hotspots ?? [])
|
||||
@@ -66,7 +70,7 @@ test('la storia è coerente: scene, oggetti, dialoghi e punti calpestabili esist
|
||||
if (e.closeup) {
|
||||
const c = story.closeups?.[e.closeup];
|
||||
assert.ok(c, `${where}: primo piano ${e.closeup}`);
|
||||
for (const src of [c.closed, c.open]) assert.ok(existsSync(new URL('../' + src, import.meta.url)), `${where}: manca ${src}`);
|
||||
for (const src of c.type === 'torch' ? [c.picture] : [c.closed, c.open]) assert.ok(existsSync(new URL('../' + src, import.meta.url)), `${where}: manca ${src}`);
|
||||
}
|
||||
if (e.who) assert.ok(story.characters[e.who], `${where}: personaggio ${e.who}`);
|
||||
}
|
||||
@@ -118,7 +122,7 @@ test('il taccuino: lungo la soluzione le tappe si spuntano in ordine, una alla v
|
||||
let last = 0;
|
||||
for (const [verb, target, item] of solution) {
|
||||
if (verb === 'choose') g.choose(g.options().findIndex(o => o.text === target)); else g.act(verb, target, item);
|
||||
settle(g);
|
||||
settle(g, { explore: true });
|
||||
const n = g.notebook().done.length;
|
||||
assert.ok(n === last || n === last + 1, `tappe saltate dopo ${verb} ${target}`);
|
||||
last = n;
|
||||
@@ -134,7 +138,7 @@ test('le fasi del giorno seguono la storia: pomeriggio, poi tramonto, poi sera,
|
||||
const ids = story.phases.map(p => p.id), seen = [g.phase().id];
|
||||
for (const [verb, target, item] of solution) {
|
||||
if (verb === 'choose') g.choose(g.options().findIndex(o => o.text === target)); else g.act(verb, target, item);
|
||||
settle(g);
|
||||
settle(g, { explore: true });
|
||||
if (seen.at(-1) !== g.phase().id) seen.push(g.phase().id);
|
||||
}
|
||||
assert.deepEqual(seen, ids);
|
||||
@@ -166,7 +170,7 @@ test('la soluzione si percorre tutta dall\'inizio alla fine', () => {
|
||||
if (item) assert.ok(g.state.inventory.includes(item), `manca ${item}`);
|
||||
assert.ok(g.act(verb, target, item), `comando rifiutato: ${verb} ${target}`);
|
||||
}
|
||||
settle(g);
|
||||
settle(g, { explore: true });
|
||||
}
|
||||
assert.ok(g.state.ended, 'la partita non è finita');
|
||||
assert.ok(g.state.flags.indizio_regolo && g.state.flags.palio_trovato, 'il palio visto nella fessura');
|
||||
@@ -367,6 +371,7 @@ function stepChecked(g, label, step) {
|
||||
for (let i = 0; i < 20000; i++) {
|
||||
seen.push(essence(g.savable()));
|
||||
if (g.speech) g.skip();
|
||||
exploreTorch(g);
|
||||
if (!g.busy && !g.walking && !g.queue.length && !g.task) break;
|
||||
g.update(0.05);
|
||||
}
|
||||
@@ -584,3 +589,17 @@ test('il pettegolezzo cambia con le scoperte: vecchietti, Arcu e Tosca', () => {
|
||||
assert.ok(texts().includes('Ho la torcia. Stasera entro nella fessura.'));
|
||||
leave();
|
||||
});
|
||||
|
||||
test('i file che il Docker copia sono leggibili da tutti (nginx gira con un altro utente)', async () => {
|
||||
const { readdirSync, statSync } = await import('node:fs');
|
||||
const root = new URL('../', import.meta.url);
|
||||
const dirs = readFileSync(new URL('Dockerfile', root), 'utf8').match(/^COPY (assets\/\S+|src)\s/gm).map(l => l.split(' ')[1]);
|
||||
const closed = [];
|
||||
const walk = dir => { for (const e of readdirSync(new URL(dir + '/', root), { withFileTypes: true })) {
|
||||
const path = `${dir}/${e.name}`;
|
||||
if (e.isDirectory()) walk(path);
|
||||
else if (!(statSync(new URL(path, root)).mode & 0o004)) closed.push(path);
|
||||
} };
|
||||
dirs.forEach(walk);
|
||||
assert.deepEqual(closed, [], 'file non leggibili da nginx (chmod 644)');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user