orbit/dev/jobboard-shot.mjs

172 lines
8.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Dev-only visual probe for the JOB BOARD console (js/ui/JobBoardWindow.js):
* boots the real flow (menu → new game → land on the home world → surface),
* then parks at the stage named in the URL hash, with a report in a DOM
* <pre> the screenshot can always read:
*
* #drawer (default) — the HOME world's EXPLORE: no drawer at all (the
* Job Board slot is absent — data/actionbar.json →
* `isHomeSystem: false`, safe space), the slot's press is a no-op
* with the console's ack ("NOTHING TO EXPLORE HERE").
* #board — the board console OPEN over the surface (nearly
* full-screen, depth 80 — over the deck + drawer + the upper-left
* HUD), driven through the scene's own seams (the slot's press →
* the toggle): the left feed (assets/videos/jobboard.mp4, muted
* loop), the right JOB BOARD title + the AVAILABLE JOBS / JOB
* DETAILS placeholder sections, the deck stood down (the slot's
* press stays live as the second door; the ✕ is the way-out).
* PROBE STAGING: the home world's isHomeSystem flag is flipped
* for this shot so the slot exists here (on a real non-home world
* it exists naturally — the drawer's item list is the
* where-clause's, covered by dev/explore.test.mjs).
* #left — …and back: the slot's press again (the second
* door) — the console down, the deck live, the slot still JOB
* BOARD (no relabel either way).
*
* node dev/server.mjs 8080
* brave --headless --disable-gpu --remote-debugging-port=9333 --window-size=1280,720 about:blank
* node dev/cdp-shot.mjs "http://127.0.0.1:8080/dev/jobboard-shot.html#drawer" jb-drawer.png "window.__JB && window.__JB.stage && !/FAILED/.test(window.__JB.stage)" 45000
* node dev/cdp-shot.mjs "http://127.0.0.1:8080/dev/jobboard-shot.html#board" jb-board.png "window.__JB && window.__JB.stage && !/FAILED/.test(window.__JB.stage)" 45000
* node dev/cdp-shot.mjs "http://127.0.0.1:8080/dev/jobboard-shot.html#left" jb-left.png "window.__JB && window.__JB.stage && !/FAILED/.test(window.__JB.stage)" 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 { 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;
const report = document.createElement('pre');
report.id = 'report';
report.style.cssText = 'position:fixed;left:10px;top:10px;z-index:9999;max-width:72%;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 → surface)');
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function waitFor(fn, tries = 200, everyMs = 100) {
for (let i = 0; i < tries; i++) {
const v = fn();
if (v) return v;
await sleep(everyMs);
}
return null;
}
const STAGE = (location.hash || '#drawer').slice(1) || 'drawer';
const deckReport = (ss) =>
(ss.actionBar?.slots ?? []).filter((s) => s.live).map((s) => `${s.id}:${s.disabled ? 'off' : 'on'}`);
const slotLabel = (ss) => {
const b = (ss.exploreSubBar?.buttons ?? []).find((x) => x.id === 'jobBoard');
return b ? b.btn.labelText.text : '(absent — no drawer on the home world)';
};
try {
// 1) The real menu flow: NEW GAME.
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 : null; }, 200);
if (!gs) throw new Error('GameScene never booted');
await sleep(600);
// 2) Land on the home world the game's own way (the target is built
// WHILE the home flag still reads true — the HUD's "Home World"
// label comes from it).
const target = gs.commsTargetFor(gs.planet);
if (STAGE !== 'drawer') gs.isHomeSystem = false; // PROBE STAGING — the slot's visual on this world
gs.startLanding(target);
const ss = await waitFor(() => { const c = game.scene.getScene('SurfaceScene'); return c?.jobBoardWindow ? c : null; }, 200);
if (!ss) throw new Error('surface never booted');
await sleep(700);
ss.startSurface(); // force the loop stage (don't wait the landing clip out)
const forcePlay = (v) => { if (v?.video) { v.video.muted = true; v.video.play().catch(() => {}); } };
forcePlay(ss.surfaceVideo);
await sleep(900);
const win = ss.jobBoardWindow;
if (STAGE === 'drawer') {
// The HOME world's EXPLORE — the slot is a no-op (no drawer: the
// Job Board's `isHomeSystem: false` clause leaves the item list
// empty, and a world with no items gets no drawer).
ss.toggleExplore();
await sleep(400); // the console note in + hold
window.__JB = {
stage: 'drawer',
home: ss.planetName,
isHomeSystem: gs.isHomeSystem === true,
drawerPresent: !!ss.exploreSubBar,
drawerItems: (ss.exploreSubBar?.buttons ?? []).map((b) => b.id),
noteG: ss.noteG ? ss.noteG.map((g) => g?.text) : null,
deck: deckReport(ss),
errors,
};
setReport(JSON.stringify(window.__JB, null, 1));
} else {
// The slot's player path: the drawer's JOB BOARD press → the toggle
// (the drawer's onAction seam — the same call the button's press
// makes on every non-home world).
ss.toggleExplore();
await sleep(1100); // the drawer's unfold
ss.exploreAction('jobBoard');
await sleep(1900); // the board’s reveal + the title’s decode
// Headless autoplay policy: force the feed visible (muted).
if (win.video?.video) { win.video.video.muted = true; win.video.video.play().catch(() => {}); }
await sleep(700); // let a frame land
window.__JB = {
stage: 'board',
world: ss.planetName,
staged: 'isHomeSystem flipped for the slot’s visual (a real non-home world has it naturally)',
jobBoardMode: ss.jobBoardMode,
openState: win?.openState,
title: win?.rightTitle?.text,
titlePos: win?.rightTitle ? [Math.round(win.rightTitle.x), Math.round(win.rightTitle.y)] : null,
jobs: win?.jobsSection ? { a: +win.jobsSection.bg.alpha.toFixed(2), y: Math.round(win.jobsSection.bg.y), head: win.jobsSection.headTxt.text, ha: +win.jobsSection.headTxt.alpha.toFixed(2) } : null,
detail: win?.detailSection ? { a: +win.detailSection.bg.alpha.toFixed(2), y: Math.round(win.detailSection.bg.y), head: win.detailSection.headTxt.text, ha: +win.detailSection.headTxt.alpha.toFixed(2) } : null,
geo: win?.geo ? { bodyY: win.geo.bodyY, bodyBottom: win.geo.bodyBottom, rightX: win.geo.rightX, rightW: win.geo.rightW, availTop: win.geo.availTop, availH: win.geo.availH, detailPanelY: win.geo.detailPanelY, detailH: win.geo.detailH } : null,
video: win?.video ? { muted: win.video.video.muted, loop: win.video.loop, volume: win.video.volume } : null,
slotLabel: slotLabel(ss),
drawerItems: (ss.exploreSubBar?.buttons ?? []).map((b) => b.id),
deck: deckReport(ss),
errors,
};
setReport(JSON.stringify(window.__JB, null, 1));
if (STAGE === 'left') {
// LEAVE — the slot's press again (the player's exact way out).
ss.exploreAction('jobBoard');
await sleep(900); // the close + the relabel back
window.__JB = {
stage: 'left',
world: ss.planetName,
jobBoardMode: ss.jobBoardMode,
openState: win?.openState,
slotLabel: slotLabel(ss),
deck: deckReport(ss),
errors,
};
setReport(JSON.stringify(window.__JB, null, 1));
}
}
} catch (err) {
window.__JB = { stage: 'FAILED', error: String(err?.message ?? err), errors };
setReport(JSON.stringify(window.__JB, null, 1));
}