PlayerControllerPlus/Modes/Ladder.cs
namespace Sandbox.MovementPlus;
public class MoveModeLadderPlus : MoveModePlus
{
public GameObject ClimbingObject { get; set; }
public Rotation ClimbingRotation { get; set; }
public override void UpdateRigidBody( Rigidbody body )
{
body.Gravity = false;
body.LinearDamping = 20.0f;
body.AngularDamping = 1.0f;
}
public override int Score( PlayerControllerPlus controller )
{
if ( ClimbingObject.IsValid() ) return controller.LadderPriority;
return -100;
}
public override void OnModeBegin()
{
Controller.IsClimbing = true;
Controller.Body.Velocity = 0;
}
public override void OnModeEnd( MoveModePlus next )
{
Controller.IsClimbing = false;
Controller.Body.Velocity = Controller.Body.Velocity.ClampLength( Controller.RunSpeed );
}
public override void PostPhysicsStep()
{
UpdatePositionOnLadder();
}
void UpdatePositionOnLadder()
{
if ( !ClimbingObject.IsValid() ) return;
var pos = Controller.WorldPosition;
var ladderPos = ClimbingObject.WorldPosition;
var ladderUp = ClimbingObject.WorldRotation.Up;
Line ladderLine = new Line( ladderPos - ladderUp * 1000, ladderPos + ladderUp * 1000 );
var idealPos = ladderLine.ClosestPoint( pos );
var delta = (idealPos - pos);
delta = delta.SubtractDirection( ClimbingObject.WorldRotation.Forward );
if ( delta.Length > 0.01f )
{
Controller.Body.Velocity = Controller.Body.Velocity.AddClamped( delta * 5.0f, delta.Length * 10.0f );
}
}
public override void FixedUpdate()
{
ScanForLadders();
}
void ScanForLadders()
{
if ( Controller?.Body == null )
return;
var wt = Controller.WorldTransform;
Vector3 head = wt.PointToWorld( new Vector3( 0, 0, Controller.CurrentHeight ) );
Vector3 foot = wt.Position;
GameObject ladderObject = default;
foreach ( var touch in Controller.Body.Touching )
{
if ( !touch.Tags.HasAny( Controller.LadderClimbableTags ) )
continue;
if ( ClimbingObject == touch.GameObject )
{
ladderObject = touch.GameObject;
continue;
}
var ladderSurface = touch.FindClosestPoint( head );
var level = Vector3.InverseLerp( ladderSurface, foot, head, true );
if ( ClimbingObject != touch.GameObject && level < 0.5f )
continue;
ladderObject = touch.GameObject;
break;
}
if ( ladderObject == ClimbingObject )
return;
ClimbingObject = ladderObject;
if ( ClimbingObject.IsValid() )
{
var directionToLadder = ClimbingObject.WorldPosition - Controller.WorldPosition;
ClimbingRotation = ClimbingObject.WorldRotation;
if ( directionToLadder.Dot( ClimbingRotation.Forward ) < 0 )
{
ClimbingRotation *= new Angles( 0, 180, 0 );
}
}
}
public override Vector3 UpdateMove( Rotation eyes, Vector3 input )
{
var wishVelocity = new Vector3( 0, 0, Input.AnalogMove.x );
if ( eyes.Pitch() > 50f )
{
wishVelocity *= -1f;
}
wishVelocity *= 1500.0f * Controller.LadderSpeed * (Controller.IsDucking ? 0.5f : 1f);
if ( Input.Down( "jump" ) )
{
Controller.Jump( ClimbingRotation.Backward * 200 );
}
return wishVelocity;
}
protected override void OnRotateRenderBody( SkinnedModelRenderer renderer )
{
renderer.WorldRotation = Rotation.Lerp( renderer.WorldRotation, ClimbingRotation, Time.Delta * 5.0f );
}
}