From 3eba38a80b62691f1583317c9d6671cea95b72b2 Mon Sep 17 00:00:00 2001 From: tom-boullay Date: Wed, 27 May 2026 17:21:11 +0200 Subject: [PATCH] fix: ebike --- public/map.json | 17 +++++++++++++ scripts/transformMap.cjs | 11 +++++++++ src/data/gameplay/repairMissionAnchors.ts | 5 ++++ src/managers/stores/useGameStore.ts | 2 +- src/world/GameMap.tsx | 17 ++++++++++++- src/world/GameStageContent.tsx | 30 ++++++++++++++++++++++- 6 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 src/data/gameplay/repairMissionAnchors.ts diff --git a/public/map.json b/public/map.json index a50a559..0ad8b92 100644 --- a/public/map.json +++ b/public/map.json @@ -37602,6 +37602,23 @@ "rotation": [0, 0, 0], "scale": [1, 1, 1], "children": [ + { + "name": "ebike", + "type": "Object3D", + "role": "group", + "position": [0, 0, 0], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "children": [ + { + "name": "ebike", + "type": "Object3D", + "position": [42.2399, 4.5484, 34.6468], + "rotation": [0, 0, 0], + "scale": [1, 1, 1] + } + ] + }, { "name": "zone1_residence", "type": "Object3D", diff --git a/scripts/transformMap.cjs b/scripts/transformMap.cjs index 798a91e..7e985a5 100644 --- a/scripts/transformMap.cjs +++ b/scripts/transformMap.cjs @@ -105,6 +105,16 @@ function addRenderable(parent, objectNode, meshNode) { getOrCreateModelGroup(parent, renderable.name).children.push(renderable); } +function addStandaloneObject(rawData, parent, name) { + const node = rawData.find( + (rawNode) => rawNode?.type === "Object3D" && rawNode.name === name, + ); + + if (!node) return; + + getOrCreateModelGroup(parent, name).children.push(cloneNode(node)); +} + function addObjectsByRange(rawData, parent, start, end, allowedNames) { let currentObject = null; @@ -279,6 +289,7 @@ function transformMap() { agriculture.children.push(champs, ferme); addObjectsByRange(rawData, direction, 6, 12, DIRECTION_MESH_NAMES); + addStandaloneObject(rawData, residence, "ebike"); createResidenceZones(rawData, residence); addObjectsByRange(rawData, energie, 61, 96, new Set(["pyloneelectrique"])); addObjectsByRange(rawData, vegetation, 98, 829, VEGETATION_MESH_NAMES); diff --git a/src/data/gameplay/repairMissionAnchors.ts b/src/data/gameplay/repairMissionAnchors.ts new file mode 100644 index 0000000..3adc442 --- /dev/null +++ b/src/data/gameplay/repairMissionAnchors.ts @@ -0,0 +1,5 @@ +import type { Vector3Tuple } from "@/types/three/three"; + +export const EBIKE_REPAIR_POSITION = [ + 42.2399, 4.5484, 34.6468, +] as const satisfies Vector3Tuple; diff --git a/src/managers/stores/useGameStore.ts b/src/managers/stores/useGameStore.ts index 3a9a0f1..bbff26e 100644 --- a/src/managers/stores/useGameStore.ts +++ b/src/managers/stores/useGameStore.ts @@ -124,7 +124,7 @@ function completeIntroState(state: GameState): GameStateUpdate { }, bike: { ...state.bike, - currentStep: "waiting", + currentStep: "locked", }, }; } diff --git a/src/world/GameMap.tsx b/src/world/GameMap.tsx index 9b85441..7b498f0 100644 --- a/src/world/GameMap.tsx +++ b/src/world/GameMap.tsx @@ -19,6 +19,7 @@ import { isMapModelVisible, useMapPerformanceStore, } from "@/managers/stores/useMapPerformanceStore"; +import { useGameStore } from "@/managers/stores/useGameStore"; import { GameMapCollision } from "@/world/GameMapCollision"; import { CloudSystem } from "@/world/clouds/CloudSystem"; import { GeneratedMapNodeInstance } from "@/world/map-generated/GeneratedMapNodeInstance"; @@ -302,8 +303,12 @@ function MapNodeInstance({ node: MapNode; modelUrl: string | null; onSettled: () => void; -}): React.JSX.Element { +}): React.JSX.Element | null { const isGeneratedModel = isGeneratedMapModelName(node.name); + const mainState = useGameStore((state) => state.mainState); + const bikeStep = useGameStore((state) => state.bike.currentStep); + const hideEbikeMapModel = + node.name === "ebike" && mainState === "bike" && bikeStep !== "locked"; useEffect(() => { if (modelUrl !== null || isGeneratedModel) return; @@ -311,6 +316,16 @@ function MapNodeInstance({ onSettled(); }, [isGeneratedModel, modelUrl, onSettled]); + useEffect(() => { + if (!hideEbikeMapModel) return; + + onSettled(); + }, [hideEbikeMapModel, onSettled]); + + if (hideEbikeMapModel) { + return null; + } + if (isGeneratedModel) { return ( }> diff --git a/src/world/GameStageContent.tsx b/src/world/GameStageContent.tsx index e18be89..6b67d73 100644 --- a/src/world/GameStageContent.tsx +++ b/src/world/GameStageContent.tsx @@ -1,4 +1,6 @@ +import { InteractableObject } from "@/components/three/interaction/InteractableObject"; import { RepairGame } from "@/components/three/gameplay/RepairGame"; +import { EBIKE_REPAIR_POSITION } from "@/data/gameplay/repairMissionAnchors"; import { useGameStore } from "@/managers/stores/useGameStore"; import type { RepairMissionId } from "@/types/gameplay/repairMission"; import type { Vector3Tuple } from "@/types/three/three"; @@ -17,7 +19,7 @@ interface GameRepairZone { const GAME_REPAIR_ZONES = [ { mission: "bike", - position: [8, 0, -6], + position: EBIKE_REPAIR_POSITION, }, { mission: "pylone", @@ -48,6 +50,31 @@ function StageAnchor({ ); } +function EbikeMissionTrigger(): React.JSX.Element | null { + const mainState = useGameStore((state) => state.mainState); + const bikeStep = useGameStore((state) => state.bike.currentStep); + const setMissionStep = useGameStore((state) => state.setMissionStep); + + if (mainState !== "bike" || bikeStep !== "locked") return null; + + return ( + + setMissionStep("bike", "waiting")} + > + + + + + + + ); +} + export function GameStageContent(): React.JSX.Element { const mainState = useGameStore((state) => state.mainState); @@ -63,6 +90,7 @@ export function GameStageContent(): React.JSX.Element { position={zone.position} /> ))} + {mainState === "outro" ? ( ) : null}