refactor: clean map gameplay architecture
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import type { AudioCategory } from "@/managers/AudioManager";
|
||||
|
||||
export const AUDIO_PATHS = {
|
||||
intro: "/sounds/effect/fa.mp3",
|
||||
bienvenue: "/sounds/effect/fa.mp3",
|
||||
@@ -5,3 +7,9 @@ export const AUDIO_PATHS = {
|
||||
searching: "/sounds/effect/fa.mp3",
|
||||
helped: "/sounds/effect/fa.mp3",
|
||||
} as const;
|
||||
|
||||
export const DEFAULT_CATEGORY_VOLUMES: Record<AudioCategory, number> = {
|
||||
music: 1,
|
||||
sfx: 1,
|
||||
dialogue: 1,
|
||||
};
|
||||
|
||||
@@ -31,19 +31,19 @@ export const TEST_SCENE_REPAIR_ZONES = [
|
||||
position: [-12, 0, -12],
|
||||
},
|
||||
{
|
||||
mission: "pylone",
|
||||
label: "Pylone",
|
||||
mission: "pylon",
|
||||
label: "Pylon",
|
||||
color: "#facc15",
|
||||
position: [0, 0, -12],
|
||||
},
|
||||
{
|
||||
mission: "ferme",
|
||||
mission: "farm",
|
||||
label: "Farm",
|
||||
color: "#86efac",
|
||||
position: [12, 0, -12],
|
||||
},
|
||||
] as const satisfies readonly {
|
||||
mission: "ebike" | "pylone" | "ferme";
|
||||
mission: "ebike" | "pylon" | "farm";
|
||||
label: string;
|
||||
color: string;
|
||||
position: Vector3Tuple;
|
||||
|
||||
@@ -38,53 +38,59 @@ export const docGroups: DocGroup[] = [
|
||||
subtitle: "Gameplay implementation",
|
||||
meta: "04",
|
||||
},
|
||||
{
|
||||
path: "/docs/mission-flow",
|
||||
title: "Mission Flow",
|
||||
subtitle: "Intro and mission progression",
|
||||
meta: "05",
|
||||
},
|
||||
{
|
||||
path: "/docs/interaction",
|
||||
title: "Interaction System",
|
||||
subtitle: "Trigger, grab, hand input",
|
||||
meta: "05",
|
||||
meta: "06",
|
||||
},
|
||||
{
|
||||
path: "/docs/target-architecture",
|
||||
title: "Target Architecture",
|
||||
subtitle: "Next direction",
|
||||
meta: "06",
|
||||
meta: "07",
|
||||
},
|
||||
{
|
||||
path: "/docs/technical-editor",
|
||||
title: "Editor Technical Notes",
|
||||
subtitle: "Implementation details",
|
||||
meta: "07",
|
||||
meta: "08",
|
||||
},
|
||||
{
|
||||
path: "/docs/audio",
|
||||
title: "Audio Technical Notes",
|
||||
subtitle: "Music, dialogue, SRT, and SFX",
|
||||
meta: "08",
|
||||
meta: "09",
|
||||
},
|
||||
{
|
||||
path: "/docs/hand-tracking",
|
||||
title: "Hand Tracking Technical Notes",
|
||||
subtitle: "Webcam interaction pipeline",
|
||||
meta: "09",
|
||||
meta: "10",
|
||||
},
|
||||
{
|
||||
path: "/docs/zustand",
|
||||
title: "Zustand Stores",
|
||||
subtitle: "Game, settings, subtitles",
|
||||
meta: "10",
|
||||
meta: "11",
|
||||
},
|
||||
{
|
||||
path: "/docs/three-debugging",
|
||||
title: "Three Debugging",
|
||||
subtitle: "Step into Three.js internals",
|
||||
meta: "11",
|
||||
meta: "12",
|
||||
},
|
||||
{
|
||||
path: "/docs/map-performance",
|
||||
title: "Map Performance",
|
||||
subtitle: "Draw calls, triangles, and streaming",
|
||||
meta: "12",
|
||||
meta: "13",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -95,25 +101,25 @@ export const docGroups: DocGroup[] = [
|
||||
path: "/docs/features",
|
||||
title: "Features",
|
||||
subtitle: "Implemented scope",
|
||||
meta: "13",
|
||||
meta: "14",
|
||||
},
|
||||
{
|
||||
path: "/docs/main-feature",
|
||||
title: "Main Feature",
|
||||
subtitle: "Repair-game prototype",
|
||||
meta: "14",
|
||||
meta: "15",
|
||||
},
|
||||
{
|
||||
path: "/docs/editor",
|
||||
title: "Editor User Guide",
|
||||
subtitle: "Editing workflow",
|
||||
meta: "15",
|
||||
meta: "16",
|
||||
},
|
||||
{
|
||||
path: "/docs/animation",
|
||||
title: "Animation & 3D Model System",
|
||||
subtitle: "Components and usage",
|
||||
meta: "16",
|
||||
meta: "17",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -124,7 +130,7 @@ export const docGroups: DocGroup[] = [
|
||||
path: "/docs/code-review",
|
||||
title: "Code Review Prep",
|
||||
subtitle: "Presentation support",
|
||||
meta: "17",
|
||||
meta: "18",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { GameStep, MainGameState } from "@/types/game";
|
||||
|
||||
export const GAME_STEPS: readonly GameStep[] = [
|
||||
"intro",
|
||||
"start-intro",
|
||||
"naming",
|
||||
"bienvenue",
|
||||
"star-move",
|
||||
"mission2",
|
||||
"searching",
|
||||
"helped",
|
||||
"manipulation",
|
||||
"outOfFabrik",
|
||||
];
|
||||
|
||||
export const MAIN_GAME_STATES: readonly MainGameState[] = [
|
||||
"intro",
|
||||
"ebike",
|
||||
"pylon",
|
||||
"farm",
|
||||
"outro",
|
||||
] as const;
|
||||
|
||||
const GAME_STEP_VALUES: ReadonlySet<string> = new Set(GAME_STEPS);
|
||||
const MAIN_GAME_STATE_VALUES: ReadonlySet<string> = new Set(MAIN_GAME_STATES);
|
||||
|
||||
export function isGameStep(value: unknown): value is GameStep {
|
||||
return typeof value === "string" && GAME_STEP_VALUES.has(value);
|
||||
}
|
||||
|
||||
export function isMainGameState(value: unknown): value is MainGameState {
|
||||
return typeof value === "string" && MAIN_GAME_STATE_VALUES.has(value);
|
||||
}
|
||||
@@ -1,21 +1,36 @@
|
||||
import type { Vector3Tuple } from "@/types/three/three";
|
||||
import type { RepairMissionId } from "@/types/gameplay/repairMission";
|
||||
|
||||
export interface RepairMissionTriggerConfig {
|
||||
mission: RepairMissionId;
|
||||
label: string;
|
||||
radius: number;
|
||||
}
|
||||
|
||||
export const EBIKE_REPAIR_POSITION = [
|
||||
42.2399, 4.5484, 34.6468,
|
||||
] as const satisfies Vector3Tuple;
|
||||
|
||||
const REPAIR_MISSION_POSITIONS = {
|
||||
ebike: EBIKE_REPAIR_POSITION,
|
||||
pylone: [64, 0, -66],
|
||||
ferme: [-24, 0, 42],
|
||||
pylon: [64, 0, -66],
|
||||
farm: [-24, 0, 42],
|
||||
} as const satisfies Record<RepairMissionId, Vector3Tuple>;
|
||||
|
||||
export const REPAIR_MISSION_POSITION_ENTRIES = [
|
||||
{ mission: "ebike", position: REPAIR_MISSION_POSITIONS.ebike },
|
||||
{ mission: "pylone", position: REPAIR_MISSION_POSITIONS.pylone },
|
||||
{ mission: "ferme", position: REPAIR_MISSION_POSITIONS.ferme },
|
||||
] as const satisfies readonly {
|
||||
export const REPAIR_MISSION_TRIGGERS = [
|
||||
{
|
||||
mission: "ebike",
|
||||
label: "Réparer l'e-bike",
|
||||
radius: 4,
|
||||
},
|
||||
] as const satisfies readonly RepairMissionTriggerConfig[];
|
||||
|
||||
export const REPAIR_MISSION_POSITION_ENTRIES = Object.entries(
|
||||
REPAIR_MISSION_POSITIONS,
|
||||
).map(([mission, position]) => ({
|
||||
mission: mission as RepairMissionId,
|
||||
position,
|
||||
})) satisfies readonly {
|
||||
mission: RepairMissionId;
|
||||
position: Vector3Tuple;
|
||||
}[];
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import type {
|
||||
MissionStep,
|
||||
RepairMissionId,
|
||||
} from "@/types/gameplay/repairMission";
|
||||
|
||||
const REPAIR_MISSION_IDS = ["ebike", "pylon", "farm"] as const;
|
||||
const REPAIR_MISSION_ID_VALUES: ReadonlySet<string> = new Set(
|
||||
REPAIR_MISSION_IDS,
|
||||
);
|
||||
|
||||
export const MISSION_STEPS = [
|
||||
"locked",
|
||||
"waiting",
|
||||
"inspected",
|
||||
"fragmented",
|
||||
"scanning",
|
||||
"repairing",
|
||||
"reassembling",
|
||||
"done",
|
||||
] as const satisfies readonly MissionStep[];
|
||||
const MISSION_STEP_VALUES: ReadonlySet<string> = new Set(MISSION_STEPS);
|
||||
|
||||
export function isRepairMissionId(value: string): value is RepairMissionId {
|
||||
return REPAIR_MISSION_ID_VALUES.has(value);
|
||||
}
|
||||
|
||||
export function isMissionStep(value: string): value is MissionStep {
|
||||
return MISSION_STEP_VALUES.has(value);
|
||||
}
|
||||
|
||||
export function getNextMissionStep(step: MissionStep): MissionStep {
|
||||
switch (step) {
|
||||
case "locked":
|
||||
return "waiting";
|
||||
case "waiting":
|
||||
return "inspected";
|
||||
case "inspected":
|
||||
return "fragmented";
|
||||
case "fragmented":
|
||||
return "scanning";
|
||||
case "scanning":
|
||||
return "repairing";
|
||||
case "repairing":
|
||||
return "reassembling";
|
||||
case "reassembling":
|
||||
case "done":
|
||||
return "done";
|
||||
}
|
||||
}
|
||||
|
||||
export function getPreviousMissionStep(step: MissionStep): MissionStep {
|
||||
switch (step) {
|
||||
case "locked":
|
||||
case "waiting":
|
||||
return "locked";
|
||||
case "inspected":
|
||||
return "waiting";
|
||||
case "fragmented":
|
||||
return "inspected";
|
||||
case "scanning":
|
||||
return "fragmented";
|
||||
case "repairing":
|
||||
return "scanning";
|
||||
case "reassembling":
|
||||
return "repairing";
|
||||
case "done":
|
||||
return "reassembling";
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,8 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
},
|
||||
],
|
||||
},
|
||||
pylone: {
|
||||
id: "pylone",
|
||||
pylon: {
|
||||
id: "pylon",
|
||||
label: "Power pylon",
|
||||
description:
|
||||
"Restore the pylon lamp relay and damaged panel before reconnecting the grid",
|
||||
@@ -64,17 +64,17 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
brokenUiPath: REPAIR_BROKEN_UI_PATH,
|
||||
case: DEFAULT_REPAIR_CASE,
|
||||
reassemblySeconds: 1.8,
|
||||
requiredReplacementPartId: "pylone-grid-relay-replacement",
|
||||
requiredReplacementPartId: "pylon-grid-relay-replacement",
|
||||
scanPartSeconds: 1.4,
|
||||
brokenParts: [
|
||||
{
|
||||
id: "pylone-grid-relay",
|
||||
id: "pylon-grid-relay",
|
||||
label: "Grid relay",
|
||||
nodeName: "lampe",
|
||||
caseSlotName: "placeholder_1",
|
||||
},
|
||||
{
|
||||
id: "pylone-damaged-panel",
|
||||
id: "pylon-damaged-panel",
|
||||
label: "Damaged solar panel",
|
||||
nodeName: "panneau2",
|
||||
caseSlotName: "placeholder_2",
|
||||
@@ -82,24 +82,24 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
],
|
||||
replacementParts: [
|
||||
{
|
||||
id: "pylone-grid-relay-replacement",
|
||||
id: "pylon-grid-relay-replacement",
|
||||
label: "Replacement grid relay",
|
||||
modelPath: "/models/pylone/model.gltf",
|
||||
},
|
||||
{
|
||||
id: "pylone-stone-distractor",
|
||||
id: "pylon-stone-distractor",
|
||||
label: "Stone counterweight",
|
||||
modelPath: "/models/galet/model.gltf",
|
||||
},
|
||||
{
|
||||
id: "pylone-cooling-distractor",
|
||||
id: "pylon-cooling-distractor",
|
||||
label: "Cooling core",
|
||||
modelPath: "/models/refroidisseur/model.gltf",
|
||||
},
|
||||
],
|
||||
},
|
||||
ferme: {
|
||||
id: "ferme",
|
||||
farm: {
|
||||
id: "farm",
|
||||
label: "Vertical farm",
|
||||
description:
|
||||
"Stabilize the irrigation loop and humidity sensor before restarting the farm",
|
||||
@@ -109,33 +109,33 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
brokenUiPath: REPAIR_BROKEN_UI_PATH,
|
||||
case: DEFAULT_REPAIR_CASE,
|
||||
reassemblySeconds: 1.2,
|
||||
requiredReplacementPartId: "ferme-irrigation-pump-replacement",
|
||||
requiredReplacementPartId: "farm-irrigation-pump-replacement",
|
||||
scanPartSeconds: 0.9,
|
||||
brokenParts: [
|
||||
{
|
||||
id: "ferme-irrigation-pump",
|
||||
id: "farm-irrigation-pump",
|
||||
label: "Irrigation pump",
|
||||
caseSlotName: "placeholder_1",
|
||||
},
|
||||
{
|
||||
id: "ferme-humidity-sensor",
|
||||
id: "farm-humidity-sensor",
|
||||
label: "Humidity sensor",
|
||||
caseSlotName: "placeholder_2",
|
||||
},
|
||||
],
|
||||
replacementParts: [
|
||||
{
|
||||
id: "ferme-irrigation-pump-replacement",
|
||||
id: "farm-irrigation-pump-replacement",
|
||||
label: "Replacement irrigation pump",
|
||||
modelPath: "/models/fermeverticale/model.gltf",
|
||||
},
|
||||
{
|
||||
id: "ferme-tree-distractor",
|
||||
id: "farm-tree-distractor",
|
||||
label: "Tree sensor",
|
||||
modelPath: "/models/sapin/model.gltf",
|
||||
},
|
||||
{
|
||||
id: "ferme-radio-distractor",
|
||||
id: "farm-radio-distractor",
|
||||
label: "Radio module",
|
||||
modelPath: "/models/talkie/model.gltf",
|
||||
},
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
const HAND_TRACKING_LOCAL_WS_URL = "ws://localhost:8000/ws";
|
||||
const HAND_TRACKING_PROD_WS_URL = "wss://handtracking.la-fabrik.fr/ws";
|
||||
|
||||
export const HAND_TRACKING_FRAME_WIDTH = 320;
|
||||
export const HAND_TRACKING_FRAME_HEIGHT = 240;
|
||||
export const HAND_TRACKING_TARGET_FPS = 10;
|
||||
@@ -11,15 +8,3 @@ export const HAND_TRACKING_BROWSER_WASM_URL =
|
||||
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.35/wasm";
|
||||
export const HAND_TRACKING_BROWSER_MODEL_URL =
|
||||
"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task";
|
||||
|
||||
export function getHandTrackingWsUrl(): string {
|
||||
const configuredUrl = import.meta.env.VITE_HAND_TRACKING_WS_URL;
|
||||
|
||||
if (configuredUrl) {
|
||||
return configuredUrl;
|
||||
}
|
||||
|
||||
return import.meta.env.DEV
|
||||
? HAND_TRACKING_LOCAL_WS_URL
|
||||
: HAND_TRACKING_PROD_WS_URL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const GENERATED_MAP_MODEL_NAMES = new Set([
|
||||
"ecole",
|
||||
"fermeverticale",
|
||||
"generateur",
|
||||
"lafabrik",
|
||||
]);
|
||||
|
||||
export function isGeneratedMapModelName(name: string): boolean {
|
||||
return GENERATED_MAP_MODEL_NAMES.has(name);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
export const GRASS_CONFIG = {
|
||||
enabled: true,
|
||||
patchSize: 30,
|
||||
bladeCount: 32000,
|
||||
bladeWidth: 0.08,
|
||||
maxBladeHeight: 0.56,
|
||||
randomHeightAmount: 0.25,
|
||||
surfaceOffset: 0.025,
|
||||
heightTextureSize: 128,
|
||||
windNoiseScale: 0.9,
|
||||
windStrength: 0.35,
|
||||
baldPatchModifier: 1.1,
|
||||
falloffSharpness: 0.35,
|
||||
heightNoiseFrequency: 9,
|
||||
heightNoiseAmplitude: 1,
|
||||
clumpFrequency: 2.6,
|
||||
clumpThreshold: 0.18,
|
||||
clumpSoftness: 0.45,
|
||||
zoneFrequency: 0.035,
|
||||
noGrassZoneThreshold: 0.2,
|
||||
sparseZoneThreshold: 0.4,
|
||||
mediumZoneThreshold: 0.65,
|
||||
zoneSoftness: 0.08,
|
||||
noGrassZoneHeight: 0,
|
||||
sparseZoneHeight: 0.08,
|
||||
mediumZoneHeight: 0.45,
|
||||
tallZoneHeight: 1,
|
||||
noGrassZoneDensity: 0,
|
||||
sparseZoneDensity: 0.08,
|
||||
mediumZoneDensity: 0.72,
|
||||
tallZoneDensity: 1,
|
||||
maxBendAngle: 14,
|
||||
} as const;
|
||||
|
||||
export const GRASS_COLORS = ["#84C66B", "#67B058", "#A3CA5B"] as const;
|
||||
export const GRASS_BASE_COLOR = "#1A3A1A" as const;
|
||||
@@ -89,13 +89,3 @@ export type MapInstancingAssetType =
|
||||
|
||||
export type MapInstancingAssetConfig =
|
||||
(typeof MAP_INSTANCING_ASSETS)[MapInstancingAssetType];
|
||||
|
||||
const MAP_INSTANCED_NODE_NAMES: ReadonlySet<string> = new Set(
|
||||
Object.values(MAP_INSTANCING_ASSETS)
|
||||
.filter((config) => config.enabled)
|
||||
.map((config) => config.mapName),
|
||||
);
|
||||
|
||||
export function isInstancedMapNodeName(name: string): boolean {
|
||||
return MAP_INSTANCED_NODE_NAMES.has(name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
export type MapPerformanceGroupName =
|
||||
| "vegetation"
|
||||
| "crops"
|
||||
| "trees"
|
||||
| "buildings"
|
||||
| "landmarks"
|
||||
| "props"
|
||||
| "terrain"
|
||||
| "sky";
|
||||
|
||||
export type MapPerformanceModelName =
|
||||
| "buisson"
|
||||
| "arbre"
|
||||
| "sapin"
|
||||
| "champdeble"
|
||||
| "champdesoja"
|
||||
| "champsdetournesol"
|
||||
| "ecole"
|
||||
| "generateur"
|
||||
| "fermeverticale"
|
||||
| "lafabrik"
|
||||
| "immeuble1"
|
||||
| "eolienne"
|
||||
| "pylone"
|
||||
| "boiteauxlettres"
|
||||
| "maison1"
|
||||
| "panneauaffichage"
|
||||
| "panneauclassique"
|
||||
| "panneaufleche"
|
||||
| "panneausolaire"
|
||||
| "parcebike"
|
||||
| "terrain"
|
||||
| "sky";
|
||||
|
||||
export const MAP_PERFORMANCE_GROUP_NAMES: readonly MapPerformanceGroupName[] = [
|
||||
"vegetation",
|
||||
"crops",
|
||||
"trees",
|
||||
"buildings",
|
||||
"landmarks",
|
||||
"props",
|
||||
"terrain",
|
||||
"sky",
|
||||
];
|
||||
|
||||
export const MAP_PERFORMANCE_MODEL_NAMES: readonly MapPerformanceModelName[] = [
|
||||
"buisson",
|
||||
"arbre",
|
||||
"sapin",
|
||||
"champdeble",
|
||||
"champdesoja",
|
||||
"champsdetournesol",
|
||||
"ecole",
|
||||
"generateur",
|
||||
"fermeverticale",
|
||||
"lafabrik",
|
||||
"immeuble1",
|
||||
"eolienne",
|
||||
"pylone",
|
||||
"boiteauxlettres",
|
||||
"maison1",
|
||||
"panneauaffichage",
|
||||
"panneauclassique",
|
||||
"panneaufleche",
|
||||
"panneausolaire",
|
||||
"parcebike",
|
||||
"terrain",
|
||||
"sky",
|
||||
];
|
||||
|
||||
export const MAP_PERFORMANCE_MODEL_GROUPS: Record<
|
||||
MapPerformanceModelName,
|
||||
readonly MapPerformanceGroupName[]
|
||||
> = {
|
||||
buisson: ["vegetation"],
|
||||
arbre: ["vegetation", "trees"],
|
||||
sapin: ["vegetation", "trees"],
|
||||
champdeble: ["vegetation", "crops"],
|
||||
champdesoja: ["vegetation", "crops"],
|
||||
champsdetournesol: ["vegetation", "crops"],
|
||||
ecole: ["buildings", "landmarks"],
|
||||
generateur: ["landmarks"],
|
||||
fermeverticale: ["buildings", "landmarks"],
|
||||
lafabrik: ["buildings", "landmarks"],
|
||||
immeuble1: ["buildings"],
|
||||
eolienne: ["props"],
|
||||
pylone: ["props"],
|
||||
boiteauxlettres: ["props"],
|
||||
maison1: ["buildings"],
|
||||
panneauaffichage: ["props"],
|
||||
panneauclassique: ["props"],
|
||||
panneaufleche: ["props"],
|
||||
panneausolaire: ["props"],
|
||||
parcebike: ["props"],
|
||||
terrain: ["terrain"],
|
||||
sky: ["sky"],
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { TERRAIN_COLORS, TERRAIN_TILE_SIZE } from "@/data/world/terrainConfig";
|
||||
|
||||
export const PATH_SURFACE_KEY = "chemin";
|
||||
export const PATH_DEBUG_PREVIEW_ENABLED = false;
|
||||
export const PATH_TILE_RENDER_ENABLED = false;
|
||||
export const PATH_TILE_MODEL_PATH = TERRAIN_COLORS.chemin.modelPath;
|
||||
export const PATH_TILE_SIZE =
|
||||
TERRAIN_COLORS.chemin.tileSize ?? TERRAIN_TILE_SIZE;
|
||||
export const PATH_TILE_SAMPLE_STEP = 2;
|
||||
export const PATH_TILE_MAX_COUNT = 1500;
|
||||
export const PATH_TILE_ROTATION = [0, 0, 0] as const;
|
||||
export const PATH_TILE_SCALE = [1, 1, 1] as const;
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { SceneLoadingState } from "@/types/world/sceneLoading";
|
||||
|
||||
export const INITIAL_SCENE_LOADING_STATE: SceneLoadingState = {
|
||||
currentStep: "Initialisation du jeu",
|
||||
progress: 0,
|
||||
status: "loading",
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
export const VEGETATION_TYPES = {
|
||||
buissons: {
|
||||
mapName: "buisson",
|
||||
modelPath: "/models/buisson/model.gltf",
|
||||
scaleMultiplier: 1.5,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
windStrength: 0.08,
|
||||
enabled: true,
|
||||
},
|
||||
sapin: {
|
||||
mapName: "sapin",
|
||||
modelPath: "/models/sapin/model.gltf",
|
||||
scaleMultiplier: 4,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
windStrength: 0.04,
|
||||
enabled: true,
|
||||
},
|
||||
arbre: {
|
||||
mapName: "arbre",
|
||||
modelPath: "/models/arbre/model.gltf",
|
||||
scaleMultiplier: 1,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
windStrength: 0.06,
|
||||
enabled: true,
|
||||
},
|
||||
champdeble: {
|
||||
mapName: "champdeble",
|
||||
modelPath: "/models/champdeble/model.gltf",
|
||||
scaleMultiplier: 1,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
windStrength: 0.18,
|
||||
enabled: true,
|
||||
},
|
||||
champdesoja: {
|
||||
mapName: "champdesoja",
|
||||
modelPath: "/models/champdesoja/model.gltf",
|
||||
scaleMultiplier: 1,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
windStrength: 0.16,
|
||||
enabled: true,
|
||||
},
|
||||
champsdetournesol: {
|
||||
mapName: "champsdetournesol",
|
||||
modelPath: "/models/champsdetournesol/model.gltf",
|
||||
scaleMultiplier: 1,
|
||||
castShadow: true,
|
||||
receiveShadow: true,
|
||||
windStrength: 0.14,
|
||||
enabled: true,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const VEGETATION_TYPE_KEYS = [
|
||||
"buissons",
|
||||
"sapin",
|
||||
"arbre",
|
||||
"champdeble",
|
||||
"champdesoja",
|
||||
"champsdetournesol",
|
||||
] as const satisfies readonly (keyof typeof VEGETATION_TYPES)[];
|
||||
|
||||
export type VegetationType = (typeof VEGETATION_TYPE_KEYS)[number];
|
||||
|
||||
export const INSTANCED_MAP_EXCEPTIONS = new Set([
|
||||
"Scene",
|
||||
"blocking",
|
||||
"terrain",
|
||||
]);
|
||||
@@ -13,12 +13,3 @@ export const WIND_BOUNDS = {
|
||||
};
|
||||
|
||||
export type WindState = typeof WIND_DEFAULTS;
|
||||
|
||||
export function getWindVector(wind: WindState): { x: number; z: number } {
|
||||
const intensity = wind.speed * wind.strength;
|
||||
|
||||
return {
|
||||
x: Math.cos(wind.direction) * intensity,
|
||||
z: Math.sin(wind.direction) * intensity,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user