PURPOSE
Apply per-frame gameplay overrides to a ship while it is inside a warp puddle. Scales thrust, suppresses heat generation, and bleeds existing heat as a function of how deep the ship is inside the puddle (ship.warpT, 0..1).
OWNS
applyWarpPuddleOverrides(ship)— the override function called each frame.THRUST_MULT_MAXconstant (1.25) — the thrust multiplier at fully-inside.- The contract that overrides are absolute writes derived from
ship._base, not multiplicative accumulations onship.
READS FROM
ship.warpT— interpolation parameter (0 = outside, 1 = fully inside).ship._base.thrust— snapshotted base thrust value.ship._base.heatBurnRate— snapshotted base heat burn rate.ship.heat— current heat state (read and rewritten).
PUSHES TO
ship.thrust— written each frame asbase.thrust * thrustMult.ship.heatBurnRate— written each frame asbase.heatBurnRate * (1 - warpT).ship.heat— multiplied by(1 - warpT)whenwarpT > 0.
DOES NOT
- Does not modify
ship._base(the snapshot is read-only here). - Does not handle camera zoom — that lives in
camera.ts. - Does not snapshot
ship._base— that is the bridge’s responsibility at mission start. - Does not detect puddle entry/exit — only consumes the already-computed
warpT. - Does not early-exit on
warpT === 0; the write path is intentionally idempotent atwarpT = 0so the ship snaps back to base on exit. - Does not call
Modifiers.recalcand does not depend on the ship being marked dirty.
Signals
None. Pure per-frame mutation of the passed-in ShipState.
Entry points
applyWarpPuddleOverrides(ship: ShipState): void— invoked by the ship/world update path each frame while warp puddles are in play.
Pattern notes
- Idempotent absolute writes: each frame writes the effective value from
_basewith the warp multiplier folded in, rather than multiplying the live value. AtwarpT = 0the multiplier is1.0, so values immediately match base on exit — no stuck state. - Historical bug fixed by this pattern: a previous version used
ship.thrust *= Nper frame. BecauseModifiers.recalc(ship, _base)only runs on dirty frames, the multiply compounded across non-dirty frames, producing ~20× speed after ~30 frames with no recovery on exit. - Defensive abort: if
ship._basehas not yet been snapshotted, the function returns without touching the ship. - Type guards (
typeof base.thrust === 'number') gate each write so missing base fields don’t writeNaN. - Thrust scaling is linear in
warpT:1 + (THRUST_MULT_MAX - 1) * warpT. - Heat-burn suppression and heat drain both use the
(1 - warpT)factor, reaching zero at fully-inside. ship.heatis treated as dynamic state (not a base stat), so a per-frame multiply is acceptable; thet > 0guard makes it a no-op outside the puddle.- Requires
bridge.tsto snapshotship._base.thrustandship._base.heatBurnRateat mission start.