A Campomigliaio, accanto all'Arcu, il Betino (motorino verde e crema con la scritta sul serbatoio) e la Ghiacciomobile (la Corsa nera): sprite di Codex dalle foto di riferimento di Andrea, battute di Andrea (guardandoli, e parlando alla macchina che non risponde), voci; «Ghiacciomòbile» nella pronuncia
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1dxvtsZx8NE9fwjXUNDBr
This commit is contained in:
@@ -182,7 +182,7 @@ test('i personaggi in scena hanno il foglio e tutte le pose che usano', () => {
|
||||
for (const [id, scene] of Object.entries(story.scenes))
|
||||
for (const p of scene.people ?? []) {
|
||||
assert.ok(story.characters[p.who]?.sprite, `${id}: ${p.who} senza foglio`);
|
||||
assert.ok(scene.speakers?.[p.who], `${id}: manca il punto del fumetto di ${p.who}`);
|
||||
if ((p.talk ?? ['talk']).length) assert.ok(scene.speakers?.[p.who], `${id}: manca il punto del fumetto di ${p.who}`);
|
||||
const f = frames(p.who);
|
||||
for (const pose of [p.idle ?? 'sit', p.blink, ...(p.talk ?? ['talk'])].filter(Boolean)) assert.ok(f[pose], `${p.who}: manca ${pose}`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Verifica visiva mirata: node tests/veicoli.browser.mjs
|
||||
// Usa lo stesso Chromium/SwiftShader di browser.mjs, senza titolo e audio.
|
||||
import { chromium } from 'playwright';
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import assert from 'node:assert/strict';
|
||||
import { staticServer } from './helpers.mjs';
|
||||
|
||||
const out = process.env.OUT ?? '/tmp/regolo-shots';
|
||||
mkdirSync(out, { recursive: true });
|
||||
const server = await staticServer();
|
||||
let browser;
|
||||
try {
|
||||
browser = await chromium.launch({ args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
|
||||
const errors = [];
|
||||
page.on('pageerror', e => errors.push(e.message));
|
||||
page.on('response', r => { if (r.status() >= 400) errors.push(`${r.status()} ${r.url()}`); });
|
||||
await page.route('**/__veicoli', route => route.fulfill({ contentType: 'text/html', body:
|
||||
'<style>body{margin:0}canvas{display:block;width:1280px;height:720px;image-rendering:pixelated}</style><canvas width="640" height="360"></canvas>' }));
|
||||
await page.goto(`http://127.0.0.1:${server.port}/__veicoli`);
|
||||
await page.evaluate(async () => {
|
||||
const [{ story }, { Game }, renderer] = await Promise.all([
|
||||
import('/src/story/story.js'), import('/src/engine/game.js'), import('/src/engine/render.js'),
|
||||
]);
|
||||
const loaded = await renderer.loadAssets(story, 'campomigliaio_circolo');
|
||||
if (loaded.some(ok => !ok)) throw new Error('Uno sfondo/personaggio non è arrivato');
|
||||
const game = new Game(story);
|
||||
game.go('campomigliaio_circolo');
|
||||
game.time = 8;
|
||||
const ctx = document.querySelector('canvas').getContext('2d');
|
||||
renderer.render(ctx, game, { time: 8000 });
|
||||
});
|
||||
assert.deepEqual(errors, []);
|
||||
await page.screenshot({ path: `${out}/campomigliaio-veicoli.png` });
|
||||
console.log(`Schermata: ${out}/campomigliaio-veicoli.png`);
|
||||
} finally {
|
||||
await browser?.close();
|
||||
server.kill();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { story } from '../src/story/story.js';
|
||||
import { Game } from '../src/engine/game.js';
|
||||
import { inside } from '../src/engine/geometry.js';
|
||||
import { settle } from './helpers.mjs';
|
||||
|
||||
test('i veicoli del circolino restano fuori dal percorso e lasciano liberi gli altri hotspot', () => {
|
||||
const g = new Game(story);
|
||||
g.go('campomigliaio_circolo');
|
||||
settle(g);
|
||||
const vehicles = ['betino', 'ghiacciomobile'];
|
||||
for (const id of vehicles) {
|
||||
const person = g.people().find(p => p.who === id), h = g.hotspot(id);
|
||||
const url = new URL('../' + story.characters[id].sprite, import.meta.url);
|
||||
const atlas = JSON.parse(readFileSync(url, 'utf8')), frame = atlas.frames.idle;
|
||||
assert.ok(existsSync(new URL(atlas.image, url)));
|
||||
assert.deepEqual(Object.keys(atlas.frames), ['idle']);
|
||||
const k = 62 * g.scaleAt(person.at[1]) * person.size / atlas.height;
|
||||
const width = frame.w * k;
|
||||
assert.ok(id === 'betino' ? width >= 70 && width <= 80 : width >= 150 && width <= 170);
|
||||
// Tutto l'ingombro (non soltanto l'ancora) deve restare fuori dall'area calpestabile.
|
||||
for (let x = h.box[0]; x <= h.box[0] + h.box[2]; x++)
|
||||
for (let y = h.box[1]; y <= h.box[1] + h.box[3]; y++)
|
||||
assert.ok(!inside(g.scene.walk, [x, y]), `${id}: invade walk in ${x},${y}`);
|
||||
for (const other of g.hotspots().filter(o => !vehicles.includes(o.id))) {
|
||||
const [x, y, w, hgt] = h.box, [ox, oy, ow, oh] = other.box;
|
||||
assert.ok(x + w <= ox || ox + ow <= x || y + hgt <= oy || oy + oh <= y,
|
||||
`${id}: copre ${other.id}`);
|
||||
}
|
||||
for (const time of [0, 3.9, 4.1, 20]) {
|
||||
g.time = time;
|
||||
assert.equal(g.personPose(person), 'idle');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('guarda e usa i veicoli: parla solo Andrea e non cambia il progresso', () => {
|
||||
const g = new Game(story);
|
||||
g.go('campomigliaio_circolo');
|
||||
settle(g);
|
||||
const flags = { ...g.state.flags }, inventory = [...g.state.inventory];
|
||||
for (const id of ['betino', 'ghiacciomobile']) {
|
||||
const h = g.hotspot(id);
|
||||
assert.equal(g.hotspotAt([h.box[0] + h.box[2] / 2, h.box[1] + h.box[3] / 2]).id, id);
|
||||
for (const verb of ['look', 'use']) {
|
||||
assert.ok(g.act(verb, id));
|
||||
const said = settle(g);
|
||||
assert.ok(said.length && said.every(s => s.who === 'andrea'));
|
||||
assert.deepEqual([g.state.x, g.state.y], h.at);
|
||||
assert.deepEqual(g.state.flags, flags);
|
||||
assert.deepEqual(g.state.inventory, inventory);
|
||||
assert.equal(g.state.scene, 'campomigliaio_circolo');
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user