Player/PlayerUse.cs
public sealed class PlayerUse : Component, PlayerController.IEvents
{
[RequireComponent] public Player Player { get; set; }
Rigidbody carrying;
Transform carryTransform;
Transform carryOriginalTransform;
public bool Interative;
public string TooltipIcon;
public string Tooltip;
/// <summary>
/// Hook into the PlayerController's use system and tell it we want to
/// interact with RigidBodies that we can carry
/// </summary>
Component PlayerController.IEvents.GetUsableComponent( GameObject go )
{
var rb = go.GetComponent<Rigidbody>();
if ( CanCarry( rb ) )
{
return rb;
}
return default;
}
/// <summary>
/// Can carry rigidbodies that are networked and we can take ownership of
/// </summary>
private bool CanCarry( Rigidbody rb )
{
if ( !rb.IsValid() ) return false;
if ( !rb.Network.Active ) return false;
if ( rb.Network.OwnerTransfer != OwnerTransfer.Takeover ) return false;
return true;
}
void UpdateTooltips( Component lookingAt, Component pressed )
{
if ( !lookingAt.IsValid() || pressed.IsValid() )
{
Tooltip = null;
Interative = false;
return;
}
var tt = lookingAt.GetComponent<Tooltip>();
if ( tt is not null )
{
Tooltip = tt.Text;
TooltipIcon = tt.Icon;
Interative = true;
return;
}
if ( lookingAt is Rigidbody rbb && CanCarry( rbb ) )
{
Tooltip = "Pick Up";
TooltipIcon = "back_hand";
Interative = true;
}
else
{
Tooltip = null;
Interative = false;
}
}
void PlayerController.IEvents.StartPressing( Sandbox.Component target )
{
var rb = target.GetComponent<Rigidbody>();
if ( CanCarry( rb ) )
{
StartCarry( rb );
}
}
void PlayerController.IEvents.StopPressing( Sandbox.Component target )
{
StopCarrying();
}
protected override void OnUpdate()
{
if ( IsProxy )
return;
UpdateTooltips( Player.Controller.Hovered, Player.Controller.Pressed );
}
private void StartCarry( Rigidbody rb )
{
if ( !rb.Network.TakeOwnership() )
return;
StopCarrying();
carrying = rb;
carryOriginalTransform = rb.Transform.World;
carryTransform = Player.EyeTransform.ToLocal( rb.Transform.World );
}
void StopCarrying()
{
if ( carrying.IsValid() )
{
carrying.Network.DropOwnership();
}
carrying = default;
}
protected override void OnFixedUpdate()
{
base.OnFixedUpdate();
if ( carrying.IsValid() && !carrying.IsProxy )
{
var targetTransform = Player.EyeTransform.ToWorld( carryTransform );
targetTransform.Rotation = carryOriginalTransform.Rotation.Angles().WithYaw( targetTransform.Rotation.Angles().yaw );
var distance = Vector3.DistanceBetween( targetTransform.Position, carrying.WorldPosition );
if ( distance > 50.0f )
{
StopCarrying();
return;
}
var mass = carrying.PhysicsBody.Mass;
var moveSpeed = mass.Remap( 50, 3000, 0.05f, 2.0f, true );
carrying.PhysicsBody.SmoothMove( targetTransform, moveSpeed, Time.Delta );
}
}
object TryGetLookedAt( float radius )
{
var eyeTrace = Scene.Trace
.Ray( Scene.Camera.Transform.World.ForwardRay, 150 )
.IgnoreGameObjectHierarchy( GameObject )
.Radius( radius )
.Run();
if ( !eyeTrace.Hit ) return default;
if ( !eyeTrace.GameObject.IsValid() ) return default;
var button = eyeTrace.GameObject.Components.Get<IPressable>();
if ( button is not null && button.CanPress( new IPressable.Event( this ) ) ) return button;
var rigidbody = eyeTrace.GameObject.Components.Get<Rigidbody>();
if ( CanCarry( rigidbody ) ) return rigidbody;
return default;
}
}
public class Tooltip : Component
{
[Property] public string Text { get; set; }
[Property] public string Icon { get; set; }
}