things/enemyProjectiles/TossedProjectile.cs

A tossed enemy projectile entity. It interpolates a 2D trajectory and an arc height over its Lifetime, updates WorldPosition each tick, and spawns impact particles when it hits the ground or is removed.

using System;
using Sandbox;

public class TossedProjectile : EnemyProjectile
{
	[Property] public GameObject Model { get; set; }
	[Sync] public float Damage { get; set; }
	public float Lifetime { get; set; }

	[Sync] public Enemy Shooter { get; set; }

	protected float _hitForce;
	protected Color _impactParticleColor;

	protected float _height;
	protected float _startHeight;

	public Vector2 StartPos2D { get; set; }
	public Vector2 TargetPos2D { get; set; }

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

		if ( IsProxy )
			return;

		_startHeight = WorldPosition.z;
	}

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

		if ( IsProxy )
			return;

		//Gizmo.Draw.Color = Color.White;
		//Gizmo.Draw.Text( $"TimeSinceSpawn: {TimeSinceSpawn}\nLifetime: {Lifetime}", new global::Transform( WorldPosition ) );

		// todo: move with wind force

		Vector2 pos2D = Vector2.Lerp( StartPos2D, TargetPos2D, Utils.Map( TimeSinceSpawn, 0f, Lifetime, 0f, 1f, EasingType.Linear ) );
		float zPos = TimeSinceSpawn < Lifetime * 0.5f
			? Utils.Map( TimeSinceSpawn, 0f, Lifetime * 0.5f, _startHeight, _height, EasingType.QuadOut )
			: Utils.Map( TimeSinceSpawn, Lifetime * 0.5f, Lifetime, _height, 0f, EasingType.QuadIn );

		WorldPosition = new Vector3( pos2D.x, pos2D.y, zPos );

		if ( TimeSinceSpawn > Lifetime )
		{
			HitGround();
		}
	}

	protected virtual void HitGround()
	{
		RemoveRpc( shouldSpawnEffects: true );
	}

	protected override void Remove( bool shouldSpawnEffects )
	{
		base.Remove( shouldSpawnEffects );

		if( shouldSpawnEffects )
			Manager.Instance.SpawnBulletImpactParticles( WorldPosition.WithZ( Math.Max( WorldPosition.z, 10f ) ), Vector3.Up, _impactParticleColor );

		if ( IsProxy )
			return;

	}
}