refactor(ebike): drop redundant 'locked' substate, single entry trigger

The ebike mission previously had two redundant entry-point sub-states
('locked' and 'waiting') that were behaviorally identical from the
player's perspective:
- both showed the same 'Lancer le Repair Game' prompt
- both allowed the press-E handler in Ebike.tsx to jump to 'inspected'

In addition, the locked state caused two latent bugs:
- the static-map ebike node (GameMap) and the live <Ebike> component
  were rendered simultaneously at the same world position
- a generic RepairMissionTrigger anchor sphere was rendered in
  parallel to Ebike's own InteractableObject (two triggers, same area)

Changes:
- useGameStore: ebike's initial currentStep + completeIntroState target
  is now 'waiting' (pylon/farm still init at 'locked' — they need it).
- Ebike.tsx: drop dead === 'locked' branches in repairGameOwnsEbikeModel
  and the press-E handler.
- EbikeRepairNarrator: only reset the played-set on 'waiting'.
- RepairGame: drop 'locked' from the ebike livePosition guard.
- REPAIR_MISSION_TRIGGERS: empty array (the duplicate ebike anchor
  sphere is gone). Keep the array + RepairMissionTrigger component for
  future re-use.
- GameMap: hide the static-map ebike node as soon as
  mainState === 'ebike' (was: only when ebikeStep !== 'locked').
- repairMissionState.getPreviousMissionStep: ebike rewinds from
  'waiting' to 'waiting' (cap), pylon to 'npc-return', farm to 'locked'.

The 'locked' value is intentionally kept in the MissionStep type union
because the farm mission still uses it as a meaningful kickoff state
driving FarmNarrativeFlow's auto-transition to electricienne_history.
This commit is contained in:
Tom Boullay
2026-06-03 04:02:32 +02:00
parent 5ad2e27a89
commit acdcb5515b
7 changed files with 27 additions and 23 deletions
+1 -5
View File
@@ -73,7 +73,6 @@ export function Ebike({
const updateEbikeSounds = useEbikeSounds();
const repairGameOwnsEbikeModel =
mainState === "ebike" &&
ebikeStep !== "locked" &&
ebikeStep !== "waiting" &&
ebikeStep !== "inspected";
@@ -362,10 +361,7 @@ export function Ebike({
if (window.ebikeBreakdownActive === true) return;
if (movementMode === "walk") {
if (
mainState === "ebike" &&
(ebikeStep === "locked" || ebikeStep === "waiting")
) {
if (mainState === "ebike" && ebikeStep === "waiting") {
setMissionStep("ebike", "inspected");
return;
}
+3 -3
View File
@@ -17,8 +17,8 @@ import { playDialogueById } from "@/utils/dialogues/playDialogue";
* - `done` -> "Eeeet voilà! Il fonctionne comme une horloge!..."
*
* Each cue is one-shot per mission run; the played-set resets when the
* mission state rolls back to `locked`/`waiting` so debug-panel replays
* still trigger the narration.
* mission state rolls back to `waiting` so debug-panel replays still
* trigger the narration.
*
* Audio AND subtitles are strictly scoped to `mainState === "ebike"`. If
* the player leaves the ebike main state mid-line (debug panel jump,
@@ -46,7 +46,7 @@ export function EbikeRepairNarrator(): null {
const activeAudioRef = useRef<HTMLAudioElement | null>(null);
useEffect(() => {
if (ebikeStep === "locked" || ebikeStep === "waiting") {
if (ebikeStep === "waiting") {
playedRef.current.clear();
}
}, [ebikeStep]);
+2 -2
View File
@@ -74,13 +74,13 @@ export function RepairGame({
readonly RepairScannedBrokenPart[]
>([]);
// For the ebike mission, use the bike's live parked world position once
// the repair flow leaves the waiting/locked phase so the repair happens
// the repair flow leaves the waiting phase so the repair happens
// wherever the player parked the bike, not at the static zone anchor.
// window.ebikeParkedPosition is set by Ebike when the player drops the
// bike and stays stable through the rest of the repair flow.
const livePosition = useMemo<Vector3Tuple>(() => {
if (mission !== "ebike" || mainState !== mission) return position;
if (step === "locked" || step === "waiting") return position;
if (step === "waiting") return position;
const parked = window.ebikeParkedPosition;
if (!parked) return position;
return [parked[0], parked[1], parked[2]];