La pista col gesso: le macchinine sul parcheggio di Cardetole contro il Chiaru (consenso del 29 ottobre, sprite di Codex dalla foto), un giro a spinte con le penalità alla francese (fuori, tagliè, cappottè, accompagnè: si torna dov'eri); Bertovo diventa Trappizzio Berti; Fabrizio Berti e il suo libro nei Ringraziamenti (Andrea)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiECd9KMfjthRUnujCGSzZ
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
// La pista col gesso nel browser vero, su desktop e touch: la scena di Cardetole col Chiaru, la mira, la spinta
|
||||
// tenendo premuto e i comandi del pannello. npm run browser:micro — schermate in /tmp/regolo-micro.
|
||||
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-micro';
|
||||
mkdirSync(out, { recursive: true });
|
||||
const server = await staticServer();
|
||||
let browser;
|
||||
const errors = [];
|
||||
|
||||
try {
|
||||
browser = await chromium.launch({ args: ['--autoplay-policy=no-user-gesture-required', '--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] });
|
||||
for (const [name, mobile, viewport] of [
|
||||
['desktop', false, { width: 1280, height: 860 }],
|
||||
['touch', true, { width: 844, height: 390 }],
|
||||
['portrait', true, { width: 390, height: 844 }],
|
||||
]) {
|
||||
const page = await browser.newPage({ viewport, hasTouch: mobile, isMobile: mobile });
|
||||
page.on('pageerror', e => errors.push(e.message));
|
||||
await page.goto(`http://127.0.0.1:${server.port}/`);
|
||||
await page.click('#splash');
|
||||
await page.click('#new-game');
|
||||
await page.click('#rione [data-rione="campomigliaio"]');
|
||||
await page.click('#skip-intro');
|
||||
await page.evaluate(async () => {
|
||||
const { settle } = await import('/tests/helpers.mjs');
|
||||
settle(game); game.go('cardetole'); settle(game);
|
||||
});
|
||||
await page.waitForTimeout(1200);
|
||||
if (name === 'desktop') await page.screenshot({ path: `${out}/${name}-cardetole.png` });
|
||||
await page.evaluate(async () => {
|
||||
const { settle } = await import('/tests/helpers.mjs');
|
||||
game.act('talk', 'chiaru'); settle(game);
|
||||
game.choose(game.options().findIndex(o => o.text === 'Si corre?')); settle(game);
|
||||
game.choose(game.options().findIndex(o => o.text === 'Alla bona.')); settle(game);
|
||||
});
|
||||
await page.waitForSelector('#bocce-controls:not([hidden])');
|
||||
await page.waitForFunction(async () => {
|
||||
const { hasBackground } = await import('/src/engine/render.js');
|
||||
return hasBackground('assets/closeups/micro-parcheggio.png') && hasBackground('assets/closeups/micro/rossa.png');
|
||||
});
|
||||
assert.equal(await page.locator('#bocce-throw').textContent(), 'Spingi');
|
||||
assert.ok(await page.locator('#micro-help').isVisible(), 'il «Come si gioca» delle macchinine');
|
||||
const exit = await page.locator('#closeup-close').boundingBox(), canvas = await page.locator('#scene').boundingBox();
|
||||
assert.ok(exit.y >= canvas.y + canvas.height, 'l\'uscita sta fuori dalla pista');
|
||||
await page.waitForTimeout(1600);
|
||||
const state = () => page.evaluate(() => ({ angle: game.closeup.angle, power: game.closeup.power, step: game.closeup.step, x: game.closeup.cars.player.x }));
|
||||
// Un punto della pista davanti alla macchinina di Andrea, in pixel della pagina.
|
||||
const ahead = await page.evaluate(() => { const c = game.closeup, i = (c.cars.player.idx + 50) % c.track.n; return c.track.pts[i]; });
|
||||
const target = { x: canvas.x + ahead[0] / 640 * canvas.width, y: canvas.y + ahead[1] / 360 * canvas.height };
|
||||
if (!mobile) {
|
||||
const before = (await state()).angle;
|
||||
await page.mouse.move(target.x, target.y + 30, { steps: 4 });
|
||||
assert.notEqual((await state()).angle, before, 'senza clic la mira segue il mouse');
|
||||
await page.mouse.click(target.x, target.y);
|
||||
} else await page.touchscreen.tap(target.x, target.y);
|
||||
const aimed = await state();
|
||||
assert.equal(aimed.step, 'aim', 'un tocco mira senza spingere');
|
||||
await page.screenshot({ path: `${out}/${name}-mira.png` });
|
||||
// Si spinge: col mouse tenendo premuto, col dito dal pulsante dopo aver scelto la forza.
|
||||
if (!mobile) {
|
||||
// Si lascia appena la carica arriva: restando troppo alla carica piena sarebbe un accompagnè.
|
||||
await page.mouse.move(target.x, target.y); await page.mouse.down();
|
||||
await page.waitForFunction(() => game.closeup.charging && game.closeup.power >= 0.75);
|
||||
await page.mouse.up();
|
||||
} else {
|
||||
await page.locator('#bocce-power').fill('70');
|
||||
await page.locator('#bocce-throw').tap();
|
||||
}
|
||||
await page.waitForTimeout(250);
|
||||
const rolling = await state();
|
||||
assert.equal(rolling.step, 'rolling', 'la macchinina parte');
|
||||
await page.screenshot({ path: `${out}/${name}-corsa.png` });
|
||||
// Poi spinge il Chiaru, e si torna a mirare.
|
||||
await page.waitForFunction(() => game.closeup.current === 'player' && game.closeup.step === 'aim', null, { timeout: 15000 });
|
||||
assert.notEqual((await state()).x, aimed.x, 'la macchinina di Andrea si è mossa');
|
||||
await page.screenshot({ path: `${out}/${name}-dopo.png` });
|
||||
await page.close();
|
||||
}
|
||||
assert.deepEqual(errors, []);
|
||||
console.log(`ok: schermate in ${out}`);
|
||||
} finally {
|
||||
await browser?.close();
|
||||
server.kill();
|
||||
}
|
||||
Reference in New Issue
Block a user