OpponentMagazine.cs
using System;

namespace PaintballBaddies;

/// <summary>Finite opponent ammunition with three-shot bursts and visible reload windows.</summary>
public sealed class OpponentMagazine
{
    public int Ammo { get; private set; } = 30;
    public int Reserve { get; private set; } = 120;
    public bool UnlimitedAmmo { get; set; }
    public int Reloads { get; private set; }
    public float ReloadRemaining { get; private set; }
    public float ReloadDuration { get; set; } = 2.2f;
    public float ActiveReloadDuration { get; private set; } = 2.2f;
    public float PauseRemaining { get; private set; }
    private int burstShots;
    public event Action ReloadStarted;
    public event Action ReloadCancelled;
    public void CancelReload()
    {
        if (ReloadRemaining<=0) return;
        ReloadRemaining=0;
        ReloadCancelled?.Invoke();
    }
    public bool BeginReload()
    {
        if(UnlimitedAmmo || ReloadRemaining>0 || Ammo>=30 || Reserve<=0)return false;
        ActiveReloadDuration=Math.Clamp(ReloadDuration,.1f,10f);
        ReloadRemaining=ActiveReloadDuration;
        ReloadStarted?.Invoke();return true;
    }

    public void Advance( float delta )
    {
        PauseRemaining = MathF.Max( 0, PauseRemaining - delta );
        if ( ReloadRemaining <= 0 ) return;
        ReloadRemaining = MathF.Max( 0, ReloadRemaining - delta );
        if ( ReloadRemaining > 0 ) return;
        int amount = Math.Min( 30 - Ammo, Reserve );
        Ammo += amount;
        Reserve -= amount;
        Reloads++;
        burstShots = 0;
    }
    public bool TryFire()
    {
        if ( ReloadRemaining > 0 || PauseRemaining > 0 || (!UnlimitedAmmo && Ammo <= 0) ) return false;
        if(!UnlimitedAmmo)Ammo--;
        burstShots++;
        PauseRemaining = PaintballFlight.ShotInterval;
        if ( burstShots >= 3 ) { burstShots = 0; PauseRemaining = BotCombatTuning.BurstRestSeconds; }
        if ( Ammo == 0 && Reserve > 0 ) BeginReload();
        return true;
    }
}