diff --git a/src/runtime-three/navigation-controller.ts b/src/runtime-three/navigation-controller.ts index 3601ffad..a621a0d1 100644 --- a/src/runtime-three/navigation-controller.ts +++ b/src/runtime-three/navigation-controller.ts @@ -14,7 +14,51 @@ import type { RuntimeSpawnPoint } from "./runtime-scene-build"; -export interface FirstPersonTelemetry { +export interface RuntimeMovementTransitionSignals { + jumpStarted: boolean; + leftGround: boolean; + startedFalling: boolean; + landed: boolean; + enteredWater: boolean; + exitedWater: boolean; + wallContactStarted: boolean; + headBump: boolean; +} + +export interface RuntimePlayerCameraHookState { + jumping: boolean; + falling: boolean; + landing: boolean; + swimming: boolean; + underwaterAmount: number; +} + +export interface RuntimePlayerAudioHookState { + underwaterAmount: number; + enteredWater: boolean; + exitedWater: boolean; +} + +export interface RuntimePlayerAnimationHookState { + locomotionMode: RuntimeLocomotionMode; + airborneKind: RuntimeAirborneKind; + gait: RuntimeLocomotionGait; + moving: boolean; + movementAmount: number; + grounded: boolean; + crouched: boolean; + sprinting: boolean; + inWater: boolean; + signals: RuntimeMovementTransitionSignals; +} + +export interface RuntimePlayerMovementHooks { + camera: RuntimePlayerCameraHookState; + audio: RuntimePlayerAudioHookState; + animation: RuntimePlayerAnimationHookState; +} + +export interface PlayerControllerTelemetry { feetPosition: Vec3; eyePosition: Vec3; grounded: boolean; @@ -25,8 +69,12 @@ export interface FirstPersonTelemetry { inFogVolume: boolean; pointerLocked: boolean; spawn: RuntimeSpawnPoint; + signals: RuntimeMovementTransitionSignals; + hooks: RuntimePlayerMovementHooks; } +export type FirstPersonTelemetry = PlayerControllerTelemetry; + export type RuntimeLocomotionMode = | "grounded" | "airborne" @@ -95,7 +143,9 @@ export interface RuntimeControllerContext { radius: number ): Vec3; setRuntimeMessage(message: string | null): void; - setFirstPersonTelemetry(telemetry: FirstPersonTelemetry | null): void; + setPlayerControllerTelemetry( + telemetry: PlayerControllerTelemetry | null + ): void; } export interface NavigationControllerDeactivateOptions { diff --git a/src/runtime-three/player-locomotion.ts b/src/runtime-three/player-locomotion.ts index 043656fb..6b3511e9 100644 --- a/src/runtime-three/player-locomotion.ts +++ b/src/runtime-three/player-locomotion.ts @@ -95,6 +95,8 @@ export interface StepPlayerLocomotionResult { verticalVelocity: number; crouched: boolean; jumpPressed: boolean; + jumpStarted: boolean; + headBump: boolean; locomotionState: RuntimeLocomotionState; inWaterVolume: boolean; inFogVolume: boolean; @@ -338,9 +340,14 @@ export function stepPlayerLocomotion( options.probeGround ); const grounded = + !jumpTriggered && activeShape.mode !== "none" && !nextVolumeState.inWater && (resolvedMotion.grounded || groundProbe.grounded); + const headBump = + verticalDisplacement > 0 && + resolvedMotion.collidedAxes.y && + resolvedMotion.feetPosition.y <= options.feetPosition.y + 1e-4; if (activeShape.mode === "none" || nextVolumeState.inWater || grounded) { verticalVelocity = 0; @@ -365,6 +372,8 @@ export function stepPlayerLocomotion( verticalVelocity, crouched, jumpPressed, + jumpStarted: jumpTriggered, + headBump, locomotionState: { locomotionMode, airborneKind: resolveAirborneKind(locomotionMode, verticalVelocity),