Weapons/Base/SingleUseWeapon.cs
namespace Opium;

public partial class SingleUseWeapon : BaseWeapon
{
	[Property] public Action OnStartAction { get; set; }
	[Property] public Action OnDiscardAction { get; set; }

	[Property] public CameraEffect OnStartCameraEffect { get; set; }
	[Property] public CameraEffect OnDiscardCameraEffect { get; set; }

	[Property, Group( "Basic Shooting" )] public bool DoShoot { get; set; } = false;
	[Property, Group( "Basic Shooting" )] public float Range { get; set; } = 100f;
	[Property, Group( "Basic Shooting" )] public float InitialDelay { get; set; } = 0f;
	[Property, Group( "Basic Shooting" )] public float BulletSize { get; set; } = 5f;
	[Property, Group( "Basic Shooting" )] public float Randomization { get; set; } = 0f;
	[Property, Group( "Basic Shooting" )] public Vector3 ShotOffset { get; set; } = Vector3.Zero;

	[Property] public SoundEvent Sound { get; set; }

	public override void OnCreateViewModel()
	{
		Start();
	}

	protected override void OnEnabled()
	{
		TimeUntilDiscard = DiscardTime;

		if ( OnStartCameraEffect is not null && Actor is Opium.PlayerController player )
		{
			OnStartCameraEffect.Enabled = true;
			OnStartCameraEffect.Player = player;
		}
	}

	[Property] public float DiscardTime { get; set; } = 3f;
	[Property, ReadOnly] public TimeUntil TimeUntilDiscard { get; set; } = 3f;

	private bool HasShot { get; set; } = false;

	/// <summary>
	/// Called when the weapon is created.
	/// </summary>
	protected virtual void Start()
	{
		OnStartAction?.Invoke();

		if ( Sound is not null )
		{
			Log.Info( "Playing sound!" );
			Sandbox.Sound.Play( Sound );
		}

		if ( DoShoot )
		{
			AsyncShoot();
		}
	}

	/// <summary>
	/// Runs a trace with all the data we have supplied it, and returns the result
	/// </summary>
	/// <returns></returns>
	protected virtual SceneTraceResult GetShootTrace()
	{
		var cameraOrigin = Actor.CameraObject.Transform;

		var forward = cameraOrigin.Rotation.Forward;
		forward += (Vector3.Random + Vector3.Random + Vector3.Random + Vector3.Random) * Randomization;
		forward = forward.Normal;

		var offset = Vector3.Zero;
		offset += cameraOrigin.Rotation.Forward * ShotOffset.x;
		offset += cameraOrigin.Rotation.Left * ShotOffset.y;
		offset += cameraOrigin.Rotation.Up * ShotOffset.z;

		var ray = new Ray( cameraOrigin.Position + offset, forward );

		var tr = Scene.Trace.Ray( ray, Range )
			.IgnoreGameObjectHierarchy( GameObject.Root )
			.Size( BulletSize )
			.UseHitboxes()
			.Run();

		return tr;
	}

	async void AsyncShoot()
	{
		if ( HasShot ) return;

		HasShot = true;

		await GameTask.DelaySeconds( InitialDelay );

		var tr = GetShootTrace();
		var damageInfo = Opium.DamageInfo.Generic( BaseDamage, Actor.GameObject, GameObject, "melee" );
		CalculateDamage( damageInfo );

		tr.GameObject?.TakeDamage( damageInfo );

		var skinnedModelRenderer = tr.GameObject?.Components.Get<SkinnedModelRenderer>();
		if ( skinnedModelRenderer is not null && tr.Hitbox is not null )
		{
			CreateImpactEffects( skinnedModelRenderer.GetBoneObject( tr.Hitbox.Bone ), GetSurfaceFromTrace( tr ), tr.EndPosition, tr.Normal );
		}
		else
			CreateImpactEffects( tr.GameObject, GetSurfaceFromTrace( tr ), tr.EndPosition, tr.Normal );
	}

	/// <summary>
	/// Called when the weapon is discarded.
	/// </summary>
	public virtual void Discard()
	{
		Actor.SetupViewModel( this, false );

		OnDiscardAction?.Invoke();

		// Cya
		GameObject.Destroy();
	}

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

		if ( TimeUntilDiscard )
		{
			if ( OnDiscardCameraEffect is not null && Actor is Opium.PlayerController player )
			{
				OnDiscardCameraEffect.Enabled = true;
				OnDiscardCameraEffect.Player = player;
			}

			Discard();
		}
	}
}