Merge branch 'develop' into feat/polish-mission-2
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
import type { Vector3Tuple } from "@/types/three/three";
|
||||
import type { RepairMissionId } from "@/types/gameplay/repairMission";
|
||||
|
||||
const DEG_TO_RAD = Math.PI / 180;
|
||||
|
||||
export const TEST_SCENE_FLOOR_POSITION: Vector3Tuple = [0, -0.5, 0];
|
||||
export const TEST_SCENE_FLOOR_SIZE: Vector3Tuple = [200, 1, 200];
|
||||
export const TEST_SCENE_FLOOR_COLLIDER_HALF_EXTENTS: Vector3Tuple = [
|
||||
100, 0.5, 100,
|
||||
];
|
||||
|
||||
export const TEST_SCENE_GRABBABLE_POSITION: Vector3Tuple = [0, 1, -3];
|
||||
export const TEST_SCENE_GRABBABLE_POSITION: Vector3Tuple = [0, 0.25, -3];
|
||||
export const TEST_SCENE_GRABBABLE_BOX_SIZE: Vector3Tuple = [0.5, 0.5, 0.5];
|
||||
export const TEST_SCENE_GRABBABLE_COLOR = "#e07b39";
|
||||
export const TEST_SCENE_GRABBABLE_ROUGHNESS = 0.6;
|
||||
@@ -23,6 +25,12 @@ export const TEST_SCENE_TRIGGER_METALNESS = 0.5;
|
||||
|
||||
export const TEST_SCENE_REPAIR_ZONE_MARKER_RADIUS = 1.65;
|
||||
export const TEST_SCENE_REPAIR_ZONE_MARKER_TUBE_RADIUS = 0.045;
|
||||
export const TEST_SCENE_GPS_PREVIEW_POSITION: Vector3Tuple = [0, 5, -4.8];
|
||||
export const TEST_SCENE_GPS_PREVIEW_ROTATION: Vector3Tuple = [
|
||||
-33 * DEG_TO_RAD,
|
||||
0,
|
||||
0,
|
||||
];
|
||||
|
||||
export const GAME_REPAIR_ZONES = [
|
||||
{
|
||||
|
||||
@@ -6,22 +6,22 @@ export interface CameraTransform {
|
||||
}
|
||||
|
||||
export const EBIKE_CAMERA_TRANSFORM: CameraTransform = {
|
||||
position: [-3.5, 6, 0],
|
||||
position: [-2.6, 4.5, 0],
|
||||
rotation: [-10, -90, 0],
|
||||
};
|
||||
|
||||
export const EBIKE_DROP_PLAYER_TRANSFORM: CameraTransform = {
|
||||
position: [0, 1.5, -3],
|
||||
position: [0, 1.3, -2.25],
|
||||
rotation: [0, 0, 0],
|
||||
};
|
||||
|
||||
export const EBIKE_WORLD_POSITION: Vector3Tuple = [61.5, 10, 62.4];
|
||||
export const EBIKE_WORLD_ROTATION_Y = 2.4107;
|
||||
export const EBIKE_WORLD_POSITION: Vector3Tuple = [65, 0.8, 72];
|
||||
export const EBIKE_WORLD_ROTATION_Y = -2.5;
|
||||
export const EBIKE_WORLD_SCALE = 0.35;
|
||||
|
||||
export const EBIKE_INTRO_RIDE_DURATION_MS = 5000;
|
||||
export const EBIKE_INTRO_BREAKDOWN_DISTANCE = 15;
|
||||
export const EBIKE_BREAKDOWN_DIALOGUE_DELAY_MS = 250;
|
||||
|
||||
export const EBIKE_MAX_SPEED = 3;
|
||||
export const EBIKE_ACCELERATION_DURATION_MS = 2000;
|
||||
export const EBIKE_DECELERATION_DURATION_MS = 2000;
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ export const galleryModels: GalleryModel[] = [
|
||||
},
|
||||
{ id: "sapin", name: "Sapin", path: "/models/sapin/model.gltf" },
|
||||
{ id: "skybox", name: "Skybox", path: "/models/skybox/skybox.gltf" },
|
||||
{ id: "talkie", name: "Talkie", path: "/models/talkie/model.gltf" },
|
||||
{ id: "talkie", name: "Talkie", path: "/models/talkie/model.glb" },
|
||||
{ id: "terrain", name: "Terrain", path: "/models/terrain/model.gltf" },
|
||||
{
|
||||
id: "tuyauxlac",
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { RepairMissionId } from "@/types/gameplay/repairMission";
|
||||
|
||||
export const INTRO_MISSION_NOTIFICATION_IMAGE_PATH =
|
||||
"/assets/world/UI/intro-mission-notification.png";
|
||||
|
||||
export const MISSION_NOTIFICATION_IMAGE_PATHS: Record<RepairMissionId, string> =
|
||||
{
|
||||
ebike: "/assets/world/UI/ebike-mission-notification.png",
|
||||
pylon: "/assets/world/UI/pylon-mission-notification.png",
|
||||
farm: "/assets/world/UI/farm-mission-notification.png",
|
||||
ebike: "/assets/world/UI/ebike-mission-notification.webm",
|
||||
pylon: "/assets/world/UI/pylon-mission-notification.webm",
|
||||
farm: "/assets/world/UI/farm-mission-notification.webm",
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
"Repair the damaged cooling module before relaunching the bike",
|
||||
modelPath: "/models/ebike/model.gltf",
|
||||
modelScale: 0.3,
|
||||
stageUiPath: "/assets/world/UI/ebike.webm",
|
||||
stageUiPath: "/assets/world/UI/ebike-mission-notification.webm",
|
||||
interactUiPath: REPAIR_INTERACT_UI_PATH,
|
||||
brokenUiPath: REPAIR_BROKEN_UI_PATH,
|
||||
case: DEFAULT_REPAIR_CASE,
|
||||
@@ -41,11 +41,6 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
label: "Replacement cooling core",
|
||||
modelPath: "/models/refroidisseur/model.gltf",
|
||||
},
|
||||
{
|
||||
id: "ebike-radio-distractor",
|
||||
label: "Radio module",
|
||||
modelPath: "/models/talkie/model.gltf",
|
||||
},
|
||||
{
|
||||
id: "ebike-glove-distractor",
|
||||
label: "Insulation glove",
|
||||
@@ -59,7 +54,7 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
description:
|
||||
"Restore the pylon lamp relay and damaged panel before reconnecting the grid",
|
||||
modelPath: "/models/pylone/model.gltf",
|
||||
stageUiPath: "/assets/world/UI/centrale.webm",
|
||||
stageUiPath: "/assets/world/UI/pylon-mission-notification.webm",
|
||||
interactUiPath: REPAIR_INTERACT_UI_PATH,
|
||||
brokenUiPath: REPAIR_BROKEN_UI_PATH,
|
||||
case: DEFAULT_REPAIR_CASE,
|
||||
@@ -104,7 +99,7 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
description:
|
||||
"Stabilize the irrigation loop and humidity sensor before restarting the farm",
|
||||
modelPath: "/models/fermeverticale/model.gltf",
|
||||
stageUiPath: "/assets/world/UI/laferme.webm",
|
||||
stageUiPath: "/assets/world/UI/farm-mission-notification.webm",
|
||||
interactUiPath: REPAIR_INTERACT_UI_PATH,
|
||||
brokenUiPath: REPAIR_BROKEN_UI_PATH,
|
||||
case: DEFAULT_REPAIR_CASE,
|
||||
@@ -134,11 +129,6 @@ export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
|
||||
label: "Tree sensor",
|
||||
modelPath: "/models/sapin/model.gltf",
|
||||
},
|
||||
{
|
||||
id: "farm-radio-distractor",
|
||||
label: "Radio module",
|
||||
modelPath: "/models/talkie/model.gltf",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,3 +8,9 @@ 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 const HAND_TRACKING_BROWSER_DELEGATE: "CPU" | "GPU" = "CPU";
|
||||
|
||||
// Delay before the runtime actually starts after `enabled` flips to true.
|
||||
// Absorbs React StrictMode's mount/unmount/mount cycle in dev and rapid
|
||||
// `nearby` toggles at trigger borders. Invisible to the user (~5 frames).
|
||||
export const HAND_TRACKING_RUNTIME_START_DELAY_MS = 80;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Vector3Tuple } from "@/types/three/three";
|
||||
import { LA_FABRIK_PLAYER_SPAWN } from "@/data/world/laFabrikConfig";
|
||||
|
||||
export const PLAYER_EYE_HEIGHT = 1.75;
|
||||
export const PLAYER_CAPSULE_RADIUS = 0.35;
|
||||
@@ -14,5 +15,13 @@ export const PLAYER_XZ_DAMPING_FACTOR = 8;
|
||||
export const PLAYER_FALL_RESPAWN_Y = -20;
|
||||
export const PLAYER_FALL_RESPAWN_DELAY = 3;
|
||||
|
||||
export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [59.5, 10, 64.64];
|
||||
export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0];
|
||||
export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [
|
||||
LA_FABRIK_PLAYER_SPAWN[0] + 1,
|
||||
LA_FABRIK_PLAYER_SPAWN[1],
|
||||
LA_FABRIK_PLAYER_SPAWN[2] - 1,
|
||||
];
|
||||
export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [
|
||||
0,
|
||||
PLAYER_EYE_HEIGHT,
|
||||
0,
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
const BACKGROUND_IMAGE = "/assets/bg-site.png";
|
||||
const BACKGROUND_IMAGE = "/assets/bg-site.webp";
|
||||
|
||||
export const SITE_CONFIG = {
|
||||
backgroundImage: BACKGROUND_IMAGE,
|
||||
|
||||
@@ -19,7 +19,7 @@ export const CLOUD_DEFAULTS = {
|
||||
maxRotation: Math.PI * 2,
|
||||
minSpeedMultiplier: 0.4,
|
||||
maxSpeedMultiplier: 1,
|
||||
castShadow: false,
|
||||
castShadow: true,
|
||||
receiveShadow: false,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { CHUNK_CONFIG } from "@/data/world/chunkStreamingConfig";
|
||||
|
||||
export const GRAPHICS_PRESET_KEYS = ["low", "medium", "high", "ultra"] as const;
|
||||
export const GRAPHICS_PRESET_KEYS = [
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
"ultra",
|
||||
"max",
|
||||
] as const;
|
||||
|
||||
export type GraphicsPreset = (typeof GRAPHICS_PRESET_KEYS)[number];
|
||||
|
||||
export interface GraphicsPresetConfig {
|
||||
chunkLoadRadius: number;
|
||||
chunkStreamingEnabled: boolean;
|
||||
chunkUnloadRadius: number;
|
||||
fogEnabled: boolean;
|
||||
forceLodModels: boolean;
|
||||
@@ -18,6 +23,7 @@ export const GRAPHICS_PRESETS = {
|
||||
label: "Basse",
|
||||
chunkLoadRadius: 10,
|
||||
chunkUnloadRadius: 18,
|
||||
chunkStreamingEnabled: true,
|
||||
fogEnabled: true,
|
||||
forceLodModels: true,
|
||||
lodHighDetailDistance: 0,
|
||||
@@ -26,25 +32,37 @@ export const GRAPHICS_PRESETS = {
|
||||
label: "Moyenne",
|
||||
chunkLoadRadius: 20,
|
||||
chunkUnloadRadius: 30,
|
||||
chunkStreamingEnabled: true,
|
||||
fogEnabled: true,
|
||||
forceLodModels: true,
|
||||
lodHighDetailDistance: 0,
|
||||
},
|
||||
high: {
|
||||
label: "High",
|
||||
chunkLoadRadius: CHUNK_CONFIG.loadRadius,
|
||||
chunkUnloadRadius: CHUNK_CONFIG.unloadRadius,
|
||||
chunkLoadRadius: 30,
|
||||
chunkUnloadRadius: 40,
|
||||
chunkStreamingEnabled: true,
|
||||
fogEnabled: false,
|
||||
forceLodModels: false,
|
||||
lodHighDetailDistance: 10,
|
||||
lodHighDetailDistance: 20,
|
||||
},
|
||||
ultra: {
|
||||
label: "Ultra",
|
||||
chunkLoadRadius: 50,
|
||||
chunkUnloadRadius: 65,
|
||||
chunkStreamingEnabled: true,
|
||||
fogEnabled: false,
|
||||
forceLodModels: false,
|
||||
lodHighDetailDistance: 20,
|
||||
lodHighDetailDistance: 30,
|
||||
},
|
||||
max: {
|
||||
label: "Max",
|
||||
chunkLoadRadius: 50,
|
||||
chunkUnloadRadius: 65,
|
||||
chunkStreamingEnabled: false,
|
||||
fogEnabled: false,
|
||||
forceLodModels: false,
|
||||
lodHighDetailDistance: 50,
|
||||
},
|
||||
} as const satisfies Record<GraphicsPreset, GraphicsPresetConfig>;
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { Vector3Tuple } from "@/types/three/three";
|
||||
|
||||
export const LA_FABRIK_CENTER: Vector3Tuple = [59.4973, 6.2746, 64.6354];
|
||||
export const LA_FABRIK_ROTATION_Y = 2.4107;
|
||||
export const LA_FABRIK_HALF_EXTENTS = {
|
||||
x: 8.5,
|
||||
z: 7.5,
|
||||
} as const;
|
||||
export const LA_FABRIK_PLAYER_SPAWN: Vector3Tuple = [59.5, 6.3, 64.64];
|
||||
export const LA_FABRIK_INITIAL_LOOK_AT: Vector3Tuple = [58, 7.3, 62.5];
|
||||
export const LA_FABRIK_INTERIOR_LIGHT_POSITION: Vector3Tuple = [59.5, 9, 64.64];
|
||||
|
||||
export function isInsideLaFabrikFootprint(
|
||||
x: number,
|
||||
z: number,
|
||||
padding = 0,
|
||||
): boolean {
|
||||
const dx = x - LA_FABRIK_CENTER[0];
|
||||
const dz = z - LA_FABRIK_CENTER[2];
|
||||
const cos = Math.cos(-LA_FABRIK_ROTATION_Y);
|
||||
const sin = Math.sin(-LA_FABRIK_ROTATION_Y);
|
||||
const localX = dx * cos - dz * sin;
|
||||
const localZ = dx * sin + dz * cos;
|
||||
|
||||
return (
|
||||
Math.abs(localX) <= LA_FABRIK_HALF_EXTENTS.x + padding &&
|
||||
Math.abs(localZ) <= LA_FABRIK_HALF_EXTENTS.z + padding
|
||||
);
|
||||
}
|
||||
@@ -3,9 +3,9 @@ const SUN_LIGHT_COLOR = "#ffe2bf";
|
||||
|
||||
export const LIGHTING_DEFAULTS = {
|
||||
ambientColor: AMBIENT_LIGHT_COLOR,
|
||||
ambientIntensity: 0.9,
|
||||
ambientIntensity: 0.7,
|
||||
sunColor: SUN_LIGHT_COLOR,
|
||||
sunIntensity: 2.2,
|
||||
sunIntensity: 1.9,
|
||||
sunX: 70,
|
||||
sunY: 45,
|
||||
sunZ: 35,
|
||||
@@ -30,3 +30,12 @@ export const SUN_Y_STEP = 1;
|
||||
export const SUN_Z_MIN = -100;
|
||||
export const SUN_Z_MAX = 100;
|
||||
export const SUN_Z_STEP = 1;
|
||||
|
||||
export const SHADOW_CONFIG = {
|
||||
mapSize: 2048,
|
||||
cameraSize: 95,
|
||||
cameraNear: 0.5,
|
||||
cameraFar: 300,
|
||||
bias: 0,
|
||||
normalBias: 0,
|
||||
} as const;
|
||||
|
||||
@@ -13,7 +13,9 @@ export const MAP_LOD_MODEL_PATHS = {
|
||||
lafabrik: "/models/lafabrik-LOD/model.gltf",
|
||||
maison1: "/models/maison1-LOD/model.gltf",
|
||||
panneauaffichage: "/models/panneauaffichage-LOD/model.gltf",
|
||||
talkie: "/models/talkie-LOD/model.gltf",
|
||||
arbre: "/models/arbre-LOD/model.glb",
|
||||
buisson: "/models/buisson-LOD/model.glb",
|
||||
sapin: "/models/sapin-LOD/model.glb",
|
||||
} as const satisfies Record<string, string>;
|
||||
|
||||
export function getMapLodModelPath(modelName: string): string | null {
|
||||
@@ -22,6 +24,19 @@ export function getMapLodModelPath(modelName: string): string | null {
|
||||
);
|
||||
}
|
||||
|
||||
export const MAP_LOD_SCALE_MULTIPLIERS = {
|
||||
sapin: 0.35,
|
||||
buisson: 0.7,
|
||||
} as const satisfies Partial<Record<keyof typeof MAP_LOD_MODEL_PATHS, number>>;
|
||||
|
||||
export function getMapLodScaleMultiplier(modelName: string): number {
|
||||
return (
|
||||
MAP_LOD_SCALE_MULTIPLIERS[
|
||||
modelName as keyof typeof MAP_LOD_SCALE_MULTIPLIERS
|
||||
] ?? 1
|
||||
);
|
||||
}
|
||||
|
||||
export function selectMapModelPathByDistance({
|
||||
distance,
|
||||
modelName,
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { Vector3Tuple } from "@/types/three/three";
|
||||
|
||||
export interface OctreeCollisionBox {
|
||||
center: Vector3Tuple;
|
||||
size: Vector3Tuple;
|
||||
}
|
||||
|
||||
export interface MapOctreeCollisionBox extends OctreeCollisionBox {
|
||||
bottomY: number;
|
||||
}
|
||||
|
||||
export const MAP_OCTREE_COLLISION_BOXES = {
|
||||
immeuble1: {
|
||||
center: [-0.0308, 5.8389, 0],
|
||||
size: [17.2522, 11.6098, 9.2668],
|
||||
bottomY: 0.034,
|
||||
},
|
||||
maison1: {
|
||||
center: [0, 1.3638, 0.0536],
|
||||
size: [2.7813, 3.022, 2.8609],
|
||||
bottomY: -0.1472,
|
||||
},
|
||||
} as const satisfies Record<string, MapOctreeCollisionBox>;
|
||||
|
||||
export const LA_FABRIK_INTERIOR_COLLISION_BOXES = [
|
||||
// NOTE: removed — this thin wall (size [0.2, 1.94, 3.71]) sat at x≈-6.93 and
|
||||
// sealed the doorway despite the geometry having a hole there. The fabrik
|
||||
// mesh octree already provides the surrounding wall collision, so this
|
||||
// proxy was both redundant and bug-causing.
|
||||
// {
|
||||
// center: [-6.9351, 2.278, -0.0001],
|
||||
// size: [0.2, 1.94, 3.711],
|
||||
// },
|
||||
{
|
||||
center: [0.8026, 0.719, -3.639],
|
||||
size: [4.346, 1.108, 1.181],
|
||||
},
|
||||
{
|
||||
center: [-5.8519, 0.9362, 2.5742],
|
||||
size: [1.67, 1.551, 2.566],
|
||||
},
|
||||
{
|
||||
center: [-2.0627, 1.4875, -1.2243],
|
||||
size: [0.691, 0.723, 0.687],
|
||||
},
|
||||
{
|
||||
center: [-3.5502, 1.4378, -1.2485],
|
||||
size: [1.055, 0.657, 0.563],
|
||||
},
|
||||
] as const satisfies readonly OctreeCollisionBox[];
|
||||
|
||||
export const CHARACTER_OCTREE_COLLISION_BOX = {
|
||||
center: [0, 0.875, 0],
|
||||
size: [0.62, 1.75, 0.62],
|
||||
} as const satisfies OctreeCollisionBox;
|
||||
|
||||
export function hasMapOctreeCollisionBox(
|
||||
name: string,
|
||||
): name is keyof typeof MAP_OCTREE_COLLISION_BOXES {
|
||||
return name in MAP_OCTREE_COLLISION_BOXES;
|
||||
}
|
||||
Reference in New Issue
Block a user