@hology/core 0.0.264 → 0.0.266
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/effects/vfx/gpu-material-uniforms.d.ts +23 -0
- package/dist/effects/vfx/gpu-material-uniforms.js +4 -0
- package/dist/effects/vfx/gpu-sprite-runtime.js +1 -1
- package/dist/effects/vfx/vfx-actor.d.ts +6 -0
- package/dist/effects/vfx/vfx-actor.js +1 -1
- package/dist/effects/vfx/vfx-materializer.d.ts +1 -0
- package/dist/effects/vfx/vfx-materializer.js +1 -1
- package/dist/effects/vfx/vfx-param.js +1 -1
- package/dist/effects/vfx/vfx-render-preparation.d.ts +8 -0
- package/dist/effects/vfx/vfx-render-preparation.js +4 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +53 -0
- package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
- package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
- package/dist/gameplay/ai/behavior-tree/behavior-tree.d.ts +33 -0
- package/dist/gameplay/ai/behavior-tree/behavior-tree.js +4 -0
- package/dist/gameplay/ai/behavior-tree/behavior-tree.test.d.ts +2 -0
- package/dist/gameplay/ai/behavior-tree/behavior-tree.test.js +4 -0
- package/dist/gameplay/ai/behavior-tree/bt.d.ts +92 -70
- package/dist/gameplay/ai/behavior-tree/bt.js +1 -1
- package/dist/gameplay/ai/behavior-tree/bt.test.d.ts +2 -0
- package/dist/gameplay/ai/behavior-tree/bt.test.js +4 -0
- package/dist/gameplay/ai/behavior-tree/move.d.ts +1 -0
- package/dist/gameplay/ai/behavior-tree/move.js +1 -1
- package/dist/gameplay/ai/index.d.ts +1 -0
- package/dist/gameplay/ai/index.js +1 -1
- package/dist/gameplay/animation/foot-contact-baker.d.ts +23 -0
- package/dist/gameplay/animation/foot-contact-baker.js +4 -0
- package/dist/gameplay/animation/foot-contact.d.ts +27 -0
- package/dist/gameplay/animation/foot-contact.js +4 -0
- package/dist/gameplay/animation/locomotion-body.d.ts +62 -0
- package/dist/gameplay/animation/locomotion-body.js +4 -0
- package/dist/gameplay/animation/pose-inertialization.d.ts +46 -0
- package/dist/gameplay/animation/pose-inertialization.js +4 -0
- package/dist/gameplay/animation/root-motion.js +1 -1
- package/dist/gameplay/index.d.ts +4 -0
- package/dist/gameplay/index.js +1 -1
- package/dist/gameplay/initiate.js +1 -1
- package/dist/gameplay/input/input-service.d.ts +4 -0
- package/dist/gameplay/input/input-service.js +1 -1
- package/dist/gameplay/rendering-host.d.ts +2 -1
- package/dist/gameplay/rendering-host.js +1 -1
- package/dist/gameplay/services/physics/physics-system.d.ts +7 -0
- package/dist/gameplay/services/physics/physics-system.js +1 -1
- package/dist/gameplay/services/render.d.ts +2 -0
- package/dist/gameplay/services/render.js +1 -1
- package/dist/rendering/webgpu-experimental-backend.d.ts +1 -1
- package/dist/rendering/webgpu-experimental-backend.js +1 -1
- package/dist/rendering.d.ts +4 -2
- package/dist/rendering.js +1 -1
- package/dist/scene/animation-binary.d.ts +6 -0
- package/dist/scene/animation-binary.js +4 -0
- package/dist/scene/asset-resource-loader.d.ts +3 -1
- package/dist/scene/asset-resource-loader.js +1 -1
- package/dist/scene/landscape/landscape-manager.d.ts +4 -1
- package/dist/scene/landscape/landscape-manager.js +1 -1
- package/dist/scene/landscape/landscape-static-draw-sets.d.ts +18 -0
- package/dist/scene/landscape/landscape-static-draw-sets.js +4 -0
- package/dist/scene/materializer.d.ts +2 -0
- package/dist/scene/materializer.js +1 -1
- package/dist/shader/shader.d.ts +8 -0
- package/dist/test/animation-binary.test.d.ts +2 -0
- package/dist/test/animation-binary.test.js +4 -0
- package/dist/test/character-foot-contact.test.d.ts +2 -0
- package/dist/test/character-foot-contact.test.js +4 -0
- package/dist/test/character-inertialization.test.d.ts +2 -0
- package/dist/test/character-inertialization.test.js +4 -0
- package/dist/test/character-movement-solve.test.d.ts +2 -0
- package/dist/test/character-movement-solve.test.js +4 -0
- package/dist/test/foot-contact.test.d.ts +2 -0
- package/dist/test/foot-contact.test.js +4 -0
- package/dist/test/gpu-material-uniforms.test.d.ts +2 -0
- package/dist/test/gpu-material-uniforms.test.js +4 -0
- package/dist/test/gpu-sprite-pipeline-cache.test.d.ts +2 -0
- package/dist/test/gpu-sprite-pipeline-cache.test.js +4 -0
- package/dist/test/input.test.js +1 -1
- package/dist/test/landscape-static-draw-sets.test.d.ts +2 -0
- package/dist/test/landscape-static-draw-sets.test.js +4 -0
- package/dist/test/locomotion-body.test.d.ts +2 -0
- package/dist/test/locomotion-body.test.js +4 -0
- package/dist/test/physics-ray-object.test.d.ts +2 -0
- package/dist/test/physics-ray-object.test.js +4 -0
- package/dist/test/pose-inertialization.test.d.ts +2 -0
- package/dist/test/pose-inertialization.test.js +4 -0
- package/dist/test/renderer-loading.test.d.ts +2 -0
- package/dist/test/renderer-loading.test.js +4 -0
- package/dist/test/rendering-host.test.js +1 -1
- package/dist/test/sequence-property-parameters.test.js +1 -1
- package/dist/test/sequence-vfx.test.js +1 -1
- package/dist/test/vfx-render-preparation.test.d.ts +2 -0
- package/dist/test/vfx-render-preparation.test.js +4 -0
- package/dist/utils/structs.d.ts +4 -0
- package/dist/utils/structs.js +4 -0
- package/package.json +4 -2
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{__decorate as t,__metadata as e}from"tslib";import i from"@dimforge/rapier3d-simd-compat";import{takeUntil as o}from"rxjs";import*as s from"three";import{ArrowHelper as n,MathUtils as r,Quaternion as a,Vector3 as h}from"three";import{RootMotionClip as l}from"../../../../animation/root-motion.js";import{inject as c}from"../../../../inject.js";import{ActionInput as d,AxisInput as u,RotationInput as p}from"../../../../input/index.js";import{NetRole as m}from"../../../../net/service/net-actor-role.js";import{RunOnClient as y,RunOnNotOwner as v,RunOnServer as g}from"../../../../net/service/rpc-decorator.js";import{NetService as S}from"../../../../net/service/net-service.js";import{PhysicsSystem as M,RayTestResult as f}from"../../../../services/physics/physics-system.js";import{CapsuleCollisionShape as x}from"../../../../../scene/collision/collision-shape.js";import{Parameter as T}from"../../../../../shader/parameter.js";import{PhysicsBodyType as C}from"../../../../services/physics/physics-system.js";import{ActorComponent as w,Component as R}from"../../../component.js";import{CharacterMovementMode as A}from"./modes.js";import{calculateRootMotionTargetApproachCorrection as D,constrainRootMotionTargetApproach as q,warpRootMotionTargetApproach as b}from"./root-motion-constraint.js";import{DEFAULT_MAX_CLIENT_TIME_AHEAD as z,DEFAULT_LARGE_CORRECTION_THRESHOLD as k,DEFAULT_MAX_COMBINED_MOVE_DELTA_TIME as P,DEFAULT_MAX_MOVE_DELTA_TIME as I,DEFAULT_MAX_MOVES_PER_PACKET as O,DEFAULT_MAX_REPLAYED_MOVES as j,DEFAULT_MAX_SERVER_MOVES_PER_TICK as G,DEFAULT_MAX_SAVED_MOVES as Y,DEFAULT_MAX_SERVER_MOVE_DELTA_TIME_SCALAR as N,DEFAULT_MAX_SERVER_MOVE_TIME_BUDGET as F,DEFAULT_MAX_SUBSTEP_DELTA_TIME as V,DEFAULT_MOVE_SEND_INTERVAL as H,DEFAULT_SIMULATED_INTERPOLATION_DELAY as U,DEFAULT_SIMULATED_STATE_INTERVAL as X,DEFAULT_SMALL_CORRECTION_TOLERANCE as E,DEFAULT_TELEPORT_SNAP_THRESHOLD as L,MOVE_FLAG_CROUCH as B,MOVE_FLAG_JUMP as Z,MOVE_FLAG_ROOT_MOTION as W,MOVE_FLAG_SLIDE_START as $,MOVE_FLAG_SPRINT as Q,MOVE_FLAG_SPRINT_START as J,SNAPSHOT_FLAG_CROUCH_SUPPRESSED as K,SNAPSHOT_FLAG_CROUCHING as _,NetCharacterProxySmoother as tt,NetCharacterSavedMoveBuffer as et,accrueNetCharacterServerMoveTime as it,createNetCharacterMovementSnapshot as ot,createNetCharacterServerMoveTimingState as st,consumeNetCharacterServerMoveTime as nt,decodeNetCharacterMoveBatch as rt,decodeNetCharacterOwnerAck as at,decodeNetCharacterOwnerCorrection as ht,decodeNetCharacterSimulatedState as lt,encodeNetCharacterMoveBatch as ct,encodeNetCharacterOwnerAck as dt,encodeNetCharacterOwnerCorrection as ut,encodeNetCharacterSimulatedState as pt,validateNetCharacterMove as mt}from"./net-character-movement-protocol.js";import{NetMode as yt}from"../../../../net/net-session.js";import{setSlideJumpVelocity as vt,shouldContinueSlide as gt,shouldStartSlide as St,updateSlideVelocity as Mt,updateSlideVelocityFromDisplacement as ft}from"./character-slide.js";export*from"./net-character-movement-protocol.js";const xt=131070,Tt=new h(0,1,0),Ct=new h,wt=new h,Rt=new h,At=new h,Dt=new h,qt=new h,bt=new h,zt=new h,kt=new h,Pt=new h,It=new h,Ot=new h,jt=new h,Gt=new h,Yt=new h(0,1,0),Nt=new h(0,-.05,0),Ft=new h,Vt=new a,Ht=new s.Euler,Ut=new h,Xt=new h,Et=new h,Lt=new h,Bt=new i.CharacterCollision;let Zt=class extends w{get autoStepMinWidth(){return this.cc.autostepMinWidth()}set autoStepMinWidth(t){this.cc.enableAutostep(this.cc.autostepMaxHeight(),t,this.cc.autostepIncludesDynamicBodies())}get autoStepDynamicObjects(){return this.cc.autostepIncludesDynamicBodies()}set autoStepDynamicObjects(t){this.cc.enableAutostep(this.cc.autostepMaxHeight(),this.cc.autostepMinWidth(),t)}get autoStepMaxHeight(){return this.cc.autostepMaxHeight()}set autoStepMaxHeight(t){this.cc.enableAutostep(t,this.cc.autostepMinWidth(),this.cc.autostepIncludesDynamicBodies())}get snapToGround(){return this.cc.snapToGroundDistance()}set snapToGround(t){this.cc.enableSnapToGround(t)}set offset(t){this.cc.setOffset(t)}get offset(){return this.cc.offset()}set normalNudgeFactor(t){this.cc.setNormalNudgeFactor(t)}get normalNudgeFactor(){return this.cc.normalNudgeFactor()}teleport(t,e=this.actor.rotation.y){this.state.position.copy(t),this.state.velocity.set(0,0,0),this.state.yaw=e,this.actor.position.copy(t),this.actor.rotation.y=e,this.rotationInput.rotation.y=e,this.physicsSystem.setPosition(this.actor,t),this.physicsSystem.setRotation(this.actor,this.actor.quaternion),this.physicsSystem.flushModifiedBodyPositionsToColliders()}constructor(){super(),this.isDefaultCharacterMovementComponent=!0,this.directionInput=new u,this.jumpInput=new d,this.sprintInput=new d,this.crouchInput=new d,this.rotationInput=new p,this.horizontalSpeed=0,this.maxSpeed=8,this.maxSpeedBackwards=8,this.maxSpeedSprint=12,this.maxSpeedCrouch=4,this.slideEnabled=!1,this.slideMinStartSpeed=8,this.slideMinExitSpeed=4,this.slideDeceleration=3,this.slideGravityScale=1,this.slideSteeringRate=2,this.sprintStartBoostSpeed=0,this.sprintStartBoostDamping=6,this.jumpVelocity=7,this.fallingMovementControl=.5,this.fallingReorientation=!1,this.groundAccelerationRate=4,this.gravityOverride=null,this.colliderHeight=2,this.colliderRadius=.5,this.crouchColliderHeight=1,this.crouchColliderRadius=null,this.jumpInAir=!1,this.mass=50,this.allowSliding=!0,this.minSlopeSlideAngle=r.degToRad(70),this.maxSlopeClimbAngle=r.degToRad(70),this.applyImpulsesToDynamicBodies=!0,this.normalizedDirection=!0,this.characterCollision=!1,this.allowRootMotionJumpCancel=!1,this.enabled=!0,this.rotateToMovementDirection=!1,this.smoothRotation=!0,this.rotationSpeed=40,this.maxRotationSpeed=100,this.impulseDamping=2,this.moveSendInterval=H,this.simulatedStateInterval=X,this.maxSavedMoves=Y,this.maxReplayedMoves=j,this.maxServerMovesPerTick=G,this.maxMovesPerPacket=O,this.maxMoveDeltaTime=I,this.maxCombinedMoveDeltaTime=P,this.maxServerMoveDeltaTimeScalar=N,this.maxClientTimeAhead=z,this.maxServerMoveTimeBudget=F,this.maxSubstepDeltaTime=V,this.fallGraceTime=.1,this.correctionToleranceSq=E*E,this.largeCorrectionThreshold=k,this.teleportSnapThreshold=L,this.listenServerSmoothing=!0,this.listenServerSmoothLocationTime=.04,this.listenServerMaxSmoothUpdateDistance=1,this.listenServerNoSmoothUpdateDistance=2.5,this.debugCorrections=!0,this.suppressUncrouchLift=!1,this.velocity=new h,this.visualSmoothingOffset=new h,this.slideDirection=new h,this.slideSurfaceNormal=new h(0,1,0),this.mode=A.walking,this.isSprinting=!1,this.isCrouching=!1,this.isSliding=!1,this.pressedJump=!1,this.rayTestResult=new f,this.groundRayTestOptions={debug:!1,excludeTriggers:!0,excludeActor:void 0,resolveActor:!1},this.physicsSystem=c(M),this.netService=c(S),this.state={sequence:0,position:new h,velocity:new h,yaw:0,lookYaw:0,mode:A.walking,grounded:!1,currentSpeed:0,fallGraceTime:0,prevInputDirection:new h,prevRotateToMovementDirection:!1},this.movementModes=new Map,this.savedMoves=new et(Y),this.serverMoveTiming=st(),this.serverMoveQueue=[],this.inputMoveScratch={sequence:0,clientTime:0,dt:0,inputX:0,inputY:0,yaw:0,flags:0,clientX:0,clientY:0,clientZ:0,rootMotionX:0,rootMotionY:0,rootMotionZ:0},this.authoritativeMoveScratch={sequence:0,clientTime:0,dt:0,inputX:0,inputY:0,yaw:0,flags:0,clientX:0,clientY:0,clientZ:0,rootMotionX:0,rootMotionY:0,rootMotionZ:0},this.movementModeContext={component:this,state:this.state,move:this.inputMoveScratch,deltaTime:0,rotatedDirection:wt,platformVelocity:At,outVelocity:Rt,rootMotionActive:!1,startedSprinting:!1},this.proxySmoother=new tt(U,L),this.proxySample=ot(),this.pendingSnapshot=ot(),this.impulse=new h,this.prevRootMotionPos=new h,this.rootMotionOptions={},this.rootMotionTargetConstraints=new Set,this.resetRootMotion=!1,this.nextMoveSequence=0,this.clientMoveTime=0,this.lastSentClientMoveTime=0,this.moveSendAccumulator=0,this.simulatedStateAccumulator=0,this.proxyTime=0,this.lastServerProcessedSequence=0,this.lastServerReceivedSequence=0,this.lastAutonomousMoveFlags=0,this.previousSprintInput=!1,this.previousCrouchInput=!1,this.crouchSuppressedUntilRelease=!1,this.slideOnLandingRequested=!1,this.initialized=!1,this.cc=this.physicsSystem.getCharacterController(.1),this.cc.enableSnapToGround(.1),this.cc.enableAutostep(0,.1,!1),this.registerMovementMode(Wt),this.registerMovementMode($t),this.registerMovementMode({mode:A.sliding,update:t=>this.updateSlidingMode(t)})}onInit(){this.groundRayTestOptions.excludeActor=this.actor,this.configureCharacterController(),this.physicsSystem.addActor(this.actor,[this.createCollisionShape()],{mass:0,type:C.kinematic,continousCollisionDetection:!1,friction:0,restitution:.5,ignoreForNavMesh:!0}),this.rotationInput.rotation.copy(this.actor.rotation),this.initializeStateFromActor(),this.initialized=!0,this.physicsSystem.beforeStep.pipe(o(this.disposed)).subscribe(t=>this.tick(t))}onLateUpdate(t){this.updateVisualSmoothing(t)}registerMovementMode(t){this.movementModes.set(t.mode,t)}applyImpulse(t){this.impulse.add(t)}getEffectiveGravity(){return this.gravityOverride??this.physicsSystem.getGravity().y}get isGrounded(){return this.rayTestResult.hasHit||this.cc.computedGrounded()}setRootMotionAction(t,e={}){const i=t?.getClip();if(i instanceof l){this.rootMotionAction=t,this.rootMotionOptions=e,this.resetRootMotion=!0;const o=[];this.rootMotionInterpolant=i.motionTrack.InterpolantFactoryMethodSmooth(o);const n=s.PropertyBinding.parseTrackName(i.motionTrack.name);this.rootMotionRootBone=this.actor.object.getObjectByName(n.nodeName)}}addRootMotionTargetConstraint(t,e,i=0){const o={targetPosition:t,minimumHorizontalDistance:Math.max(0,e),maximumAddedHorizontalDistance:Math.max(0,i),initialHorizontalDistance:Math.hypot(t.x-this.actor.position.x,t.z-this.actor.position.z),addedHorizontalDistance:0,requestedProgress:0};this.rootMotionTargetConstraints.add(o);let s=!1;return{setProgress:t=>{o.requestedProgress=Math.max(o.requestedProgress,Math.min(1,Math.max(0,t)))},dispose:()=>{s||(s=!0,this.rootMotionTargetConstraints.delete(o))}}}serverMove(t){const e=rt(t);if(e.ok)for(const t of e.value.moves)mt(t,this.lastServerReceivedSequence,this.maxMoveDeltaTime)&&(this.lastServerReceivedSequence=t.sequence,this.serverMoveQueue.push(t))}clientMoveAck(t){const e=at(t);e.ok&&this.actor.netRole===m.autonomousProxy&&(this.savedMoves.ack(e.value.sequence),this.sprintPolicy?.ack?.(e.value.sequence,!1))}clientMoveCorrection(t){const e=ht(t);e.ok&&this.actor.netRole===m.autonomousProxy&&(this.debugCorrections&&(null==this.correctionDebug&&(this.correctionDebug=new _t(ee(this.actor.object))),this.correctionDebug.update(this.state.position,e.value.snapshot)),this.applyCorrection(e.value.snapshot))}simulatedState(t){const e=lt(t);e.ok&&this.actor.netRole===m.simulatedProxy&&this.proxySmoother.push(e.value,this.proxyTime)}tick(t){if(!this.initialized)return;t=Math.min(.1,t);const e=this.actor.netRole,i=this.netService.mode;e!==m.simulatedProxy?this.enabled&&(e!==m.autonomousProxy?this.isRemoteControlledAuthority(i,e)?this.tickRemoteAuthority(t):this.tickAuthority(t,i):this.tickAutonomousProxy(t)):this.tickSimulatedProxy(t)}tickAutonomousProxy(t){const e=this.state.mode,i=this.lastAutonomousMoveFlags,o=this.createMoveFromInput(t,++this.nextMoveSequence,{quantizeClientTime:!0});this.performMove(o,ie),this.resetToggleCrouchAfterSlide(e);const s=this.state.mode!==e||o.flags!==i;this.lastAutonomousMoveFlags=o.flags,this.savedMoves.capacity=this.maxSavedMoves,this.savedMoves.maxCombinedDeltaTime=Math.min(this.maxCombinedMoveDeltaTime,this.maxMoveDeltaTime),this.savedMoves.push(o),this.sendSavedMoves(t,s)}tickAuthority(t,e){const i=this.state.mode,o=this.createMoveFromInput(t,++this.nextMoveSequence);this.performMove(o,ie),this.resetToggleCrouchAfterSlide(i),e!==yt.none&&this.sendSimulatedState(t,!1)}resetToggleCrouchAfterSlide(t){t===A.sliding&&this.state.mode!==A.sliding&&"toggle"===this.crouchInput.mode&&this.crouchInput.activated&&this.crouchInput.setActivated(!1)}tickRemoteAuthority(t){it(this.serverMoveTiming,t,this.maxServerMoveTimeBudget);let e=!1;const i=Number.isFinite(this.maxServerMovesPerTick)?Math.max(1,Math.floor(this.maxServerMovesPerTick)):this.serverMoveQueue.length,o=Math.min(this.serverMoveQueue.length,i),s=Et.copy(this.actor.position);let n=!1;for(let t=0;t<o;t++){const t=this.serverMoveQueue.shift();if(null==t)break;if(!mt(t,this.lastServerProcessedSequence,this.maxMoveDeltaTime)){console.log("Net: Invalid move",t),this.sendOwnerCorrection(!0),e=!0;continue}const i=nt(this.serverMoveTiming,t,{maxMoveDeltaTime:this.maxMoveDeltaTime,maxServerMoveDeltaTimeScalar:this.maxServerMoveDeltaTimeScalar,maxClientTimeAhead:this.maxClientTimeAhead});if(i<0){this.sendOwnerCorrection(!0),e=!0;continue}const o=Jt(t,this.authoritativeMoveScratch);o.dt=i,o.rootMotionX=0,o.rootMotionY=0,o.rootMotionZ=0,o.flags&=~W,this.performMove(o,{immediate:!0,savedRootMotion:!1}),n=!0,this.lastServerProcessedSequence=o.sequence;const s=Kt(this.state,t);if(s>this.correctionToleranceSq){console.log(`Net server: Correction error: ${Math.sqrt(s)}`);const o=this.state.position.x-t.clientX,n=this.state.position.y-t.clientY,r=this.state.position.z-t.clientZ;console.log(`diff x: ${o}, y: ${n}, z: ${r}`),console.log({sequence:t.sequence,moveDt:t.dt,serverMoveDeltaTime:i,flags:t.flags,mode:this.state.mode,grounded:this.state.grounded,fallGraceTime:this.state.fallGraceTime}),this.sendOwnerCorrection(s>this.largeCorrectionThreshold*this.largeCorrectionThreshold),e=!0}}0===this.lastServerProcessedSequence||e||(this.sprintPolicy?.ack?.(this.lastServerProcessedSequence,!1),this.clientMoveAck(dt(this.lastServerProcessedSequence))),n&&this.accumulateListenServerVisualSmoothing(s,this.actor.position),this.sendSimulatedState(t,!1)}tickSimulatedProxy(t){this.proxyTime+=t,this.proxySmoother.sample(this.proxyTime,this.proxySample)&&this.applySnapshot(this.proxySample,!0)}accumulateListenServerVisualSmoothing(t,e){if(!this.shouldSmoothListenServerRemoteAuthority())return void this.visualSmoothingOffset.set(0,0,0);Lt.subVectors(t,e);const i=Lt.lengthSq();i>1e-8&&(i>this.listenServerNoSmoothUpdateDistance*this.listenServerNoSmoothUpdateDistance?this.visualSmoothingOffset.set(0,0,0):(i>this.listenServerMaxSmoothUpdateDistance*this.listenServerMaxSmoothUpdateDistance&&Lt.setLength(this.listenServerMaxSmoothUpdateDistance),this.visualSmoothingOffset.add(Lt)))}updateVisualSmoothing(t){if(this.visualSmoothingOffset.lengthSq()<=1e-8)return void this.visualSmoothingOffset.set(0,0,0);if(!this.shouldSmoothListenServerRemoteAuthority())return void this.visualSmoothingOffset.set(0,0,0);const e=Math.max(.001,this.listenServerSmoothLocationTime);t<e?this.visualSmoothingOffset.multiplyScalar(1-t/e):this.visualSmoothingOffset.set(0,0,0),this.visualSmoothingOffset.lengthSq()<=1e-4&&this.visualSmoothingOffset.set(0,0,0)}shouldSmoothListenServerRemoteAuthority(){return this.listenServerSmoothing&&this.actor.netRole===m.authority&&this.netService.isServer&&!this.netService.isDedicatedServer&&this.isRemoteControlledAuthority()}createMoveFromInput(t,e,i={}){const o=r.clamp(t,.001,this.maxMoveDeltaTime);this.clientMoveTime+=o;let s=this.clientMoveTime,n=o;!0===i.quantizeClientTime&&(s=Math.max(this.lastSentClientMoveTime+.001,Math.round(1e3*this.clientMoveTime)/1e3),n=r.clamp(s-this.lastSentClientMoveTime,.001,this.maxMoveDeltaTime),this.lastSentClientMoveTime=s),this.sampleRootMotion(bt);const a=this.crouchInput.activated,h=a&&!this.previousCrouchInput;this.previousCrouchInput=a;const l=!a&&this.sprintInput.activated,c=l&&!this.previousSprintInput;this.previousSprintInput=l;const d=(this.jumpInput.activated?Z:0)|(l?Q:0)|(a?B:0)|(h&&this.slideEnabled?$:0)|(c?J:0)|(bt.lengthSq()>0?W:0),u=function(t,e){e.set(t.x,0,t.y),e.lengthSq()>1&&e.normalize();return e}(this.directionInput.vector,Ut),p=this.inputMoveScratch;return p.sequence=e>>>0,p.clientTime=s,p.dt=n,p.inputX=u.x,p.inputY=u.z,p.yaw=this.rotationInput.rotation.y,p.flags=d,p.clientX=this.state.position.x,p.clientY=this.state.position.y,p.clientZ=this.state.position.z,p.rootMotionX=bt.x,p.rootMotionY=bt.y,p.rootMotionZ=bt.z,p}performMove(t,e){const i=r.clamp(t.dt,.001,this.maxMoveDeltaTime),o=Math.max(1,Math.ceil(i/this.maxSubstepDeltaTime)),s=0!==(t.flags&Q)&&0===(t.flags&B);this.sprintPolicy?.update?.(i,t.sequence,s),Ot.set(e.savedRootMotion?t.rootMotionX:0,e.savedRootMotion?t.rootMotionY:0,e.savedRootMotion?t.rootMotionZ:0),e.savedRootMotion||this.sampleRootMotion(Ot);let n=i;const a=i/o;for(let s=0;s<o;s++){const o=i>0?a/i:1;this.performSubstep(t,Math.min(a,n),Ot,o,e.immediate,0===s),n-=a}t.clientX=this.state.position.x,t.clientY=this.state.position.y,t.clientZ=this.state.position.z,t.flags=Ot.lengthSq()>0?t.flags|W:t.flags&~W,this.state.sequence=t.sequence,this.syncPublicState()}performSubstep(t,e,i,o,s,n){if(e<.001)return;this.checkGrounded(this.state.position),this.state.grounded=this.isGrounded;const r=null!=this.rootMotionAction&&this.rootMotionAction.enabled,a=i.lengthSq()>0,h=!r&&0!==(t.flags&Z),l=r||a;this.updateCrouchState(t,n,l,h),this.pressedJump=h,this.isSprinting=!1,this.updateRotation(t,e,r,a),this.resolvePlatformVelocity(At);const c=this.movementModeContext,d=this.movementModes.get(this.state.mode);if(null!=d){c.move=t,c.deltaTime=e,c.rootMotionActive=l,c.startedSprinting=n&&!l&&0===(t.flags&B)&&0!==(t.flags&J);const i=d.update(c);"number"==typeof i&&(this.state.mode=i)}else Rt.set(0,0,0);if(a){zt.copy(i).multiplyScalar(o).applyQuaternion(this.actor.quaternion),kt.copy(zt).divideScalar(e),this.state.mode===A.walking?(Rt.x=kt.x+At.x,Rt.z=kt.z+At.z):(Rt.x=kt.x,Rt.z=kt.z);const t=Math.sqrt(kt.x*kt.x+kt.z*kt.z);t>.4&&(this.state.currentSpeed=t)}if(this.applyImpulseToVelocity(Rt,e),h&&this.isSprinting){const t=Math.max(0,this.maxSpeedSprint),e=Qt(Rt);if(e>t){const i=t/e;Rt.x*=i,Rt.z*=i,this.state.velocity.x*=i,this.state.velocity.z*=i}}this.state.mode===A.walking&&this.state.velocity.copy(Rt),this.applyMovement(Rt,e,s),this.state.mode===A.sliding&&(ft(this.state.velocity,qt,At,this.slideSurfaceNormal,e),this.state.currentSpeed=Qt(this.state.velocity)),this.checkGrounded(this.state.position),this.state.grounded=this.isGrounded,this.updateModeAfterMove(Rt,e),this.state.mode===A.sliding&&this.state.velocity.length()<Math.max(0,this.slideMinExitSpeed)&&(this.state.mode=A.walking)}updateRotation(t,e,i,o){Ct.set(-t.inputX,0,t.inputY),Ct.lengthSq()>1&&Ct.normalize();const s=this.state.prevRotateToMovementDirection!==this.rotateToMovementDirection;let n=r.euclideanModulo(t.yaw-this.state.lookYaw+Math.PI,2*Math.PI)-Math.PI;if(s&&(this.actor.object.quaternion.setFromEuler(Ht.set(0,t.yaw,0)),n=0,this.state.lookYaw=t.yaw,this.state.prevRotateToMovementDirection=this.rotateToMovementDirection),wt.set(0,0,0),i)this.state.lookYaw=t.yaw;else if(this.rotateToMovementDirection){if(Ct.lengthSq()>0&&this.state.mode!==A.falling){const i=Ht.setFromQuaternion(this.actor.object.quaternion,"YXZ").y,o=Math.atan2(Ct.x,Ct.z),s=.99*t.yaw+o;if(this.smoothRotation){const t=r.euclideanModulo(s-i+Math.PI,2*Math.PI)-Math.PI;let o=r.clamp(t*this.rotationSpeed*e,-this.maxRotationSpeed*e,this.maxRotationSpeed*e);Math.abs(o)>Math.abs(t)&&(o=t),this.actor.object.quaternion.setFromEuler(Ht.set(0,i+o,0))}else this.actor.object.quaternion.setFromEuler(Ht.set(0,s,0));this.state.prevInputDirection.lerp(Ct,e*this.rotationSpeed),this.state.lookYaw+=n,wt.copy(this.actor.object.getWorldDirection(Ut).normalize())}}else o||(this.state.lookYaw=t.yaw,this.actor.object.quaternion.setFromEuler(Ht.set(0,t.yaw,0)),Ct.lengthSq()>0&&wt.copy(Ct).applyAxisAngle(Tt,t.yaw).normalize())}applyMovement(t,e,i){if(t.lengthSq()>0||!this.state.grounded){if(Dt.copy(t).multiplyScalar(e),this.state.grounded&&((o=this.state.mode)===A.walking||o===A.sliding)&&(this.state.mode===A.walking&&this.rayTestResult.distance<=this.cc.offset()&&(Dt.y=Math.max(0,At.y*e)),this.physicsSystem.getActorComputedMovement(this.actor,this.cc,Dt,this.getCollisionGroup()),this.cc.computedCollision(0,Bt),null!=Bt.normal1)){const t=Ut.copy(Bt.normal1);Math.acos(r.clamp(t.dot(Tt),-1,1))>this.cc.maxSlopeClimbAngle()&&(Dt.y=.016*this.getEffectiveGravity()*.5)}qt.copy(this.physicsSystem.getActorComputedMovement(this.actor,this.cc,Dt,this.getCollisionGroup()))}else qt.set(0,0,0);var o;this.suppressUncrouchLift&&(this.suppressUncrouchLift=!1,this.state.grounded&&this.state.mode===A.walking&&Dt.y<=1e-6&&qt.y>0&&(qt.y=0)),this.state.position.add(qt),i?(this.actor.position.copy(this.state.position),this.physicsSystem.updateActorTransform(this.actor),this.physicsSystem.flushModifiedBodyPositionsToColliders()):this.physicsSystem.setNextKinematicPosition(this.actor,this.state.position)}updateModeAfterMove(t,e){const i=function(t){if(0===t.numComputedCollisions())return!1;const e=t.computedCollision(0);Ft.set(e.normal2.x,e.normal2.y,e.normal2.z);const i=Ft.angleTo(Tt);Ft.set(e.normal1.x,e.normal1.y,e.normal1.z);const o=Ft.angleTo(Tt);if(i<100)return!1;return o>t.minSlopeSlideAngle()}(this.cc);!this.state.grounded||i?this.state.mode!==A.falling&&(this.state.fallGraceTime+=e,this.state.fallGraceTime>this.fallGraceTime+1e-6&&(this.state.mode=A.falling,this.state.velocity.copy(t))):this.state.velocity.y<=0&&(this.state.mode===A.falling&&(this.state.mode=A.walking),this.state.mode!==A.sliding&&(this.state.velocity.y=0),this.state.fallGraceTime=0)}applyImpulseToVelocity(t,e){if(this.impulse.lengthSq()<=.1)return void this.impulse.set(0,0,0);const i=Math.min(1,this.impulse.length()/5);t.x=r.lerp(t.x,this.impulse.x,i),t.z=r.lerp(t.z,this.impulse.z,i),t.y+=this.impulse.y;const o=Math.exp(-this.impulseDamping*e);this.impulse.x*=o,this.impulse.y>0?this.impulse.y+=e*this.getEffectiveGravity():this.impulse.y*=o,this.impulse.z*=o}sendSavedMoves(t,e=!1){if(this.moveSendAccumulator+=t,!e&&this.moveSendAccumulator<this.moveSendInterval)return;this.moveSendAccumulator=0;const i=this.savedMoves.getMovesForSend(this.maxMovesPerPacket);0!==i.length&&this.serverMove(ct(i))}sendOwnerCorrection(t){const e=this.toSnapshot(this.pendingSnapshot);t&&(e.horizontalSpeed=this.horizontalSpeed),this.sprintPolicy?.ack?.(e.sequence,!0),this.clientMoveCorrection(ut(e))}sendSimulatedState(t,e){this.simulatedStateAccumulator+=t,!e&&this.simulatedStateAccumulator<this.simulatedStateInterval||(this.simulatedStateAccumulator=0,this.simulatedState(pt(this.toSnapshot(this.pendingSnapshot))))}applyCorrection(t){this.sprintPolicy?.restore?.(t.sequence),this.savedMoves.ack(t.sequence),this.sprintPolicy?.ack?.(t.sequence,!0),this.applySnapshot(t,!0),this.savedMoves.replayAfter(t.sequence,t=>(this.performMove(t,{immediate:!0,savedRootMotion:!0}),null),this.maxReplayedMoves)}applySnapshot(t,e){this.state.sequence=t.sequence,this.state.position.set(t.x,t.y,t.z),this.state.velocity.set(t.velocityX,t.velocityY,t.velocityZ),this.state.yaw=t.yaw,this.state.lookYaw=t.yaw,this.state.mode=t.mode,this.state.currentSpeed=t.horizontalSpeed,this.state.fallGraceTime=0,this.actor.position.copy(this.state.position),this.actor.object.quaternion.setFromEuler(Ht.set(0,t.yaw,0)),this.crouchSuppressedUntilRelease=0!==(t.flags&K),this.setCrouching(0!==(t.flags&_)||t.mode===A.sliding),e&&(this.physicsSystem.updateActorTransform(this.actor),this.physicsSystem.flushModifiedBodyPositionsToColliders()),this.checkGrounded(this.state.position),this.state.grounded=this.isGrounded,this.syncPublicState()}toSnapshot(t){return t.sequence=this.state.sequence,t.x=this.state.position.x,t.y=this.state.position.y,t.z=this.state.position.z,t.velocityX=this.state.velocity.x,t.velocityY=this.state.velocity.y,t.velocityZ=this.state.velocity.z,t.yaw=Ht.setFromQuaternion(this.actor.object.quaternion,"YXZ").y,t.mode=this.state.mode,t.flags=(this.isCrouching?_:0)|(this.crouchSuppressedUntilRelease?K:0),t.horizontalSpeed=this.state.currentSpeed,t}syncPublicState(){this.velocity.copy(this.state.velocity),this.mode=this.state.mode,this.horizontalSpeed=this.state.currentSpeed,this.isSliding=this.state.mode===A.sliding,this.isSliding?(this.resolveGroundNormal(this.slideSurfaceNormal),this.slideDirection.copy(this.state.velocity).projectOnPlane(this.slideSurfaceNormal),this.slideDirection.lengthSq()>1e-8?this.slideDirection.normalize():this.slideDirection.set(0,0,0)):(this.slideDirection.set(0,0,0),this.slideSurfaceNormal.copy(Tt))}sampleRootMotion(t){if(t.set(0,0,0),null==this.rootMotionAction||this.rootMotionAction.isRunning()||this.clearRootMotionAction(this.rootMotionAction),null!=this.rootMotionAction&&this.rootMotionOptions.cancelWithJump&&this.jumpInput.activated&&(this.isGrounded||this.jumpInAir)){const e=this.rootMotionAction,i=this.rootMotionOptions.onJumpCancel;return this.clearRootMotionAction(e),e.stop(),i?.(),t}if(null==this.rootMotionAction)return t;if(!(this.rootMotionAction.getClip()instanceof l))return t;const e=this.rootMotionInterpolant;this.resetRootMotion&&(this.prevRootMotionPos.fromArray(e.evaluate(0)),this.resetRootMotion=!1),It.fromArray(e.evaluate(this.rootMotionAction.time)),t.subVectors(It,this.prevRootMotionPos),this.prevRootMotionPos.copy(It);if((this.rootMotionRootBone??this.rootMotionAction.getRoot()).getWorldScale(Pt),t.multiply(Pt),this.rootMotionTargetConstraints.size>0){zt.copy(t).applyQuaternion(this.actor.quaternion);for(const t of this.rootMotionTargetConstraints)if(t.requestedProgress>0&&t.addedHorizontalDistance<t.maximumAddedHorizontalDistance){const e=r.lerp(t.initialHorizontalDistance,t.minimumHorizontalDistance,t.requestedProgress),i=Math.min(D(zt,this.actor.position,t.targetPosition,e),t.maximumAddedHorizontalDistance-t.addedHorizontalDistance);t.addedHorizontalDistance+=b(zt,this.actor.position,t.targetPosition,t.minimumHorizontalDistance,i)}else q(zt,this.actor.position,t.targetPosition,t.minimumHorizontalDistance);t.copy(zt).applyQuaternion(Vt.copy(this.actor.quaternion).invert())}return t}clearRootMotionAction(t){null!=t&&this.rootMotionAction!==t||(this.rootMotionAction=null,this.rootMotionOptions={},this.resetRootMotion=!1)}isRemoteControlledAuthority(t=this.netService.mode,e=this.actor.netRole){if(t===yt.none||e!==m.authority||t>=yt.client)return!1;return null!=this.netService.resolveActorConnection(this.actor)&&!this.netService.isLocalOwner(this.actor)}configureCharacterController(){const t=this.cc;t.setApplyImpulsesToDynamicBodies(this.applyImpulsesToDynamicBodies),t.setMinSlopeSlideAngle(this.minSlopeSlideAngle),t.setMaxSlopeClimbAngle(this.maxSlopeClimbAngle),t.setCharacterMass(this.mass),t.setSlideEnabled(this.allowSliding)}initializeStateFromActor(){this.state.position.copy(this.actor.position),this.state.velocity.set(0,0,0),this.state.yaw=this.actor.rotation.y,this.state.lookYaw=this.rotationInput.rotation.y,this.state.mode=this.mode,this.state.currentSpeed=this.horizontalSpeed,this.state.prevRotateToMovementDirection=this.rotateToMovementDirection}createCollisionShape(){const t=this.getColliderHeight(!1),e=this.getColliderRadius(!1),i=new x(t,e);return i.offset.y=this.getCapsuleOffsetY(t,e),i.collisionGroup=xt,i}updateCrouchState(t,e,i,o){if(!(0!==(t.flags&B)))return this.crouchSuppressedUntilRelease=!1,this.slideOnLandingRequested=!1,this.state.mode===A.sliding&&(this.state.mode=A.walking),void this.tryUncrouch();if(this.state.mode===A.sliding&&(!this.slideEnabled||i))return this.slideOnLandingRequested=!1,this.state.mode=A.walking,void(this.crouchSuppressedUntilRelease||this.setCrouching(!0));if(this.state.mode===A.sliding&&o)return this.crouchSuppressedUntilRelease=!0,this.slideOnLandingRequested=!1,void this.tryUncrouch();if(this.crouchSuppressedUntilRelease)return void(this.slideOnLandingRequested=!1);if(this.setCrouching(!0),!this.slideEnabled||i)return void(this.slideOnLandingRequested=!1);if(this.state.mode===A.falling)return void(this.slideOnLandingRequested=!0);this.resolveGroundNormal(Xt);const s=this.slideOnLandingRequested&&this.state.mode===A.walking&&this.state.grounded,n=e&&0!==(t.flags&$)||s;s&&(this.slideOnLandingRequested=!1);const r=Qt(this.state.velocity);this.state.mode===A.walking&&St(this.slideEnabled,n,this.state.grounded,this.isWalkableSlideSurface(Xt),i,r,this.slideMinStartSpeed)&&(this.state.mode=A.sliding,this.slideSurfaceNormal.copy(Xt),this.state.velocity.projectOnPlane(this.slideSurfaceNormal),this.state.currentSpeed=Qt(this.state.velocity))}updateSlidingMode(t){const e=t.state,i=t.move;this.resolveGroundNormal(this.slideSurfaceNormal);const o=this.isWalkableSlideSurface(this.slideSurfaceNormal),s=e.velocity.length();if(!e.grounded||!o)return e.mode=A.falling,$t.update(t),A.falling;if(this.slideEnabled&&0!==(i.flags&B)&&!t.rootMotionActive&&0!==(i.flags&Z))return vt(t.outVelocity,e.velocity,this.jumpVelocity),e.velocity.copy(t.outVelocity),e.currentSpeed=Qt(e.velocity),A.falling;if(!gt(this.slideEnabled,0!==(i.flags&B),e.grounded,o,t.rootMotionActive,s,this.slideMinExitSpeed))return e.mode=A.walking,Wt.update(t),A.walking;const n=Mt(e.velocity,e.velocity,this.slideSurfaceNormal,t.rotatedDirection,this.getEffectiveGravity(),t.deltaTime,this.slideDeceleration,this.slideGravityScale,this.slideSteeringRate);if(e.currentSpeed=Qt(e.velocity),n<Math.max(0,this.slideMinExitSpeed))return e.mode=A.walking,Wt.update(t),A.walking;t.outVelocity.copy(e.velocity).add(t.platformVelocity)}tryUncrouch(){this.isCrouching&&this.canUncrouch()&&this.setCrouching(!1)&&(this.suppressUncrouchLift=!0,this.state.fallGraceTime=0)}resolveGroundNormal(t){return!this.rayTestResult.hasHit||this.rayTestResult.hitNormal.lengthSq()<=1e-8?t.copy(Tt):(t.copy(this.rayTestResult.hitNormal).normalize(),t.dot(Tt)<0&&t.multiplyScalar(-1),t)}isWalkableSlideSurface(t){return t.dot(Tt)>=Math.cos(this.cc.maxSlopeClimbAngle())}setCrouching(t){return this.isCrouching!==t&&(this.isCrouching=t,this.syncColliderShape(),!0)}canUncrouch(){const t=this.getColliderHeight(!1),e=this.getColliderRadius(!1);return!this.physicsSystem.hasActorCapsuleIntersection(this.actor,this.state.position,t,e,this.getCapsuleOffsetY(t,e),this.getCollisionGroup())}syncColliderShape(){const t=this.getColliderHeight(this.isCrouching),e=this.getColliderRadius(this.isCrouching);this.physicsSystem.setActorCapsuleCollider(this.actor,t,e,this.getCapsuleOffsetY(t,e))}getColliderHeight(t){const e=Number.isFinite(this.colliderHeight)?Math.max(0,this.colliderHeight):0;if(!t)return e;const i=Number.isFinite(this.crouchColliderHeight)?this.crouchColliderHeight:e;return r.clamp(i,0,e)}getColliderRadius(t){const e=Number.isFinite(this.colliderRadius)?Math.max(.001,this.colliderRadius):.001;if(!t)return e;const i=this.crouchColliderRadius??e,o=Number.isFinite(i)?i:e;return r.clamp(o,.001,e)}getCapsuleOffsetY(t,e){return e+t/2+this.offset}checkGrounded(t){Nt.y=-Math.max(.05,this.cc.snapToGroundDistance()),this.physicsSystem.rayTest(jt.addVectors(t,Yt.set(0,this.offset,0)),Gt.addVectors(t,Nt),this.rayTestResult,this.groundRayTestOptions)}resolvePlatformVelocity(t){return t.set(0,0,0),this.rayTestResult.hasHit&&null!=this.rayTestResult.actor&&this.physicsSystem.getLinearVelocity(this.rayTestResult.actor,t),t}getCollisionGroup(){return this.characterCollision?null:xt}};t([T(),e("design:type",Number)],Zt.prototype,"colliderHeight",void 0),t([T(),e("design:type",Number)],Zt.prototype,"colliderRadius",void 0),t([T(),e("design:type",Number)],Zt.prototype,"crouchColliderHeight",void 0),t([T({optional:!0}),e("design:type",Number)],Zt.prototype,"crouchColliderRadius",void 0),t([g(!1),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"serverMove",null),t([y(!1),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"clientMoveAck",null),t([y(!0),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"clientMoveCorrection",null),t([v(!1),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"simulatedState",null),Zt=t([R({inEditor:!1}),e("design:paramtypes",[])],Zt);export{Zt as CharacterMovementComponent};const Wt={mode:A.walking,update(t){const e=t.component,i=t.move,o=t.state,s=t.rotatedDirection,n=Math.min(1,Math.sqrt(i.inputX*i.inputX+i.inputY*i.inputY)),r=!e.rotateToMovementDirection&&i.inputY<0,a=e.isCrouching,h=!t.rootMotionActive&&!a&&0===(i.flags&B)&&0!==(i.flags&Q)&&!r&&0!==s.lengthSq()&&(e.sprintPolicy?.canSprint()??!0),l=(a?e.maxSpeedCrouch:r?e.maxSpeedBackwards:h?e.maxSpeedSprint:e.maxSpeed)*(e.normalizedDirection?1:n);if(0!==s.lengthSq()){e.isSprinting=h,h&&e.sprintPolicy?.commitSprint(t.deltaTime,i.sequence);let a=Math.min(l,o.currentSpeed),c=e.groundAccelerationRate;if(h&&t.startedSprinting&&e.sprintStartBoostSpeed>0){const t=e.normalizedDirection?1:n;a=Math.max(a,l+e.sprintStartBoostSpeed*t),c=e.sprintStartBoostDamping}else h&&!r&&e.sprintStartBoostSpeed>0&&o.currentSpeed>l&&(a=o.currentSpeed,c=e.sprintStartBoostDamping);o.currentSpeed=function(t,e,i,o){return e+(t-e)*Math.exp(-i*o)}(a,l,c,t.deltaTime),t.outVelocity.copy(s).multiplyScalar(function(t,e,i,o){const s=i*o;if(s<=1e-6)return t;return e+(t-e)*(1-Math.exp(-s))/s}(a,l,c,t.deltaTime))}else o.currentSpeed=0,t.outVelocity.set(0,0,0);if(0!==(i.flags&Z))return o.mode=A.falling,o.velocity.copy(t.outVelocity),o.velocity.y=e.jumpVelocity,A.falling;t.outVelocity.y=t.deltaTime*e.getEffectiveGravity(),t.outVelocity.add(t.platformVelocity)}},$t={mode:A.falling,update(t){const e=t.component,i=t.state,o=t.move;0!==(o.flags&Z)&&e.jumpInAir&&(i.velocity.copy(t.outVelocity),i.velocity.y=e.jumpVelocity),i.velocity.y+=t.deltaTime*e.getEffectiveGravity(),t.outVelocity.copy(i.velocity),t.outVelocity.addScaledVector(t.rotatedDirection,e.fallingMovementControl),e.fallingReorientation&&t.outVelocity.applyAxisAngle(Tt,r.euclideanModulo(o.yaw-i.lookYaw+Math.PI,2*Math.PI)-Math.PI)}};function Qt(t){return Math.sqrt(t.x*t.x+t.z*t.z)}function Jt(t,e){return e.sequence=t.sequence,e.clientTime=t.clientTime,e.dt=t.dt,e.inputX=t.inputX,e.inputY=t.inputY,e.yaw=t.yaw,e.flags=t.flags,e.clientX=t.clientX,e.clientY=t.clientY,e.clientZ=t.clientZ,e.rootMotionX=t.rootMotionX,e.rootMotionY=t.rootMotionY,e.rootMotionZ=t.rootMotionZ,e}function Kt(t,e){const i=t.position.x-e.clientX,o=t.position.y-e.clientY,s=t.position.z-e.clientZ;return i*i+o*o+s*s}class _t{constructor(t){this.scene=t,this.serverPosition=new h,this.direction=new h}update(t,e){const i=this.serverPosition.set(e.x,e.y,e.z),o=i.distanceTo(t);o>.1&&console.log(`Correction applied. Error: ${o.toFixed(3)}. Server position: (${i.x.toFixed(2)}, ${i.y.toFixed(2)}, ${i.z.toFixed(2)}), Client position: (${t.x.toFixed(2)}, ${t.y.toFixed(2)}, ${t.z.toFixed(2)})`);const s=this.direction.copy(i).sub(t).normalize();te.position.copy(t),te.setDirection(s),te.setLength(o);const n=ee(this.scene);n.add(te),setTimeout(()=>{n.remove(te)},1e3)}}const te=new n(new h(0,1,0),new h,1,16711680);function ee(t){return null==t.parent?t:ee(t.parent)}const ie={immediate:!1,savedRootMotion:!0};/*
|
|
1
|
+
import{__decorate as t,__metadata as e}from"tslib";import i from"@dimforge/rapier3d-simd-compat";import{takeUntil as o}from"rxjs";import*as s from"three";import{ArrowHelper as n,MathUtils as r,Quaternion as a,Vector3 as h}from"three";import{RootMotionClip as l}from"../../../../animation/root-motion.js";import{inject as c}from"../../../../inject.js";import{ActionInput as d,AxisInput as u,RotationInput as p}from"../../../../input/index.js";import{NetRole as m}from"../../../../net/service/net-actor-role.js";import{RunOnClient as y,RunOnNotOwner as v,RunOnServer as g}from"../../../../net/service/rpc-decorator.js";import{NetService as S}from"../../../../net/service/net-service.js";import{PhysicsSystem as M,RayTestResult as f}from"../../../../services/physics/physics-system.js";import{CapsuleCollisionShape as x}from"../../../../../scene/collision/collision-shape.js";import{Parameter as T}from"../../../../../shader/parameter.js";import{PhysicsBodyType as C}from"../../../../services/physics/physics-system.js";import{ActorComponent as w,Component as R}from"../../../component.js";import{CharacterMovementMode as A}from"./modes.js";import{calculateRootMotionTargetApproachCorrection as D,constrainRootMotionTargetApproach as q,warpRootMotionTargetApproach as b}from"./root-motion-constraint.js";import{DEFAULT_MAX_CLIENT_TIME_AHEAD as z,DEFAULT_LARGE_CORRECTION_THRESHOLD as k,DEFAULT_MAX_COMBINED_MOVE_DELTA_TIME as P,DEFAULT_MAX_MOVE_DELTA_TIME as I,DEFAULT_MAX_MOVES_PER_PACKET as G,DEFAULT_MAX_REPLAYED_MOVES as O,DEFAULT_MAX_SERVER_MOVES_PER_TICK as j,DEFAULT_MAX_SAVED_MOVES as Y,DEFAULT_MAX_SERVER_MOVE_DELTA_TIME_SCALAR as N,DEFAULT_MAX_SERVER_MOVE_TIME_BUDGET as F,DEFAULT_MAX_SUBSTEP_DELTA_TIME as V,DEFAULT_MOVE_SEND_INTERVAL as H,DEFAULT_SIMULATED_INTERPOLATION_DELAY as U,DEFAULT_SIMULATED_STATE_INTERVAL as X,DEFAULT_SMALL_CORRECTION_TOLERANCE as E,DEFAULT_TELEPORT_SNAP_THRESHOLD as L,MOVE_FLAG_CROUCH as B,MOVE_FLAG_JUMP as Z,MOVE_FLAG_ROOT_MOTION as W,MOVE_FLAG_SLIDE_START as $,MOVE_FLAG_SPRINT as Q,MOVE_FLAG_SPRINT_START as J,SNAPSHOT_FLAG_CROUCH_SUPPRESSED as K,SNAPSHOT_FLAG_CROUCHING as _,NetCharacterProxySmoother as tt,NetCharacterSavedMoveBuffer as et,accrueNetCharacterServerMoveTime as it,createNetCharacterMovementSnapshot as ot,createNetCharacterServerMoveTimingState as st,consumeNetCharacterServerMoveTime as nt,decodeNetCharacterMoveBatch as rt,decodeNetCharacterOwnerAck as at,decodeNetCharacterOwnerCorrection as ht,decodeNetCharacterSimulatedState as lt,encodeNetCharacterMoveBatch as ct,encodeNetCharacterOwnerAck as dt,encodeNetCharacterOwnerCorrection as ut,encodeNetCharacterSimulatedState as pt,validateNetCharacterMove as mt}from"./net-character-movement-protocol.js";import{NetMode as yt}from"../../../../net/net-session.js";import{setSlideJumpVelocity as vt,shouldContinueSlide as gt,shouldStartSlide as St,updateSlideVelocity as Mt,updateSlideVelocityFromDisplacement as ft}from"./character-slide.js";export*from"./net-character-movement-protocol.js";const xt=131070,Tt=new h(0,1,0),Ct=new h,wt=new h,Rt=new h,At=new h,Dt=new h,qt=new h,bt=new h,zt=new h,kt=new h,Pt=new h,It=new h,Gt=new h,Ot=new h,jt=new h,Yt=new h(0,1,0),Nt=new h(0,-.05,0),Ft=new h,Vt=new a,Ht=new s.Euler,Ut=new h,Xt=new h,Et=new h,Lt=new h,Bt=new i.CharacterCollision;let Zt=class extends w{get autoStepMinWidth(){return this.cc.autostepMinWidth()}set autoStepMinWidth(t){this.cc.enableAutostep(this.cc.autostepMaxHeight(),t,this.cc.autostepIncludesDynamicBodies())}get autoStepDynamicObjects(){return this.cc.autostepIncludesDynamicBodies()}set autoStepDynamicObjects(t){this.cc.enableAutostep(this.cc.autostepMaxHeight(),this.cc.autostepMinWidth(),t)}get autoStepMaxHeight(){return this.cc.autostepMaxHeight()}set autoStepMaxHeight(t){this.cc.enableAutostep(t,this.cc.autostepMinWidth(),this.cc.autostepIncludesDynamicBodies())}get snapToGround(){return this.cc.snapToGroundDistance()}set snapToGround(t){this.cc.enableSnapToGround(t)}set offset(t){this.cc.setOffset(t)}get offset(){return this.cc.offset()}set normalNudgeFactor(t){this.cc.setNormalNudgeFactor(t)}get normalNudgeFactor(){return this.cc.normalNudgeFactor()}teleport(t,e=this.actor.rotation.y){this.state.position.copy(t),this.state.velocity.set(0,0,0),this.state.yaw=e,this.actor.position.copy(t),this.actor.rotation.y=e,this.rotationInput.rotation.y=e,this.physicsSystem.setPosition(this.actor,t),this.physicsSystem.setRotation(this.actor,this.actor.quaternion),this.physicsSystem.flushModifiedBodyPositionsToColliders()}constructor(){super(),this.isDefaultCharacterMovementComponent=!0,this.directionInput=new u,this.jumpInput=new d,this.sprintInput=new d,this.crouchInput=new d,this.rotationInput=new p,this.horizontalSpeed=0,this.maxSpeed=8,this.maxSpeedBackwards=8,this.maxSpeedSprint=12,this.maxSpeedCrouch=4,this.slideEnabled=!1,this.slideMinStartSpeed=8,this.slideMinExitSpeed=4,this.slideDeceleration=3,this.slideGravityScale=1,this.slideSteeringRate=2,this.sprintStartBoostSpeed=0,this.sprintStartBoostDamping=6,this.jumpVelocity=7,this.fallingMovementControl=.5,this.fallingReorientation=!1,this.groundAccelerationRate=4,this.gravityOverride=null,this.colliderHeight=2,this.colliderRadius=.5,this.crouchColliderHeight=1,this.crouchColliderRadius=null,this.jumpInAir=!1,this.mass=50,this.allowSliding=!0,this.minSlopeSlideAngle=r.degToRad(70),this.maxSlopeClimbAngle=r.degToRad(70),this.applyImpulsesToDynamicBodies=!0,this.normalizedDirection=!0,this.characterCollision=!1,this.allowRootMotionJumpCancel=!1,this.enabled=!0,this.rotateToMovementDirection=!1,this.smoothRotation=!0,this.rotationSpeed=40,this.maxRotationSpeed=100,this.impulseDamping=2,this.moveSendInterval=H,this.simulatedStateInterval=X,this.maxSavedMoves=Y,this.maxReplayedMoves=O,this.maxServerMovesPerTick=j,this.maxMovesPerPacket=G,this.maxMoveDeltaTime=I,this.maxCombinedMoveDeltaTime=P,this.maxServerMoveDeltaTimeScalar=N,this.maxClientTimeAhead=z,this.maxServerMoveTimeBudget=F,this.maxSubstepDeltaTime=V,this.fallGraceTime=.1,this.correctionToleranceSq=E*E,this.largeCorrectionThreshold=k,this.teleportSnapThreshold=L,this.listenServerSmoothing=!0,this.listenServerSmoothLocationTime=.04,this.listenServerMaxSmoothUpdateDistance=1,this.listenServerNoSmoothUpdateDistance=2.5,this.debugCorrections=!0,this.suppressUncrouchLift=!1,this.velocity=new h,this.visualSmoothingOffset=new h,this.slideDirection=new h,this.slideSurfaceNormal=new h(0,1,0),this.mode=A.walking,this.isSprinting=!1,this.isCrouching=!1,this.isSliding=!1,this.pressedJump=!1,this.rayTestResult=new f,this.groundRayTestOptions={debug:!1,excludeTriggers:!0,excludeActor:void 0,resolveActor:!1},this.physicsSystem=c(M),this.netService=c(S),this.state={sequence:0,position:new h,velocity:new h,yaw:0,lookYaw:0,mode:A.walking,grounded:!1,currentSpeed:0,fallGraceTime:0,prevInputDirection:new h,prevRotateToMovementDirection:!1},this.movementModes=new Map,this.savedMoves=new et(Y),this.serverMoveTiming=st(),this.serverMoveQueue=[],this.inputMoveScratch={sequence:0,clientTime:0,dt:0,inputX:0,inputY:0,yaw:0,flags:0,clientX:0,clientY:0,clientZ:0,rootMotionX:0,rootMotionY:0,rootMotionZ:0},this.authoritativeMoveScratch={sequence:0,clientTime:0,dt:0,inputX:0,inputY:0,yaw:0,flags:0,clientX:0,clientY:0,clientZ:0,rootMotionX:0,rootMotionY:0,rootMotionZ:0},this.movementModeContext={component:this,state:this.state,move:this.inputMoveScratch,deltaTime:0,rotatedDirection:wt,platformVelocity:At,outVelocity:Rt,rootMotionActive:!1,startedSprinting:!1},this.proxySmoother=new tt(U,L),this.proxySample=ot(),this.pendingSnapshot=ot(),this.impulse=new h,this.prevRootMotionPos=new h,this.rootMotionOptions={},this.rootMotionTargetConstraints=new Set,this.resetRootMotion=!1,this.nextMoveSequence=0,this.clientMoveTime=0,this.lastSentClientMoveTime=0,this.moveSendAccumulator=0,this.simulatedStateAccumulator=0,this.proxyTime=0,this.lastServerProcessedSequence=0,this.lastServerReceivedSequence=0,this.lastAutonomousMoveFlags=0,this.previousSprintInput=!1,this.previousCrouchInput=!1,this.crouchSuppressedUntilRelease=!1,this.slideOnLandingRequested=!1,this.initialized=!1,this.cc=this.physicsSystem.getCharacterController(.1),this.cc.enableSnapToGround(.1),this.cc.enableAutostep(0,.1,!1),this.registerMovementMode(Wt),this.registerMovementMode($t),this.registerMovementMode({mode:A.sliding,update:t=>this.updateSlidingMode(t)})}onInit(){this.groundRayTestOptions.excludeActor=this.actor,this.configureCharacterController(),this.physicsSystem.addActor(this.actor,[this.createCollisionShape()],{mass:0,type:C.kinematic,continousCollisionDetection:!1,friction:0,restitution:.5,ignoreForNavMesh:!0}),this.rotationInput.rotation.copy(this.actor.rotation),this.initializeStateFromActor(),this.initialized=!0,this.physicsSystem.beforeStep.pipe(o(this.disposed)).subscribe(t=>this.tick(t))}onLateUpdate(t){this.updateVisualSmoothing(t)}registerMovementMode(t){this.movementModes.set(t.mode,t)}applyImpulse(t){this.impulse.add(t)}getEffectiveGravity(){return this.gravityOverride??this.physicsSystem.getGravity().y}get isGrounded(){return this.rayTestResult.hasHit||this.cc.computedGrounded()}setRootMotionAction(t,e={}){const i=t?.getClip();if(i instanceof l){this.rootMotionAction=t,this.rootMotionOptions=e,this.resetRootMotion=!0;const o=[];this.rootMotionInterpolant=i.motionTrack.InterpolantFactoryMethodSmooth(o);const n=s.PropertyBinding.parseTrackName(i.motionTrack.name);this.rootMotionRootBone=this.actor.object.getObjectByName(n.nodeName)}}addRootMotionTargetConstraint(t,e,i=0){const o={targetPosition:t,minimumHorizontalDistance:Math.max(0,e),maximumAddedHorizontalDistance:Math.max(0,i),initialHorizontalDistance:Math.hypot(t.x-this.actor.position.x,t.z-this.actor.position.z),addedHorizontalDistance:0,requestedProgress:0};this.rootMotionTargetConstraints.add(o);let s=!1;return{setProgress:t=>{o.requestedProgress=Math.max(o.requestedProgress,Math.min(1,Math.max(0,t)))},dispose:()=>{s||(s=!0,this.rootMotionTargetConstraints.delete(o))}}}serverMove(t){const e=rt(t);if(e.ok)for(const t of e.value.moves)mt(t,this.lastServerReceivedSequence,this.maxMoveDeltaTime)&&(this.lastServerReceivedSequence=t.sequence,this.serverMoveQueue.push(t))}clientMoveAck(t){const e=at(t);e.ok&&this.actor.netRole===m.autonomousProxy&&(this.savedMoves.ack(e.value.sequence),this.sprintPolicy?.ack?.(e.value.sequence,!1))}clientMoveCorrection(t){const e=ht(t);e.ok&&this.actor.netRole===m.autonomousProxy&&(this.debugCorrections&&(null==this.correctionDebug&&(this.correctionDebug=new _t(ee(this.actor.object))),this.correctionDebug.update(this.state.position,e.value.snapshot)),this.applyCorrection(e.value.snapshot))}simulatedState(t){const e=lt(t);e.ok&&this.actor.netRole===m.simulatedProxy&&this.proxySmoother.push(e.value,this.proxyTime)}tick(t){if(!this.initialized)return;t=Math.min(.1,t);const e=this.actor.netRole,i=this.netService.mode;e!==m.simulatedProxy?this.enabled&&(e!==m.autonomousProxy?this.isRemoteControlledAuthority(i,e)?this.tickRemoteAuthority(t):this.tickAuthority(t,i):this.tickAutonomousProxy(t)):this.tickSimulatedProxy(t)}tickAutonomousProxy(t){const e=this.state.mode,i=this.lastAutonomousMoveFlags,o=this.createMoveFromInput(t,++this.nextMoveSequence,{quantizeClientTime:!0});this.performMove(o,ie),this.resetToggleCrouchAfterSlide(e);const s=this.state.mode!==e||o.flags!==i;this.lastAutonomousMoveFlags=o.flags,this.savedMoves.capacity=this.maxSavedMoves,this.savedMoves.maxCombinedDeltaTime=Math.min(this.maxCombinedMoveDeltaTime,this.maxMoveDeltaTime),this.savedMoves.push(o),this.sendSavedMoves(t,s)}tickAuthority(t,e){const i=this.state.mode,o=this.createMoveFromInput(t,++this.nextMoveSequence);this.performMove(o,ie),this.resetToggleCrouchAfterSlide(i),e!==yt.none&&this.sendSimulatedState(t,!1)}resetToggleCrouchAfterSlide(t){t===A.sliding&&this.state.mode!==A.sliding&&"toggle"===this.crouchInput.mode&&this.crouchInput.activated&&this.crouchInput.setActivated(!1)}tickRemoteAuthority(t){it(this.serverMoveTiming,t,this.maxServerMoveTimeBudget);let e=!1;const i=Number.isFinite(this.maxServerMovesPerTick)?Math.max(1,Math.floor(this.maxServerMovesPerTick)):this.serverMoveQueue.length,o=Math.min(this.serverMoveQueue.length,i),s=Et.copy(this.actor.position);let n=!1;for(let t=0;t<o;t++){const t=this.serverMoveQueue.shift();if(null==t)break;if(!mt(t,this.lastServerProcessedSequence,this.maxMoveDeltaTime)){console.log("Net: Invalid move",t),this.sendOwnerCorrection(!0),e=!0;continue}const i=nt(this.serverMoveTiming,t,{maxMoveDeltaTime:this.maxMoveDeltaTime,maxServerMoveDeltaTimeScalar:this.maxServerMoveDeltaTimeScalar,maxClientTimeAhead:this.maxClientTimeAhead});if(i<0){this.sendOwnerCorrection(!0),e=!0;continue}const o=Jt(t,this.authoritativeMoveScratch);o.dt=i,o.rootMotionX=0,o.rootMotionY=0,o.rootMotionZ=0,o.flags&=~W,this.performMove(o,{immediate:!0,savedRootMotion:!1}),n=!0,this.lastServerProcessedSequence=o.sequence;const s=Kt(this.state,t);if(s>this.correctionToleranceSq){console.log(`Net server: Correction error: ${Math.sqrt(s)}`);const o=this.state.position.x-t.clientX,n=this.state.position.y-t.clientY,r=this.state.position.z-t.clientZ;console.log(`diff x: ${o}, y: ${n}, z: ${r}`),console.log({sequence:t.sequence,moveDt:t.dt,serverMoveDeltaTime:i,flags:t.flags,mode:this.state.mode,grounded:this.state.grounded,fallGraceTime:this.state.fallGraceTime}),this.sendOwnerCorrection(s>this.largeCorrectionThreshold*this.largeCorrectionThreshold),e=!0}}0===this.lastServerProcessedSequence||e||(this.sprintPolicy?.ack?.(this.lastServerProcessedSequence,!1),this.clientMoveAck(dt(this.lastServerProcessedSequence))),n&&this.accumulateListenServerVisualSmoothing(s,this.actor.position),this.sendSimulatedState(t,!1)}tickSimulatedProxy(t){this.proxyTime+=t,this.proxySmoother.sample(this.proxyTime,this.proxySample)&&this.applySnapshot(this.proxySample,!0)}accumulateListenServerVisualSmoothing(t,e){if(!this.shouldSmoothListenServerRemoteAuthority())return void this.visualSmoothingOffset.set(0,0,0);Lt.subVectors(t,e);const i=Lt.lengthSq();i>1e-8&&(i>this.listenServerNoSmoothUpdateDistance*this.listenServerNoSmoothUpdateDistance?this.visualSmoothingOffset.set(0,0,0):(i>this.listenServerMaxSmoothUpdateDistance*this.listenServerMaxSmoothUpdateDistance&&Lt.setLength(this.listenServerMaxSmoothUpdateDistance),this.visualSmoothingOffset.add(Lt)))}updateVisualSmoothing(t){if(this.visualSmoothingOffset.lengthSq()<=1e-8)return void this.visualSmoothingOffset.set(0,0,0);if(!this.shouldSmoothListenServerRemoteAuthority())return void this.visualSmoothingOffset.set(0,0,0);const e=Math.max(.001,this.listenServerSmoothLocationTime);t<e?this.visualSmoothingOffset.multiplyScalar(1-t/e):this.visualSmoothingOffset.set(0,0,0),this.visualSmoothingOffset.lengthSq()<=1e-4&&this.visualSmoothingOffset.set(0,0,0)}shouldSmoothListenServerRemoteAuthority(){return this.listenServerSmoothing&&this.actor.netRole===m.authority&&this.netService.isServer&&!this.netService.isDedicatedServer&&this.isRemoteControlledAuthority()}createMoveFromInput(t,e,i={}){const o=r.clamp(t,.001,this.maxMoveDeltaTime);this.clientMoveTime+=o;let s=this.clientMoveTime,n=o;!0===i.quantizeClientTime&&(s=Math.max(this.lastSentClientMoveTime+.001,Math.round(1e3*this.clientMoveTime)/1e3),n=r.clamp(s-this.lastSentClientMoveTime,.001,this.maxMoveDeltaTime),this.lastSentClientMoveTime=s),this.sampleRootMotion(bt);const a=this.crouchInput.activated,h=a&&!this.previousCrouchInput;this.previousCrouchInput=a;const l=!a&&this.sprintInput.activated,c=l&&!this.previousSprintInput;this.previousSprintInput=l;const d=(this.jumpInput.activated?Z:0)|(l?Q:0)|(a?B:0)|(h&&this.slideEnabled?$:0)|(c?J:0)|(bt.lengthSq()>0?W:0),u=function(t,e){e.set(t.x,0,t.y),e.lengthSq()>1&&e.normalize();return e}(this.directionInput.vector,Ut),p=this.inputMoveScratch;return p.sequence=e>>>0,p.clientTime=s,p.dt=n,p.inputX=u.x,p.inputY=u.z,p.yaw=this.rotationInput.rotation.y,p.flags=d,p.clientX=this.state.position.x,p.clientY=this.state.position.y,p.clientZ=this.state.position.z,p.rootMotionX=bt.x,p.rootMotionY=bt.y,p.rootMotionZ=bt.z,p}performMove(t,e){const i=r.clamp(t.dt,.001,this.maxMoveDeltaTime),o=Math.max(1,Math.ceil(i/this.maxSubstepDeltaTime)),s=0!==(t.flags&Q)&&0===(t.flags&B);this.sprintPolicy?.update?.(i,t.sequence,s),Gt.set(e.savedRootMotion?t.rootMotionX:0,e.savedRootMotion?t.rootMotionY:0,e.savedRootMotion?t.rootMotionZ:0),e.savedRootMotion||this.sampleRootMotion(Gt);let n=i;const a=i/o;for(let s=0;s<o;s++){const o=i>0?a/i:1;this.performSubstep(t,Math.min(a,n),Gt,o,e.immediate,0===s),n-=a}t.clientX=this.state.position.x,t.clientY=this.state.position.y,t.clientZ=this.state.position.z,t.flags=Gt.lengthSq()>0?t.flags|W:t.flags&~W,this.state.sequence=t.sequence,this.syncPublicState()}performSubstep(t,e,i,o,s,n){if(e<.001)return;this.checkGrounded(this.state.position),this.state.grounded=this.isGrounded;const r=null!=this.rootMotionAction&&this.rootMotionAction.enabled,a=i.lengthSq()>0,h=!r&&0!==(t.flags&Z),l=r||a;this.updateCrouchState(t,n,l,h),this.pressedJump=h,this.isSprinting=!1,this.updateRotation(t,e,r,a),this.resolvePlatformVelocity(At);const c=this.movementModeContext,d=this.movementModes.get(this.state.mode);if(null!=d){c.move=t,c.deltaTime=e,c.rootMotionActive=l,c.startedSprinting=n&&!l&&0===(t.flags&B)&&0!==(t.flags&J);const i=d.update(c);"number"==typeof i&&(this.state.mode=i)}else Rt.set(0,0,0);if(a){zt.copy(i).multiplyScalar(o).applyQuaternion(this.actor.quaternion),kt.copy(zt).divideScalar(e),this.state.mode===A.walking?(Rt.x=kt.x+At.x,Rt.z=kt.z+At.z):(Rt.x=kt.x,Rt.z=kt.z);const t=Math.sqrt(kt.x*kt.x+kt.z*kt.z);t>.4&&(this.state.currentSpeed=t)}if(this.applyImpulseToVelocity(Rt,e),h&&this.isSprinting){const t=Math.max(0,this.maxSpeedSprint),e=Qt(Rt);if(e>t){const i=t/e;Rt.x*=i,Rt.z*=i,this.state.velocity.x*=i,this.state.velocity.z*=i}}this.state.mode===A.walking&&this.state.velocity.copy(Rt),this.applyMovement(Rt,e,s),this.state.mode===A.sliding&&(ft(this.state.velocity,qt,At,this.slideSurfaceNormal,e),this.state.currentSpeed=Qt(this.state.velocity)),this.checkGrounded(this.state.position),this.state.grounded=this.isGrounded,this.updateModeAfterMove(Rt,e),this.state.mode===A.sliding&&this.state.velocity.length()<Math.max(0,this.slideMinExitSpeed)&&(this.state.mode=A.walking)}updateRotation(t,e,i,o){Ct.set(-t.inputX,0,t.inputY),Ct.lengthSq()>1&&Ct.normalize();const s=this.state.prevRotateToMovementDirection!==this.rotateToMovementDirection;let n=r.euclideanModulo(t.yaw-this.state.lookYaw+Math.PI,2*Math.PI)-Math.PI;if(s&&(this.actor.object.quaternion.setFromEuler(Ht.set(0,t.yaw,0)),n=0,this.state.lookYaw=t.yaw,this.state.prevRotateToMovementDirection=this.rotateToMovementDirection),wt.set(0,0,0),i)this.state.lookYaw=t.yaw;else if(this.rotateToMovementDirection){if(Ct.lengthSq()>0&&this.state.mode!==A.falling){const i=Ht.setFromQuaternion(this.actor.object.quaternion,"YXZ").y,o=Math.atan2(Ct.x,Ct.z),s=.99*t.yaw+o;if(this.smoothRotation){const t=r.euclideanModulo(s-i+Math.PI,2*Math.PI)-Math.PI;let o=r.clamp(t*this.rotationSpeed*e,-this.maxRotationSpeed*e,this.maxRotationSpeed*e);Math.abs(o)>Math.abs(t)&&(o=t),this.actor.object.quaternion.setFromEuler(Ht.set(0,i+o,0))}else this.actor.object.quaternion.setFromEuler(Ht.set(0,s,0));this.state.prevInputDirection.lerp(Ct,e*this.rotationSpeed),this.state.lookYaw+=n,wt.copy(this.actor.object.getWorldDirection(Ut).normalize())}}else o||(this.state.lookYaw=t.yaw,this.actor.object.quaternion.setFromEuler(Ht.set(0,t.yaw,0)),Ct.lengthSq()>0&&wt.copy(Ct).applyAxisAngle(Tt,t.yaw).normalize())}applyMovement(t,e,i){if(t.lengthSq()>0||!this.state.grounded)if(Dt.copy(t).multiplyScalar(e),this.state.grounded&&((o=this.state.mode)===A.walking||o===A.sliding)){if(this.state.mode===A.walking&&this.rayTestResult.distance<=this.cc.offset()&&(Dt.y=Math.max(0,At.y*e)),qt.copy(this.physicsSystem.getActorComputedMovement(this.actor,this.cc,Dt,this.getCollisionGroup())),this.cc.computedCollision(0,Bt),null!=Bt.normal1){const t=Ut.copy(Bt.normal1);if(Math.acos(r.clamp(t.dot(Tt),-1,1))>this.cc.maxSlopeClimbAngle()){const t=.016*this.getEffectiveGravity()*.5;Dt.y!==t&&(Dt.y=t,qt.copy(this.physicsSystem.getActorComputedMovement(this.actor,this.cc,Dt,this.getCollisionGroup())))}}}else qt.copy(this.physicsSystem.getActorComputedMovement(this.actor,this.cc,Dt,this.getCollisionGroup()));else qt.set(0,0,0);var o;this.suppressUncrouchLift&&(this.suppressUncrouchLift=!1,this.state.grounded&&this.state.mode===A.walking&&Dt.y<=1e-6&&qt.y>0&&(qt.y=0)),this.state.position.add(qt),i?(this.actor.position.copy(this.state.position),this.physicsSystem.updateActorTransform(this.actor),this.physicsSystem.flushModifiedBodyPositionsToColliders()):this.physicsSystem.setNextKinematicPosition(this.actor,this.state.position)}updateModeAfterMove(t,e){const i=function(t){if(0===t.numComputedCollisions())return!1;const e=t.computedCollision(0);Ft.set(e.normal2.x,e.normal2.y,e.normal2.z);const i=Ft.angleTo(Tt);Ft.set(e.normal1.x,e.normal1.y,e.normal1.z);const o=Ft.angleTo(Tt);if(i<100)return!1;return o>t.minSlopeSlideAngle()}(this.cc);!this.state.grounded||i?this.state.mode!==A.falling&&(this.state.fallGraceTime+=e,this.state.fallGraceTime>this.fallGraceTime+1e-6&&(this.state.mode=A.falling,this.state.velocity.copy(t))):this.state.velocity.y<=0&&(this.state.mode===A.falling&&(this.state.mode=A.walking),this.state.mode!==A.sliding&&(this.state.velocity.y=0),this.state.fallGraceTime=0)}applyImpulseToVelocity(t,e){if(this.impulse.lengthSq()<=.1)return void this.impulse.set(0,0,0);const i=Math.min(1,this.impulse.length()/5);t.x=r.lerp(t.x,this.impulse.x,i),t.z=r.lerp(t.z,this.impulse.z,i),t.y+=this.impulse.y;const o=Math.exp(-this.impulseDamping*e);this.impulse.x*=o,this.impulse.y>0?this.impulse.y+=e*this.getEffectiveGravity():this.impulse.y*=o,this.impulse.z*=o}sendSavedMoves(t,e=!1){if(this.moveSendAccumulator+=t,!e&&this.moveSendAccumulator<this.moveSendInterval)return;this.moveSendAccumulator=0;const i=this.savedMoves.getMovesForSend(this.maxMovesPerPacket);0!==i.length&&this.serverMove(ct(i))}sendOwnerCorrection(t){const e=this.toSnapshot(this.pendingSnapshot);t&&(e.horizontalSpeed=this.horizontalSpeed),this.sprintPolicy?.ack?.(e.sequence,!0),this.clientMoveCorrection(ut(e))}sendSimulatedState(t,e){this.simulatedStateAccumulator+=t,!e&&this.simulatedStateAccumulator<this.simulatedStateInterval||(this.simulatedStateAccumulator=0,this.simulatedState(pt(this.toSnapshot(this.pendingSnapshot))))}applyCorrection(t){this.sprintPolicy?.restore?.(t.sequence),this.savedMoves.ack(t.sequence),this.sprintPolicy?.ack?.(t.sequence,!0),this.applySnapshot(t,!0),this.savedMoves.replayAfter(t.sequence,t=>(this.performMove(t,{immediate:!0,savedRootMotion:!0}),null),this.maxReplayedMoves)}applySnapshot(t,e){this.state.sequence=t.sequence,this.state.position.set(t.x,t.y,t.z),this.state.velocity.set(t.velocityX,t.velocityY,t.velocityZ),this.state.yaw=t.yaw,this.state.lookYaw=t.yaw,this.state.mode=t.mode,this.state.currentSpeed=t.horizontalSpeed,this.state.fallGraceTime=0,this.actor.position.copy(this.state.position),this.actor.object.quaternion.setFromEuler(Ht.set(0,t.yaw,0)),this.crouchSuppressedUntilRelease=0!==(t.flags&K),this.setCrouching(0!==(t.flags&_)||t.mode===A.sliding),e&&(this.physicsSystem.updateActorTransform(this.actor),this.physicsSystem.flushModifiedBodyPositionsToColliders()),this.checkGrounded(this.state.position),this.state.grounded=this.isGrounded,this.syncPublicState()}toSnapshot(t){return t.sequence=this.state.sequence,t.x=this.state.position.x,t.y=this.state.position.y,t.z=this.state.position.z,t.velocityX=this.state.velocity.x,t.velocityY=this.state.velocity.y,t.velocityZ=this.state.velocity.z,t.yaw=Ht.setFromQuaternion(this.actor.object.quaternion,"YXZ").y,t.mode=this.state.mode,t.flags=(this.isCrouching?_:0)|(this.crouchSuppressedUntilRelease?K:0),t.horizontalSpeed=this.state.currentSpeed,t}syncPublicState(){this.velocity.copy(this.state.velocity),this.mode=this.state.mode,this.horizontalSpeed=this.state.currentSpeed,this.isSliding=this.state.mode===A.sliding,this.isSliding?(this.resolveGroundNormal(this.slideSurfaceNormal),this.slideDirection.copy(this.state.velocity).projectOnPlane(this.slideSurfaceNormal),this.slideDirection.lengthSq()>1e-8?this.slideDirection.normalize():this.slideDirection.set(0,0,0)):(this.slideDirection.set(0,0,0),this.slideSurfaceNormal.copy(Tt))}sampleRootMotion(t){if(t.set(0,0,0),null==this.rootMotionAction||this.rootMotionAction.isRunning()||this.clearRootMotionAction(this.rootMotionAction),null!=this.rootMotionAction&&this.rootMotionOptions.cancelWithJump&&this.jumpInput.activated&&(this.isGrounded||this.jumpInAir)){const e=this.rootMotionAction,i=this.rootMotionOptions.onJumpCancel;return this.clearRootMotionAction(e),e.stop(),i?.(),t}if(null==this.rootMotionAction)return t;if(!(this.rootMotionAction.getClip()instanceof l))return t;const e=this.rootMotionInterpolant;this.resetRootMotion&&(this.prevRootMotionPos.fromArray(e.evaluate(0)),this.resetRootMotion=!1),It.fromArray(e.evaluate(this.rootMotionAction.time)),t.subVectors(It,this.prevRootMotionPos),this.prevRootMotionPos.copy(It);if((this.rootMotionRootBone??this.rootMotionAction.getRoot()).getWorldScale(Pt),t.multiply(Pt),this.rootMotionTargetConstraints.size>0){zt.copy(t).applyQuaternion(this.actor.quaternion);for(const t of this.rootMotionTargetConstraints)if(t.requestedProgress>0&&t.addedHorizontalDistance<t.maximumAddedHorizontalDistance){const e=r.lerp(t.initialHorizontalDistance,t.minimumHorizontalDistance,t.requestedProgress),i=Math.min(D(zt,this.actor.position,t.targetPosition,e),t.maximumAddedHorizontalDistance-t.addedHorizontalDistance);t.addedHorizontalDistance+=b(zt,this.actor.position,t.targetPosition,t.minimumHorizontalDistance,i)}else q(zt,this.actor.position,t.targetPosition,t.minimumHorizontalDistance);t.copy(zt).applyQuaternion(Vt.copy(this.actor.quaternion).invert())}return t}clearRootMotionAction(t){null!=t&&this.rootMotionAction!==t||(this.rootMotionAction=null,this.rootMotionOptions={},this.resetRootMotion=!1)}isRemoteControlledAuthority(t=this.netService.mode,e=this.actor.netRole){if(t===yt.none||e!==m.authority||t>=yt.client)return!1;return null!=this.netService.resolveActorConnection(this.actor)&&!this.netService.isLocalOwner(this.actor)}configureCharacterController(){const t=this.cc;t.setApplyImpulsesToDynamicBodies(this.applyImpulsesToDynamicBodies),t.setMinSlopeSlideAngle(this.minSlopeSlideAngle),t.setMaxSlopeClimbAngle(this.maxSlopeClimbAngle),t.setCharacterMass(this.mass),t.setSlideEnabled(this.allowSliding)}initializeStateFromActor(){this.state.position.copy(this.actor.position),this.state.velocity.set(0,0,0),this.state.yaw=this.actor.rotation.y,this.state.lookYaw=this.rotationInput.rotation.y,this.state.mode=this.mode,this.state.currentSpeed=this.horizontalSpeed,this.state.prevRotateToMovementDirection=this.rotateToMovementDirection}createCollisionShape(){const t=this.getColliderHeight(!1),e=this.getColliderRadius(!1),i=new x(t,e);return i.offset.y=this.getCapsuleOffsetY(t,e),i.collisionGroup=xt,i}updateCrouchState(t,e,i,o){if(!(0!==(t.flags&B)))return this.crouchSuppressedUntilRelease=!1,this.slideOnLandingRequested=!1,this.state.mode===A.sliding&&(this.state.mode=A.walking),void this.tryUncrouch();if(this.state.mode===A.sliding&&(!this.slideEnabled||i))return this.slideOnLandingRequested=!1,this.state.mode=A.walking,void(this.crouchSuppressedUntilRelease||this.setCrouching(!0));if(this.state.mode===A.sliding&&o)return this.crouchSuppressedUntilRelease=!0,this.slideOnLandingRequested=!1,void this.tryUncrouch();if(this.crouchSuppressedUntilRelease)return void(this.slideOnLandingRequested=!1);if(this.setCrouching(!0),!this.slideEnabled||i)return void(this.slideOnLandingRequested=!1);if(this.state.mode===A.falling)return void(this.slideOnLandingRequested=!0);this.resolveGroundNormal(Xt);const s=this.slideOnLandingRequested&&this.state.mode===A.walking&&this.state.grounded,n=e&&0!==(t.flags&$)||s;s&&(this.slideOnLandingRequested=!1);const r=Qt(this.state.velocity);this.state.mode===A.walking&&St(this.slideEnabled,n,this.state.grounded,this.isWalkableSlideSurface(Xt),i,r,this.slideMinStartSpeed)&&(this.state.mode=A.sliding,this.slideSurfaceNormal.copy(Xt),this.state.velocity.projectOnPlane(this.slideSurfaceNormal),this.state.currentSpeed=Qt(this.state.velocity))}updateSlidingMode(t){const e=t.state,i=t.move;this.resolveGroundNormal(this.slideSurfaceNormal);const o=this.isWalkableSlideSurface(this.slideSurfaceNormal),s=e.velocity.length();if(!e.grounded||!o)return e.mode=A.falling,$t.update(t),A.falling;if(this.slideEnabled&&0!==(i.flags&B)&&!t.rootMotionActive&&0!==(i.flags&Z))return vt(t.outVelocity,e.velocity,this.jumpVelocity),e.velocity.copy(t.outVelocity),e.currentSpeed=Qt(e.velocity),A.falling;if(!gt(this.slideEnabled,0!==(i.flags&B),e.grounded,o,t.rootMotionActive,s,this.slideMinExitSpeed))return e.mode=A.walking,Wt.update(t),A.walking;const n=Mt(e.velocity,e.velocity,this.slideSurfaceNormal,t.rotatedDirection,this.getEffectiveGravity(),t.deltaTime,this.slideDeceleration,this.slideGravityScale,this.slideSteeringRate);if(e.currentSpeed=Qt(e.velocity),n<Math.max(0,this.slideMinExitSpeed))return e.mode=A.walking,Wt.update(t),A.walking;t.outVelocity.copy(e.velocity).add(t.platformVelocity)}tryUncrouch(){this.isCrouching&&this.canUncrouch()&&this.setCrouching(!1)&&(this.suppressUncrouchLift=!0,this.state.fallGraceTime=0)}resolveGroundNormal(t){return!this.rayTestResult.hasHit||this.rayTestResult.hitNormal.lengthSq()<=1e-8?t.copy(Tt):(t.copy(this.rayTestResult.hitNormal).normalize(),t.dot(Tt)<0&&t.multiplyScalar(-1),t)}isWalkableSlideSurface(t){return t.dot(Tt)>=Math.cos(this.cc.maxSlopeClimbAngle())}setCrouching(t){return this.isCrouching!==t&&(this.isCrouching=t,this.syncColliderShape(),!0)}canUncrouch(){const t=this.getColliderHeight(!1),e=this.getColliderRadius(!1);return!this.physicsSystem.hasActorCapsuleIntersection(this.actor,this.state.position,t,e,this.getCapsuleOffsetY(t,e),this.getCollisionGroup())}syncColliderShape(){const t=this.getColliderHeight(this.isCrouching),e=this.getColliderRadius(this.isCrouching);this.physicsSystem.setActorCapsuleCollider(this.actor,t,e,this.getCapsuleOffsetY(t,e))}getColliderHeight(t){const e=Number.isFinite(this.colliderHeight)?Math.max(0,this.colliderHeight):0;if(!t)return e;const i=Number.isFinite(this.crouchColliderHeight)?this.crouchColliderHeight:e;return r.clamp(i,0,e)}getColliderRadius(t){const e=Number.isFinite(this.colliderRadius)?Math.max(.001,this.colliderRadius):.001;if(!t)return e;const i=this.crouchColliderRadius??e,o=Number.isFinite(i)?i:e;return r.clamp(o,.001,e)}getCapsuleOffsetY(t,e){return e+t/2+this.offset}checkGrounded(t){Nt.y=-Math.max(.05,this.cc.snapToGroundDistance()),this.physicsSystem.rayTest(Ot.addVectors(t,Yt.set(0,this.offset,0)),jt.addVectors(t,Nt),this.rayTestResult,this.groundRayTestOptions)}resolvePlatformVelocity(t){return t.set(0,0,0),this.rayTestResult.hasHit&&null!=this.rayTestResult.actor&&this.physicsSystem.getLinearVelocity(this.rayTestResult.actor,t),t}getCollisionGroup(){return this.characterCollision?null:xt}};t([T(),e("design:type",Number)],Zt.prototype,"colliderHeight",void 0),t([T(),e("design:type",Number)],Zt.prototype,"colliderRadius",void 0),t([T(),e("design:type",Number)],Zt.prototype,"crouchColliderHeight",void 0),t([T({optional:!0}),e("design:type",Number)],Zt.prototype,"crouchColliderRadius",void 0),t([g(!1),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"serverMove",null),t([y(!1),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"clientMoveAck",null),t([y(!0),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"clientMoveCorrection",null),t([v(!1),e("design:type",Function),e("design:paramtypes",[Uint8Array]),e("design:returntype",void 0)],Zt.prototype,"simulatedState",null),Zt=t([R({inEditor:!1}),e("design:paramtypes",[])],Zt);export{Zt as CharacterMovementComponent};const Wt={mode:A.walking,update(t){const e=t.component,i=t.move,o=t.state,s=t.rotatedDirection,n=Math.min(1,Math.sqrt(i.inputX*i.inputX+i.inputY*i.inputY)),r=!e.rotateToMovementDirection&&i.inputY<0,a=e.isCrouching,h=!t.rootMotionActive&&!a&&0===(i.flags&B)&&0!==(i.flags&Q)&&!r&&0!==s.lengthSq()&&(e.sprintPolicy?.canSprint()??!0),l=(a?e.maxSpeedCrouch:r?e.maxSpeedBackwards:h?e.maxSpeedSprint:e.maxSpeed)*(e.normalizedDirection?1:n);if(0!==s.lengthSq()){e.isSprinting=h,h&&e.sprintPolicy?.commitSprint(t.deltaTime,i.sequence);let a=Math.min(l,o.currentSpeed),c=e.groundAccelerationRate;if(h&&t.startedSprinting&&e.sprintStartBoostSpeed>0){const t=e.normalizedDirection?1:n;a=Math.max(a,l+e.sprintStartBoostSpeed*t),c=e.sprintStartBoostDamping}else h&&!r&&e.sprintStartBoostSpeed>0&&o.currentSpeed>l&&(a=o.currentSpeed,c=e.sprintStartBoostDamping);o.currentSpeed=function(t,e,i,o){return e+(t-e)*Math.exp(-i*o)}(a,l,c,t.deltaTime),t.outVelocity.copy(s).multiplyScalar(function(t,e,i,o){const s=i*o;if(s<=1e-6)return t;return e+(t-e)*(1-Math.exp(-s))/s}(a,l,c,t.deltaTime))}else o.currentSpeed=0,t.outVelocity.set(0,0,0);if(0!==(i.flags&Z))return o.mode=A.falling,o.velocity.copy(t.outVelocity),o.velocity.y=e.jumpVelocity,A.falling;t.outVelocity.y=t.deltaTime*e.getEffectiveGravity(),t.outVelocity.add(t.platformVelocity)}},$t={mode:A.falling,update(t){const e=t.component,i=t.state,o=t.move;0!==(o.flags&Z)&&e.jumpInAir&&(i.velocity.copy(t.outVelocity),i.velocity.y=e.jumpVelocity),i.velocity.y+=t.deltaTime*e.getEffectiveGravity(),t.outVelocity.copy(i.velocity),t.outVelocity.addScaledVector(t.rotatedDirection,e.fallingMovementControl),e.fallingReorientation&&t.outVelocity.applyAxisAngle(Tt,r.euclideanModulo(o.yaw-i.lookYaw+Math.PI,2*Math.PI)-Math.PI)}};function Qt(t){return Math.sqrt(t.x*t.x+t.z*t.z)}function Jt(t,e){return e.sequence=t.sequence,e.clientTime=t.clientTime,e.dt=t.dt,e.inputX=t.inputX,e.inputY=t.inputY,e.yaw=t.yaw,e.flags=t.flags,e.clientX=t.clientX,e.clientY=t.clientY,e.clientZ=t.clientZ,e.rootMotionX=t.rootMotionX,e.rootMotionY=t.rootMotionY,e.rootMotionZ=t.rootMotionZ,e}function Kt(t,e){const i=t.position.x-e.clientX,o=t.position.y-e.clientY,s=t.position.z-e.clientZ;return i*i+o*o+s*s}class _t{constructor(t){this.scene=t,this.serverPosition=new h,this.direction=new h}update(t,e){const i=this.serverPosition.set(e.x,e.y,e.z),o=i.distanceTo(t);o>.1&&console.log(`Correction applied. Error: ${o.toFixed(3)}. Server position: (${i.x.toFixed(2)}, ${i.y.toFixed(2)}, ${i.z.toFixed(2)}), Client position: (${t.x.toFixed(2)}, ${t.y.toFixed(2)}, ${t.z.toFixed(2)})`);const s=this.direction.copy(i).sub(t).normalize();te.position.copy(t),te.setDirection(s),te.setLength(o);const n=ee(this.scene);n.add(te),setTimeout(()=>{n.remove(te)},1e3)}}const te=new n(new h(0,1,0),new h,1,16711680);function ee(t){return null==t.parent?t:ee(t.parent)}const ie={immediate:!1,savedRootMotion:!0};/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { NodeState, type BehaviorTreeContext, type Node, type StopReason } from './bt.js';
|
|
2
|
+
/** Owns one tree instance. Build the topology before construction; do not share nodes. */
|
|
3
|
+
export declare class BehaviorTree implements BehaviorTreeContext {
|
|
4
|
+
readonly root: Node;
|
|
5
|
+
private readonly executions;
|
|
6
|
+
private readonly activeNodes;
|
|
7
|
+
private current;
|
|
8
|
+
private elapsed;
|
|
9
|
+
private visit;
|
|
10
|
+
private updating;
|
|
11
|
+
private ticks;
|
|
12
|
+
constructor(root: Node);
|
|
13
|
+
get dt(): number;
|
|
14
|
+
/** Unordered live view, intended for inspection. */
|
|
15
|
+
get active(): readonly Node[];
|
|
16
|
+
getState(node: Node): NodeState | undefined;
|
|
17
|
+
isActive(node: Node): boolean;
|
|
18
|
+
/** Monotonic update number, including updates that throw. */
|
|
19
|
+
get tickCount(): number;
|
|
20
|
+
/** Last update that evaluated this node; zero means never evaluated. */
|
|
21
|
+
getLastTick(node: Node): number;
|
|
22
|
+
/** Last completion/interruption reason; cleared when execution starts again. */
|
|
23
|
+
getStopReason(node: Node): StopReason | undefined;
|
|
24
|
+
tick(dt: number): NodeState;
|
|
25
|
+
execute(node: Node, dt?: number): NodeState;
|
|
26
|
+
/** Abort a subtree, or the whole tree when called without an argument. */
|
|
27
|
+
abort(node?: Node): void;
|
|
28
|
+
retain(node: Node): void;
|
|
29
|
+
private finish;
|
|
30
|
+
private lookup;
|
|
31
|
+
private register;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=behavior-tree.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{NodeState as t}from"./bt.js";export class BehaviorTree{constructor(t){this.root=t,this.executions=new Map,this.activeNodes=[],this.elapsed=0,this.visit=0,this.updating=!1,this.ticks=0,this.register(t,void 0)}get dt(){return this.elapsed}get active(){return this.activeNodes}getState(t){return this.lookup(t).result}isActive(t){return this.lookup(t).active}get tickCount(){return this.ticks}getLastTick(t){return this.lookup(t).lastTick}getStopReason(t){return this.lookup(t).stopReason}tick(t){if(this.updating)throw new Error("BehaviorTree.tick cannot be reentered");this.updating=!0,this.ticks++,this.elapsed=t;try{return this.execute(this.root)}catch(t){try{this.abort(this.root)}catch{}throw t}finally{this.updating=!1}}execute(e,i=this.elapsed){const s=this.lookup(e);if(!this.updating||(this.current?s.parent!==this.current:e!==this.root))throw new Error("Execute nodes through tree.tick or their parent context");if(s.executing)throw new Error("A node cannot execute recursively");const r=this.current,o=this.elapsed,n=++this.visit;s.visited=n,s.lastTick=this.ticks,s.executing=!0,this.current=s,this.elapsed=i;try{s.active||(s.active=!0,s.activeIndex=this.activeNodes.length,this.activeNodes.push(e),s.result=void 0,s.stopReason=void 0,e.start());const r=e.tick(i,this);if(r!==t.RUNNING&&r!==t.SUCCESS&&r!==t.FAILURE)throw new Error("Node returned an invalid execution result");if(s.result=r,r!==t.RUNNING)this.finish(s,r===t.SUCCESS?"success":"failure");else for(const t of s.children)t.active&&t.visited<=n&&this.finish(t,"aborted");return r}finally{s.executing=!1,this.current=r,this.elapsed=o}}abort(t=this.root){const e=this.lookup(t);if(e.executing)throw new Error("Cannot abort a node during its own execution");this.finish(e,"aborted")}retain(t){const e=this.lookup(t);if(!this.current||e.parent!==this.current)throw new Error("Only the executing parent can retain its child");e.visited=++this.visit}finish(t,e){if(!t.active)return;let i,s=!1;for(const e of t.children)try{this.finish(e,"aborted")}catch(t){s||(i=t,s=!0)}if(t.active){t.active=!1,t.stopReason=e;const r=this.activeNodes.pop();r!==t.node&&(this.activeNodes[t.activeIndex]=r,this.lookup(r).activeIndex=t.activeIndex),t.activeIndex=-1;try{t.node.stop(e)}catch(t){s||(i=t,s=!0)}}if(s)throw i}lookup(t){const e=this.executions.get(t);if(!e)throw new Error("Node is not part of this behavior tree");return e}register(t,e){if(this.executions.has(t))throw new Error("Behavior tree nodes cannot be shared or cyclic");const i={node:t,parent:e,children:[],active:!1,activeIndex:-1,executing:!1,visited:0,result:void 0,lastTick:0,stopReason:void 0};this.executions.set(t,i);for(const e of t.getChildren())i.children.push(this.register(e,i));return i}}/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{describe as t,expect as e,it as o,vi as a}from"vitest";import{BehaviorTree as n}from"./behavior-tree.js";import{Node as i,NodeState as s,SequenceNode as c,GuardNode as r,ThrottleNode as l}from"./bt.js";class d extends i{constructor(t=s.RUNNING){super(),this.result=t,this.start=a.fn(),this.stop=a.fn(t=>{}),this.tick=a.fn((t,e)=>this.result)}}class h extends i{constructor(t){super(),this.child=t,this.enabled=!0}getChildren(){return[this.child]}tick(t,e){return this.enabled&&e.execute(this.child,t),s.RUNNING}}t("BehaviorTree lifecycle",()=>{o("exposes instant evaluations and distinguishes old results from current activity",()=>{const t=new d(s.SUCCESS),o=new d,a=new c;a.addChild(t),a.addChild(o);const i=new n(a);e(i.tickCount).toBe(0),e(i.getLastTick(t)).toBe(0),i.tick(.1),e(i.isActive(t)).toBe(!1),e(i.getState(t)).toBe(s.SUCCESS),e(i.getLastTick(t)).toBe(1),e(i.getStopReason(t)).toBe("success"),i.tick(.1),e(i.tickCount).toBe(2),e(i.getLastTick(t)).toBe(1),e(i.getLastTick(o)).toBe(2),i.abort(),e(i.getState(o)).toBe(s.RUNNING),e(i.isActive(o)).toBe(!1),e(i.getStopReason(o)).toBe("aborted"),i.tick(.1),e(i.getStopReason(o)).toBeUndefined(),e(i.getLastTick(t)).toBe(3)}),o("does not report retained descendants as evaluated during a skipped update",()=>{const t=new d,o=new n(new l(t,1));o.tick(.1),o.tick(.1),e(o.isActive(t)).toBe(!0),e(o.getLastTick(t)).toBe(1),e(o.tickCount).toBe(2),e(o.getStopReason(t)).toBeUndefined()}),o("starts once, stops once, and restarts after completion",()=>{const t=new d,o=new n(t);o.tick(.1),o.tick(.1),e(t.start).toHaveBeenCalledTimes(1),e(o.active).toEqual([t]),e(o.getState(t)).toBe(s.RUNNING),t.result=s.SUCCESS,o.tick(.1),e(t.stop).toHaveBeenCalledExactlyOnceWith("success"),e(o.active).toEqual([]),o.tick(.1),e(t.start).toHaveBeenCalledTimes(2),e(t.stop).toHaveBeenCalledTimes(2)}),o("pairs start and stop for immediate failure",()=>{const t=new d(s.FAILURE),o=new n(t);e(o.tick(.1)).toBe(s.FAILURE),e(t.start).toHaveBeenCalledTimes(1),e(t.stop).toHaveBeenCalledExactlyOnceWith("failure")}),o("stops descendants before their parent and makes abort idempotent",()=>{const t=[],o=new d,a=new h(o),i=new n(a);o.stop.mockImplementation(()=>{t.push("child")}),a.stop=()=>{t.push("parent")},i.tick(.1),i.abort(),i.abort(),e(t).toEqual(["child","parent"]),e(i.active).toEqual([]),i.tick(.1),e(o.start).toHaveBeenCalledTimes(2)}),o("automatically cancels an abandoned running child",()=>{const t=new d,o=new h(t),a=new n(o);a.tick(.1),o.enabled=!1,a.tick(.1),e(t.stop).toHaveBeenCalledExactlyOnceWith("aborted"),e(a.active).toEqual([o])}),o("retains a throttled subtree without restarting or ticking it",()=>{const t=new d,o=new c;o.addChild(t);const a=new l(o,1),i=new n(a);i.tick(.1),i.tick(.1),i.tick(.1),e(t.start).toHaveBeenCalledTimes(1),e(t.tick).toHaveBeenCalledTimes(1),e(t.stop).not.toHaveBeenCalled(),e(i.active).toHaveLength(3),i.abort(),e(t.stop).toHaveBeenCalledExactlyOnceWith("aborted")}),o("restores context dt after a child executes with accumulated time",()=>{const t=[],o=new d;new n(new class extends h{tick(e,o){return t.push(o.dt),o.execute(this.child,.75),t.push(o.dt),s.RUNNING}}(o)).tick(.1),e(t).toEqual([.1,.1]),e(o.tick.mock.calls[0][0]).toBe(.75)}),o("cleans up when start or tick throws and remains reusable",()=>{const t=new d,o=new n(t);t.start.mockImplementationOnce(()=>{throw new Error("start failure")}),e(()=>o.tick(.1)).toThrow("start failure"),e(t.stop).toHaveBeenCalledExactlyOnceWith("aborted"),e(o.active).toEqual([]),o.tick(.1),t.tick.mockImplementationOnce(()=>{throw new Error("tick failure")}),e(()=>o.tick(.1)).toThrow("tick failure"),e(o.active).toEqual([]),o.tick(.1),e(o.isActive(t)).toBe(!0)}),o("continues cleaning siblings and parents if a stop hook throws",()=>{const t=new d,o=new d;const c=new class extends i{constructor(){super(...arguments),this.stop=a.fn()}getChildren(){return[t,o]}tick(e,a){return a.execute(t,e),a.execute(o,e),s.RUNNING}},r=new n(c);t.stop.mockImplementationOnce(()=>{throw new Error("stop failure")}),r.tick(.1),e(()=>r.abort()).toThrow("stop failure"),e(o.stop).toHaveBeenCalledExactlyOnceWith("aborted"),e(c.stop).toHaveBeenCalledExactlyOnceWith("aborted"),e(r.active).toEqual([])}),o("rejects shared children, cycles, foreign nodes and reentrant ticks",()=>{const t=new d,o=new c;o.addChild(t),o.addChild(t),e(()=>new n(o)).toThrow("shared or cyclic");const a=new c;a.addChild(a),e(()=>new n(a)).toThrow("shared or cyclic");const i=new n(t);e(()=>i.execute(t)).toThrow("through tree.tick"),e(()=>i.abort(new d)).toThrow("not part"),t.tick.mockImplementationOnce(()=>i.tick(.1)),e(()=>i.tick(.1)).toThrow("reentered"),e(i.active).toEqual([])}),o("starts a fresh guarded execution after interruption",()=>{let t=!0;const o=new d,a=new r(o,()=>t),i=new n(a);i.tick(.1),t=!1,e(i.tick(.1)).toBe(s.FAILURE),e(o.stop).toHaveBeenCalledExactlyOnceWith("aborted"),t=!0,i.tick(.1),e(o.start).toHaveBeenCalledTimes(2)})});/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
export declare abstract class Node {
|
|
2
|
-
/**
|
|
3
|
-
* @param dt The amount of time passed between behaviour tree updates
|
|
4
|
-
* to be used by actions that depend on the time elapsed in game.
|
|
5
|
-
*/
|
|
6
|
-
abstract tick(dt: number): NodeState;
|
|
7
|
-
}
|
|
8
1
|
export declare enum NodeState {
|
|
9
2
|
SUCCESS = 0,
|
|
10
3
|
FAILURE = 1,
|
|
11
4
|
RUNNING = 2
|
|
12
5
|
}
|
|
6
|
+
export type StopReason = 'success' | 'failure' | 'aborted';
|
|
7
|
+
export interface BehaviorTreeContext {
|
|
8
|
+
readonly dt: number;
|
|
9
|
+
execute(node: Node, dt?: number): NodeState;
|
|
10
|
+
abort(node: Node): void;
|
|
11
|
+
/** Preserve a running subtree when intentionally skipping its update. */
|
|
12
|
+
retain(node: Node): void;
|
|
13
|
+
}
|
|
14
|
+
export declare abstract class Node {
|
|
15
|
+
/** Optional human-readable label for development inspectors. */
|
|
16
|
+
debugLabel?: string;
|
|
17
|
+
/** dt is elapsed game time in seconds. */
|
|
18
|
+
abstract tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
19
|
+
/** Called once before the first update of each execution. */
|
|
20
|
+
start(): void;
|
|
21
|
+
/** Called once on completion or interruption, after active descendants stop. */
|
|
22
|
+
stop(_reason: StopReason): void;
|
|
23
|
+
/** Children are fixed for the lifetime of a runtime; nodes must not be shared. */
|
|
24
|
+
getChildren(): readonly Node[];
|
|
25
|
+
}
|
|
13
26
|
export declare abstract class LeafNode extends Node {
|
|
14
27
|
}
|
|
15
28
|
export declare class ActionNode extends LeafNode {
|
|
@@ -27,6 +40,17 @@ export declare class FailNode extends LeafNode {
|
|
|
27
40
|
constructor(action: () => unknown);
|
|
28
41
|
tick(): NodeState;
|
|
29
42
|
}
|
|
43
|
+
/** Starts once; aborted completions are ignored. Rejections produce FAILURE. */
|
|
44
|
+
export declare class AsyncActionNode extends LeafNode {
|
|
45
|
+
private action;
|
|
46
|
+
private result;
|
|
47
|
+
private execution;
|
|
48
|
+
constructor(action: () => Promise<NodeState>);
|
|
49
|
+
start(): void;
|
|
50
|
+
tick(): NodeState;
|
|
51
|
+
private finish;
|
|
52
|
+
stop(): void;
|
|
53
|
+
}
|
|
30
54
|
export declare class ConditionNode extends LeafNode {
|
|
31
55
|
private condition;
|
|
32
56
|
constructor(condition: () => boolean);
|
|
@@ -34,68 +58,73 @@ export declare class ConditionNode extends LeafNode {
|
|
|
34
58
|
}
|
|
35
59
|
export declare abstract class CompositeNode extends Node {
|
|
36
60
|
protected children: Node[];
|
|
61
|
+
getChildren(): readonly Node[];
|
|
37
62
|
addChild(node: Node): void;
|
|
38
63
|
}
|
|
64
|
+
/** Resumes the running child without repeating completed steps. */
|
|
39
65
|
export declare class SequenceNode extends CompositeNode {
|
|
40
66
|
private current;
|
|
41
|
-
tick(dt: number): NodeState;
|
|
42
|
-
|
|
67
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
68
|
+
stop(): void;
|
|
43
69
|
}
|
|
70
|
+
/** Reactive selector: higher priorities are checked every tick. */
|
|
44
71
|
export declare class SelectorNode extends CompositeNode {
|
|
45
|
-
|
|
72
|
+
private runningChild;
|
|
73
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
74
|
+
stop(): void;
|
|
46
75
|
}
|
|
47
76
|
export declare abstract class DecoratorNode extends Node {
|
|
48
77
|
protected child: Node;
|
|
49
78
|
constructor(child: Node);
|
|
79
|
+
getChildren(): readonly Node[];
|
|
80
|
+
}
|
|
81
|
+
/** Updates shared state before its branch on every active tick, including while tasks run.
|
|
82
|
+
* Services do not select branches or complete tasks; the supplied child owns execution.
|
|
83
|
+
* Use start/stop for service activation and cleanup. */
|
|
84
|
+
export declare abstract class ServiceNode extends DecoratorNode {
|
|
85
|
+
protected abstract update(dt: number, context: BehaviorTreeContext): void;
|
|
86
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
50
87
|
}
|
|
51
88
|
export declare class InverterNode extends DecoratorNode {
|
|
52
|
-
tick(dt: number): NodeState;
|
|
89
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
53
90
|
}
|
|
91
|
+
/** At most one iteration per tick, including for the default infinite limit. */
|
|
54
92
|
export declare class RepeatTimesNode extends DecoratorNode {
|
|
55
93
|
private limit;
|
|
56
94
|
private counter;
|
|
57
95
|
constructor(child: Node, limit?: number);
|
|
58
|
-
tick(dt: number): NodeState;
|
|
96
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
97
|
+
stop(): void;
|
|
59
98
|
}
|
|
60
99
|
export declare class RepeatNode extends DecoratorNode {
|
|
61
|
-
|
|
62
|
-
tick(dt: number): NodeState;
|
|
100
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
63
101
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Repeats its child node until a specific condition is met, then stops.
|
|
66
|
-
* Useful for looping actions like searching for the player until they’re found.
|
|
67
|
-
*/
|
|
68
102
|
export declare class RepeatUntilNode extends DecoratorNode {
|
|
69
103
|
private condition;
|
|
70
104
|
constructor(child: Node, condition: () => boolean);
|
|
71
|
-
tick(dt: number): NodeState;
|
|
105
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
72
106
|
}
|
|
73
107
|
export declare class RepeatUntilFailNode extends DecoratorNode {
|
|
74
|
-
|
|
75
|
-
tick(dt: number): NodeState;
|
|
108
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
76
109
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Delay starting the child node.
|
|
79
|
-
* Can be used when performing multiple actions in parallel and one should start slightly later.
|
|
80
|
-
*/
|
|
110
|
+
/** Delay in milliseconds before starting the child. */
|
|
81
111
|
export declare class DelayNode extends DecoratorNode {
|
|
82
112
|
private delay;
|
|
83
|
-
private state;
|
|
84
113
|
private elapsed;
|
|
85
114
|
constructor(child: Node, delay: number);
|
|
86
|
-
tick(dt: number): NodeState;
|
|
115
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
116
|
+
stop(): void;
|
|
87
117
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Delay after the child node
|
|
90
|
-
* Used to stop from making any actions.
|
|
91
|
-
*/
|
|
118
|
+
/** Delay in milliseconds after the child finishes, preserving its result. */
|
|
92
119
|
export declare class DelayAfterNode extends DecoratorNode {
|
|
93
120
|
private delay;
|
|
94
|
-
private state;
|
|
95
121
|
private elapsed;
|
|
122
|
+
private state;
|
|
96
123
|
constructor(child: Node, delay: number);
|
|
97
|
-
tick(dt: number): NodeState;
|
|
124
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
125
|
+
stop(): void;
|
|
98
126
|
}
|
|
127
|
+
/** Wait duration in milliseconds. */
|
|
99
128
|
export declare class WaitNode extends LeafNode {
|
|
100
129
|
private delayMin;
|
|
101
130
|
private delayMax?;
|
|
@@ -104,68 +133,61 @@ export declare class WaitNode extends LeafNode {
|
|
|
104
133
|
constructor(delayMin: number, delayMax?: number);
|
|
105
134
|
tick(dt: number): NodeState;
|
|
106
135
|
reset(): void;
|
|
136
|
+
stop(): void;
|
|
137
|
+
start(): void;
|
|
107
138
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Prevents its child node from executing until a cooldown period has passed.
|
|
110
|
-
* Useful for enforcing time constraints, like ensuring an NPC cannot attack again too quickly.
|
|
111
|
-
*/
|
|
139
|
+
/** Cooldown in wall-clock milliseconds, preserved across interruptions. */
|
|
112
140
|
export declare class CooldownNode extends DecoratorNode {
|
|
113
141
|
private cooldownTime;
|
|
114
142
|
private lastExecutionTime;
|
|
115
143
|
constructor(child: Node, cooldownTime: number);
|
|
116
|
-
tick(dt: number): NodeState;
|
|
144
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
117
145
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Execute an action for a limited amount of time, such as searching for a player for 10 seconds before giving up.
|
|
120
|
-
*/
|
|
146
|
+
/** Maximum execution duration in wall-clock milliseconds. */
|
|
121
147
|
export declare class TimerNode extends DecoratorNode {
|
|
122
148
|
private duration;
|
|
123
149
|
private startTime;
|
|
124
150
|
constructor(child: Node, duration: number);
|
|
125
|
-
tick(dt: number): NodeState;
|
|
151
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
152
|
+
stop(): void;
|
|
126
153
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Selects a child node randomly, but with weights assigned to each node to influence the likelihood of selection.
|
|
129
|
-
* Add randomness to NPC behavior, like having a higher chance to patrol but occasionally stopping to rest.
|
|
130
|
-
*/
|
|
154
|
+
/** Retains the randomly selected child until it finishes. */
|
|
131
155
|
export declare class WeightedRandomSelectorNode extends CompositeNode {
|
|
156
|
+
private current;
|
|
132
157
|
private weights;
|
|
133
158
|
private totalWeight;
|
|
134
159
|
constructor(weights: number[]);
|
|
135
160
|
private getRandomIndex;
|
|
136
|
-
tick(dt: number): NodeState;
|
|
161
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
162
|
+
stop(): void;
|
|
137
163
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Executes its child node only if a condition is met.
|
|
140
|
-
* Add conditional logic to restrict when a node should run, such as checking if the player is within a certain range.
|
|
141
|
-
*
|
|
142
|
-
* Compared to ConditionNode, it can be used for any node when a precondition has to be met rathern
|
|
143
|
-
* than using sequence nodes with consitions. For example, check that the character has a specific
|
|
144
|
-
* resource before trying.
|
|
145
|
-
*/
|
|
146
164
|
export declare class GuardNode extends DecoratorNode {
|
|
147
165
|
private condition;
|
|
148
166
|
constructor(child: Node, condition: () => boolean);
|
|
149
|
-
tick(dt: number): NodeState;
|
|
167
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
150
168
|
}
|
|
151
|
-
/**
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
tick(dt: number): NodeState;
|
|
169
|
+
/** Stores terminal results until the parallel execution completes. */
|
|
170
|
+
declare abstract class ParallelNode extends CompositeNode {
|
|
171
|
+
private states;
|
|
172
|
+
protected tickParallel(dt: number, context: BehaviorTreeContext, terminal: NodeState): NodeState;
|
|
173
|
+
stop(): void;
|
|
157
174
|
}
|
|
158
|
-
/**
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
/** Succeeds on any success; fails when all children fail. */
|
|
176
|
+
export declare class ParallelSelectorNode extends ParallelNode {
|
|
177
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
178
|
+
}
|
|
179
|
+
/** Succeeds when all children succeed; fails on any failure. */
|
|
180
|
+
export declare class ParallelSequenceNode extends ParallelNode {
|
|
181
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
164
182
|
}
|
|
183
|
+
/** Duration in seconds. Skipped updates accumulate into the child's dt. */
|
|
165
184
|
export declare class ThrottleNode extends DecoratorNode {
|
|
166
185
|
private duration;
|
|
186
|
+
private elapsed;
|
|
187
|
+
private started;
|
|
167
188
|
constructor(child: Node, duration: number);
|
|
168
|
-
|
|
169
|
-
|
|
189
|
+
tick(dt: number, context: BehaviorTreeContext): NodeState;
|
|
190
|
+
stop(): void;
|
|
170
191
|
}
|
|
192
|
+
export {};
|
|
171
193
|
//# sourceMappingURL=bt.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export var NodeState;!function(t){t[t.SUCCESS=0]="SUCCESS",t[t.FAILURE=1]="FAILURE",t[t.RUNNING=2]="RUNNING"}(NodeState||(NodeState={}));export class Node{start(){}stop(t){}getChildren(){return[]}}export class LeafNode extends Node{}export class ActionNode extends LeafNode{constructor(t){super(),this.action=t}tick(){return this.action()}}export class SuccessNode extends LeafNode{constructor(t){super(),this.action=t}tick(){return this.action(),NodeState.SUCCESS}}export class FailNode extends LeafNode{constructor(t){super(),this.action=t}tick(){return this.action(),NodeState.FAILURE}}export class AsyncActionNode extends LeafNode{constructor(t){super(),this.action=t,this.execution=0}start(){this.result=void 0;const t=++this.execution;try{this.action().then(e=>this.finish(t,e),()=>this.finish(t,NodeState.FAILURE))}catch{this.result=NodeState.FAILURE}}tick(){return this.result??NodeState.RUNNING}finish(t,e){t===this.execution&&(this.result=e)}stop(){this.execution++,this.result=void 0}}export class ConditionNode extends LeafNode{constructor(t){super(),this.condition=t}tick(){return this.condition()?NodeState.SUCCESS:NodeState.FAILURE}}export class CompositeNode extends Node{constructor(){super(...arguments),this.children=[]}getChildren(){return this.children}addChild(t){this.children.push(t)}}export class SequenceNode extends CompositeNode{constructor(){super(...arguments),this.current=0}tick(t,e){for(;this.current<this.children.length;){const s=e.execute(this.children[this.current],t);if(s===NodeState.RUNNING)return s;if(s===NodeState.FAILURE)return this.current=0,s;this.current++}return this.current=0,NodeState.SUCCESS}stop(){this.current=0}}export class SelectorNode extends CompositeNode{tick(t,e){for(const s of this.children){const i=e.execute(s,t);if(i!==NodeState.FAILURE)return this.runningChild!==s&&this.runningChild&&e.abort(this.runningChild),this.runningChild=i===NodeState.RUNNING?s:void 0,i;this.runningChild===s&&(this.runningChild=void 0)}return NodeState.FAILURE}stop(){this.runningChild=void 0}}export class DecoratorNode extends Node{constructor(t){super(),this.child=t}getChildren(){return[this.child]}}export class ServiceNode extends DecoratorNode{tick(t,e){return this.update(t,e),e.execute(this.child,t)}}export class InverterNode extends DecoratorNode{tick(t,e){const s=e.execute(this.child,t);return s===NodeState.SUCCESS?NodeState.FAILURE:s===NodeState.FAILURE?NodeState.SUCCESS:s}}export class RepeatTimesNode extends DecoratorNode{constructor(t,e=1/0){super(t),this.limit=e,this.counter=0}tick(t,e){if(this.counter>=this.limit)return this.counter=0,NodeState.SUCCESS;const s=e.execute(this.child,t);return s===NodeState.RUNNING?s:(this.counter++,this.counter>=this.limit?(this.counter=0,NodeState.SUCCESS):NodeState.RUNNING)}stop(){this.counter=0}}export class RepeatNode extends DecoratorNode{tick(t,e){return e.execute(this.child,t),NodeState.RUNNING}}export class RepeatUntilNode extends DecoratorNode{constructor(t,e){super(t),this.condition=e}tick(t,e){return this.condition()?(e.abort(this.child),NodeState.SUCCESS):e.execute(this.child,t)===NodeState.FAILURE?NodeState.FAILURE:NodeState.RUNNING}}export class RepeatUntilFailNode extends DecoratorNode{tick(t,e){return e.execute(this.child,t)===NodeState.FAILURE?NodeState.FAILURE:NodeState.RUNNING}}export class DelayNode extends DecoratorNode{constructor(t,e){super(t),this.delay=e,this.elapsed=0}tick(t,e){if(this.elapsed+=1e3*t,this.elapsed<this.delay)return NodeState.RUNNING;const s=e.execute(this.child,t);return s!==NodeState.RUNNING&&(this.elapsed=0),s}stop(){this.elapsed=0}}export class DelayAfterNode extends DecoratorNode{constructor(t,e){super(t),this.delay=e,this.elapsed=null,this.state=NodeState.RUNNING}tick(t,e){if(null===this.elapsed){if(this.state=e.execute(this.child,t),this.state===NodeState.RUNNING)return this.state;if(this.elapsed=0,this.delay>0)return NodeState.RUNNING}else this.elapsed+=1e3*t;return this.elapsed<this.delay?NodeState.RUNNING:(this.elapsed=null,this.state)}stop(){this.elapsed=null,this.state=NodeState.RUNNING}}export class WaitNode extends LeafNode{constructor(t,e){super(),this.delayMin=t,this.delayMax=e,this.elapsed=0,this.delay=t}tick(t){return this.elapsed+=1e3*t,this.elapsed<this.delay?NodeState.RUNNING:(this.elapsed=0,NodeState.SUCCESS)}reset(){this.elapsed=0,this.delay=null==this.delayMax?this.delayMin:(this.delayMax-this.delayMin)*Math.random()+this.delayMin}stop(){this.elapsed=0}start(){this.reset()}}export class CooldownNode extends DecoratorNode{constructor(t,e){super(t),this.cooldownTime=e,this.lastExecutionTime=null}tick(t,e){const s=Date.now();if(null!==this.lastExecutionTime&&s-this.lastExecutionTime<this.cooldownTime)return NodeState.FAILURE;const i=e.execute(this.child,t);return i===NodeState.SUCCESS&&(this.lastExecutionTime=s),i}}export class TimerNode extends DecoratorNode{constructor(t,e){super(t),this.duration=e,this.startTime=null}tick(t,e){const s=Date.now();if(this.startTime??(this.startTime=s),s-this.startTime>=this.duration)return e.abort(this.child),NodeState.FAILURE;const i=e.execute(this.child,t);return i!==NodeState.RUNNING&&(this.startTime=null),i}stop(){this.startTime=null}}export class WeightedRandomSelectorNode extends CompositeNode{constructor(t){if(super(),t.some(t=>!Number.isFinite(t)||t<0))throw new Error("Selector weights must be finite and non-negative");if(this.weights=t.slice(),this.totalWeight=this.weights.reduce((t,e)=>t+e,0),!Number.isFinite(this.totalWeight))throw new Error("Selector total weight must be finite")}getRandomIndex(){let t=Math.random()*this.totalWeight;for(let e=0;e<this.weights.length;e++)if(t-=this.weights[e],t<0)return e;for(let t=this.weights.length-1;t>=0;t--)if(this.weights[t]>0)return t;return-1}tick(t,e){if(this.children.length!==this.weights.length||this.totalWeight<=0)return NodeState.FAILURE;this.current??(this.current=this.getRandomIndex());const s=e.execute(this.children[this.current],t);return s!==NodeState.RUNNING&&(this.current=void 0),s}stop(){this.current=void 0}}export class GuardNode extends DecoratorNode{constructor(t,e){super(t),this.condition=e}tick(t,e){return this.condition()?e.execute(this.child,t):(e.abort(this.child),NodeState.FAILURE)}}class t extends CompositeNode{constructor(){super(...arguments),this.states=[]}tickParallel(t,e,s){let i=!1;for(let o=0;o<this.children.length;o++){const r=this.states[o],d=void 0===r||r===NodeState.RUNNING?e.execute(this.children[o],t):r;if(this.states[o]=d,d===s)return s;d===NodeState.RUNNING&&(i=!0)}return i?NodeState.RUNNING:(this.states.length=0,s===NodeState.SUCCESS?NodeState.FAILURE:NodeState.SUCCESS)}stop(){this.states.length=0}}export class ParallelSelectorNode extends t{tick(t,e){return this.tickParallel(t,e,NodeState.SUCCESS)}}export class ParallelSequenceNode extends t{tick(t,e){return this.tickParallel(t,e,NodeState.FAILURE)}}export class ThrottleNode extends DecoratorNode{constructor(t,e){super(t),this.duration=e,this.elapsed=0,this.started=!1}tick(t,e){if(this.elapsed+=t,this.started&&this.elapsed<this.duration)return e.retain(this.child),NodeState.RUNNING;this.started=!0;const s=this.elapsed;this.elapsed=0;const i=e.execute(this.child,s);return i!==NodeState.RUNNING&&(this.started=!1),i}stop(){this.elapsed=0,this.started=!1}}/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{BehaviorTree as e}from"./behavior-tree.js";import{describe as t,expect as n,it as o,vi as s}from"vitest";import{AsyncActionNode as a,DelayAfterNode as i,DelayNode as r,GuardNode as c,Node as l,NodeState as d,ParallelSelectorNode as w,ParallelSequenceNode as C,RepeatTimesNode as S,RepeatUntilNode as u,SelectorNode as p,SequenceNode as m,ServiceNode as B,ThrottleNode as N,TimerNode as U,WaitNode as h,WeightedRandomSelectorNode as k}from"./bt.js";class E extends l{constructor(e=d.RUNNING){super(),this.state=e,this.tick=s.fn(e=>this.state),this.start=s.fn(),this.stop=s.fn()}}const R=new WeakMap;function v(t){let n=R.get(t);return n||(n=new e(t),R.set(t,n)),n}function I(e,t=0){return v(e).tick(t)}function f(e,...t){return t.forEach(t=>e.addChild(t)),e}t("behavior tree execution",()=>{o("resumes a sequence and only reports success after all steps complete",()=>{const e=new E(d.SUCCESS),t=new E,o=new E(d.SUCCESS),s=f(new m,e,t,o);n(I(s,.1)).toBe(d.RUNNING),n(o.tick).not.toHaveBeenCalled(),t.state=d.SUCCESS,n(I(s,.1)).toBe(d.SUCCESS),n(e.tick).toHaveBeenCalledTimes(1),n(o.tick).toHaveBeenCalledTimes(1)}),o("restarts interrupted sequences from the beginning",()=>{const e=new E(d.SUCCESS),t=new E,o=f(new m,e,t);I(o,.1),v(o).abort(),n(t.stop).toHaveBeenCalledTimes(1),I(o,.1),n(e.tick).toHaveBeenCalledTimes(2)}),o("keeps selectors reactive and aborts the displaced branch once",()=>{const e=new E(d.FAILURE),t=new E,o=f(new p,e,t);I(o,.1),e.state=d.RUNNING,I(o,.1),I(o,.1),n(t.stop).toHaveBeenCalledTimes(1),n(t.tick).toHaveBeenCalledTimes(1),v(o).abort(),n(e.stop).toHaveBeenLastCalledWith("aborted")}),o("retains random choices and rejects unusable weights safely",()=>{const e=s.spyOn(Math,"random").mockReturnValue(0);try{const t=new E,o=new E,s=f(new k([1,1]),t,o);I(s,.1),e.mockReturnValue(.9),I(s,.1),n(o.tick).not.toHaveBeenCalled(),t.state=d.SUCCESS,I(s,.1),I(s,.1),n(o.tick).toHaveBeenCalledTimes(1),n(I(new k([]),.1)).toBe(d.FAILURE),n(()=>new k([-1])).toThrow()}finally{e.mockRestore()}}),o("does not repeat terminal parallel children and cancels unfinished siblings",()=>{const e=new E(d.SUCCESS),t=new E,o=f(new C,e,t);I(o,.1),I(o,.1),n(e.tick).toHaveBeenCalledTimes(1),t.state=d.FAILURE,n(I(o,.1)).toBe(d.FAILURE),e.state=d.RUNNING,t.state=d.SUCCESS;const s=f(new w,e,t);n(I(s,.1)).toBe(d.SUCCESS),n(e.stop).toHaveBeenLastCalledWith("aborted");const a=new E(d.FAILURE),i=new E,r=f(new w,a,i);I(r,.1),I(r,.1),n(a.tick).toHaveBeenCalledTimes(1)}),o("cancels guarded and repeated tasks when their conditions change",()=>{let e=!0;const t=new E,o=new c(t,()=>e);I(o,.1),e=!1,I(o,.1),I(o,.1),n(t.stop).toHaveBeenCalledTimes(1);const s=new u(t,()=>e);I(s,.1),e=!0,n(I(s,.1)).toBe(d.SUCCESS),n(t.stop).toHaveBeenCalledTimes(2)}),o("clears interrupted waits and does not return a stale delay result",()=>{const e=new h(100);I(e,.06),v(e).abort(),n(I(e,.06)).toBe(d.RUNNING),n(I(e,.04)).toBe(d.SUCCESS);const t=new r(new E(d.SUCCESS),100);n(I(t,.1)).toBe(d.SUCCESS),n(I(t,.05)).toBe(d.RUNNING),v(t).abort(),n(I(t,.05)).toBe(d.RUNNING)}),o("delays after completion without counting time before completion",()=>{const e=new i(new E(d.FAILURE),100);n(I(e,1)).toBe(d.RUNNING),n(I(e,.05)).toBe(d.RUNNING),n(I(e,.05)).toBe(d.FAILURE),n(I(e,1)).toBe(d.RUNNING)}),o("bounds repeat work even with the default infinite limit",()=>{const e=new E(d.SUCCESS);n(I(new S(e),.1)).toBe(d.RUNNING),n(e.tick).toHaveBeenCalledTimes(1);const t=new S(e,2);n(I(t,.1)).toBe(d.RUNNING),n(I(t,.1)).toBe(d.SUCCESS)}),o("passes accumulated elapsed time to throttled running tasks",()=>{const e=new E,t=new N(e,.3);I(t,.1),I(t,.1),I(t,.1),I(t,.1),n(e.tick).toHaveBeenCalledTimes(2),n(e.tick.mock.calls[1][0]).toBeCloseTo(.3),v(t).abort(),n(e.stop).toHaveBeenCalledTimes(1)}),o("resets timer deadlines after completion and aborts on timeout",()=>{const e=s.spyOn(Date,"now").mockReturnValue(0);try{const t=new E(d.SUCCESS),o=new U(t,100);n(I(o,.1)).toBe(d.SUCCESS),e.mockReturnValue(200),t.state=d.RUNNING,n(I(o,.1)).toBe(d.RUNNING),e.mockReturnValue(300),n(I(o,.1)).toBe(d.FAILURE),n(t.stop).toHaveBeenLastCalledWith("aborted")}finally{e.mockRestore()}}),o("starts async work once and ignores completion from an aborted execution",async()=>{let e;const t=s.fn(()=>new Promise(t=>{e=t})),o=new a(t);I(o),I(o),n(t).toHaveBeenCalledTimes(1);const i=e;v(o).abort(),I(o),i(d.SUCCESS),await Promise.resolve(),n(I(o)).toBe(d.RUNNING),e(d.FAILURE),await Promise.resolve(),n(I(o)).toBe(d.FAILURE),I(o),n(t).toHaveBeenCalledTimes(3)}),o("reports async rejections and synchronous throws as failure",async()=>{const e=new a(()=>Promise.reject(new Error("failed")));I(e),await Promise.resolve(),n(I(e)).toBe(d.FAILURE),n(I(new a(()=>{throw new Error("failed")}))).toBe(d.FAILURE)})}),t("branch services",()=>{o("updates before a running child and cleans up on interruption",()=>{const t=[];const o=new class extends B{start(){t.push("activate")}update(e){t.push("update:"+e)}stop(){t.push("deactivate")}}(new class extends l{tick(){return t.push("task"),d.RUNNING}stop(){t.push("task stopped")}}),s=new e(o);s.tick(.1),s.tick(.2),s.abort(),n(t).toEqual(["activate","update:0.1","task","update:0.2","task","task stopped","deactivate"])}),o("runs while its branch is selected and propagates completion",()=>{let t=!0,o=0;const s=new E(d.SUCCESS),a=new e(new c(new class extends B{update(){o++}}(s),()=>t));n(a.tick(.1)).toBe(d.SUCCESS),t=!1,n(a.tick(.1)).toBe(d.FAILURE),n(o).toBe(1),n(s.tick).toHaveBeenCalledTimes(1)})});/*
|
|
2
|
+
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
|
+
* See the LICENSE.md file for details.
|
|
4
|
+
*/
|
|
@@ -23,6 +23,7 @@ export declare class CharacterMoveToNode extends LeafNode {
|
|
|
23
23
|
constructor(navigation: Navigation, movement: CharacterMovementLike);
|
|
24
24
|
private cardinalAxis?;
|
|
25
25
|
tick(dt: number): NodeState;
|
|
26
|
+
stop(): void;
|
|
26
27
|
private snapInputToCardinalAxis;
|
|
27
28
|
}
|
|
28
29
|
//# sourceMappingURL=move.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Vector3 as t}from"three";import{LeafNode as i,NodeState as e}from"./bt.js";export class CharacterMoveToNode extends i{constructor(t,i){super(),this.navigation=t,this.movement=i,this.distanceFrom=.5,this.speedScale=1,this.lateralSpeedScale=1,this.cardinalMovement=!1}tick(t){const i=this.movement.actor.object.position,s=null!=this.target?this.target():null;if(null==s)return this.movement.directionInput.vector.set(0,0),this.cardinalAxis=void 0,e.FAILURE;if(i.distanceTo(s)<this.distanceFrom)return this.movement.directionInput.vector.set(0,0),this.cardinalAxis=void 0,e.SUCCESS;const{success:a,path:o}=this.navigation.findPath(i,s);if(!a||o.length<2)return this.movement.directionInput.vector.set(0,0),this.cardinalAxis=void 0,e.FAILURE;const r=o[1],c=n.subVectors(r,i);c.y=0,c.normalize();const h=this.facingTarget?.(),l=null!=h?h.x-i.x:0,m=null!=h?h.z-i.z:0,d=l*l+m*m;if(null!=h&&d>1e-4){const t=Math.atan2(l,m),i=1/Math.sqrt(d),e=l*i,n=m*i,s=n,a=-e;this.movement.rotationInput.rotation.y=t,this.movement.directionInput.vector.set(-(c.x*s+c.z*a),c.x*e+c.z*n)}else this.movement.rotationInput.rotation.y=Math.atan2(c.x,c.z),this.movement.directionInput.vector.set(0,1);return this.cardinalMovement&&this.snapInputToCardinalAxis(),Math.abs(this.movement.directionInput.vector.x)>Math.abs(this.movement.directionInput.vector.y)&&this.movement.directionInput.vector.multiplyScalar(Math.max(0,Math.min(1,this.lateralSpeedScale))),this.movement.directionInput.vector.multiplyScalar(Math.max(0,Math.min(1,this.speedScale))),e.RUNNING}snapInputToCardinalAxis(){const t=this.movement.directionInput.vector,i=Math.abs(t.x),e=Math.abs(t.y);if(i+e<=1e-4)return void(this.cardinalAxis=void 0);const n=i>e?"x":"y";if(null==this.cardinalAxis)this.cardinalAxis=n;else{const t="x"===this.cardinalAxis?i:e;("x"===this.cardinalAxis?e:i)>t*s&&(this.cardinalAxis="x"===this.cardinalAxis?"y":"x")}"x"===this.cardinalAxis?t.set(Math.sign(t.x),0):t.set(0,Math.sign(t.y))}}const n=new t,s=1.25;/*
|
|
1
|
+
import{Vector3 as t}from"three";import{LeafNode as i,NodeState as e}from"./bt.js";export class CharacterMoveToNode extends i{constructor(t,i){super(),this.navigation=t,this.movement=i,this.distanceFrom=.5,this.speedScale=1,this.lateralSpeedScale=1,this.cardinalMovement=!1}tick(t){const i=this.movement.actor.object.position,s=null!=this.target?this.target():null;if(null==s)return this.movement.directionInput.vector.set(0,0),this.cardinalAxis=void 0,e.FAILURE;if(i.distanceTo(s)<this.distanceFrom)return this.movement.directionInput.vector.set(0,0),this.cardinalAxis=void 0,e.SUCCESS;const{success:a,path:o}=this.navigation.findPath(i,s);if(!a||o.length<2)return this.movement.directionInput.vector.set(0,0),this.cardinalAxis=void 0,e.FAILURE;const r=o[1],c=n.subVectors(r,i);c.y=0,c.normalize();const h=this.facingTarget?.(),l=null!=h?h.x-i.x:0,m=null!=h?h.z-i.z:0,d=l*l+m*m;if(null!=h&&d>1e-4){const t=Math.atan2(l,m),i=1/Math.sqrt(d),e=l*i,n=m*i,s=n,a=-e;this.movement.rotationInput.rotation.y=t,this.movement.directionInput.vector.set(-(c.x*s+c.z*a),c.x*e+c.z*n)}else this.movement.rotationInput.rotation.y=Math.atan2(c.x,c.z),this.movement.directionInput.vector.set(0,1);return this.cardinalMovement&&this.snapInputToCardinalAxis(),Math.abs(this.movement.directionInput.vector.x)>Math.abs(this.movement.directionInput.vector.y)&&this.movement.directionInput.vector.multiplyScalar(Math.max(0,Math.min(1,this.lateralSpeedScale))),this.movement.directionInput.vector.multiplyScalar(Math.max(0,Math.min(1,this.speedScale))),e.RUNNING}stop(){this.cardinalAxis=void 0}snapInputToCardinalAxis(){const t=this.movement.directionInput.vector,i=Math.abs(t.x),e=Math.abs(t.y);if(i+e<=1e-4)return void(this.cardinalAxis=void 0);const n=i>e?"x":"y";if(null==this.cardinalAxis)this.cardinalAxis=n;else{const t="x"===this.cardinalAxis?i:e;("x"===this.cardinalAxis?e:i)>t*s&&(this.cardinalAxis="x"===this.cardinalAxis?"y":"x")}"x"===this.cardinalAxis?t.set(Math.sign(t.x),0):t.set(0,Math.sign(t.y))}}const n=new t,s=1.25;/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export*from"./navigation.js";export*from"./navigation-areas.js";export*from"./behavior-tree/bt.js";export*from"./behavior-tree/move.js";/*
|
|
1
|
+
export*from"./navigation.js";export*from"./navigation-areas.js";export*from"./behavior-tree/bt.js";export*from"./behavior-tree/move.js";export*from"./behavior-tree/behavior-tree.js";/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AnimationClip } from 'three';
|
|
2
|
+
import type { Object3D, VectorKeyframeTrack } from 'three';
|
|
3
|
+
import type { AnimationFootContacts } from './foot-contact.js';
|
|
4
|
+
export type FootContactBakeOptions = {
|
|
5
|
+
samplesPerSecond?: number;
|
|
6
|
+
/** Height above each foot's lowest sampled position, in presentation-space units. */
|
|
7
|
+
heightThreshold?: number;
|
|
8
|
+
maxVerticalSpeed?: number;
|
|
9
|
+
minimumContactTime?: number;
|
|
10
|
+
/** Restore authored root travel while sampling a fixed-in-place RootMotionClip. */
|
|
11
|
+
rootMotionTrack?: VectorKeyframeTrack;
|
|
12
|
+
};
|
|
13
|
+
/** Clip preprocessing: uses an isolated rig, never seeks the live character mixer. */
|
|
14
|
+
export declare class FootContactBaker {
|
|
15
|
+
private readonly root;
|
|
16
|
+
private readonly left;
|
|
17
|
+
private readonly right;
|
|
18
|
+
private readonly mixer;
|
|
19
|
+
private readonly point;
|
|
20
|
+
constructor(root: Object3D, leftFootName: string, rightFootName: string);
|
|
21
|
+
bake(clip: AnimationClip, options?: FootContactBakeOptions): AnimationFootContacts | undefined;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=foot-contact-baker.d.ts.map
|