The fragmented -> scanning transition used to fire on a blind
setTimeout of REPAIR_FRAGMENTATION_SEQUENCE_SECONDS regardless of
whether the explode lerp had actually finished. With the new sphere-
reveal flow this got out of sync (sphere grows for 2.5s before
fragmented even mounts), so the timer could fire too early or while
the parts were still flying out.
Now the ExplodedModel emits a single 'settled' callback when its
internal lerp converges on its target (1 = fully exploded, 0 = fully
reassembled). RepairGame listens for settled-at-1 on the fragmented
ExplodableModel and advances to scanning on that event.
The legacy timer is kept as a generous safety net
(REPAIR_FRAGMENTATION_SEQUENCE_SECONDS + 2 seconds) so that if the
model fails to load (no parts -> no settled event ever fires) the
flow can never get stuck on the fragmented step.
Changes:
- ExplodedModel.ts:
- new ExplodedModelOptions.onSettled: (settledAt: 0 | 1) => void
- track settledAtTarget to ensure the callback fires exactly once
per lerp (re-armed when setSplit() flips the target).
- ExplodableModel.tsx: new onSplitSettled prop, forwarded to the
underlying ExplodedModel via a stable useCallback that reads the
latest prop through a ref so the instance is not recreated mid-anim.
- RepairGame.tsx:
- wire onSplitSettled on the fragmented ExplodableModel to
setMissionStep(mission, 'scanning').
- keep the existing setTimeout but extend it as a fallback only.
Pylon and farm benefit from the same fix automatically since they
share the same RepairGame fragmented branch.
Render the exploded mission model during the repairing step so broken
nodes (e.g. ebike refroidisseur) stay visible in the world, and surface
their world positions to RepairRepairingStep so broken pieces spawn
from where they belong on the model rather than from a static offset.
- ExplodableModel: add hideNodeNames (mesh visibility off, restored on
unmount) and nodeAnchorNames + onNodeAnchorsChange (per-frame world
positions debounced via signature so React state updates only when
the values actually move).
- RepairGame: render ExplodableModel during 'repairing' with the broken
node names hidden + anchors forwarded; threads brokenAnchors state
to RepairRepairingStep.
- RepairScanSequence + RepairRepairingStep: propagate targetNodeName
through scanned and fallback broken-part lists.
- RepairRepairingStep: broken parts spawn at brokenAnchors[targetNodeName]
when set, falling back to legacy BROKEN_PART_START_OFFSETS otherwise.