feat(ui): Phase 30 ship-class calculator with goal-seek and reach circles
Fuse the standalone ship-class designer (Phases 17/18) into a sidebar calculator: live mass/speed/attack/defence/bombing results, a planet build-rate readout, single-target goal-seek, a modernization-cost mode, and auto reach circles on the map for the selected planet. pkg/calc becomes the single source for the new math (no mirroring): extract BombingPower from the engine model and the per-turn ship-production loop from controller.ProduceShip into pkg/calc (engine now delegates), and add inverse goal-seek solvers in pkg/calc/solve.go. Thin-bridge the combat, planet-build, and solver functions through ui/core/calc + ui/wasm and rebuild core.wasm. Remove the standalone designer view/route; the ship-classes table and the view/bottom menus open the calculator via a shared request store. Docs: rewrite ui/PLAN.md Phase 30, adjust Phase 34 (realistic forecast + CAP/COL ownership), add ui/docs/calculator-ux.md, extend calc-bridge.md, fix navigation.md; remove ui/CALCULATOR.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,8 @@ preference the store already manages.
|
||||
} from "../../map/index";
|
||||
import { buildCargoRouteLines } from "../../map/cargo-routes";
|
||||
import { buildPendingSendLines } from "../../map/pending-send-routes";
|
||||
import { computeReachCircles } from "../../map/reach-circles";
|
||||
import { reachStore } from "$lib/calculator/reach.svelte";
|
||||
import {
|
||||
reportToWorld,
|
||||
type HitTarget,
|
||||
@@ -196,6 +198,11 @@ preference the store already manages.
|
||||
void toggles.bombingMarkers;
|
||||
void toggles.visibleHyperspace;
|
||||
|
||||
// Subscribe to the calculator's published reach so the rings
|
||||
// redraw as the design or the selected planet changes.
|
||||
void reachStore.origin;
|
||||
void reachStore.speedPerTurn;
|
||||
|
||||
// Phase 29 visibility derivation. Cargo routes and pending-
|
||||
// Send overlay are extras (no Pixi remount on flip); the
|
||||
// cascade-filtering happens here so the extras list shrinks
|
||||
@@ -219,8 +226,14 @@ preference the store already manages.
|
||||
// the visible set reliably triggers a push.
|
||||
const draftCommands = orderDraft?.commands ?? [];
|
||||
const draftStatuses = orderDraft?.statuses ?? {};
|
||||
const reachOrigin = reachStore.origin;
|
||||
const reachFingerprint =
|
||||
reachOrigin === null
|
||||
? ""
|
||||
: `${reachOrigin.x},${reachOrigin.y},${reachStore.speedPerTurn}`;
|
||||
const extrasFingerprint =
|
||||
`cr=${toggles.cargoRoutes ? "1" : "0"}|hp=${hiddenPlanetFingerprint}|` +
|
||||
`reach=${reachFingerprint}|` +
|
||||
computeRoutesFingerprint(report.routes) +
|
||||
"|" +
|
||||
computePendingSendFingerprint(draftCommands, draftStatuses);
|
||||
@@ -256,6 +269,7 @@ preference the store already manages.
|
||||
draftStatuses,
|
||||
toggles,
|
||||
hiddenPlanetNumbers,
|
||||
mode,
|
||||
),
|
||||
);
|
||||
});
|
||||
@@ -289,6 +303,7 @@ preference the store already manages.
|
||||
draftStatuses: Readonly<Record<string, string>>,
|
||||
toggles: MapToggles,
|
||||
hiddenPlanetNumbers: ReadonlySet<number>,
|
||||
mode: "torus" | "no-wrap",
|
||||
): import("../../map/world").Primitive[] {
|
||||
const skip = hiddenPlanetNumbers.size > 0 ? hiddenPlanetNumbers : undefined;
|
||||
const cargo = toggles.cargoRoutes
|
||||
@@ -300,7 +315,21 @@ preference the store already manages.
|
||||
draftStatuses,
|
||||
skip ? { skipPlanets: skip } : undefined,
|
||||
);
|
||||
return [...cargo, ...pending];
|
||||
// Reach circles published by the ship-class calculator. Empty
|
||||
// when no own planet is selected or the design is invalid, so
|
||||
// this is a no-op for the rest of the map.
|
||||
const reachOrigin = reachStore.origin;
|
||||
const reach =
|
||||
reachOrigin !== null && reachStore.speedPerTurn > 0
|
||||
? computeReachCircles(
|
||||
reachOrigin,
|
||||
reachStore.speedPerTurn,
|
||||
report.mapWidth,
|
||||
report.mapHeight,
|
||||
mode,
|
||||
)
|
||||
: [];
|
||||
return [...cargo, ...pending, ...reach];
|
||||
}
|
||||
|
||||
function applyVisibilityState(
|
||||
@@ -342,6 +371,7 @@ preference the store already manages.
|
||||
draftStatuses,
|
||||
toggles,
|
||||
hiddenPlanetNumbers,
|
||||
mode,
|
||||
),
|
||||
);
|
||||
lastExtrasFingerprint = extrasFingerprint;
|
||||
|
||||
Reference in New Issue
Block a user