La campana al campone delle poste col Taglia: il sassolino tirato con mira e forza, i salti a tempo con la lancetta dell'equilibrio, le caselle tinte coi gessi colorati e in cima la CAMPANA, sfondo del vialetto di 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:
@@ -0,0 +1,75 @@
|
||||
// La campana nel browser vero: dal Taglia al campone delle poste, il tiro col mouse e col dito, i salti.
|
||||
// npm run browser:campana — schermate in /tmp/regolo-campana.
|
||||
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-campana';
|
||||
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 }]]) {
|
||||
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('giardini_poste'); settle(game);
|
||||
game.act('talk', 'taglia'); settle(game);
|
||||
game.choose(game.options().findIndex(o => o.text.startsWith('E una partita a campana'))); 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 () => (await import('/src/engine/render.js')).hasBackground('assets/closeups/campone-campana.png'));
|
||||
assert.equal(await page.locator('#bocce-throw').textContent(), 'Tira');
|
||||
assert.ok(await page.locator('#campana-help').isVisible());
|
||||
assert.ok(!(await page.locator('#bocce-left').isVisible()), 'niente pulsanti Mira');
|
||||
const box = await page.locator('#scene').boundingBox(), P = (x, y) => [box.x + x / 640 * box.width, box.y + y / 360 * box.height];
|
||||
// Mira alla 1, poi la forza giusta: col mouse tenendo premuto, col dito il cursore e Tira.
|
||||
const one = await page.evaluate(async () => { const m = window.campanaModule = await import('/src/engine/campana.js'); const p = m.center(1); return [p.x, p.y, Math.hypot(p.x - m.FROM.x, p.y - m.FROM.y) / m.REACH]; });
|
||||
if (mobile) {
|
||||
await page.touchscreen.tap(...P(one[0], one[1]));
|
||||
assert.equal(await page.evaluate(() => game.closeup.step), 'aim', 'un tocco mira soltanto');
|
||||
await page.evaluate(p => { game.closeup.power = p; game.closeup.skill.scatter = 0; }, one[2]);
|
||||
await page.locator('#bocce-throw').tap();
|
||||
} else {
|
||||
await page.mouse.move(...P(one[0], one[1]));
|
||||
await page.evaluate(() => { game.closeup.skill.scatter = 0; });
|
||||
await page.mouse.down();
|
||||
await page.waitForFunction(p => Math.abs(game.closeup.power - p) < 0.02, one[2], { polling: 'raf' });
|
||||
await page.screenshot({ path: `${out}/${name}-forza.png` });
|
||||
await page.mouse.up();
|
||||
// Col mouse il momento giusto dipende da quanto è lesta la pagina: se è andato fuori, si ritira col pulsante.
|
||||
await page.waitForFunction(() => !['aim', 'flying'].includes(game.closeup.step));
|
||||
if (await page.evaluate(() => game.closeup.step) !== 'hop') {
|
||||
await page.evaluate(p => { Object.assign(game.closeup, { step: 'aim', current: 'player', next: null, line: null, power: p }); }, one[2]);
|
||||
await page.locator('#bocce-throw').click();
|
||||
}
|
||||
}
|
||||
await page.waitForFunction(() => game.closeup.step === 'hop', null, { timeout: 5000 }).catch(async e => { console.log(await page.evaluate(() => JSON.stringify({ ...game.closeup, events: 0 }))); await page.screenshot({ path: `${out}/${name}-errore.png` }); throw e; });
|
||||
assert.equal(await page.locator('#bocce-throw').textContent(), 'Salta');
|
||||
// Quanti salti riescono: la lancetta dopo una schermata (lenta, con swiftshader) può già essere scappata.
|
||||
let hops = 0;
|
||||
// I salti: si preme quando la lancetta passa nel verde (col dito sul pulsante Salta, col mouse sulla scena).
|
||||
for (let i = 0; i < 4; i++) {
|
||||
await page.waitForFunction(() => { const m = window.campanaModule, c = game.closeup; return c.step !== 'hop' || ((!c.hop || c.hop.t >= m.HOP) && c.meter.t > 0 && Math.abs(m.needle(c)) < 0.15); }, null, { polling: 'raf', timeout: 5000 });
|
||||
if (await page.evaluate(() => game.closeup.step) !== 'hop') break;
|
||||
if (mobile) await page.locator('#bocce-throw').tap(); else await page.mouse.click(...P(320, 200));
|
||||
hops = Math.max(hops, await page.evaluate(() => game.closeup.beat + 1));
|
||||
if (i === 1) await page.screenshot({ path: `${out}/${name}-salti.png` });
|
||||
}
|
||||
assert.ok(hops >= 2, `salti riusciti: ${hops}`);
|
||||
// Poi tocca al Taglia: lo si fa sbagliare per vederlo saltare.
|
||||
await page.evaluate(() => { const c = game.closeup; c.step = 'rest'; c.t = 0; c.next = 'cpu'; c.line = 'Prova: tocca al Taglia.'; });
|
||||
await page.waitForFunction(() => game.closeup.step === 'cpuHop' && game.closeup.beat >= 3 || game.closeup.current === 'player' && game.closeup.step === 'aim', null, { timeout: 20000 });
|
||||
await page.screenshot({ path: `${out}/${name}-taglia.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