Merge branch 'develop' into feat/polish-mission-2
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
isMapModelVisible,
|
||||
useMapPerformanceStore,
|
||||
} from "@/managers/stores/useMapPerformanceStore";
|
||||
import { useRepairFocusStore } from "@/managers/stores/useRepairFocusStore";
|
||||
import { SkyModel } from "@/components/three/world/SkyModel";
|
||||
import { CloudSystem } from "@/world/clouds/CloudSystem";
|
||||
import { FogSystem } from "@/world/fog/FogSystem";
|
||||
@@ -24,6 +25,9 @@ export function Environment(): React.JSX.Element {
|
||||
const groups = useMapPerformanceStore((state) => state.groups);
|
||||
const models = useMapPerformanceStore((state) => state.models);
|
||||
const showSky = isMapModelVisible("sky", { groups, models });
|
||||
// Hide vegetation while the repair focus bubble is active so the cocoon
|
||||
// shroud is not pierced by tall trees / bushes around the repair model.
|
||||
const repairFocusActive = useRepairFocusStore((state) => state.active);
|
||||
|
||||
if (sceneMode === "physics") {
|
||||
return (
|
||||
@@ -52,7 +56,7 @@ export function Environment(): React.JSX.Element {
|
||||
<WaterSystem />
|
||||
<CloudSystem />
|
||||
<GrassSystem />
|
||||
<VegetationSystem />
|
||||
{repairFocusActive ? null : <VegetationSystem />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -250,7 +250,10 @@ export function animateCameraTransformTransition(
|
||||
targetRotation: Vector3Tuple,
|
||||
duration: number = 1,
|
||||
onComplete?: () => void,
|
||||
options: { lockInput?: boolean } = {},
|
||||
): void {
|
||||
const { lockInput = true } = options;
|
||||
|
||||
if (!globalCamera) {
|
||||
logger.warn("GameCinematics", "Camera not found for transition");
|
||||
onComplete?.();
|
||||
@@ -260,7 +263,9 @@ export function animateCameraTransformTransition(
|
||||
const camera = globalCamera;
|
||||
|
||||
cameraTransitionTimeline?.kill();
|
||||
useGameStore.getState().setCinematicPlaying(true);
|
||||
if (lockInput) {
|
||||
useGameStore.getState().setCinematicPlaying(true);
|
||||
}
|
||||
|
||||
// Convert target rotation in degrees to quaternion
|
||||
const targetEuler = new THREE.Euler(
|
||||
@@ -282,7 +287,9 @@ export function animateCameraTransformTransition(
|
||||
},
|
||||
onComplete: () => {
|
||||
cameraTransitionTimeline = null;
|
||||
useGameStore.getState().setCinematicPlaying(false);
|
||||
if (lockInput) {
|
||||
useGameStore.getState().setCinematicPlaying(false);
|
||||
}
|
||||
onComplete?.();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Ebike } from "@/components/ebike/Ebike";
|
||||
import { InteractableObject } from "@/components/three/interaction/InteractableObject";
|
||||
import { RepairFocusBubble } from "@/components/three/gameplay/RepairFocusBubble";
|
||||
import { RepairGame } from "@/components/three/gameplay/RepairGame";
|
||||
import { FarmNarrativeFlow } from "@/components/gameplay/farm/FarmNarrativeFlow";
|
||||
import { PylonDownedPylon } from "@/components/gameplay/pylon/PylonDownedPylon";
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
OUTRO_STAGE_ANCHOR,
|
||||
} from "@/data/gameplay/gameStageAnchors";
|
||||
import { useGameStore } from "@/managers/stores/useGameStore";
|
||||
import { useRepairFocusStore } from "@/managers/stores/useRepairFocusStore";
|
||||
import { useRepairMissionAnchorStore } from "@/managers/stores/useRepairMissionAnchorStore";
|
||||
import {
|
||||
isFarmNarrativeStep,
|
||||
@@ -25,13 +27,7 @@ import {
|
||||
import type { RepairMissionTriggerConfig } from "@/types/gameplay/repairMission";
|
||||
import type { Vector3Tuple } from "@/types/three/three";
|
||||
import { getRepairMissionPosition } from "@/utils/gameplay/repairMissionPosition";
|
||||
import {
|
||||
EBIKE_WORLD_POSITION,
|
||||
EBIKE_WORLD_ROTATION_Y,
|
||||
EBIKE_WORLD_SCALE,
|
||||
} from "@/data/ebike/ebikeConfig";
|
||||
|
||||
const EBIKE_CONFIG_KEY = `${EBIKE_WORLD_POSITION.join(",")}:${EBIKE_WORLD_ROTATION_Y}:${EBIKE_WORLD_SCALE}`;
|
||||
import { EBIKE_WORLD_POSITION } from "@/data/ebike/ebikeConfig";
|
||||
|
||||
interface StageAnchorProps {
|
||||
color: string;
|
||||
@@ -96,6 +92,7 @@ export function GameStageContent(): React.JSX.Element {
|
||||
const mainState = useGameStore((state) => state.mainState);
|
||||
const pylonStep = useGameStore((state) => state.pylon.currentStep);
|
||||
const anchors = useRepairMissionAnchorStore((state) => state.anchors);
|
||||
const repairFocusActive = useRepairFocusStore((state) => state.active);
|
||||
|
||||
const farmStep = useGameStore((state) => state.farm.currentStep);
|
||||
|
||||
@@ -110,7 +107,7 @@ export function GameStageContent(): React.JSX.Element {
|
||||
<Ebike position={EBIKE_WORLD_POSITION} />
|
||||
<PylonLightingEffect />
|
||||
<PylonDownedPylon />
|
||||
{isDebugEnabled() ? (
|
||||
{isDebugEnabled() && !repairFocusActive ? (
|
||||
<>
|
||||
<ZoneDebugVisual zone={PYLON_APPROACH_ZONE} active={false} />
|
||||
<ZoneDebugVisual zone={PYLON_ARRIVED_ZONE} active={false} />
|
||||
@@ -131,6 +128,7 @@ export function GameStageContent(): React.JSX.Element {
|
||||
<RepairMissionTrigger key={config.mission} config={config} />
|
||||
))}
|
||||
{mainState === "outro" ? <StageAnchor {...OUTRO_STAGE_ANCHOR} /> : null}
|
||||
<RepairFocusBubble />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+9
-3
@@ -6,6 +6,7 @@ import {
|
||||
} from "@/data/player/playerConfig";
|
||||
import { LA_FABRIK_INITIAL_LOOK_AT } from "@/data/world/laFabrikConfig";
|
||||
import { useCameraMode } from "@/hooks/debug/useCameraMode";
|
||||
import { useDebugStore } from "@/hooks/debug/useDebugStore";
|
||||
import { useEnvironmentDebug } from "@/hooks/debug/useEnvironmentDebug";
|
||||
import { useMapPerformanceDebug } from "@/hooks/debug/useMapPerformanceDebug";
|
||||
import { useCharacterDebug } from "@/hooks/debug/useCharacterDebug";
|
||||
@@ -32,7 +33,6 @@ import { CharacterSystem } from "@/world/characters/CharacterSystem";
|
||||
import { Player } from "@/world/player/Player";
|
||||
import { TestMap } from "@/world/debug/TestMap";
|
||||
import type { SceneLoadingChangeHandler } from "@/types/world/sceneLoading";
|
||||
import type { HandTrackingGloveHandedness } from "@/hooks/handTracking/useHandTrackingGloveStatus";
|
||||
import type { HandTrackingHand } from "@/types/handTracking/handTracking";
|
||||
|
||||
interface WorldProps {
|
||||
@@ -41,7 +41,7 @@ interface WorldProps {
|
||||
|
||||
function hasTrackedHand(
|
||||
hands: HandTrackingHand[],
|
||||
handedness: HandTrackingGloveHandedness,
|
||||
handedness: "left" | "right",
|
||||
): boolean {
|
||||
return hands.some((hand) => hand.handedness.toLowerCase() === handedness);
|
||||
}
|
||||
@@ -60,6 +60,9 @@ export function World({ onLoadingStateChange }: WorldProps): React.JSX.Element {
|
||||
(state) => state.showPlayerModel,
|
||||
);
|
||||
const showDebugOctree = useDebugVisualsStore((state) => state.showOctree);
|
||||
const showHandTrackingModel = useDebugStore((debug) =>
|
||||
debug.getShowHandTrackingModel(),
|
||||
);
|
||||
const { hands, status, usageStatus } = useHandTrackingSnapshot();
|
||||
const {
|
||||
octree,
|
||||
@@ -74,7 +77,10 @@ export function World({ onLoadingStateChange }: WorldProps): React.JSX.Element {
|
||||
? PLAYER_SPAWN_POSITION_GAME
|
||||
: PLAYER_SPAWN_POSITION_PHYSICS;
|
||||
const showHandTrackingGloves =
|
||||
status === "connected" && usageStatus !== "inactive" && hands.length > 0;
|
||||
showHandTrackingModel &&
|
||||
status === "connected" &&
|
||||
usageStatus !== "inactive" &&
|
||||
hands.length > 0;
|
||||
const showLeftHandTrackingGlove =
|
||||
showHandTrackingGloves && hasTrackedHand(hands, "left");
|
||||
const showRightHandTrackingGlove =
|
||||
|
||||
@@ -3,7 +3,9 @@ import { Component, useRef, useState, useEffect } from "react";
|
||||
import * as THREE from "three";
|
||||
import { Physics, RigidBody, CuboidCollider } from "@react-three/rapier";
|
||||
import { Line } from "@react-three/drei";
|
||||
import { Ebike } from "@/components/ebike/Ebike";
|
||||
import { RepairGame } from "@/components/three/gameplay/RepairGame";
|
||||
import { RepairFocusBubble } from "@/components/three/gameplay/RepairFocusBubble";
|
||||
import { GrabbableObject } from "@/components/three/interaction/GrabbableObject";
|
||||
import { AnimatedModel } from "@/components/three/models/AnimatedModel";
|
||||
import { TriggerObject } from "@/components/three/interaction/TriggerObject";
|
||||
@@ -239,11 +241,16 @@ export function TestMap({ onOctreeReady }: TestMapProps): React.JSX.Element {
|
||||
<group position={zone.position}>
|
||||
<RepairPlaygroundZoneMarker color={zone.color} />
|
||||
</group>
|
||||
{zone.mission === "ebike" ? (
|
||||
<Ebike position={zone.position} snapToTerrain={false} />
|
||||
) : null}
|
||||
<RepairGame mission={zone.mission} position={zone.position} />
|
||||
</group>
|
||||
))}
|
||||
</Physics>
|
||||
|
||||
<RepairFocusBubble />
|
||||
|
||||
{/* Dynamic Futuristic 3D GPS Dashboard Preview */}
|
||||
<group
|
||||
position={TEST_SCENE_GPS_PREVIEW_POSITION}
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
PLAYER_MAX_DELTA,
|
||||
PLAYER_XZ_DAMPING_FACTOR,
|
||||
} from "@/data/player/playerConfig";
|
||||
import { useRepairMovementLocked } from "@/hooks/gameplay/useRepairMovementLocked";
|
||||
import { useTerrainHeightSampler } from "@/hooks/three/useTerrainHeight";
|
||||
import { InteractionManager } from "@/managers/InteractionManager";
|
||||
import { useGameStore } from "@/managers/stores/useGameStore";
|
||||
@@ -154,9 +153,7 @@ export function PlayerController({
|
||||
}: PlayerControllerProps): null {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const sceneMode = useSceneMode();
|
||||
const movementLocked = useRepairMovementLocked();
|
||||
const terrainHeight = useTerrainHeightSampler();
|
||||
const movementLockedRef = useRef(movementLocked);
|
||||
const keys = useRef<Keys>({ ...DEFAULT_KEYS });
|
||||
const velocity = useRef(new THREE.Vector3());
|
||||
const fallDuration = useRef(0);
|
||||
@@ -249,17 +246,6 @@ export function PlayerController({
|
||||
initializedRef.current = true;
|
||||
}, [camera, initialLookAt, spawnPosition]);
|
||||
|
||||
useEffect(() => {
|
||||
movementLockedRef.current = movementLocked;
|
||||
|
||||
if (!movementLocked) return;
|
||||
|
||||
keys.current = { ...DEFAULT_KEYS };
|
||||
wantsJump.current = false;
|
||||
velocity.current.setX(0);
|
||||
velocity.current.setZ(0);
|
||||
}, [movementLocked]);
|
||||
|
||||
useEffect(() => {
|
||||
const interaction = InteractionManager.getInstance();
|
||||
|
||||
@@ -267,20 +253,11 @@ export function PlayerController({
|
||||
if (isPlayerInputLocked()) return;
|
||||
|
||||
if (setMovementKey(keys.current, event.key, true)) {
|
||||
if (movementLockedRef.current) {
|
||||
keys.current = { ...DEFAULT_KEYS };
|
||||
}
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === JUMP_KEY) {
|
||||
if (movementLockedRef.current) {
|
||||
wantsJump.current = false;
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
wantsJump.current = true;
|
||||
event.preventDefault();
|
||||
return;
|
||||
@@ -386,7 +363,7 @@ export function PlayerController({
|
||||
}
|
||||
|
||||
_wishDir.set(0, 0, 0);
|
||||
if (!movementLocked && !isEbikeBreakdown) {
|
||||
if (!isEbikeBreakdown) {
|
||||
if (keys.current.forward) _wishDir.add(_forward);
|
||||
if (keys.current.backward) _wishDir.sub(_forward);
|
||||
if (!isEbikeMounted) {
|
||||
|
||||
Reference in New Issue
Block a user