things/items/ArmorItem.cs

Item subclass representing an armor pickup. It sets spawn properties, handles idle rotation and attraction toward nearby players, and gives armor plus a sound/effect when a player collides with it.

Networking
using System;
using Sandbox;

public class ArmorItem : Item
{
	public int ArmorAmount { get; set; } = 10;

	private float _rotateTimeOffset;
	private float _personalRotateSpeed;

	public override Vector3 SpawnScale => new Vector3( 0.3f );
	protected override float AttractRangeFactor => 0.95f;
	protected override float AttractStrengthFactor => 0.8f;

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

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

		BaseZPos = 18f;
		WorldPosition = WorldPosition.WithZ( BaseZPos );

		if ( IsProxy )
			return;

		Deceleration = 0.92f;

		_rotateTimeOffset = Game.Random.Float( 0f, 10f );
		_personalRotateSpeed = Game.Random.Float( 3f, 5f );
	}

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

		if ( Manager.Instance.IsGameOver )
			return;

		if ( IsProxy )
			return;

		LocalRotation = Rotation.From( new Angles( -30f, -90f + Utils.FastSin( _rotateTimeOffset + Time.Now * _personalRotateSpeed * 0.75f ) * 25f, Utils.FastSin( _rotateTimeOffset + Time.Now * _personalRotateSpeed ) * 3f ) );

		// todo: this already happens in base class for ArmorItem?
		foreach ( var player in Manager.Instance.AlivePlayers )
		{
			if( player.Armor < 999 )
			{
				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;
				}
			}
		}

		//if ( !IsFlying )
		//	ZPos = BaseZPos + 3f + Utils.FastSin( Time.Now * _personalBounceSpeed ) * 4f;
	}

	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.GainArmorRpc( ArmorAmount );
				Manager.Instance.PlaySfxNearbyRpc( "heal", player.Position2D, pitch: Game.Random.Float(2.1f, 2.2f), volume: 1.1f, maxDist: 350f );

				player.CollectItemEffect();

				Remove();
			}
		}
	}
}