/** * Dev-only: the quest tracker's READY state (the "A + C" attract), shot live. * * Boots the GameScene (deterministic seed), waits for the open dossier to * auto-fold (the tracker slides in), then completes the starter quest all * at once — the flip fires the one-shot moment (flash + ◆ burst) and the * same-frame repaint goes READY (amber row). `window.__QUEST_READY__` is * set the instant the burst is still alive, so the capture lands on it: * * node dev/server.mjs 8080 * node dev/shot-firefox.mjs http://localhost:8080/dev/quest-ready.html \ * "window.__QUEST_READY__" /tmp/quest-ready.png 45000 */ 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'; import { SurfaceScene } from '../js/scenes/SurfaceScene.js'; const data = await ConfigLoader.load(); config.init(data); const gameConfig = createGameConfig(); globalThis.__ORBIT_DEV_SEED = '7'; // deterministic galaxy — reproducible shots // Dev: surface console errors on the flag (a shot with a READY row AND a // console error is a regression, even if the shot looks fine). const __errors = []; if (typeof window !== 'undefined') { 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))); } gameConfig.scene = [GameScene, SurfaceScene]; const game = new Phaser.Game(gameConfig); window.game = game; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const until = async (fn, ms = 30000) => { const t0 = Date.now(); for (;;) { const v = fn(); if (v) return v; if (Date.now() - t0 > ms) return null; await sleep(200); } }; setTimeout(async () => { try { const s = await until(() => (game.scene.getScene('GameScene')?.questTracker ? game.scene.getScene('GameScene') : null)); if (!s) { window.__QUEST_READY__ = { error: 'scene never became ready' }; return; } // Wait for the open dossier to auto-fold → the tracker slides in // (the tracker's first poll establishes its READY baseline while the // quest is still open, so the flip below is a true transition). await until(() => s.questTracker.shown === true); await sleep(250); // let the slide fully settle // Complete the starter quest (all four checks), live — the flip: s.totalMined = 200; s.researchState.isUnlocked = (cat, node) => true; s.tetherLevelFor = (name) => 2; s.discoveredWorldCount = () => 1; s._questRefresh(); // toast + chime + tracker repaint (the same frame) await sleep(380); // inside the burst's life (700–1000 ms) — catch the glyphs const snap = s.questSnapshot(); const q = snap.quests.find((x) => x.id === 'start_your_journey'); window.__QUEST_READY__ = { trackerShown: s.questTracker.shown, questComplete: q?.complete === true, questClaimed: q?.claimed === true, baselineHasStarter: s.questTracker._readyBaseline?.has('start_your_journey') === true, errors: __errors.slice(0, 3), }; // This geckodriver can't read the flag back (execute/sync → null), so // paint the verdict on the canvas too — the screenshot IS the readout. s.add.text(16, 690, `READY-FLAG ${JSON.stringify(window.__QUEST_READY__)}`, { fontFamily: 'monospace', fontSize: '11px', color: '#7ce8a4', align: 'left' }) .setOrigin(0, 1).setScrollFactor(0).setDepth(9000); } catch (err) { window.__QUEST_READY__ = { error: String(err?.stack ?? err) }; } }, 400);