feat(ui): Phase 29 map visibility toggles
Tests · Go / test (push) Successful in 2m31s
Tests · UI / test (push) Failing after 8m7s

Adds the gear-icon popover on the map view with per-game persistence
of every category toggle plus the wrap-mode radio. Hide-by-id and
visibility-fog facilities land on the renderer so every flip applies
within one frame without a Pixi remount; the wrap-mode toggle keeps
its existing remount + camera-preserve path. A new server-side turn
force-resets every flag to defaults so a hidden category never makes
the player miss the next turn's news.

Also fixes the FligthDistance → FlightDistance typo in pkg/calc/race.go
(plus the single Go caller); the TS side keeps duplicating the formula
until a race-level WASM bridge lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-19 21:33:53 +02:00
parent 65c0fbb87d
commit 2bd1b54936
32 changed files with 3046 additions and 63 deletions
+14 -1
View File
@@ -86,18 +86,31 @@ const HEAD_HALF_ANGLE = (25 * Math.PI) / 180;
* not present in the planet list (e.g. a destination newly
* unidentified after a turn cutoff). Pure: relies only on the
* report; no DOM access; no Pixi calls.
*
* `opts.skipPlanets` (Phase 29) is an optional set of planet numbers
* whose routes — outgoing or incoming — should be filtered out so the
* arrows do not point at hidden glyphs. Empty / undefined means no
* extra filtering, preserving the pre-Phase-29 contract.
*/
export function buildCargoRouteLines(report: GameReport): LinePrim[] {
export function buildCargoRouteLines(
report: GameReport,
opts?: { skipPlanets?: ReadonlySet<number> },
): LinePrim[] {
if (report.routes.length === 0) return [];
const skip = opts?.skipPlanets;
const planetById = new Map<number, ReportPlanet>();
for (const planet of report.planets) {
planetById.set(planet.number, planet);
}
const lines: LinePrim[] = [];
for (const route of report.routes) {
if (skip !== undefined && skip.has(route.sourcePlanetNumber)) continue;
const source = planetById.get(route.sourcePlanetNumber);
if (source === undefined) continue;
for (const entry of route.entries) {
if (skip !== undefined && skip.has(entry.destinationPlanetNumber)) {
continue;
}
const dest = planetById.get(entry.destinationPlanetNumber);
if (dest === undefined) continue;
const dx = torusShortestDelta(source.x, dest.x, report.mapWidth);