221 lines
8.3 KiB
JavaScript
221 lines
8.3 KiB
JavaScript
/**
|
|
* Dev: the deep-space station VARIANTS (spacestations.png, frames 0..2)
|
|
* end-to-end — the sprite in-world, the comms panel (the variant label,
|
|
* REQUEST LANDING live), and the station landing flow (the ss-land /
|
|
* ss-takeoff clips, the silent flat deck — surface/shop clips are
|
|
* authored later):
|
|
*
|
|
* world — a deep-space station in its system, framed (the variant art
|
|
* the spread pass assigned it — settlement.stationFrame)
|
|
* panel — the comms panel on that station (variant label + REQUEST
|
|
* LANDING enabled)
|
|
* land — startLanding through the scene's real path (SurfaceScene up,
|
|
* the station's land clip queued under the station key)
|
|
* deck — the surface deck on a station (no surface clip — the flat
|
|
* deck; SHOP is a "no shop" note)
|
|
*
|
|
* The starting system (VESTRA — Oribreicor) holds a deep-space station
|
|
* ("Terminal 11", variant 1 — the smuggler's bar), so all four states are
|
|
* one system.
|
|
*
|
|
* node dev/server.mjs 8080
|
|
* node dev/wdshot.mjs http://localhost:8080/dev/station-shot.html /tmp/station-world.png \
|
|
* 'return await window.__STATION_SHOT__("world");' 40000 5000
|
|
* node dev/wdshot.mjs http://localhost:8080/dev/station-shot.html /tmp/station-panel.png \
|
|
* 'return await window.__STATION_SHOT__("panel");' 40000 5000
|
|
* node dev/wdshot.mjs http://localhost:8080/dev/station-shot.html /tmp/station-land.png \
|
|
* 'return await window.__STATION_SHOT__("land");' 40000 5000
|
|
* node dev/wdshot.mjs http://localhost:8080/dev/station-shot.html /tmp/station-deck.png \
|
|
* 'return await window.__STATION_SHOT__("deck");' 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';
|
|
import { SurfaceScene } from '../js/scenes/SurfaceScene.js';
|
|
|
|
const data = await ConfigLoader.load();
|
|
config.init(data);
|
|
|
|
// The game measures its UI text in the web font — 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)),
|
|
]);
|
|
}
|
|
}
|
|
|
|
// The deterministic starting system (VESTRA — Oribreicor holds a
|
|
// deep-space station — variant 1, "Terminal 11"). A different starting
|
|
// system (and thus station variant) can be pinned per run:
|
|
// http://localhost:8080/dev/station-shot.html?seed=probe-0 (variant 0)
|
|
// http://localhost:8080/dev/station-shot.html?seed=probe-25 (variant 2)
|
|
globalThis.__ORBIT_DEV_SEED =
|
|
(globalThis.location?.search && new URLSearchParams(globalThis.location.search).get('seed')) || 'VESTRA';
|
|
const gameConfig = createGameConfig();
|
|
gameConfig.scene = [GameScene, SurfaceScene]; // GameScene boots first
|
|
if (typeof Phaser !== 'undefined') Phaser.NoAudioContext = true;
|
|
|
|
const game = new Phaser.Game(gameConfig);
|
|
window.game = game;
|
|
|
|
/**
|
|
* Drive the station-variant states through the scene's real paths and
|
|
* report a result object for dev/wdshot.mjs to print.
|
|
*/
|
|
window.__STATION_SHOT__ = async (which = 'world') => {
|
|
const g = window.game;
|
|
const s = g.scene.getScene('GameScene');
|
|
if (!s || !s.systemStations) return { ready: false, note: s ? 'scene not ready (yet?)' : 'no scene' };
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
|
// ---- The station (the system's deep-space station) -------------------
|
|
const st = s.systemStations.find((o) => o.kind === 'deepSpaceStation') ?? s.systemStations[0] ?? null;
|
|
if (!st) return { ready: false, note: 'no free-space station in this system' };
|
|
|
|
// ---- Camera: frame the station at screen (640, 430) ------------------
|
|
const sx = 640;
|
|
const sy = 430;
|
|
s.ship.stop();
|
|
s.hideHint?.();
|
|
const cam = s.cameras.main;
|
|
|
|
if (which === 'world') {
|
|
// Park the ship just off the rim (it was flying there), lower-right.
|
|
const rr = st.radius + 70;
|
|
s.ship.x = st.x + rr * Math.cos(0.5);
|
|
s.ship.y = st.y + rr * Math.sin(0.5);
|
|
cam.scrollX = st.x - sx;
|
|
cam.scrollY = st.y - sy;
|
|
s.cameraFollowShip = false;
|
|
for (let i = 0; i < 200; i++) g.loop.step(performance.now());
|
|
cam.scrollX = st.x - sx;
|
|
cam.scrollY = st.y - sy;
|
|
const r = {
|
|
ready: true,
|
|
which,
|
|
station: st.discoveryName,
|
|
kind: st.kind,
|
|
size: st.size,
|
|
sheetFrame: st.sheetFrame,
|
|
variantLabel: config.get(`stations.typeLabels.${st.sheetFrame}`),
|
|
childType: st.list?.[0]?.type ?? st.list?.[0]?.constructor?.name,
|
|
childTexture: st.list?.[0]?.texture?.key ?? null,
|
|
childScale: st.list?.[0]?.scale,
|
|
comms: s.commsTargetFor(st),
|
|
};
|
|
window.__STATION_SHOT__RESULT__ = r;
|
|
return r;
|
|
}
|
|
|
|
if (which === 'panel') {
|
|
s.ship.x = st.x + (st.radius + 70) * Math.cos(0.5);
|
|
s.ship.y = st.y + (st.radius + 70) * Math.sin(0.5);
|
|
cam.scrollX = st.x - sx;
|
|
cam.scrollY = st.y - sy;
|
|
s.openCommsPanel(st, { worldX: st.x, worldY: st.y, x: sx, y: sy });
|
|
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 = st.x - sx;
|
|
cam.scrollY = st.y - sy;
|
|
const cp = s.commsPanel;
|
|
const r = {
|
|
ready: true,
|
|
which,
|
|
station: st.discoveryName,
|
|
sheetFrame: st.sheetFrame,
|
|
name: cp.nameText.text,
|
|
kindLabel: cp.settledText?.text ?? null,
|
|
settled: cp.settled,
|
|
btn1: cp.btn1.text.text,
|
|
btn1Disabled: cp.btn1.disabled,
|
|
btn2: cp.btn2.text.text,
|
|
onScreen: cp.rect
|
|
? (() => {
|
|
const lx = cp.rect.x - cam.scrollX;
|
|
const ly = cp.rect.y - cam.scrollY;
|
|
return lx >= -1 && ly >= -1 && lx + cp.rect.w <= s.scale.width + 1 && ly + cp.rect.h <= s.scale.height + 1;
|
|
})()
|
|
: false,
|
|
};
|
|
window.__STATION_SHOT__RESULT__ = r;
|
|
return r;
|
|
}
|
|
|
|
if (which === 'land') {
|
|
// The real entry point — the comms panel's own REQUEST LANDING path.
|
|
const target = s.commsTargetFor(st);
|
|
if (!target?.isStation || !target?.canLand) {
|
|
const r = { ready: false, which, target };
|
|
window.__STATION_SHOT__RESULT__ = r;
|
|
return r;
|
|
}
|
|
s.startLanding(target);
|
|
await sleep(300);
|
|
const ss = g.scene.getScene('SurfaceScene');
|
|
const landKey = `__surf_st_land_${ss.planetFrame}`;
|
|
const r = {
|
|
ready: true,
|
|
which,
|
|
station: st.discoveryName,
|
|
isStation: ss.isStation,
|
|
planetFrame: ss.planetFrame,
|
|
phase: ss.phase,
|
|
musicSpec: ss.musicSpec,
|
|
landKey,
|
|
landCached: ss.cache.video.has(landKey),
|
|
landVideo: ss.landVideo !== null,
|
|
gsSleeping: s.sys.isSleeping(),
|
|
};
|
|
window.__STATION_SHOT__RESULT__ = r;
|
|
return r;
|
|
}
|
|
|
|
if (which === 'deck') {
|
|
// Land, then force the deck (the station has NO surface clip — the
|
|
// flat deck is the station's surface state today).
|
|
const target = s.commsTargetFor(st);
|
|
if (!target?.isStation || !target?.canLand) {
|
|
const r = { ready: false, which, target };
|
|
window.__STATION_SHOT__RESULT__ = r;
|
|
return r;
|
|
}
|
|
s.startLanding(target);
|
|
await sleep(300);
|
|
const ss = g.scene.getScene('SurfaceScene');
|
|
ss.startSurface();
|
|
await sleep(300);
|
|
const ids = (ss.actionBar?.slots ?? []).map((slot) => slot.id);
|
|
const loopKey = `__surf_st_loop_${ss.planetFrame}`;
|
|
const r = {
|
|
ready: true,
|
|
which,
|
|
station: st.discoveryName,
|
|
isStation: ss.isStation,
|
|
planetFrame: ss.planetFrame,
|
|
phase: ss.phase,
|
|
deckSlots: ids,
|
|
loopKey,
|
|
loopCached: ss.cache.video.has(loopKey),
|
|
surfaceVideo: ss.surfaceVideo !== null,
|
|
hudName: ss.hudName?.text ?? null,
|
|
hudLine: ss.hudLine?.text ?? null,
|
|
};
|
|
window.__STATION_SHOT__RESULT__ = r;
|
|
return r;
|
|
}
|
|
|
|
const r = { ready: false, which, note: 'unknown mode' };
|
|
window.__STATION_SHOT__RESULT__ = r;
|
|
return r;
|
|
};
|