orbit/dev/popup-shot.mjs

91 lines
3.5 KiB
JavaScript

/**
* Dev: screenshot the mining context menu — in the REAL web font — and
* report whether the buttons fit inside the panel.
*
* The menu sizes itself from its buttons (js/ui/MiningPopup.js), and the
* buttons measure their label in whatever font is live at construction.
* index.html waits for the typefaces before boot (js/main.js → awaitFonts),
* so this probe does the same before creating the game.
*
* python3 -m http.server 8080
* node dev/wdshot.mjs http://localhost:8080/dev/popup-shot.html /tmp/popup.png \
* 'window.__POPUP_SHOT__(); return window.__POPUP_SHOT__RESULT__;' 40000 5000
*/
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);
// The game measures its UI text in the web font (Centauri) — wait for it
// BEFORE the scenes build any text (same contract as js/main.js).
const __fonts = globalThis.document?.fonts;
if (__fonts && typeof __fonts.load === 'function') {
const fams = ['header', 'body']
.map((k) => config.get(`theme.fonts.${k}.family`))
.filter((f) => typeof f === 'string' && f.length > 0);
if (fams.length > 0) {
await Promise.race([
Promise.allSettled(fams.map((f) => __fonts.load(`16px "${f}"`).catch(() => {}))),
new Promise((r) => setTimeout(r, 2000)),
]);
}
}
// A deterministic galaxy (same clusters every run) + quiet audio in headless.
globalThis.__ORBIT_DEV_SEED = 'MINING';
const gameConfig = createGameConfig();
gameConfig.scene = [GameScene]; // GameScene boots first
if (typeof Phaser !== 'undefined') Phaser.NoAudioContext = true;
const game = new Phaser.Game(gameConfig);
window.game = game;
/**
* Frame the first cluster, open the mining menu on it, pump until the open
* tween is done, and report the panel/button geometry (px).
*/
window.__POPUP_SHOT__ = () => {
const g = window.game;
const s = g.scene.getScene('GameScene');
if (!s || !Array.isArray(s.asteroidClusters) || s.asteroidClusters.length === 0) {
return { ready: false, note: s ? 'no clusters (yet?)' : 'no scene' };
}
const c = s.asteroidClusters[0];
const m = c.members[0];
// Park the ship by the rock so the camera frames it, and centre on it.
s.ship.stop();
s.ship.x = m.wx + 34;
s.ship.y = m.wy - 22;
const cam = s.cameras.main;
cam.scrollX = m.wx - cam.width / 2;
cam.scrollY = m.wy - cam.height / 2;
const rock = { cluster: c, member: m, x: m.wx, y: m.wy };
s.openMiningMenu(rock, { x: cam.width / 2, y: m.wy - cam.scrollY });
// Pump until the open tween (150 ms) is done — this box freezes rAF.
for (let i = 0; i < 300 && s.miningPopup.alpha < 0.999; i++) g.loop.step(performance.now());
const pop = s.miningPopup;
const r = {
ready: true,
fontStatus: __fonts?.status ?? 'n/a',
header: pop.headerText.text,
headerW: Math.round(pop.headerText.width),
panelW: pop.W,
panelH: pop.H,
primary: { label: pop.primaryBtn.labelText.text, labelW: Math.round(pop.primaryBtn.labelText.width), btnW: pop.primaryBtn.style.width },
cancel: { label: pop.cancelBtn.labelText.text, labelW: Math.round(pop.cancelBtn.labelText.width), btnW: pop.cancelBtn.style.width },
primaryFits: pop.primaryBtn.style.width + 24 <= pop.W,
cancelFits: pop.cancelBtn.style.width + 24 <= pop.W,
headerFits: pop.headerText.width <= pop.W - 16,
};
window.__POPUP_SHOT__RESULT__ = r;
return r;
};