Feat/map-environment #6

Merged
math-pixel merged 116 commits from feat/map-environment into develop 2026-05-29 00:00:51 +00:00
6 changed files with 79 additions and 3 deletions
Showing only changes of commit 3eba38a80b - Show all commits
+17
View File
@@ -37602,6 +37602,23 @@
"rotation": [0, 0, 0], "rotation": [0, 0, 0],
"scale": [1, 1, 1], "scale": [1, 1, 1],
"children": [ "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", "name": "zone1_residence",
"type": "Object3D", "type": "Object3D",
+11
View File
@@ -105,6 +105,16 @@ function addRenderable(parent, objectNode, meshNode) {
getOrCreateModelGroup(parent, renderable.name).children.push(renderable); 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) { function addObjectsByRange(rawData, parent, start, end, allowedNames) {
let currentObject = null; let currentObject = null;
@@ -279,6 +289,7 @@ function transformMap() {
agriculture.children.push(champs, ferme); agriculture.children.push(champs, ferme);
addObjectsByRange(rawData, direction, 6, 12, DIRECTION_MESH_NAMES); addObjectsByRange(rawData, direction, 6, 12, DIRECTION_MESH_NAMES);
addStandaloneObject(rawData, residence, "ebike");
createResidenceZones(rawData, residence); createResidenceZones(rawData, residence);
addObjectsByRange(rawData, energie, 61, 96, new Set(["pyloneelectrique"])); addObjectsByRange(rawData, energie, 61, 96, new Set(["pyloneelectrique"]));
addObjectsByRange(rawData, vegetation, 98, 829, VEGETATION_MESH_NAMES); addObjectsByRange(rawData, vegetation, 98, 829, VEGETATION_MESH_NAMES);
@@ -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;
+1 -1
View File
@@ -124,7 +124,7 @@ function completeIntroState(state: GameState): GameStateUpdate {
}, },
bike: { bike: {
...state.bike, ...state.bike,
currentStep: "waiting", currentStep: "locked",
}, },
}; };
} }
+16 -1
View File
@@ -19,6 +19,7 @@ import {
isMapModelVisible, isMapModelVisible,
useMapPerformanceStore, useMapPerformanceStore,
} from "@/managers/stores/useMapPerformanceStore"; } from "@/managers/stores/useMapPerformanceStore";
import { useGameStore } from "@/managers/stores/useGameStore";
import { GameMapCollision } from "@/world/GameMapCollision"; import { GameMapCollision } from "@/world/GameMapCollision";
import { CloudSystem } from "@/world/clouds/CloudSystem"; import { CloudSystem } from "@/world/clouds/CloudSystem";
import { GeneratedMapNodeInstance } from "@/world/map-generated/GeneratedMapNodeInstance"; import { GeneratedMapNodeInstance } from "@/world/map-generated/GeneratedMapNodeInstance";
@@ -302,8 +303,12 @@ function MapNodeInstance({
node: MapNode; node: MapNode;
modelUrl: string | null; modelUrl: string | null;
onSettled: () => void; onSettled: () => void;
}): React.JSX.Element { }): React.JSX.Element | null {
const isGeneratedModel = isGeneratedMapModelName(node.name); 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(() => { useEffect(() => {
if (modelUrl !== null || isGeneratedModel) return; if (modelUrl !== null || isGeneratedModel) return;
@@ -311,6 +316,16 @@ function MapNodeInstance({
onSettled(); onSettled();
}, [isGeneratedModel, modelUrl, onSettled]); }, [isGeneratedModel, modelUrl, onSettled]);
useEffect(() => {
if (!hideEbikeMapModel) return;
onSettled();
}, [hideEbikeMapModel, onSettled]);
if (hideEbikeMapModel) {
return null;
}
if (isGeneratedModel) { if (isGeneratedModel) {
return ( return (
<Suspense fallback={<FallbackMapNode node={node} />}> <Suspense fallback={<FallbackMapNode node={node} />}>
+29 -1
View File
@@ -1,4 +1,6 @@
import { InteractableObject } from "@/components/three/interaction/InteractableObject";
import { RepairGame } from "@/components/three/gameplay/RepairGame"; import { RepairGame } from "@/components/three/gameplay/RepairGame";
import { EBIKE_REPAIR_POSITION } from "@/data/gameplay/repairMissionAnchors";
import { useGameStore } from "@/managers/stores/useGameStore"; import { useGameStore } from "@/managers/stores/useGameStore";
import type { RepairMissionId } from "@/types/gameplay/repairMission"; import type { RepairMissionId } from "@/types/gameplay/repairMission";
import type { Vector3Tuple } from "@/types/three/three"; import type { Vector3Tuple } from "@/types/three/three";
@@ -17,7 +19,7 @@ interface GameRepairZone {
const GAME_REPAIR_ZONES = [ const GAME_REPAIR_ZONES = [
{ {
mission: "bike", mission: "bike",
position: [8, 0, -6], position: EBIKE_REPAIR_POSITION,
}, },
{ {
mission: "pylone", 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 (
<group position={EBIKE_REPAIR_POSITION}>
<InteractableObject
kind="trigger"
label="Réparer l'e-bike"
position={EBIKE_REPAIR_POSITION}
radius={4}
onPress={() => setMissionStep("bike", "waiting")}
>
<mesh>
<sphereGeometry args={[1.3, 16, 16]} />
<meshBasicMaterial transparent opacity={0} depthWrite={false} />
</mesh>
</InteractableObject>
</group>
);
}
export function GameStageContent(): React.JSX.Element { export function GameStageContent(): React.JSX.Element {
const mainState = useGameStore((state) => state.mainState); const mainState = useGameStore((state) => state.mainState);
@@ -63,6 +90,7 @@ export function GameStageContent(): React.JSX.Element {
position={zone.position} position={zone.position}
/> />
))} ))}
<EbikeMissionTrigger />
{mainState === "outro" ? ( {mainState === "outro" ? (
<StageAnchor color="#fb7185" position={[0, 6, 10]} scale={1.25} /> <StageAnchor color="#fb7185" position={[0, 6, 10]} scale={1.25} />
) : null} ) : null}