diff --git a/assets/videos/rocky-shop-02.mp4 b/assets/videos/rocky-shop-02.mp4 new file mode 100644 index 0000000..566f7de Binary files /dev/null and b/assets/videos/rocky-shop-02.mp4 differ diff --git a/dev/dialog-shot.html b/dev/dialog-shot.html new file mode 100644 index 0000000..ac3d40a --- /dev/null +++ b/dev/dialog-shot.html @@ -0,0 +1,17 @@ + + + + + + Orbit — Map confirm (dev probe) + + + + +
+ + + diff --git a/dev/dialog-shot.mjs b/dev/dialog-shot.mjs new file mode 100644 index 0000000..c27579e --- /dev/null +++ b/dev/dialog-shot.mjs @@ -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 }; +}