ui/phase-18: ship-class calc bridge with live designer preview
Wires pkg/calc/ship.go into the WASM Core boundary as seven thin
wrappers (DriveEffective, EmptyMass, WeaponsBlockMass, FullMass,
Speed, CargoCapacity, CarryingMass). The ship-class designer reads
Core through a new CORE_CONTEXT_KEY populated by the in-game layout
and renders a five-row preview pane (mass, full-load mass, max
speed, range at full load, cargo capacity) that updates reactively
on every form edit and on the player's localPlayer{Drive,Weapons,
Shields,Cargo} tech levels — three of which are now decoded from
the report's Player block alongside the existing localPlayerDrive.
CarryingMass is the seventh wrapper added to the original six-function
list so that "full-load mass" composes through pkg/calc/ functions
without putting math in TypeScript.
This commit is contained in:
@@ -149,15 +149,23 @@ export interface GameReport {
|
||||
*/
|
||||
routes: ReportRoute[];
|
||||
/**
|
||||
* localPlayerDrive is the local player's drive tech level. The
|
||||
* engine's reach formula is `40 * driveTech`
|
||||
* (`game/internal/model/game/race.go.FlightDistance`); the
|
||||
* cargo-route picker filters destinations through it, so the
|
||||
* value is propagated all the way through `applyOrderOverlay`
|
||||
* to the inspector subsection. Zero on boot or when the
|
||||
* report's player block is missing the local entry.
|
||||
* localPlayerDrive, localPlayerWeapons, localPlayerShields,
|
||||
* localPlayerCargo carry the local player's four tech levels,
|
||||
* read from the matching `Player` row in the report. Drive
|
||||
* powers reach (`40 * driveTech`,
|
||||
* `game/internal/model/game/race.go.FlightDistance`) and the
|
||||
* cargo-route picker; cargo feeds the ship-class designer's
|
||||
* cargo-capacity preview (`pkg/calc/ship.go.CargoCapacity` and
|
||||
* `CarryingMass`); weapons and shields are surfaced ahead of
|
||||
* Phases 19-21 (ship-group inspector, science designer) so
|
||||
* future patches do not need to re-extend the report decoder.
|
||||
* All four are zero on boot or when the report's player block
|
||||
* is missing the local entry.
|
||||
*/
|
||||
localPlayerDrive: number;
|
||||
localPlayerWeapons: number;
|
||||
localPlayerShields: number;
|
||||
localPlayerCargo: number;
|
||||
}
|
||||
|
||||
export async function fetchGameReport(
|
||||
@@ -290,7 +298,7 @@ function decodeReport(report: Report): GameReport {
|
||||
|
||||
const raceName = report.race() ?? "";
|
||||
const routes = decodeReportRoutes(report);
|
||||
const localPlayerDrive = findLocalPlayerDrive(report, raceName);
|
||||
const localTech = findLocalPlayerTech(report, raceName);
|
||||
|
||||
return {
|
||||
turn: Number(report.turn()),
|
||||
@@ -301,7 +309,10 @@ function decodeReport(report: Report): GameReport {
|
||||
race: raceName,
|
||||
localShipClass,
|
||||
routes,
|
||||
localPlayerDrive,
|
||||
localPlayerDrive: localTech.drive,
|
||||
localPlayerWeapons: localTech.weapons,
|
||||
localPlayerShields: localTech.shields,
|
||||
localPlayerCargo: localTech.cargo,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -356,24 +367,39 @@ function compareRouteEntriesByLoadType(
|
||||
return LOAD_TYPE_ORDER[a.loadType] - LOAD_TYPE_ORDER[b.loadType];
|
||||
}
|
||||
|
||||
interface LocalPlayerTech {
|
||||
drive: number;
|
||||
weapons: number;
|
||||
shields: number;
|
||||
cargo: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* findLocalPlayerDrive locates the local player's drive tech
|
||||
* level by matching `Player.name` against the report's `race`
|
||||
* field (the engine uses race name as the runtime player
|
||||
* identifier). Returns 0 when the lookup fails — boot state, an
|
||||
* findLocalPlayerTech locates the local player's four tech levels
|
||||
* by matching `Player.name` against the report's `race` field (the
|
||||
* engine uses race name as the runtime player identifier). Returns
|
||||
* a zero-filled record when the lookup fails — boot state, an
|
||||
* incomplete report, or a future schema bump that switches to
|
||||
* UUIDs. Wrapping the lookup in one helper keeps the migration
|
||||
* cost contained.
|
||||
*/
|
||||
function findLocalPlayerDrive(report: Report, raceName: string): number {
|
||||
if (raceName === "") return 0;
|
||||
function findLocalPlayerTech(
|
||||
report: Report,
|
||||
raceName: string,
|
||||
): LocalPlayerTech {
|
||||
if (raceName === "") return { drive: 0, weapons: 0, shields: 0, cargo: 0 };
|
||||
for (let i = 0; i < report.playerLength(); i++) {
|
||||
const player = report.player(i);
|
||||
if (player === null) continue;
|
||||
if ((player.name() ?? "") !== raceName) continue;
|
||||
return player.drive();
|
||||
return {
|
||||
drive: player.drive(),
|
||||
weapons: player.weapons(),
|
||||
shields: player.shields(),
|
||||
cargo: player.cargo(),
|
||||
};
|
||||
}
|
||||
return 0;
|
||||
return { drive: 0, weapons: 0, shields: 0, cargo: 0 };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user