From 059db31c82c5d78494b0ccdc19c3430a18884db6 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Mon, 11 May 2026 13:15:16 +0200 Subject: [PATCH] update: show repair movement lock indicator --- src/components/ui/GameUI.tsx | 2 ++ .../ui/RepairMovementLockIndicator.tsx | 20 +++++++++++++ src/hooks/gameplay/useRepairMovementLocked.ts | 28 ++++++++++++++++++ src/index.css | 29 +++++++++++++++++++ src/world/player/PlayerController.tsx | 29 +------------------ 5 files changed, 80 insertions(+), 28 deletions(-) create mode 100644 src/components/ui/RepairMovementLockIndicator.tsx create mode 100644 src/hooks/gameplay/useRepairMovementLocked.ts diff --git a/src/components/ui/GameUI.tsx b/src/components/ui/GameUI.tsx index 6b3482a..9926cf5 100644 --- a/src/components/ui/GameUI.tsx +++ b/src/components/ui/GameUI.tsx @@ -2,12 +2,14 @@ import { Crosshair } from "@/components/ui/Crosshair"; import { DebugOverlayLayout } from "@/components/ui/debug/DebugOverlayLayout"; import { HandTrackingVisualizer } from "@/components/ui/HandTrackingVisualizer"; import { InteractPrompt } from "@/components/ui/InteractPrompt"; +import { RepairMovementLockIndicator } from "@/components/ui/RepairMovementLockIndicator"; export function GameUI(): React.JSX.Element { return ( <> + diff --git a/src/components/ui/RepairMovementLockIndicator.tsx b/src/components/ui/RepairMovementLockIndicator.tsx new file mode 100644 index 0000000..4f8d825 --- /dev/null +++ b/src/components/ui/RepairMovementLockIndicator.tsx @@ -0,0 +1,20 @@ +import { useCameraMode } from "@/hooks/debug/useCameraMode"; +import { useRepairMovementLocked } from "@/hooks/gameplay/useRepairMovementLocked"; + +export function RepairMovementLockIndicator(): React.JSX.Element | null { + const cameraMode = useCameraMode(); + const movementLocked = useRepairMovementLocked(); + + if (cameraMode !== "player") return null; + if (!movementLocked) return null; + + return ( +
+
+ ); +} diff --git a/src/hooks/gameplay/useRepairMovementLocked.ts b/src/hooks/gameplay/useRepairMovementLocked.ts new file mode 100644 index 0000000..11953ce --- /dev/null +++ b/src/hooks/gameplay/useRepairMovementLocked.ts @@ -0,0 +1,28 @@ +import { useGameStore } from "@/managers/stores/useGameStore"; +import type { MissionStep } from "@/types/gameplay/repairMission"; + +export function useRepairMovementLocked(): boolean { + return useGameStore((state) => { + switch (state.mainState) { + case "bike": + return isRepairMovementLocked(state.bike.currentStep); + case "pylone": + return isRepairMovementLocked(state.pylone.currentStep); + case "ferme": + return isRepairMovementLocked(state.ferme.currentStep); + case "intro": + case "outro": + return false; + } + }); +} + +function isRepairMovementLocked(step: MissionStep): boolean { + return ( + step === "inspected" || + step === "fragmented" || + step === "scanning" || + step === "repairing" || + step === "reassembling" + ); +} diff --git a/src/index.css b/src/index.css index 67f39df..25383a5 100644 --- a/src/index.css +++ b/src/index.css @@ -397,6 +397,35 @@ canvas { letter-spacing: 0.03em; } +.repair-movement-lock-indicator { + position: fixed; + top: 22px; + left: 50%; + z-index: 10; + display: inline-flex; + align-items: center; + gap: 9px; + padding: 9px 13px; + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 999px; + background: rgba(5, 9, 16, 0.72); + color: rgba(255, 255, 255, 0.88); + font-size: 12px; + font-weight: 650; + letter-spacing: 0.02em; + pointer-events: none; + transform: translateX(-50%); + backdrop-filter: blur(10px); +} + +.repair-movement-lock-indicator__dot { + width: 7px; + height: 7px; + border-radius: 999px; + background: #38bdf8; + box-shadow: 0 0 14px rgba(56, 189, 248, 0.86); +} + .scene-loading-overlay { position: fixed; inset: 0; diff --git a/src/world/player/PlayerController.tsx b/src/world/player/PlayerController.tsx index 2f045dd..f97b446 100644 --- a/src/world/player/PlayerController.tsx +++ b/src/world/player/PlayerController.tsx @@ -23,9 +23,8 @@ import { PLAYER_WALK_SPEED, PLAYER_XZ_DAMPING_FACTOR, } from "@/data/player/playerConfig"; +import { useRepairMovementLocked } from "@/hooks/gameplay/useRepairMovementLocked"; import { InteractionManager } from "@/managers/InteractionManager"; -import { useGameStore } from "@/managers/stores/useGameStore"; -import type { MissionStep } from "@/types/gameplay/repairMission"; import type { Vector3Tuple } from "@/types/three/three"; type Keys = { @@ -75,32 +74,6 @@ function setMovementKey(keys: Keys, key: string, pressed: boolean): boolean { } } -function isRepairMovementLocked(step: MissionStep): boolean { - return ( - step === "inspected" || - step === "fragmented" || - step === "scanning" || - step === "repairing" || - step === "reassembling" - ); -} - -function useRepairMovementLocked(): boolean { - return useGameStore((state) => { - switch (state.mainState) { - case "bike": - return isRepairMovementLocked(state.bike.currentStep); - case "pylone": - return isRepairMovementLocked(state.pylone.currentStep); - case "ferme": - return isRepairMovementLocked(state.ferme.currentStep); - case "intro": - case "outro": - return false; - } - }); -} - export function PlayerController({ octree, spawnPosition,