Schermo intero (pulsante accanto ai volumi e tasto F) e gioco fino a 2560 px; nei minigiochi la scena lascia lo spazio vero dei comandi; flipper: «Solo tavola», sul telefono senza pulsantiera e «Lascia stare» in alto, molla toccando al centro con «TIENI PREMUTO QUI», ponti rifatti da Codex; prove più salde (tressette)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiECd9RWGMKV7n3K1mdy8T
This commit is contained in:
2026-11-01 21:38:44 +01:00
co-authored by Claude Opus 5.5
parent 428fb4bbec
commit 531aee18bf
10 changed files with 234 additions and 50 deletions
+16
View File
@@ -508,6 +508,22 @@ try {
assert.ok(stage0.width >= 1200, `a 1280×860 la scena è larga ${stage0.width} px`);
assert.ok(await page.evaluate(() => document.documentElement.scrollHeight <= innerHeight), 'la pagina scorre in verticale a 1280×860');
assert.ok(await page.evaluate(() => [...document.querySelectorAll('#verbs button')].every(b => b.scrollWidth <= b.clientWidth + 1 && b.scrollHeight <= b.clientHeight + 1)), 'verbi tagliati a 1280×860');
// Su uno schermo grande (1920×1080, a schermo intero) il gioco non si ferma più a 1280 px: prende tutta l'altezza.
await page.setViewportSize({ width: 1920, height: 1080 });
await page.waitForFunction(() => document.getElementById('stage').getBoundingClientRect().width > 1500);
assert.ok(await page.evaluate(() => document.documentElement.scrollHeight <= innerHeight && document.getElementById('app').getBoundingClientRect().height >= innerHeight - 2), 'a 1920×1080 il gioco prende tutta l\'altezza, senza scorrere');
await page.setViewportSize({ width: 1280, height: 860 });
// Schermo intero: il pulsante accanto ai volumi e il tasto F, per entrare e per uscire.
if (await page.evaluate(() => document.fullscreenEnabled)) {
await page.click('#fullscreen-hud');
await page.waitForFunction(() => !!document.fullscreenElement && document.getElementById('fullscreen-hud').getAttribute('aria-pressed') === 'true');
await page.keyboard.press('f');
await page.waitForFunction(() => !document.fullscreenElement);
await page.keyboard.press('F');
await page.waitForFunction(() => !!document.fullscreenElement);
await page.click('#fullscreen-hud');
await page.waitForFunction(() => !document.fullscreenElement && document.getElementById('fullscreen-hud').getAttribute('aria-pressed') === 'false');
} else assert.ok(await page.evaluate(() => document.getElementById('fullscreen-hud').hidden), 'senza schermo intero il pulsante sparisce');
// Pausa: Esc o il pulsante fermano il gioco; Riprendi torna dov'eri; il titolo offre «Continua».
await page.keyboard.press('Escape');
await page.waitForSelector('#pause:not([hidden])');
+20 -11
View File
@@ -25,16 +25,31 @@ try {
game.act('use', 'river'); settle(game);
game.choose(game.options().findIndex(o => o.text.startsWith('Una partita alla bona'))); settle(game);
});
await page.waitForSelector('#bocce-controls:not([hidden])');
await page.waitForSelector('#bocce-controls:not([hidden])', { state: 'attached' });
await page.waitForFunction(async () => (await import('/src/engine/render.js')).hasBackground('assets/closeups/river-flipper.png'));
// Aspetta che finisca la dissolvenza d'ingresso: le immagini devono mostrare i colori della tavola.
const readyAt = await page.evaluate(() => game.closeup.visual.time);
await page.waitForFunction(t => game.closeup.visual.time > t + 1, readyAt);
assert.equal(await page.locator('#scene').getAttribute('width'), '1280', 'il flipper usa la risoluzione doppia');
assert.equal(await page.locator('#bocce-throw').textContent(), 'Lancia');
assert.ok(await page.locator('#flipper-help').isVisible());
assert.equal(await page.locator('#bocce-left').textContent(), '← Flipper');
assert.ok(!(await page.locator('#bocce-left').isDisabled()), 'i pulsanti dei flipper sono attivi anche prima del lancio');
if (!mobile) {
// «Solo tavola»: la pulsantiera sparisce e la tavola si allarga; ripremuto, torna tutto com'era.
const before = (await page.locator('#scene').boundingBox()).width;
await page.click('#table-only');
await page.waitForFunction(() => getComputedStyle(document.getElementById('side')).display === 'none');
await page.waitForFunction(w => document.getElementById('scene').getBoundingClientRect().width > w + 50, before);
assert.equal(await page.locator('#table-only').textContent(), 'Pulsantiera');
await page.click('#table-only');
await page.waitForFunction(() => getComputedStyle(document.getElementById('side')).display !== 'none');
assert.ok(await page.locator('#flipper-help').isVisible());
assert.equal(await page.locator('#bocce-left').textContent(), '← Flipper');
assert.ok(!(await page.locator('#bocce-left').isDisabled()), 'i pulsanti dei flipper sono attivi anche prima del lancio');
} else {
// Sul telefono in orizzontale (Andrea): niente pulsantiera, la scena larga sotto i due pollici.
assert.ok(!(await page.locator('#side').isVisible()), 'sul telefono la pulsantiera del flipper sparisce');
const w = (await page.locator('#scene').boundingBox()).width;
assert.ok(w > viewport.width * 0.6, `sul telefono la scena del flipper è larga ${w} px`);
}
await page.screenshot({ path: `${out}/${name}-tavola.png` });
const box = await page.locator('#scene').boundingBox(), P = (x, y) => [box.x + x / 640 * box.width, box.y + y / 360 * box.height];
if (!mobile) await page.screenshot({ path: `${out}/desktop-dettaglio.png`, clip: { x: P(200, 0)[0], y: box.y, width: 248 / 640 * box.width, height: box.height } });
@@ -42,7 +57,7 @@ try {
const touch = (type, points) => cdp.send('Input.dispatchTouchEvent', { type, touchPoints: points.map(([x, y]) => ({ x, y })) });
// La molla: tenendo premuto sulla scena, col mouse e col dito.
if (mobile) {
await touch('touchStart', [P(440, 320)]);
await touch('touchStart', [P(320, 320)]); // al centro: la molla
await page.waitForFunction(() => game.closeup.holding && game.closeup.power > 0.3, null, { polling: 'raf' });
await touch('touchEnd', []);
} else {
@@ -63,12 +78,6 @@ try {
await page.waitForFunction(() => game.closeup.flippers.left.held.length === 0 && game.closeup.flippers.right.held.length > 0, null, { timeout: 3000 });
await touch('touchEnd', []);
await page.waitForFunction(() => game.closeup.flippers.right.held.length === 0);
// I pulsanti tenuti premuti.
const b = await page.locator('#bocce-left').boundingBox();
await touch('touchStart', [[b.x + b.width / 2, b.y + b.height / 2]]);
await page.waitForFunction(() => game.closeup.flippers.left.held.length > 0);
await touch('touchEnd', []);
await page.waitForFunction(() => game.closeup.flippers.left.held.length === 0);
} else {
await page.mouse.move(...P(400, 200)); await page.mouse.down();
await page.waitForFunction(() => game.closeup.flippers.right.held.length > 0);
+7 -3
View File
@@ -130,13 +130,17 @@ test('funghi, bersagli, Regolo e corsie fanno punti solo a un urto nuovo; le qua
assert.ok(!c.lanes[1].lit);
});
test('comandi: la molla si carica e si lancia lasciando; annullare non lancia; due dita, pulsanti e tasti', () => {
test('comandi: la molla si carica al centro e si lancia lasciando, ai lati le palette anche prima del lancio; annullare non lancia; due dita, pulsanti e tasti', () => {
const c = m.newFlipper(8, { level: 'bona' });
m.aimScreen(c, [440, 320]); assert.ok(m.press(c, 1)); run(c, 0.6);
// A pallina ferma i lati alzano le palette, il centro carica la molla.
m.aimScreen(c, [120, 300]); assert.ok(m.press(c, 5)); assert.ok(c.flippers.left.held.includes(5) && !c.holding, 'a sinistra, la paletta');
m.aimScreen(c, [520, 300]); assert.ok(m.press(c, 6)); assert.ok(c.flippers.right.held.includes(6) && !c.holding, 'a destra, la paletta');
m.release(c, 5); m.release(c, 6); assert.ok(m.waiting(c));
m.aimScreen(c, [320, 320]); assert.ok(m.press(c, 1)); run(c, 0.6);
assert.ok(c.holding && c.power > 0.4 && c.power < 0.6, `molla a ${c.power.toFixed(2)}`);
assert.ok(!m.release(c, 2), 'un altro dito non lancia');
m.cancel(c, 1); assert.ok(m.waiting(c) && !c.holding, 'annullata la carica: niente lancio');
m.aimScreen(c, [440, 320]); m.press(c, 1); run(c, 0.6); assert.ok(m.release(c, 1)); assert.ok(!m.waiting(c));
m.aimScreen(c, [300, 320]); m.press(c, 1); run(c, 0.6); assert.ok(m.release(c, 1)); assert.ok(!m.waiting(c));
m.aimScreen(c, [250, 200]); assert.ok(m.press(c, 3)); assert.ok(c.flippers.left.held.includes(3));
m.aimScreen(c, [400, 200]); assert.ok(m.press(c, 4)); assert.ok(c.flippers.right.held.includes(4));
assert.ok(m.release(c, 3)); assert.ok(!c.flippers.left.held.length && c.flippers.right.held.includes(4));
+19 -5
View File
@@ -13,18 +13,32 @@ async function tap(page, mode, selector) {
if (mode === 'touch') { const r = await page.locator(selector).boundingBox(); await page.touchscreen.tap(r.x + r.width / 2, r.y + r.height / 2); }
else await page.click(selector);
}
// Una battuta che compare proprio in quell'istante (le battute del giocatore partono dal ciclo del gioco) si prende il
// tasto e la salta, come nel gioco: allora, come un giocatore, si ripreme. Al più tre volte.
async function pressUntil(page, key, done, arg) {
for (let i = 0; i < 3; i++) {
await page.keyboard.press(key);
if (await page.evaluate(done, arg)) return;
await page.waitForFunction(() => !game.speech);
}
}
async function playCard(page, mode, index, signal) {
// Un tasto premuto mentre qualcuno parla salta la battuta e basta (come nel gioco): si aspetta che abbia finito.
if (mode === 'keyboard') await page.waitForFunction(() => !game.speech);
// Un tasto premuto mentre qualcuno parla salta la battuta e basta, e il segnale si sceglie solo quando tocca a noi
// (come nel gioco): si aspetta che la battuta sia finita e che si possa giocare.
if (mode === 'keyboard') {
await page.evaluate(async () => { window.tressetteRules ??= await import('/src/engine/tressette.js'); });
await page.waitForFunction(() => !game.speech && tressetteRules.canPlay(game.closeup));
}
if (signal) {
if (mode === 'keyboard') await page.keyboard.press(signal[0]);
if (mode === 'keyboard') await pressUntil(page, signal[0], s => game.closeup.signal === s, signal);
else await tap(page, mode, `#tressette-${signal}`);
assert.equal(await page.evaluate(() => game.closeup.signal), signal);
}
if (mode === 'keyboard') {
await page.keyboard.press(String((index + 1) % 10));
await pressUntil(page, String((index + 1) % 10), i => game.closeup.selected === i, index);
assert.equal(await page.evaluate(() => game.closeup.selected), index);
await page.keyboard.press('Enter');
const held = await page.evaluate(() => game.closeup.hands[0].length);
await pressUntil(page, 'Enter', n => game.closeup.hands[0].length < n || !!game.closeup.refused, held);
return;
}
const at = await page.evaluate(async i => {