/** * Dev-only: boot the game (menu → new game → land on the home world), * open the BUILD console and stage the full row-state matrix, so a * headless screenshot shows every indicator in one frame: * * PLANET tab — tether-l1 BUILT (the lit row: soft accent fill, glow * ring, icon halo, the stamped seal, "INSTALLED ON …"), * tether-l2 ACTIVE (progress bar + the pulsing tab pip), * tether-l3 LOCKED (missing requirements); * MINING tab — improved arm BUILT ship-wide (installed on ANOTHER * world: "SHIP-WIDE · INSTALLED FROM KEPLER-186F"), * advanced arm READY, storage LOCKED. * * node dev/server.mjs 8080 * chrome --headless --disable-gpu --remote-debugging-port=9333 \ * --window-size=1280,720 about:blank * # planet tab (tether-l1 selected — the BUILT detail readout): * node dev/cdp-shot.mjs "http://127.0.0.1:8080/dev/build-shot.html" out.png \ * "!!window.__BUILD_SHOT?.ready" 25000 \ * "document.getElementById('report').style.display='none'; true" * # mining tab (improved arm selected — the SHIP-WIDE built readout): * node dev/cdp-shot.mjs "http://127.0.0.1:8080/dev/build-shot.html" out-mining.png \ * "!!window.__BUILD_SHOT?.ready" 25000 \ * "window.__buildShotMining(); document.getElementById('report').style.display='none'; true" */ 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 { MenuScene } from '../js/scenes/MenuScene.js'; import { GameScene } from '../js/scenes/GameScene.js'; import { SurfaceScene } from '../js/scenes/SurfaceScene.js'; const data = await ConfigLoader.load(); config.init(data); // Capture console errors + uncaught exceptions for the report. 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 = [MenuScene, GameScene, SurfaceScene]; const game = new Phaser.Game(gameConfig); window.game = game; // The report lives OUTSIDE the canvas — a DOM
 the screenshot can
// always read (headless canvases don't have to cooperate).
const report = document.createElement('pre');
report.id = 'report';
report.style.cssText = 'position:fixed;left:10px;top:10px;z-index:9999;max-width:70%;margin:0;padding:8px 12px;font:13px/1.5 monospace;color:#eaf6ff;background:rgba(6,20,16,0.92);border:1px solid #1b3a5a;white-space:pre-wrap;';
document.body.appendChild(report);
const setReport = (lines) => { report.textContent = (Array.isArray(lines) ? lines : [lines]).join('\n'); };
setReport('booting… (menu → new game → home world → build console)');

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function waitFor(fn, tries = 150, everyMs = 100) {
  for (let i = 0; i < tries; i++) {
    const v = fn();
    if (v) return v;
    await sleep(everyMs);
  }
  return null;
}

try {
  // 1) The real menu flow: NEW GAME (fresh seed, registry reset).
  const menu = await waitFor(() => { const m = game.scene.getScene('MenuScene'); return m?.newGameBtn ? m : null; });
  if (!menu) throw new Error('menu never booted');
  menu.startNewGame();
  const gs = await waitFor(() => { const g = game.scene.getScene('GameScene'); return g?.ship && g?.buildState ? g : null; }, 200);
  if (!gs) throw new Error('GameScene never booted');
  await sleep(800);

  // 2) Land on the home world the game's own way.
  gs.startLanding(gs.commsTargetFor(gs.planet));
  const ss = await waitFor(() => { const c = game.scene.getScene('SurfaceScene'); return c?.buildWindow ? c : null; });
  if (!ss) throw new Error('surface never booted');
  await sleep(700);
  const win = ss.buildWindow;
  const home = win.planetName;

  // 3) Stage the state matrix (the scene's own seams — the same calls a
  //    player's run makes): blueprints researched, a ship upgrade
  //    installed on ANOTHER world, the level-2 tether build in flight.
  gs.ship.setMinerals(1000);
  gs.researchState.unlock('exploration', 'tether_l2');
  gs.researchState.unlock('mining', 'improved_arm');
  gs.researchState.unlock('mining', 'advanced_arm');
  gs.buildState.markBuilt('Kepler-186f', 'mining-arm-improved'); // ship-wide, from elsewhere
  const began = gs.beginBuild(home, 'tether-l2');
  if (!began.ok) throw new Error(`beginBuild refused: ${began.reason}`);

  // 4) Open the console and settle the boot reveal.
  win.open();
  await waitFor(() => win.openState === 'open' && !win.reveal.length, 60, 100);
  await sleep(300);
  win.refresh();
  win.select('planet', 'tether-l1'); // the BUILT row's detail readout
  await sleep(300);

  // The report: row states, pip states, the installed lines, errors.
  const { rowState } = await import('../js/build/BuildModel.js');
  const planetList = win.lists.get('planet');
  const miningList = win.lists.get('mining');
  const st = (list, id) =>
    `${id}=${rowState(list.entry.builds[id], win.ctxFor(id), win._activeOnPlanet(), id).toUpperCase()}`;
  const pips = (tab) => tab.pips.map((p) => `${p.id.slice(0, 6)}:${p.state}`).join(' ');
  const lines = [
    errors.length === 0 ? 'SMOKE OK — no console errors' : `ERRORS:\n${errors.slice(0, 3).join('\n')}`,
    `window: ${win.openState} · cat=${win.activeCat} · sel=${win.selected ? win.selected.category + '/' + win.selected.id : '—'}`,
    `planet rows: ${planetList.entry.order.map((id) => st(planetList, id)).join('  ')}`,
    `mining rows: ${miningList.entry.order.map((id) => st(miningList, id)).join('  ')}`,
    `planet pips: ${pips(win.tabs.find((t) => t.id === 'planet'))}`,
    `mining pips: ${pips(win.tabs.find((t) => t.id === 'mining'))}`,
    `builtOn(mining-arm-improved): ${gs.buildState.builtOn('mining-arm-improved')}`,
    `builtOn(tether-l1): ${gs.buildState.builtOn('tether-l1')}`,
    `installed line (arm): ${win._installedLine('mining-arm-improved')}`,
    `installed line (l1): ${win._installedLine('tether-l1')}`,
  ];
  setReport(lines);
  window.__BUILD_SHOT = { ready: true, lines, errors };
  // second shot: flip to the MINING tab with the ship-wide BUILT arm selected
  window.__buildShotMining = () => {
    win.switchCategory('mining');
    win.select('mining', 'mining-arm-improved');
    win.refresh();
  };
  console.info('build-shot: report painted');
} catch (err) {
  errors.push(`FATAL: ${err.message}`);
  setReport(['FATAL: ' + err.message, ...errors.slice(0, 3)]);
  window.__BUILD_SHOT = { ready: true, fatal: String(err.message), errors };
  console.error('build-shot: fatal', err);
}