Refactor elevator systems across core, viewer, and editor
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import {
|
||||
type AnyNode,
|
||||
type AnyNodeId,
|
||||
type ElevatorNode,
|
||||
resolveElevatorDispatchTarget,
|
||||
useInteractive,
|
||||
useLiveNodeOverrides,
|
||||
useLiveTransforms,
|
||||
useRegistry,
|
||||
useScene,
|
||||
} from '@pascal-app/core'
|
||||
import { type ThreeEvent, useFrame } from '@react-three/fiber'
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react'
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
import { useCallback, useLayoutEffect, useMemo, useRef } from 'react'
|
||||
import {
|
||||
BoxGeometry,
|
||||
CylinderGeometry,
|
||||
@@ -561,29 +561,8 @@ function ElevatorMeshButton({
|
||||
[action, buttonKind, disabled, elevatorId, levelId],
|
||||
)
|
||||
|
||||
const press = (event: ThreeEvent<PointerEvent>) => {
|
||||
if (event.button !== 0) return
|
||||
if (disabled) return
|
||||
if (action === 'open-door') {
|
||||
useInteractive.getState().openElevatorDoor(elevatorId)
|
||||
return
|
||||
}
|
||||
if (levelId) {
|
||||
const targetElevatorId =
|
||||
buttonKind === 'landing'
|
||||
? resolveElevatorDispatchTarget({
|
||||
elevators: useInteractive.getState().elevators,
|
||||
levelId,
|
||||
nodes: useScene.getState().nodes,
|
||||
requestedElevatorId: elevatorId,
|
||||
})
|
||||
: elevatorId
|
||||
useInteractive.getState().requestElevator(targetElevatorId, levelId)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<group onPointerDown={press} position={position} userData={userData}>
|
||||
<group position={position} userData={userData}>
|
||||
{!disabled && (active || queued) && (
|
||||
<mesh
|
||||
dispose={null}
|
||||
@@ -700,6 +679,32 @@ function getElevatorShaftDepth(node: ElevatorNode, cabDepth = getElevatorCabDept
|
||||
return Math.max(node.shaftDepth ?? cabDepth, cabDepth, 0.8)
|
||||
}
|
||||
|
||||
function getElevatorLevelContextNodes(
|
||||
elevator: ElevatorNode,
|
||||
nodes: ReturnType<typeof useScene.getState>['nodes'],
|
||||
): Record<AnyNodeId, AnyNode> {
|
||||
const result: Record<string, AnyNode> = {}
|
||||
const building = elevator.parentId ? nodes[elevator.parentId as AnyNodeId] : null
|
||||
if (building?.type !== 'building') return result as Record<AnyNodeId, AnyNode>
|
||||
|
||||
result[building.id] = building
|
||||
|
||||
for (const childId of building.children) {
|
||||
const level = nodes[childId as AnyNodeId]
|
||||
if (level?.type !== 'level') continue
|
||||
|
||||
result[level.id] = level
|
||||
for (const levelChildId of level.children) {
|
||||
const child = nodes[levelChildId as AnyNodeId]
|
||||
if (child?.type === 'ceiling' || child?.type === 'wall') {
|
||||
result[child.id] = child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result as Record<AnyNodeId, AnyNode>
|
||||
}
|
||||
|
||||
function DoorLeaf({
|
||||
animated,
|
||||
doorOpen,
|
||||
@@ -1000,7 +1005,6 @@ function LandingDoor({
|
||||
export const ElevatorRenderer = ({ node }: { node: ElevatorNode }) => {
|
||||
const ref = useRef<Group>(null!)
|
||||
const cabRef = useRef<Group>(null)
|
||||
const nodes = useScene((state) => state.nodes)
|
||||
const handlers = useNodeEvents(node, 'elevator')
|
||||
const liveOverrides = useLiveNodeOverrides((state) => state.get(node.id))
|
||||
const liveTransform = useLiveTransforms((state) => state.get(node.id))
|
||||
@@ -1008,12 +1012,15 @@ export const ElevatorRenderer = ({ node }: { node: ElevatorNode }) => {
|
||||
() => (liveOverrides ? ({ ...node, ...liveOverrides } as ElevatorNode) : node),
|
||||
[liveOverrides, node],
|
||||
)
|
||||
const levelContextNodes = useScene(
|
||||
useShallow((state) => getElevatorLevelContextNodes(renderNode, state.nodes)),
|
||||
)
|
||||
|
||||
useRegistry(node.id, 'elevator', ref)
|
||||
|
||||
const { entries, defaultEntry, shaftBaseY, totalHeight } = useMemo(
|
||||
() => resolveElevatorLevels(renderNode, nodes),
|
||||
[renderNode, nodes],
|
||||
() => resolveElevatorLevels(renderNode, levelContextNodes),
|
||||
[renderNode, levelContextNodes],
|
||||
)
|
||||
const elevatorId = node.id as AnyNodeId
|
||||
const runtimeStatus = useInteractive(
|
||||
@@ -1029,33 +1036,6 @@ export const ElevatorRenderer = ({ node }: { node: ElevatorNode }) => {
|
||||
}),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!defaultEntry) return
|
||||
|
||||
const elevatorId = node.id as AnyNodeId
|
||||
const interactive = useInteractive.getState()
|
||||
const current = interactive.elevators[elevatorId]
|
||||
if (!current) {
|
||||
interactive.initElevator(elevatorId, defaultEntry.id as AnyNodeId, defaultEntry.baseY)
|
||||
} else if (!entries.some((entry) => entry.id === current.currentLevelId)) {
|
||||
interactive.setElevatorState(elevatorId, {
|
||||
carY: defaultEntry.baseY,
|
||||
currentLevelId: defaultEntry.id as AnyNodeId,
|
||||
doorOpen: 0,
|
||||
phase: 'idle',
|
||||
phaseStartedAt: null,
|
||||
queue: [],
|
||||
targetLevelId: null,
|
||||
})
|
||||
}
|
||||
}, [defaultEntry, entries, node.id])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
useInteractive.getState().removeElevator(elevatorId)
|
||||
}
|
||||
}, [elevatorId])
|
||||
|
||||
useFrame(() => {
|
||||
if (!cabRef.current) return
|
||||
const runtime = useInteractive.getState().elevators[elevatorId]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { ElevatorOpeningSystem } from '@pascal-app/core'
|
||||
import { Bvh } from '@react-three/drei'
|
||||
import { Canvas, extend, type ThreeToJSXElements, useFrame, useThree } from '@react-three/fiber'
|
||||
import { useEffect, useMemo, useRef } from 'react'
|
||||
@@ -9,7 +10,7 @@ import { CeilingSystem } from '../../systems/ceiling/ceiling-system'
|
||||
import { DoorAnimationSystem } from '../../systems/door/door-animation-system'
|
||||
import { DoorSystem } from '../../systems/door/door-system'
|
||||
import { ElevatorAnimationSystem } from '../../systems/elevator/elevator-animation-system'
|
||||
import { ElevatorOpeningSystem } from '../../systems/elevator/elevator-opening-system'
|
||||
import { ElevatorInteractionSystem } from '../../systems/elevator/elevator-interaction-system'
|
||||
import { FenceSystem } from '../../systems/fence/fence-system'
|
||||
import { GuideSystem } from '../../systems/guide/guide-system'
|
||||
import { ItemSystem } from '../../systems/item/item-system'
|
||||
@@ -208,6 +209,7 @@ const Viewer: React.FC<ViewerProps> = ({
|
||||
<CeilingSystem />
|
||||
<DoorAnimationSystem />
|
||||
<ElevatorAnimationSystem />
|
||||
<ElevatorInteractionSystem />
|
||||
<ElevatorOpeningSystem />
|
||||
<WindowAnimationSystem />
|
||||
<DoorSystem />
|
||||
|
||||
Reference in New Issue
Block a user