139 lines
6.8 KiB
JavaScript
139 lines
6.8 KiB
JavaScript
/**
|
|
* 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 <pre> 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);
|
|
}
|