Warp Puddle Effect

Warp puddles are world features — circular zones scattered on terrain — that override ship stats while the player ship is inside them. The effect ramps with a warpT value (0 outside, 1 fully inside) and snaps back to base on exit. They are timed buffs gated on location, distinct from artifacts (permanent run-long passives picked up once).

Status

Disabled. WARP_PUDDLES_ENABLED = false since 2026-04-21. Nate parked the experiment; code paths are kept in-repo for future iteration. No puddles spawn, no groups form, no collision tests fire, no rendering happens while the flag is off.

Stat patches while inside

Applied per-frame in applyWarpPuddleOverrides(ship) (engine/world/warp-puddle-effects.ts). All writes are absolute, from ship._base, not multiplicative compounding — at warpT = 0 the multiplier is 1.0 so values return to base on exit (idempotent).

StatOverride at warpT = 1Formula
thrust+25%base.thrust * (1 + (1.25 - 1) * t)
heatBurnRate0 (no heat builds)base.heatBurnRate * (1 - t)
heat (dynamic state)drains toward 0heat *= (1 - t) per frame while t > 0
Camera zoom+20%Handled in camera.ts

THRUST_MULT_MAX = 1.25 is the tunable cap. Earlier versions read +5% in comments but the constant is the source of truth.

Accumulation safety

Previous versions multiplied ship.thrust *= N each frame. Because Modifiers.recalc(ship, _base) only runs on dirty frames, the multiplier compounded exponentially — after ~30 frames the ship moved 20× normal speed and never came back on exit. Current model writes absolute values from _base every frame, which is idempotent. Requires ship._base.thrust and ship._base.heatBurnRate to be snapshotted at mission start (see bridge.ts ~line 540).

Visual signature

Magic purple zones (per file header). Simplified rendering model from v5.95+: plain circles, not fbm-perturbed ellipses. Earlier fbm-perturbed edges:

  • Needed an identical fbm eval on every collision test and shader pixel.
  • Made overlap detection gnarly.
  • Didn’t look demonstrably better than a ringed circle with a glow.

Each puddle carries a colorSeed (1–101) so neighboring puddles don’t lockstep their color/animation phase. Overlapping puddles render as a union (fill all member circles, stroke only the outer edge).

Spawn rules

Per-hub spawning in spawnPuddlesForHub(hubId, hubX, hubY, hubRadius, rng). Deterministic via the rng passed in.

ConstantValueMeaning
ANY_PUDDLE_CHANCE0.30Chance any given hub spawns at least one puddle.
SECOND_PUDDLE_CHANCE0.10Given a hub spawns one, chance it spawns a second.
MIN_RADIUS120 pxLower bound on puddle radius.
MAX_RADIUS280 pxUpper bound (deliberately smaller than v1’s 200–600).
MIN_PUDDLE_SEPARATION_MULT1.1Min separation between two puddles on the same hub, as multiples of the larger puddle’s radius. Allows overlap so grouping kicks in.

Per puddle: 12 placement attempts at random offsets within hubRadius * 0.5 of the hub center, picking the first that satisfies the separation constraint. Returns empty array when the feature flag is off, regardless of rng.

Stable id format: ${hubId}:wp${i}.

Grouping (union-find merge)

Overlapping puddles merge into groups at level-gen time via union-find (groupPuddles(puddles)):

  • Two puddles are “merged” if centerDist² <= (r1 + r2)² — pure circle overlap.
  • O(n²) overlap check is fine: n is small (a few dozen per level max).
  • Each group gets an axis-aligned bbox for coarse culling.
  • Group id format: wg:${firstMember.id}.

A group is the collision + rendering unit. When the player enters any member circle, ship.warpGroupId = group.id for as long as they stay inside any member.

Containment tests

  • puddleContainsPoint(p, px, py) — cheap circle test, used by collision and rendering mask.
  • groupContainsPoint(g, px, py) — coarse bbox reject, then linear member scan.
  • findContainingGroup(groups, px, py) — linear across groups (fine for ~10 visible groups per frame).

Distinct from artifacts

Warp puddleArtifact
TriggerLocation (ship inside zone)One-time pickup
DurationWhile inside; ramps with warpT, snaps back on exitWhole run after pickup
StackingSingle zone overrides; absolute writes from _baseRun-long passive on ship._base
Source fileengine/world/warp-puddles.ts, engine/world/warp-puddle-effects.tsengine/world/artifacts.ts
Current statusDisabled (flag off)Active

Source files

  • src/starship-survivors/engine/world/warp-puddles.ts — spawn, grouping, geometry, feature flag.
  • src/starship-survivors/engine/world/warp-puddle-effects.ts — per-frame stat overrides while inside.