Component for dead players that lets them observe their last corpse. It caches the most recent DeathCameraTarget owned by the network owner, lets the player look around, rotates the camera around the corpse head, and allows respawn after a short delay or on input.
/// <summary>
/// Dead players become these. They try to observe their last corpse.
/// </summary>
public sealed class PlayerObserver : Component
{
Angles EyeAngles;
TimeSince timeSinceStarted;
DeathCameraTarget _cachedCorpse;
protected override void OnEnabled()
{
base.OnEnabled();
EyeAngles = Scene.Camera.WorldRotation;
timeSinceStarted = 0;
_cachedCorpse = Scene.GetAllComponents<DeathCameraTarget>()
.Where( x => x.Connection == Network.Owner )
.OrderByDescending( x => x.Created )
.FirstOrDefault();
}
protected override void OnUpdate()
{
if ( IsProxy ) return;
if ( _cachedCorpse.IsValid() )
{
RotateAround( _cachedCorpse );
}
// Don't allow immediate respawn
if ( timeSinceStarted < 1 )
return;
// If pressed a button, or has been too long
if ( Input.Pressed( "attack1" ) || Input.Pressed( "jump" ) || timeSinceStarted > 4f )
{
PlayerData.For( Network.Owner )?.RequestRespawn();
GameObject.Destroy();
}
}
private void RotateAround( Component target )
{
// Find the corpse eyes
if ( !target.Components.Get<SkinnedModelRenderer>().TryGetBoneTransform( "head", out var tx ) )
{
tx.Position = target.GameObject.GetBounds().Center + Vector3.Up * 25f;
}
var e = EyeAngles;
e += Input.AnalogLook;
e.pitch = e.pitch.Clamp( -90, 90 );
e.roll = 0.0f;
EyeAngles = e;
var center = tx.Position;
var targetPos = center - EyeAngles.Forward * 150f;
var tr = Scene.Trace.FromTo( center, targetPos ).Radius( 1.0f ).WithoutTags( "ragdoll", "effect" ).Run();
Scene.Camera.WorldPosition = Vector3.Lerp( Scene.Camera.WorldPosition, tr.EndPosition, timeSinceStarted, true );
Scene.Camera.WorldRotation = EyeAngles;
}
}