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:
Ilia Denisov
2026-05-09 23:14:40 +02:00
parent 721fa2172d
commit e4dc0ce029
25 changed files with 1056 additions and 64 deletions
+86
View File
@@ -35,6 +35,44 @@ export interface EventSigningFields {
payloadHash: Uint8Array;
}
export interface DriveEffectiveInput {
drive: number;
driveTech: number;
}
export interface ShipBlocksInput {
drive: number;
weapons: number;
armament: number;
shields: number;
cargo: number;
}
export interface WeaponsBlockInput {
weapons: number;
armament: number;
}
export interface FullMassInput {
emptyMass: number;
carryingMass: number;
}
export interface SpeedInput {
driveEffective: number;
fullMass: number;
}
export interface CargoCapacityInput {
cargo: number;
cargoTech: number;
}
export interface CarryingMassInput {
load: number;
cargoTech: number;
}
export interface Core {
/**
* signRequest returns the canonical signing input bytes for a v1
@@ -71,6 +109,54 @@ export interface Core {
payloadBytes: Uint8Array,
payloadHash: Uint8Array,
): boolean;
/**
* driveEffective wraps `pkg/calc/ship.go.DriveEffective`: effective
* drive power = ship drive block × player drive tech.
*/
driveEffective(input: DriveEffectiveInput): number;
/**
* emptyMass wraps `pkg/calc/ship.go.EmptyMass`: mass of the ship
* with empty holds. Returns null when the upstream validator
* rejects the weapons/armament pair (one zero and the other
* non-zero).
*/
emptyMass(input: ShipBlocksInput): number | null;
/**
* weaponsBlockMass wraps `pkg/calc/ship.go.WeaponsBlockMass`: mass
* of the weapons sub-block. Returns null on the same invalid
* pairing as emptyMass.
*/
weaponsBlockMass(input: WeaponsBlockInput): number | null;
/**
* fullMass wraps `pkg/calc/ship.go.FullMass`: empty mass plus the
* mass of the carried cargo.
*/
fullMass(input: FullMassInput): number;
/**
* speed wraps `pkg/calc/ship.go.Speed`: light-years per turn,
* driveEffective × 20 / fullMass; zero when fullMass ≤ 0.
*/
speed(input: SpeedInput): number;
/**
* cargoCapacity wraps `pkg/calc/ship.go.CargoCapacity`: hold
* capacity of one ship in cargo units, scaled by the player's
* cargo tech.
*/
cargoCapacity(input: CargoCapacityInput): number;
/**
* carryingMass wraps `pkg/calc/ship.go.CarryingMass`: mass of a
* payload of `load` cargo units at the player's cargo tech. Used
* by the ship-class designer to derive full-load mass from
* cargoCapacity.
*/
carryingMass(input: CarryingMassInput): number;
}
export type CoreLoader = () => Promise<Core>;
+35
View File
@@ -9,10 +9,17 @@
// served from `static/core.wasm`.
import type {
CargoCapacityInput,
CarryingMassInput,
Core,
DriveEffectiveInput,
EventSigningFields,
FullMassInput,
RequestSigningFields,
ResponseSigningFields,
ShipBlocksInput,
SpeedInput,
WeaponsBlockInput,
} from "./index";
/**
@@ -36,6 +43,13 @@ interface GalaxyCoreBridge {
payloadBytes: Uint8Array,
payloadHash: Uint8Array,
): boolean;
driveEffective(input: DriveEffectiveInput): number;
emptyMass(input: ShipBlocksInput): number | null;
weaponsBlockMass(input: WeaponsBlockInput): number | null;
fullMass(input: FullMassInput): number;
speed(input: SpeedInput): number;
cargoCapacity(input: CargoCapacityInput): number;
carryingMass(input: CarryingMassInput): number;
}
interface BridgeRequestFields {
@@ -175,6 +189,27 @@ export function adaptBridge(bridge: GalaxyCoreBridge): Core {
): boolean {
return bridge.verifyPayloadHash(payloadBytes, payloadHash);
},
driveEffective(input: DriveEffectiveInput): number {
return bridge.driveEffective(input);
},
emptyMass(input: ShipBlocksInput): number | null {
return bridge.emptyMass(input);
},
weaponsBlockMass(input: WeaponsBlockInput): number | null {
return bridge.weaponsBlockMass(input);
},
fullMass(input: FullMassInput): number {
return bridge.fullMass(input);
},
speed(input: SpeedInput): number {
return bridge.speed(input);
},
cargoCapacity(input: CargoCapacityInput): number {
return bridge.cargoCapacity(input);
},
carryingMass(input: CarryingMassInput): number {
return bridge.carryingMass(input);
},
};
}