Weapons/ToolGun/Modes/Wheel/WheelEntity.cs
public sealed class WheelEntity : Component, IPlayerControllable
{
[Property, Range( 0, 1 ), ClientEditable]
public bool Reversed { get; set; }
[Property, Range( 0, 1 ), ClientEditable]
public float Speed { get; set; } = 0.5f;
[Property, Range( 0, 1 ), ClientEditable]
public float Power { get; set; } = 0.5f;
[Property, ClientEditable]
public ClientInput Forward { get; set; }
[Property, ClientEditable]
public ClientInput Reverse { get; set; }
[Property, ClientEditable]
public ClientInput Brake { get; set; }
[Property, ClientEditable]
public ClientInput TurnLeft { get; set; }
[Property, ClientEditable]
public ClientInput TurnRight { get; set; }
private Vector3 _localAxle;
private Vector3 _localUp;
protected override void OnEnabled()
{
base.OnEnabled();
var joint = GetComponentInChildren<WheelJoint>();
if ( joint.IsValid() && joint.Body.IsValid() )
{
var bodyRotInv = joint.Body.WorldRotation.Inverse;
_localAxle = bodyRotInv * WorldRotation.Right;
_localUp = bodyRotInv * WorldRotation.Up;
}
}
protected override void OnUpdate()
{
if ( SpawnMenuHost.GetActiveMode() is not ContextMenuHost ) return;
var renderers = GameObject.GetComponentsInChildren<Renderer>();
var outlines = Game.ActiveScene.GetAllComponents<HighlightOutline>();
if ( !outlines.Any( outline => outline.Targets?.Any( target => renderers.Contains( target ) ) == true ) ) return;
var joint = GetComponentInChildren<WheelJoint>();
if ( !joint.IsValid() || !joint.Body.IsValid() || _localAxle == Vector3.Zero ) return;
var spinAxis = joint.Body.WorldRotation * _localAxle;
var stableUp = joint.Body.WorldRotation * _localUp;
var renderer = GameObject.GetComponentInChildren<ModelRenderer>();
var overlayRadius = 0f;
if ( renderer.IsValid() )
{
var size = renderer.Model.Bounds.Size;
overlayRadius = MathF.Max( size.x, size.z ) * 0.01f * WorldScale.x;
}
WheelOverlay.DrawDirection( WorldPosition, spinAxis, stableUp, overlayRadius, Reversed );
}
private void ApplyInput( float forward, float reverse, float brake, float turnLeft, float turnRight )
{
if ( !Networking.IsHost ) return;
var joint = GetComponentInChildren<WheelJoint>();
if ( !joint.IsValid() ) return;
var speed = (forward - reverse).Clamp( -1, 1 );
if ( brake > 0.1f )
{
joint.EnableSpinMotor = true;
joint.SpinMotorSpeed = 0;
joint.MaxSpinTorque = 500000 * Power;
}
else if ( speed.AlmostEqual( 0f ) )
{
joint.EnableSpinMotor = false;
}
else
{
if ( Reversed ) speed = -speed;
joint.EnableSpinMotor = true;
joint.SpinMotorSpeed = -2000 * speed * Speed;
joint.MaxSpinTorque = 200000 * Power;
}
var direction = (turnRight - turnLeft).Clamp( -1, 1 );
if ( !direction.AlmostEqual( 0f ) )
{
joint.EnableSteering = true;
joint.SteeringDampingRatio = 1f;
joint.MaxSteeringTorque = 500000;
joint.SteeringLimits = new Vector2( -45, 45 );
joint.TargetSteeringAngle = 30 * direction;
}
else
{
joint.TargetSteeringAngle = 0;
}
}
[SignalInput( Id = nameof( Forward ), Default = true )]
public void ForwardSignal( float amount ) => ApplyDrive( amount );
[SignalInput( Id = nameof( Reverse ) )]
public void ReverseSignal( float amount ) => ApplyDrive( -amount );
[SignalInput( Id = nameof( Brake ) )]
public void BrakeSignal( bool active ) => ApplyInput( 0f, 0f, active ? 1f : 0f, 0f, 0f );
[SignalInput( Id = nameof( TurnLeft ) )]
public void TurnLeftSignal( float amount ) => ApplySteering( -amount );
[SignalInput( Id = nameof( TurnRight ) )]
public void TurnRightSignal( float amount ) => ApplySteering( amount );
public void OnControl()
{
ApplyInput( Forward.GetAnalog(), Reverse.GetAnalog(), Brake.GetAnalog(), TurnLeft.GetAnalog(), TurnRight.GetAnalog() );
}
private void ApplyDrive( float speed ) => ApplyInput( speed, 0f, 0f, 0f, 0f );
private void ApplySteering( float direction )
{
var joint = GetComponentInChildren<WheelJoint>();
if ( !joint.IsValid() ) return;
joint.EnableSteering = true;
joint.SteeringDampingRatio = 1f;
joint.MaxSteeringTorque = 500000;
joint.SteeringLimits = new Vector2( -45, 45 );
joint.TargetSteeringAngle = 30 * direction.Clamp( -1f, 1f );
}
}