Add dev dialog-shot probe for map confirm screenshot
- Adds a standalone Phaser dev harness that boots GameScene, opens the MAP console, hovers a discovered object, and triggers the ENGAGE AUTOPILOT confirm so it can be captured via headless Firefox. - Records dialog state, scrim alpha, panel command buffer, and target-deferral assertions to verify the confirm renders correctly and leaves ship.target null until ENGAGE. - Includes a rocky-shop video asset for reference in the shot workflow.
This commit is contained in:
parent
aa8cf8e091
commit
642c2cc016
Binary file not shown.
|
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<base href="../" />
|
||||
<title>Orbit — Map confirm (dev probe)</title>
|
||||
<style>
|
||||
html, body { margin: 0; height: 100%; background: #04060d; overflow: hidden; }
|
||||
#game { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
|
||||
</style>
|
||||
<script src="lib/phaser.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="game"></div>
|
||||
<script type="module" src="dev/dialog-shot.mjs"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* Dev-only: boot GameScene, open the MAP console, hover a discovered
|
||||
* object and open the ENGAGE AUTOPILOT confirm — leave it up for a
|
||||
* screenshot (the dialog over the plate, scrim darkening the window).
|
||||
*
|
||||
* node dev/server.mjs 8091
|
||||
* node dev/shot-firefox.mjs \
|
||||
* "http://127.0.0.1:8091/dev/dialog-shot.html" \
|
||||
* "window.__DIALOG_SHOT ? window.__DIALOG_SHOT.ready : null" \
|
||||
* /tmp/dialog-shot.png 30000
|
||||
*/
|
||||
import Phaser from '../js/vendor/phaser.js';
|
||||
import { ConfigLoader } from '../js/config/ConfigLoader.js';
|
||||
import { config } from '../js/config/Config.js';
|
||||
import { createGameConfig } from '../js/config/GameConfig.js';
|
||||
import { GameScene } from '../js/scenes/GameScene.js';
|
||||
|
||||
const data = await ConfigLoader.load();
|
||||
config.init(data);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
const sysId = s.systemRecord.id;
|
||||
let known = s.discovery.bySystem.get(sysId);
|
||||
if (!known) { known = new Set(); s.discovery.bySystem.set(sysId, known); }
|
||||
const push = (obj) => { if (obj && !known.has(obj.id)) s.discovery.check(sysId, obj.x, obj.y, [obj]); };
|
||||
if (s.isHomeSystem && s.planet) push({ id: 'home', x: 0, y: 0, radius: s.planet.radius });
|
||||
if (s.systemPlanets[0]) push({ id: s.systemPlanets[0].discoveryId, x: s.systemPlanets[0].x, y: s.systemPlanets[0].y, radius: s.systemPlanets[0].radius });
|
||||
if (s.systemStations[0]) push({ id: s.systemStations[0].discoveryId, x: s.systemStations[0].x, y: s.systemStations[0].y, radius: s.systemStations[0].bound ?? 60 });
|
||||
if (s.asteroidClusters?.[0]) push({ id: s.asteroidClusters[0].discoveryId, x: s.asteroidClusters[0].x, y: s.asteroidClusters[0].y, radius: s.asteroidClusters[0].bound ?? 100 });
|
||||
|
||||
s.deckAction('map');
|
||||
await sleep(1600);
|
||||
const win = s.mapWindow;
|
||||
|
||||
const snap = win._snapOf();
|
||||
if (snap && win._fp !== win._fpOf(snap)) win._applySnap(snap);
|
||||
if (!win._hits?.length) throw new Error('no hits on the chart');
|
||||
const hit = win._hits[0];
|
||||
win._setHover({ x: win.geo.mapX + hit.x, y: win.geo.mapY + hit.y });
|
||||
await sleep(150);
|
||||
// open the confirm (what a click does) and leave it up
|
||||
win.mapImg.emit('pointerdown', { x: win.geo.mapX + hit.x, y: win.geo.mapY + hit.y });
|
||||
win.mapImg.emit('pointerup', {});
|
||||
await sleep(700); // open anim + title decode
|
||||
|
||||
const dlg = win.dialog;
|
||||
const frameAt = (dt) => new Promise((r) => setTimeout(r, dt));
|
||||
const probe = (label) => ({
|
||||
[label]: {
|
||||
state: dlg.state,
|
||||
scrimAlpha: +dlg.scrim.alpha.toFixed(3),
|
||||
panelCmds: dlg.panelG.commandBuffer.length,
|
||||
},
|
||||
});
|
||||
const probeShown = probe('at700ms');
|
||||
await frameAt(300);
|
||||
const probeLater = probe('at1000ms');
|
||||
const lines = [
|
||||
errors.length === 0 ? 'DIALOG OK — no console errors' : `ERRORS:\n${errors.slice(0, 4).join('\n')}`,
|
||||
`window: ${win.openState} · dialog: ${dlg.state}`,
|
||||
`title: ${dlg.title.text || '—'}`,
|
||||
`body: ${dlg.bodyTexts.map((t) => t.text).join(' | ') || '—'}`,
|
||||
`confirm: ${dlg.confirmBtn.labelText.text} · cancel: ${dlg.cancelBtn.labelText.text}`,
|
||||
`target: ${s.ship.target ? 'set (BUG — must be null until ENGAGE)' : 'null (correct — deferred)'}`,
|
||||
`probe ${JSON.stringify(probeShown)} ${JSON.stringify(probeLater)}`,
|
||||
];
|
||||
setReport(lines);
|
||||
window.__DIALOG_SHOT = { ready: true, lines, errors, probe: { ...probeShown, ...probeLater }, pass: dlg.isOpen && s.ship.target === null && errors.length === 0 && probeLater.at1000ms.scrimAlpha > 0.5 };
|
||||
} catch (err) {
|
||||
errors.push(`FATAL: ${err.message}`);
|
||||
setReport(['FATAL: ' + err.message, ...errors.slice(0, 4)]);
|
||||
window.__DIALOG_SHOT = { ready: true, fatal: String(err.message), errors };
|
||||
}
|
||||
Loading…
Reference in New Issue