refactor(repair): unify exploded model across phases, simplify ebike flow

- RepairGame: lift a single ExplodableModel mounted across fragmented
  -> done so the model loads once, animates from its real original
  positions, and never re-instantiates between phases. Eliminates the
  position/rotation jumps and re-explosion that occurred when each
  step instantiated its own model.
- ExplodableModel: expose splitSpeed prop so the explode/reassemble
  lerp can be slowed down (REPAIR_FRAGMENT_SPLIT_SPEED = 1.8) for a
  more deliberate visual where each node is seen leaving its origin.
- RepairScanSequence: drop its own ExplodableModel, receive parts
  from the upstream shared instance. Logs the available part names
  when broken-part nodes can't be matched so config drift is visible.
- RepairReassemblyStep: reduced to the completion particles + a
  delayed onSettled callback. The collapse animation is now driven by
  the shared ExplodableModel switching split=false at the reassembling
  phase. After REPAIR_REASSEMBLY_HOLD_MS (1500ms) the upstream flow
  auto-advances to done.
- RepairEbikeRepairTrigger: new minimal interactable for the ebike
  repairing step. Replaces the heavier grabbable-parts UX (cercles,
  ranger pieces) with a single 'Changez le refroidisseur' prompt that
  advances directly to reassembling. Pylon/farm keep RepairRepairingStep.
- RepairCompletionStep: drop the duplicated RepairObjectModel; the
  shared ExplodableModel renders the repaired model at done.
- RepairGame ebike-done: play narrateur_ebikerepare and call
  completeMission on the audio's ended event (with REPAIR_DONE_DIALOGUE_FALLBACK_MS
  fallback). Hands off to pylon without a Validate button.
- EbikeRepairNarrator: drop the done entry; RepairGame owns it now so
  the audio's end event can drive the mission completion handoff.
- RepairGame: drop the window.ebikeParkedPosition livePosition logic.
  Ebike movement is disabled during the repair flow so the static zone
  position is the source of truth, fixing the floating-bike issue
  observed in TestMap.
This commit is contained in:
Tom Boullay
2026-06-03 06:21:29 +02:00
parent 9841b14388
commit 5a6596b755
8 changed files with 279 additions and 110 deletions
@@ -0,0 +1,34 @@
import { TriggerObject } from "@/components/three/interaction/TriggerObject";
import { REPAIR_INTERACTION_RADIUS } from "@/data/gameplay/repairGameConfig";
import type { Vector3Tuple } from "@/types/three/three";
interface RepairEbikeRepairTriggerProps {
onRepair: () => void;
}
const TRIGGER_POSITION: Vector3Tuple = [0, 1.4, 0];
/**
* Minimal interactable used for the ebike `repairing` step. Replaces
* the heavier RepairRepairingStep (grabbable parts + placeholder
* circles) with a single "Changez le refroidisseur" prompt. The
* collider is invisible — the player just walks up and presses E.
*/
export function RepairEbikeRepairTrigger({
onRepair,
}: RepairEbikeRepairTriggerProps): React.JSX.Element {
return (
<TriggerObject
position={TRIGGER_POSITION}
colliders="ball"
label="Changez le refroidisseur"
radius={REPAIR_INTERACTION_RADIUS}
onTrigger={onRepair}
>
<mesh>
<sphereGeometry args={[0.6, 16, 16]} />
<meshBasicMaterial colorWrite={false} depthWrite={false} />
</mesh>
</TriggerObject>
);
}