@@ -543,13 +543,90 @@ pub const ClientItemDropManager = struct { // MARK: ClientItemDropManager
543543 }
544544};
545545
546+ const BobManager = struct {
547+ phase : f32 = 0 ,
548+ scale : f32 = 0 ,
549+
550+ const bobSpeed : f32 = 11 ;
551+ const bobAmountLateral : f32 = 0.08 ;
552+ const bobAmountVertical : f32 = 0.07 ;
553+
554+ const actualSpeedMax : f32 = 5 ;
555+ const inputSpeedMax : f32 = 10 ;
556+
557+ const scaleThreshold : f32 = 0.1 ;
558+
559+ const scaleMinForPhaseWhenWalking : f32 = 0.7 ;
560+
561+ const settleSpeedFly : f32 = 5 ;
562+ const scaleFly : f32 = 0.8 ;
563+
564+ const settleSpeedAirborne : f32 = 2.5 ;
565+ const scaleAirborne : f32 = 0.8 ;
566+
567+ const settleSpeedOnGround : f32 = 4 ;
568+ const scaleOnGround : f32 = 0.1 ;
569+
570+ const scaleTransitionSpeed : f32 = 4 ;
571+
572+ inline fn settlePhase (self : * @This (), dt : f32 , speed : f32 ) void {
573+ const targetPhase = std .math .round (self .phase / std .math .pi )* std .math .pi ;
574+ self .phase = std .math .lerp (self .phase , targetPhase , @min (dt * speed , 1 ));
575+ }
576+
577+ inline fn transitionScale (self : * @This (), dt : f32 , newScale : f32 ) void {
578+ self .scale = std .math .lerp (self .scale , newScale , @min (dt * scaleTransitionSpeed , 1 ));
579+ }
580+
581+ fn update (self : * @This (), dt : f32 ) void {
582+ if (game .Player .isFlying .load (.monotonic ) or game .Player .isGhost .load (.monotonic )) {
583+ self .transitionScale (dt , scaleFly );
584+ self .settlePhase (dt , settleSpeedFly );
585+ return ;
586+ }
587+
588+ const horizontalVel : Vec3f = .{
589+ @floatCast (game .Player .super .vel [0 ]),
590+ @floatCast (game .Player .super .vel [1 ]),
591+ 0 ,
592+ };
593+
594+ if (! game .Player .onGround ) {
595+ self .transitionScale (dt , scaleAirborne );
596+ self .settlePhase (dt , settleSpeedAirborne );
597+ return ;
598+ }
599+
600+ const newScale = @min (@as (f32 , @floatCast (game .Player .inputSpeed ))/ inputSpeedMax , vec .length (horizontalVel )/ actualSpeedMax , 1 );
601+
602+ if (newScale > scaleThreshold ) {
603+ self .transitionScale (dt , newScale );
604+ self .phase += dt * bobSpeed * @max (self .scale , scaleMinForPhaseWhenWalking );
605+ self .phase = std .math .mod (f32 , self .phase , 2 * std .math .pi ) catch unreachable ;
606+ return ;
607+ }
608+
609+ self .transitionScale (dt , scaleOnGround );
610+ self .settlePhase (dt , settleSpeedOnGround );
611+ }
612+
613+ fn getOffset (self : @This ()) Vec3f {
614+ const s = std .math .sin (self .phase );
615+ return .{
616+ @abs (s )* bobAmountVertical * self .scale ,
617+ 0 ,
618+ s * bobAmountLateral * self .scale ,
619+ };
620+ }
621+ };
622+
546623// Going to handle item animations and other things like - interpolation, movement reactions
547624pub const ItemDisplayManager = struct { // MARK: ItemDisplayManager
548625 pub var showItem : bool = true ;
549626 var cameraFollow : Vec3f = @splat (0 );
550627 var cameraFollowVel : Vec3f = @splat (0 );
551628 const damping : Vec3f = @splat (130 );
552- var bobPhase : f32 = 0 ;
629+ var bobManager : BobManager = .{} ;
553630
554631 pub fn update (deltaTime : f64 ) void {
555632 if (! settings .bobbing ) {
@@ -560,7 +637,9 @@ pub const ItemDisplayManager = struct { // MARK: ItemDisplayManager
560637 if (deltaTime == 0 ) return ;
561638 const dt : f32 = @floatCast (deltaTime );
562639
563- const totalOffset = calculateAscentDescentOffset () + calculateBobOffset (dt );
640+ bobManager .update (dt );
641+
642+ const totalOffset = getAscentDescentOffset () + bobManager .getOffset ();
564643
565644 // TODO: add *smooth* item sway
566645 const n1 : Vec3f = cameraFollowVel - (cameraFollow - totalOffset )* damping * damping * @as (Vec3f , @splat (dt ));
@@ -570,47 +649,10 @@ pub const ItemDisplayManager = struct { // MARK: ItemDisplayManager
570649 cameraFollow += cameraFollowVel * @as (Vec3f , @splat (dt ));
571650 }
572651
573- fn calculateAscentDescentOffset () Vec3f {
652+ fn getAscentDescentOffset () Vec3f {
574653 const playerVel : Vec3f = .{@floatCast ((game .Player .super .vel [2 ]* 0.009 + game .Player .eye .vel [2 ]* 0.0075 )), 0 , 0 };
575654 return vec .clampMag (playerVel , 0.32 );
576655 }
577-
578- fn calculateBobOffset (dt : f32 ) Vec3f {
579- const bobSpeed : f32 = 1.2 ;
580- const bobAmountLateral : f32 = 0.007 ;
581- const bobAmountVertical : f32 = 0.006 ;
582- const settleSpeed : f32 = 2.5 ;
583- const movementScaleMax : f32 = 10 ;
584- const playerSpeedThreshold : f32 = 0.01 ;
585- const movementScaleMinForPhase : f32 = 6 ;
586-
587- if (game .Player .isFlying .load (.monotonic ) or game .Player .isGhost .load (.monotonic )) {
588- bobPhase = 0 ;
589- return @splat (0 );
590- }
591-
592- const playerVel : Vec3f = .{
593- @floatCast (game .Player .super .vel [0 ]),
594- @floatCast (game .Player .super .vel [1 ]),
595- 0 ,
596- };
597- const playerSpeed = vec .length (playerVel );
598- const movementScale = @min (playerSpeed , movementScaleMax );
599-
600- if (game .Player .onGround and playerSpeed > playerSpeedThreshold ) {
601- bobPhase += dt * bobSpeed * @max (movementScale , movementScaleMinForPhase );
602- bobPhase = std .math .mod (f32 , bobPhase , 2 * std .math .pi ) catch unreachable ;
603- } else {
604- const targetPhase = std .math .round (bobPhase / std .math .pi )* std .math .pi ;
605- bobPhase = std .math .lerp (bobPhase , targetPhase , @min (dt * settleSpeed , 1 ));
606- }
607-
608- return .{
609- @abs (std .math .sin (bobPhase ))* bobAmountVertical * movementScale ,
610- 0 ,
611- std .math .sin (bobPhase )* bobAmountLateral * movementScale ,
612- };
613- }
614656};
615657
616658pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
0 commit comments