fix(ui-map): repaint fog as layered overpaint; rename to visibleHyperspace
Tests · UI / test (push) Waiting to run

The Phase 29 fog overlay rendered as a handful of random arc
segments instead of a clean union of holes around LOCAL planets
— Pixi v8's `Graphics.cut()` does not reliably subtract multiple
overlapping circles from a base path.

Replaced the cut-based approach with a layered overpaint: a
fog-tinted rectangle fills the world, then opaque background-
coloured circles are painted on top for every visibility circle.
The natural rendering order unions overlapping circles for free —
no geometry, no `cut()` quirks, one extra fill per circle.

Renamed the toggle from `visibilityFog` to `visibleHyperspace`
across the store, i18n strings, popover, tests, and docs. The
overlay still implements the visual "fog" effect at the renderer
level (FOG_COLOR, setVisibilityFog, getMapFog); the toggle is
named after the player-facing concept it controls — the portion
of the map that is visible (intelligence/scan coverage) — rather
than the obscured part.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-19 23:39:39 +02:00
parent 2f4dc01d54
commit 37580b7699
16 changed files with 86 additions and 65 deletions
+1 -1
View File
@@ -121,7 +121,7 @@ with the new mode.
Phase 29 adds a `mapToggles: MapToggles` rune that drives the
gear popover in the map view. Every flag defaults to `true`
including `unreachablePlanets` (showing every planet by default)
and `visibilityFog` (the fog overlay on by default). The
and `visibleHyperspace` (the fog overlay on by default). The
exhaustive shape lives in `src/lib/game-state.svelte.ts`; the
gear popover (`src/lib/active-view/map-toggles.svelte`) is a
thin view of the rune.
+17 -12
View File
@@ -292,20 +292,25 @@ the set from the per-game `MapToggles` rune + the planet-cascade
rule and pushes it on every effect run; toggling a checkbox
flips visibility within one frame without a Pixi remount.
## Visibility fog
## Visible-hyperspace overlay (the "fog")
`RendererHandle.setVisibilityFog(circles)` draws (or removes) the
Phase 29 fog overlay. Each entry describes a circle around a
LOCAL planet where the player has scanner / visibility coverage:
Phase 29 fog overlay used to highlight the player's visible
hyperspace. Each entry describes a circle around a LOCAL planet
where the player has scanner / visibility coverage:
- An empty list destroys the existing fog Graphics.
- A non-empty list creates one fog `Graphics` per torus copy.
Each fills the world rectangle with `FOG_COLOR` (two shades
lighter than the dark theme background) and "cuts" every
circle out of it via Pixi v8's `Graphics.cut()` path operator,
so overlapping circles compose into a union hole (no
even-odd-fill quirks). The fog is inserted at the bottom of
each copy's z-order so primitives paint on top.
Each draws a world-sized rectangle filled with `FOG_COLOR` (two
shades lighter than the dark theme background), then paints an
opaque background-coloured circle on top for every visibility
circle. The overpaint order naturally unions overlapping circles
— earlier iterations used Pixi v8's `Graphics.cut()` to subtract
holes, but `cut()` produces incorrect unions for multiple
overlapping holes; layered repainting trades one extra fill per
circle for a predictable, geometry-free union.
- The fog is inserted at the bottom of each copy's z-order so
primitives paint on top.
- The fog never participates in hit-test. Planet glyphs sit on
top of fog, so clicks on visible planets work unchanged.
- Wrap mode is honoured for free — `applyMode` hides every
@@ -313,7 +318,7 @@ LOCAL planet where the player has scanner / visibility coverage:
behaviour because the fog Graphics is a child of each copy.
The map view recomputes the fog input only when the report or the
fog toggle changes — per-frame cost stays at zero.
`visibleHyperspace` toggle changes — per-frame cost stays at zero.
## Debug surface
@@ -334,9 +339,9 @@ state without scraping pixels:
- `getMapCamera()` returns the current camera + viewport +
canvas-origin snapshot, used by Phase 29 e2e specs to assert
camera preservation across wrap-mode flips.
- `getMapFog()` returns the most recent visibility-fog input
- `getMapFog()` returns the most recent fog input
(the list of circles last passed to `setVisibilityFog`).
Empty when the fog toggle is off.
Empty when the `visibleHyperspace` toggle is off.
The active map view registers providers on mount via
`registerMapPrimitivesProvider` / `registerMapPickStateProvider`