/** * Dev check — the QUEUED celebration path: research completes while the * console is closed, and the ignition still plays when the player reopens * the console within the fresh window (a stale one is dropped, not played). * Drives itself and exposes `window.__QUEUE_CHECK__`: * * node dev/server.mjs 8090 * node dev/cdp-shot.mjs "http://127.0.0.1:8090/dev/queue-celebrate.html" \ * /tmp/q.png "window.__QUEUE_CHECK__" * # then read the report via CDP eval: window.__QUEUE_CHECK__.lines */ import Phaser from '../js/vendor/phaser.js'; import { config } from '../js/config/Config.js'; import { ConfigLoader } from '../js/config/ConfigLoader.js'; import { createGameConfig } from '../js/config/GameConfig.js'; import { GameScene } from '../js/scenes/GameScene.js'; const data = await ConfigLoader.load(); config.init(data); const errors = []; const origErr = console.error.bind(console); console.error = (...a) => { errors.push(a.map(String).join(' ')); origErr(...a); }; window.addEventListener('error', (e) => errors.push(String(e.message))); window.addEventListener('unhandledrejection', (e) => errors.push(`rejection: ${e.reason}`)); const gameConfig = createGameConfig(); gameConfig.scene = [GameScene]; const game = new Phaser.Game(gameConfig); window.game = game; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function waitScene() { for (let i = 0; i < 100; i++) { const s = game.scene.getScene('GameScene'); if (s && s.ship && s.researchWindow) return s; await sleep(100); } throw new Error('GameScene never booted'); } const lines = []; try { const s = await waitScene(); await sleep(800); const win = s.researchWindow; // 1 — open, start a short project, CLOSE the console while in flight s.deckAction('research'); await sleep(900); // boot reveal s.researchState.start('exploration', 'tether_l2', 400, s.time.now); await sleep(150); s.deckAction('research'); // toggle → close (fade ~150 ms) await sleep(700); // completion happens HERE, console closed → QUEUED const q1 = win.celebration; lines.push(`queued at close: ${q1 ? `cat=${q1.catId} played=${q1.played}` : 'NONE'}`); if (!q1 || q1.played) errors.push('CELEBRATE not queued while console closed'); // 2 — reopen within the fresh window → the ignition must play s.deckAction('research'); await sleep(200); lines.push(`after reopen: played=${win.celebration?.played} bursts=${win.bursts.length} open=${win.openState}`); if (!win.celebration?.played) errors.push('CELEBRATE did not play after reopen'); if (win.bursts.length === 0) errors.push('no bursts alive after reopen'); // 3 — stale-drop: a completion older than the fresh window is dropped, // not played (simulate by backdating `at`). Let step 2's bursts expire // first so the count below is meaningful. await sleep(1400); win.celebration = { catId: 'exploration', id: 'tether_l3', at: s.time.now - 60000, played: false }; win._maybeCelebrate(); lines.push(`stale drop: played=${win.celebration.played} bursts=${win.bursts.length}`); if (!win.celebration.played) errors.push('stale celebration was not dropped'); if (win.bursts.length !== 0) errors.push('stale celebration still pushed bursts'); } catch (err) { errors.push(`FATAL: ${err.message}`); } lines.unshift(errors.length === 0 ? 'QUEUE CHECK OK — no console errors' : 'FAILURES:'); window.__QUEUE_CHECK__ = { ok: errors.length === 0, lines, errors }; console.info('queue-celebrate: done', errors.length === 0);