116 lines
5.1 KiB
JavaScript
116 lines
5.1 KiB
JavaScript
/**
|
|
* Dev-only SHOP PANEL geometry probe (real browser, via cdp-firefox.mjs):
|
|
* boots the game, lands on the home world, forces the surface stage, opens
|
|
* the shop, clicks "Buy/Sell", waits for the slide-in, then reports the
|
|
* on-screen geometry of the planet HUD text + the SELL/BUY panels + the
|
|
* sub-bar — so the panel's top edge vs. the planet text is verifiable
|
|
* numerically. Also sets window.__SHOP_SHOT__ so a driver can screenshot
|
|
* the moment the numbers are ready.
|
|
*
|
|
* node dev/server.mjs 8126
|
|
* node dev/cdp-firefox.mjs http://127.0.0.1:8126/dev/shop-shot.html \
|
|
* 'return window.__SHOP_SHOT__;' 60000
|
|
*/
|
|
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';
|
|
import { SurfaceScene } from '../js/scenes/SurfaceScene.js';
|
|
|
|
const fail = (msg, extra = {}) => {
|
|
window.__SHOP_SHOT__ = { pass: false, error: String(msg), ...extra };
|
|
console.error('[shop-shot] FAIL:', msg);
|
|
};
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
|
const data = await ConfigLoader.load();
|
|
config.init(data);
|
|
const gameConfig = createGameConfig();
|
|
gameConfig.scene = [GameScene, SurfaceScene];
|
|
const game = new Phaser.Game(gameConfig);
|
|
window.game = game;
|
|
|
|
const bounds = (o) => {
|
|
if (!o) return null;
|
|
const wb = o.worldBounds ?? (o.getBounds && o.getBounds());
|
|
if (wb && Number.isFinite(wb.x)) {
|
|
return { x: Math.round(wb.x), y: Math.round(wb.y), w: Math.round(wb.width), h: Math.round(wb.height) };
|
|
}
|
|
return { x: Math.round(o.x ?? 0), y: Math.round(o.y ?? 0) };
|
|
};
|
|
|
|
(async () => {
|
|
let gs = null;
|
|
for (let i = 0; i < 300; i++) {
|
|
gs = game.scene?.getScene('GameScene') ?? null;
|
|
if (gs && gs.ship) break;
|
|
await sleep(100);
|
|
}
|
|
if (!gs || !gs.ship) throw new Error('GameScene never came up');
|
|
|
|
// Land on the home world + force the surface stage (skip the clip).
|
|
const target = gs.commsTargetFor(gs.planet);
|
|
gs.startLanding(target);
|
|
await sleep(300);
|
|
const ss = game.scene.getScene('SurfaceScene');
|
|
if (!ss) throw new Error('SurfaceScene never registered');
|
|
// Wait for the scene to finish loading (this build: RUNNING = 5) before
|
|
// driving it.
|
|
for (let i = 0; i < 120 && ss.sys.getStatus() !== 5; i++) await sleep(250);
|
|
ss.startSurface();
|
|
for (let i = 0; i < 40 && ss.phase !== 'surface'; i++) await sleep(100);
|
|
if (ss.phase !== 'surface') throw new Error('surface stage never reached (phase=' + ss.phase + ')');
|
|
await sleep(300);
|
|
|
|
// Give the hold a couple of goods so the SELL grid shows filled bays.
|
|
const goods = config.get('goods.goods', []);
|
|
gs.ship.cargo.add([goods[0].id, goods[1].id]);
|
|
|
|
// Open the shop the real way; if the shop clip is missing in this
|
|
// environment (headless video), build the view directly (same UI path).
|
|
ss.toggleShop();
|
|
await sleep(200);
|
|
if (!ss.shopView) ss.buildShopView();
|
|
const sv = ss.shopView;
|
|
if (!sv) throw new Error('no shopView');
|
|
|
|
// Click "Buy/Sell" (reveal the trade panel) and let the slide-in settle.
|
|
sv.setTab('trade');
|
|
await sleep(900);
|
|
|
|
const chain = (o) => o ? {
|
|
x: o.x, y: o.y, scaleX: o.scaleX, scaleY: o.scaleY,
|
|
ox: o.originX, oy: o.originY, dw: o.displayWidth, dh: o.displayHeight,
|
|
active: o.active, parent: o.parent ? (o.parent.isContainer ? 'container' : o.parent.type ?? String(o.parent)) : null,
|
|
} : null;
|
|
const out = {
|
|
pass: true,
|
|
screen: { w: ss.scale.width, h: ss.scale.height },
|
|
camera: { scrollX: ss.cameras?.main?.scrollX, scrollY: ss.cameras?.main?.scrollY, zoom: ss.cameras?.main?.zoom },
|
|
topY: sv.topY ?? null,
|
|
hudName: bounds(ss.hudName),
|
|
hudLine: bounds(ss.hudLine),
|
|
sellGrid: bounds(sv.sellGrid),
|
|
buyGrid: bounds(sv.buyGrid),
|
|
subBar: bounds(sv.subBar),
|
|
panel: { w: sv.sellGrid?.width ?? null, h: sv.sellGrid?.height ?? null },
|
|
chain: {
|
|
shopView: chain(sv),
|
|
content: chain(sv._content),
|
|
sellGrid: chain(sv.sellGrid),
|
|
buyGrid: chain(sv.buyGrid),
|
|
},
|
|
// Where the panel ACTUALLY renders: the plate + first cell + title are
|
|
// real children at known local coords — their world matrices are the truth.
|
|
actual: {
|
|
sellTitle_world: (() => { const t = sv.sellGrid.title; if (t?.getTopLeft) { const v = t.getTopLeft(undefined, true); return { x: Math.round(v.x), y: Math.round(v.y) }; } return null; })(),
|
|
sellCellName_world: (() => { const t = sv.sellGrid.cells?.[0]?.name; if (t?.getTopLeft) { const v = t.getTopLeft(undefined, true); return { x: Math.round(v.x), y: Math.round(v.y) }; } return null; })(),
|
|
buyTitle_world: (() => { const t = sv.buyGrid.title; if (t?.getTopLeft) { const v = t.getTopLeft(undefined, true); return { x: Math.round(v.x), y: Math.round(v.y) }; } return null; })(),
|
|
net_world: (() => { const t = sv._previewText; if (t?.getTopLeft) { const v = t.getTopLeft(undefined, true); return { x: Math.round(v.x), y: Math.round(v.y) }; } return null; })(),
|
|
},
|
|
};
|
|
window.__SHOP_SHOT__ = out;
|
|
console.info('[shop-shot] OK:', JSON.stringify(out));
|
|
})().catch((e) => fail(String(e?.message || e)));
|