feat(repair): per-node scan voicelines, refroidisseur diagnostic gates scanning -> repairing

The ebike refroidisseur diagnostic line used to fire on the
'repairing' step via EbikeRepairNarrator, AFTER the scan sequence had
already raced through every part on a fixed timer. Visually the red
broken-part highlight appeared and disappeared before the player ever
heard which part was actually broken.

Now the scan sequence itself can carry per-node voice lines via a new
optional config field on each broken part. When the scan lands on a
part that has a voice line:
- the audio is played immediately (with its subtitle);
- the red broken-part highlight is on screen the entire time
  (existing cumulative highlight behaviour from RepairScanSequence);
- the next-part advance is gated on the audio's  event;
- a 15s ceiling fallback (and per-part fallback when manifest is
  missing) keeps the flow from stalling if the audio never resolves.

Cancel paths (component unmount, mission switch, debug rewind) pause
the audio, clear the subtitle, and drop both the  listener and
the fallback timer to avoid leaks or double-advances.

Changes:
- types/repairMission: new optional .
- data/repairMissions: ebike refroidisseur broken part now declares
  .
- RepairScanSequence: per-part effect now branches on .
  Default per-part timer is preserved for parts without an audio line
  (incl. all pylon/farm broken parts and ebike's non-diagnostic parts).
- EbikeRepairNarrator: drop the 'repairing' entry from the step ->
  dialogue map (the diagnostic now plays earlier, during scanning).
  'fragmented' (scan hint) and 'done' (repaired voiceover) are
  unchanged.

End result: player hears 'le refroidisseur a laché...' exactly while
the red sphere is pulsing on the cooling node, and the case opens for
the repairing step the moment the line ends.
This commit is contained in:
Tom Boullay
2026-06-03 04:20:14 +02:00
parent 317db48bcc
commit 9841b14388
4 changed files with 106 additions and 4 deletions
+4
View File
@@ -4,6 +4,7 @@ import type {
RepairMissionId,
} from "@/types/gameplay/repairMission";
import {
EBIKE_DIAGNOSTIC_DIALOGUE_ID,
EBIKE_WORLD_ROTATION_Y,
EBIKE_WORLD_SCALE,
} from "@/data/ebike/ebikeConfig";
@@ -39,6 +40,9 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
nodeName: "refroidisseur",
targetNodeName: "refroidisseur",
caseSlotName: "placeholder_1",
// Plays during the scan landing on the refroidisseur node;
// the scan sequence advances on this audio's `ended` event.
voiceLineId: EBIKE_DIAGNOSTIC_DIALOGUE_ID,
},
],
replacementParts: [