things/items/ReviveSoul.cs

Item entity representing a revive soul pickup. It spawns with movement parameters, floats/rotates, attracts to dead players within range, and revives a dead player on collision.

Networking
using System;
using Sandbox;
using Sandbox.Diagnostics;
using Sandbox.Utility;

public class ReviveSoul : Item
{
	private float _rotateTimeOffset;
	private float _personalRotateSpeed;

	public override Vector3 SpawnScale => new Vector3( 0.8f );

	protected override void OnStart()
	{
		base.OnStart();

		Lifetime = 95f;
		ShouldCheckBounds = true;
		PushStrength = 500f;

		BaseZPos = -12f;
		WorldPosition = WorldPosition.WithZ( BaseZPos );

		if ( IsProxy )
			return;

		Deceleration = 0.92f;

		_rotateTimeOffset = Game.Random.Float( 0f, 10f );
		_personalRotateSpeed = Game.Random.Float( 4f, 8f );
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if ( Manager.Instance.IsGameOver )
			return;

		var localPlayer = Manager.Instance.LocalPlayer;
		ModelRenderer.Tint = Color.White.WithAlpha( localPlayer.IsValid() && localPlayer.IsDead ? 1f : 0.5f );

		if ( IsProxy )
			return;

		LocalRotation = Rotation.From( new Angles( 0f, -90f, Utils.FastSin( _rotateTimeOffset + Time.Now * _personalRotateSpeed ) * 7f ) );

		if ( !IsInTheAir )
		{
			foreach ( var player in Manager.Instance.Players )
			{
				if ( !player.IsValid() )
					continue;

				if ( player.IsDead )
				{
					var dist_sqr = (Position2D - player.Position2D).LengthSquared;
					var req_dist_sqr = MathF.Pow( player.GetSyncStat( PlayerStat.CoinAttractRange ), 2f );

					if ( dist_sqr < req_dist_sqr )
					{
						if ( (player.Position2D - Position2D).LengthSquared > Manager.TOUCH_DIST_REQUIRED_SQR )
							Velocity += (player.Position2D - Position2D).Normal * Utils.Map( dist_sqr, req_dist_sqr, 0f, 0f, 1f, EasingType.Linear ) * player.GetSyncStat( PlayerStat.CoinAttractStrength ) * Time.Delta;
					}
				}
			}

			WorldPosition = WorldPosition.WithZ( BaseZPos + 5f + Utils.MapReturn( Utils.FastSin( _rotateTimeOffset + Time.Now * _personalRotateSpeed ), -1f, 1f, -1f, 1f, EasingType.Linear ) * 5f );
		}
	}

	public override void Colliding( Thing other, float percent, float dt )
	{
		base.Colliding( other, percent, dt );

		if ( CantBeCollected )
			return;

		if ( other is Player player )
		{
			if ( player.IsDead )
			{
				player.Revive();

				player.NumItemsCollected++;

				Remove();
			}
			else if ( !Position2D.Equals( other.Position2D ) )
			{
				Velocity += (Position2D - other.Position2D).Normal * other.PushStrength * percent * ( other.Weight / Weight ) * dt;
			}
		}
	}
}