Combat/Projectile.cs
namespace Prototype;

public sealed class Projectile : Component
{
	[Sync] public Vector3 Velocity { get; set; }
	[Sync] public float Damage { get; set; }
	[Sync] public GameObject Instigator { get; set; }
	[Sync] public float Lifetime { get; set; } = 2.5f;


	protected override void OnUpdate()
	{
		// Clients display the replicated transform; only the host hits/despawns.
		if ( IsProxy ) return;
		Lifetime -= Time.Delta;
		if ( Lifetime <= 0f ) { DestroyGameObject(); return; }
		var next = WorldPosition + Velocity * Time.Delta;
		var trace = Scene.Trace.Ray( WorldPosition, next ).WithoutTags( "projectile" ).Run();
		if ( trace.Hit && trace.GameObject != Instigator )
		{
			var targetPlayer = trace.GameObject.Components.Get<FpsPlayer>( FindMode.EverythingInSelfAndAncestors );
			var targetEnemy = trace.GameObject.Components.Get<EnemyBrain>( FindMode.EverythingInSelfAndAncestors );
			var sourceIsPlayer = Instigator?.Components.Get<FpsPlayer>() is not null;
			if ( sourceIsPlayer && targetEnemy is not null ) targetEnemy.Vitality?.ApplyDamage( Damage, Instigator );
			else if ( !sourceIsPlayer && targetPlayer is not null ) targetPlayer.ReceiveDamage( Damage, Instigator );
			DestroyGameObject();
			return;
		}
		WorldPosition = next;
	}
}