/** * Dev-only: boot GameScene, set a DESTINATION (a far star's first planet), * and open the MAP console's GALAXY tab so a headless screenshot shows: * · the route (the active SET DESTINATION path) in ORANGE on the lanes, * · the destination star circled in orange, * · the compass's route gate (orange, full-sized). * * node dev/server.mjs 8083 # static server * node dev/shot-firefox.mjs \ * "http://127.0.0.1:8083/dev/route-shot.html" \ * "window.__ROUTE_SHOT ? window.__ROUTE_SHOT.ready : null" \ * ROUTE_SHOT.png 40000 * * The report (top-left) lists console errors + the route state. */ 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); // 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 = [GameScene]; 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…');

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.mapWindow) return s;
    await sleep(100);
  }
  throw new Error('GameScene never booted');
}

try {
  const s = await waitScene();
  await sleep(800); // let the boot settle

  // Pick a FAR destination: the furthest star from the current system
  // (a multi-hop route, so the orange path is visible on several lanes).
  const sysId = s.systemRecord.id;
  const records = s.galaxy.records;
  const cur = records.find((r) => r.id === sysId);
  const others = records.filter((r) => r.id !== sysId);
  others.sort((a, b) => {
    const da = Math.hypot(a.x - cur.x, a.y - cur.y);
    const db = Math.hypot(b.x - cur.x, b.y - cur.y);
    return db - da; // farthest first
  });
  // Use the 3rd-furthest (leave some margin so the route is a real path,
  // not a single hop).
  const dest = others[2] ?? others[0];
  if (!dest) throw new Error('no destination candidate');

  // Discover the destination's first planet (so it has an objectId).
  const destContent = s.galaxy.contentOf(dest.id);
  const destPlanet = (destContent.planets ?? [])[0];
  const destObjId = destPlanet?.name ?? null;

  // Set the destination (the route is derived from it).
  s.setDestination(dest.id, destObjId);
  await sleep(400);

  // Open the MAP console's GALAXY tab (the route + destination ring are
  // drawn there). Use the window's own open() (reveal tween + state).
  s.mapWindow._switchMode('galaxy');
  s.mapWindow.open();
  await sleep(1600); // let the reveal tween + ripple settle + the route paint

  // Collect the route state for the report.
  const plan = s.routePlan();
  const routeKeys = s.routeEdgeKeys();
  const snap = s.galaxySnapshot();
  const destStar = snap?.systems?.find((x) => x.isDestination);
  const routeEdges = (snap?.edges ?? []).filter((e) => e.route);

  // --- Now switch to the SYSTEM tab and open the destination system's chart.
  // --- Discover the destination object first (so it's drawn on the chart),
  // --- then the destination ring should appear around it.
  const destSysId = dest.id;
  if (destPlanet) {
    // Discover the destination planet (so it's drawn on the chart).
    let known = s.discovery.bySystem.get(destSysId);
    if (!known) { known = new Set(); s.discovery.bySystem.set(destSysId, known); }
    s.discovery.check(destSysId, destPlanet.x, destPlanet.y, [{ id: destPlanet.name, x: destPlanet.x, y: destPlanet.y, radius: 100 }]);
  }
  await sleep(300);

  // Switch to the SYSTEM tab and open the destination system's chart.
  s.mapWindow._switchMode('system');
  s.mapWindow._openSystem(destSysId);
  await sleep(1200); // let the chart repaint

  // Collect the chart state for the report.
  const chartSnap = s.systemChartSnapshotFor(destSysId);
  const destInChart = chartSnap?.objects?.find((o) => o.id === chartSnap.destinationId);

  window.__ROUTE_SHOT = {
    ready: true,
    errors,
    destSystem: dest.id,
    destName: dest.name,
    destObjId,
    plan: { path: plan?.path, hops: plan?.hops },
    routeKeys,
    destStar: destStar ? { id: destStar.id, name: destStar.name } : null,
    routeEdgeCount: routeEdges.length,
    destinationId: snap?.destinationId,
    chartDestinationId: chartSnap?.destinationId,
    chartDestDiscovered: destInChart?.discovered,
    chartDestName: destInChart?.name,
  };

  const lines = [
    `DESTINATION: ${dest.name} (${dest.id})`,
    `OBJECT: ${destObjId}`,
    `ROUTE: ${plan?.path?.join(' → ') ?? 'null'}`,
    `HOPS: ${plan?.hops}`,
    `ROUTE EDGES: ${routeEdges.length}`,
    `DEST STAR: ${destStar ? `${destStar.name} (${destStar.id})` : 'null'}`,
    `CHART DEST: ${chartSnap?.destinationId ?? 'null'}`,
    `CHART DEST DISCOVERED: ${destInChart?.discovered}`,
    `CHART DEST NAME: ${destInChart?.name}`,
    `ERRORS: ${errors.length ? errors.join(' | ') : 'none'}`,
  ];
  setReport(lines);
  console.log('ROUTE SHOT READY', JSON.stringify(window.__ROUTE_SHOT, null, 2));
} catch (e) {
  const lines = ['FATAL: ' + (e?.message ?? e), ...errors];
  setReport(lines);
  window.__ROUTE_SHOT = { ready: true, errors: [...errors, String(e?.message ?? e)] };
}