Npcs/Tasks/FireWeapon.cs

An NPC task that aims a given weapon at a target GameObject and fires for a set burst duration. It rotates the NPC body toward the target, checks facing and weapon readiness, triggers the weapon firing and an attack animation, and completes after BurstDuration seconds.

Native Interop
using Sandbox.Npcs.Layers;

namespace Sandbox.Npcs.Tasks;

/// <summary>
/// Shoots a weapon at a target for a specific duration
/// </summary>
public class FireWeapon : TaskBase
{
	/// <summary>The weapon component to fire.</summary>
	public BaseWeapon Weapon { get; }

	/// <summary>The GameObject to aim at.</summary>
	public GameObject Target { get; }

	/// <summary>How long (seconds) to keep firing before the task completes.</summary>
	public float BurstDuration { get; }

	/// <summary>Body rotation speed (degrees/s scale) used while actively aiming. Higher than the default look speed.</summary>
	public float AimTurnSpeed { get; set; } = 8f;

	private TimeUntil _burstEnd;

	public FireWeapon( BaseWeapon weapon, GameObject target, float burstDuration = 1.5f )
	{
		Weapon = weapon;
		Target = target;
		BurstDuration = burstDuration;
	}

	protected override void OnStart()
	{
		_burstEnd = BurstDuration;
	}

	protected override TaskStatus OnUpdate()
	{
		if ( !Weapon.IsValid() )
			return TaskStatus.Failed;

		if ( !Target.IsValid() )
			return TaskStatus.Failed;

		RotateBodyTowardTarget();

		// Only fire once we're actually facing the target
		if ( Npc.Animation.IsFacingTarget() && Weapon.CanPrimaryAttack() )
		{
			Weapon.PrimaryAttack();
			Npc.Animation.TriggerAttack();
		}

		return _burstEnd ? TaskStatus.Success : TaskStatus.Running;
	}

	private void RotateBodyTowardTarget()
	{
		var toTarget = (Target.WorldPosition - Npc.WorldPosition).WithZ( 0 );
		if ( toTarget.LengthSquared < 1f ) return;

		var targetRot = Rotation.LookAt( toTarget.Normal, Vector3.Up );
		Npc.WorldRotation = Rotation.Lerp( Npc.WorldRotation, targetRot, AimTurnSpeed * Time.Delta );
	}
}