orbit/dev/comms-shot.mjs

157 lines
6.1 KiB
JavaScript
Raw Permalink 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: screenshot the comms panel — in the real web font, at real panel
* geometry, in the states the player sees it:
*
* home settled (home world, pinned +20 — full red→green bar,
* REQUEST LANDING live)
* planet a settled system world with a standing set (the probe
* pins it at 8: bar lit through the red run, REQUEST
* LANDING grayed — standing ≤ 4)
* unsettled a system world with no settlement (LAND + CANCEL, no bar)
* station a free-space station (settled, neutral 0 — half bar)
*
* It calls the scene's real path (GameScene.openCommsPanel with a
* hand-built pointer), frames the click point at (640, 430) so the
* panel opens UP (the common case), and pumps until the decode +
* bar draw-in are done.
*
* python3 -m http.server 8080
* node dev/wdshot.mjs http://localhost:8080/dev/comms-shot.html /tmp/comms-home.png \
* 'window.__COMMS_SHOT__("home"); return window.__COMMS_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 objects every run) + quiet audio in headless.
// 'VESTRA's starting system has everything the panel's states need:
// settled worlds, unsettled worlds, and free-space stations.
globalThis.__ORBIT_DEV_SEED = 'VESTRA';
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 chosen object with its click point at screen (640, 430),
* open the comms panel through the scene's real path, pump until the
* name decode + the reputation bar draw-in are done, and report the
* panel's state (name, side, buttons, the lit-mark count).
*/
window.__COMMS_SHOT__ = (which = 'home') => {
const g = window.game;
const s = g.scene.getScene('GameScene');
if (!s || !s.commsPanel) return { ready: false, note: s ? 'no commsPanel (yet?)' : 'no scene' };
s.ship.stop();
s.hideHint?.();
// ---- Pick the object -------------------------------------------------
let obj = null;
let note = '';
const settledPlanet = () => {
for (const p of s.systemPlanets) {
const rec = (s.systemContent.planets ?? []).find((r) => r.name === p.discoveryName);
if (rec && (s.systemContent.settlements ?? []).some((x) => x.anchor?.type === 'planet' && x.anchor?.ordinal === rec.ordinal)) return p;
}
return null;
};
const unsettledPlanet = () => {
for (const p of s.systemPlanets) {
const rec = (s.systemContent.planets ?? []).find((r) => r.name === p.discoveryName);
if (rec && !(s.systemContent.settlements ?? []).some((x) => x.anchor?.type === 'planet' && x.anchor?.ordinal === rec.ordinal)) return p;
}
return null;
};
if (which === 'home') obj = s.planet;
else if (which === 'planet') {
obj = settledPlanet();
if (obj) {
const t = s.commsTargetFor(obj);
if (t.key) s.reputation.set(t.key, -8); // standing ≤ 4 → REQUEST LANDING grayed
}
note = obj ? '' : 'no settled planet in this system';
} else if (which === 'unsettled') {
obj = unsettledPlanet();
note = obj ? '' : 'no unsettled planet in this system';
} else if (which === 'station') {
obj = s.systemStations?.[0] ?? null;
note = obj ? '' : 'no free-space station in this system';
}
if (!obj) return { ready: false, note: note || 'no object' };
// ---- Frame it: click point at screen (640, 430) → panel opens UP -----
const wx = obj.x;
const wy = obj.y;
const sx = 640;
const sy = 430;
// Park the ship just off the rim (it was flying there), lower-right.
const rr = obj.radius + 70;
s.ship.x = wx + rr * Math.cos(0.5);
s.ship.y = wy + rr * Math.sin(0.5);
const cam = s.cameras.main;
cam.scrollX = wx - sx;
cam.scrollY = wy - sy;
// ---- Open through the scene's real path ------------------------------
s.openCommsPanel(obj, { worldX: wx, worldY: wy, x: sx, y: sy });
// Freeze the camera follow (the shot, not the flight, is under test),
// then pump until the decode + bar draw-in + the open tween are done —
// this box freezes rAF (same trick as popup-shot).
s.cameraFollowShip = false;
const done = () => s.commsPanel.alpha >= 0.999 && s.commsPanel.nameDec === null && s.commsPanel.repRevealT0 === null;
for (let i = 0; i < 6000 && !done(); i++) g.loop.step(performance.now());
cam.scrollX = wx - sx;
cam.scrollY = wy - sy;
const cp = s.commsPanel;
const r = {
ready: true,
fontStatus: __fonts?.status ?? 'n/a',
which,
note,
object: obj.discoveryName,
side: cp.side,
name: cp.nameText.text,
settled: cp.settled,
H: cp.H,
btn1: cp.btn1.text.text,
btn1Disabled: cp.btn1.disabled,
btn2: cp.btn2.text.text,
repValue: cp.repValue.visible ? cp.repValue.text : null,
rect: cp.rect && { x: Math.round(cp.rect.x), y: Math.round(cp.rect.y), w: cp.rect.w, h: cp.rect.h },
onScreen: cp.rect
? (() => {
const cam2 = s.cameras.main;
const lx = cp.rect.x - cam2.scrollX;
const ly = cp.rect.y - cam2.scrollY;
return lx >= -1 && ly >= -1 && lx + cp.rect.w <= s.scale.width + 1 && ly + cp.rect.h <= s.scale.height + 1;
})()
: false,
};
window.__COMMS_SHOT__RESULT__ = r;
return r;
};