/** * Visual probe for a screenshot (headless brave, via dev/cdp-shot.mjs): * boots the game, lands, opens the shop, reveals the buy/sell panel, and * CHECKS ONE CARGO BAY — bay 0 marked to sell (green stroke + tinted fill * + the green checkmark in its upper-left corner), bay 1 left unchecked so * the shot shows both states side by side. On ready it sets * window.__SHOT_READY__ with the checked state + a record of what the * checked bay drew (border/fill colors + the corner-check strokes). * * /opt/brave.com/brave/brave --headless --disable-gpu --remote-debugging-port=9333 about:blank & * python3 -m http.server 8128 * node dev/cdp-shot.mjs http://127.0.0.1:8128/dev/shop-select-shot.html \ * /tmp/shop-select.png 'window.__SHOT_READY__ && true' 90000 */ 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 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; (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) { window.__SHOT_READY__ = { error: 'no GameScene' }; return; } // The probe drives the trade panel on the home world — lift the TRADE gate // (the 'Special Delivery' capital-landing witness) so Buy/Sell is live. gs.story.capitalLanded = true; // A known ledger + cargo: TWO goods → bay 0 checked, bay 1 left unchecked // so the shot shows the selection look next to an idle bay. const goods = config.get('goods.goods', []); gs.ship.credits = 10000; gs.ship.cargo.add([goods[0].id, goods[1].id]); gs.startLanding(gs.commsTargetFor(gs.planet)); await sleep(300); const ss = game.scene.getScene('SurfaceScene'); if (!ss) { window.__SHOT_READY__ = { error: 'no SurfaceScene' }; return; } 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); await sleep(900); // the planet HUD decode ss.toggleShop(); await sleep(200); if (!ss.shopView) ss.buildShopView(); const sv = ss.shopView; if (!sv) { window.__SHOT_READY__ = { error: 'no shopView' }; return; } sv.setTab('trade'); await sleep(900); // the slide-in // CHECK bay 0 (queue one unit for sale) — the selection look is what // this shot is for. sv.sellGrid.toggleBay(0); await sleep(300); // Diagnostics: record what the (re)render of the CHECKED state draws on // the bay's plate (fill + border) and its sel graphics (the corner // check). Un-check first, then re-check, so the LAST recorded calls are // the checked look. let diag = null; const cell = sv.sellGrid.cells[0]; if (cell?.plate && cell?.sel) { diag = { plate: [], sel: [] }; const rec = (o, k, sink) => { const orig = o[k]; o[k] = (...a) => { sink.push({ k, a: a.map((v) => (typeof v === 'number' ? Math.round(v) : v)) }); return orig.apply(o, a); // record AND keep drawing }; }; for (const k of ['fillStyle', 'lineStyle']) { rec(cell.plate, k, diag.plate); rec(cell.sel, k, diag.sel); } rec(cell.sel, 'lineBetween', diag.sel); rec(cell.sel, 'fillCircle', diag.sel); sv.sellGrid.toggleBay(0); // un-check (idle render) sv.sellGrid.toggleBay(0); // re-check → the checked render (what we want) } window.__SHOT_READY__ = { checked: sv.sellGrid.getSellMap(), diag, slot: { selected: config.get('shopui.slot.selected'), selBgColor: config.get('shopui.slot.selBgColor'), check: config.get('shopui.slot.check'), }, }; })().catch((err) => { window.__SHOT_READY__ = { error: String(err && err.message || err) }; });