HudStateReview.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Opt-in native cover/reload HUD review.</summary>
public sealed class HudStateReview : Component
{
    [Property] public float MatchClockSeconds { get; set; }
    [Property] public string ResultMode { get; set; } = "";
    [Property] public string CaptureId { get; set; } = "";
    [Property] public bool AmmoReview { get; set; }
    [Property] public int AmmoTarget { get; set; } = 8;
    [Property] public bool ReviewReload { get; set; }
    private PlayerController player;
    private PaintballMarker weapon;
    private CoverController cover;
    private GameObject fixture;
    private float elapsed;
    private int phase;
    protected override void OnStart()
    {
        player=Scene.GetAllComponents<PlayerController>().First();weapon=player.Components.Get<PaintballMarker>();cover=player.Components.Get<CoverController>();
        if (MatchClockSeconds > 0) return;
        if(AmmoReview) { player.UseInputControls=false;player.UseLookControls=false;return; }
        player.UseInputControls=false;player.UseLookControls=false;cover.TestInput=true;
        player.WorldPosition=new Vector3(-595,70,2);player.EyeAngles=new Angles(0,0,0);
        fixture=TrainingRange.Box(null,"HUD review cover",new Vector3(-550,70,24),new Vector3(20,180,48),Color.Gray,true);
        fixture.Components.Create<CoverSurface>();
    }
    protected override void OnUpdate()
    {
        elapsed+=Time.Delta;
        if(AmmoReview)
        {
            if(elapsed>8)AmmoTarget=0;
            if(elapsed>10)ReviewReload=true;
            if(ReviewReload && phase==0) { weapon.Reload();phase=1; }
            if(!ReviewReload && weapon.Ammo>AmmoTarget && elapsed>.5f)
                weapon.FireAt(weapon.Muzzle+player.EyeAngles.Forward*1000);
            return;
        }
        if (MatchClockSeconds > 0)
        {
            var match = Scene.GetAllComponents<ArenaMatch>().FirstOrDefault();
            if (phase == 0)
            {
                if (match?.PlayerState is null) return;
                match.RoundDuration = MatchClockSeconds;
                match.StartRound();
                phase = 1;
            }
            else if (phase == 1 && elapsed > .5f && ResultMode != "")
            {
                var bots=Scene.GetAllComponents<ArenaOpponent>().Where(x => x.Components.Get<PaintballCombatant>() is not null).ToArray();
                if(bots.Length != match.OpponentCount) return;
                foreach(var bot in bots.Take(ResultMode == "victory" ? bots.Length : 2))
                {
                    var state=bot.Components.Get<PaintballCombatant>();
                    while(!state.Eliminated) state.RegisterHit(0);
                }
                if(ResultMode == "eliminated")
                    while(!match.PlayerState.Eliminated) match.PlayerState.RegisterHit(1);
                phase=2;
            }
            else if (phase == 2 && elapsed > 1)
            {
                Log.Info("HUD_RESULT_CHECK "+Json.Serialize(new { capture_id=CaptureId, mode=ResultMode, status=match.Status, eliminated=match.OpponentsEliminated, total=match.OpponentCount, remaining=match.OpponentsRemaining, hits=match.PlayerState.PaintHits, in_round=match.InRound }));
                phase=3;
            }
            else if (phase == 3 && elapsed > 3)
            {
                match.StartRound();
                Log.Info("HUD_RESTART_CHECK "+Json.Serialize(new { capture_id=CaptureId, status=match.Status, eliminated=match.OpponentsEliminated, total=match.OpponentCount, remaining=match.OpponentsRemaining, hits=match.PlayerState.PaintHits, in_round=match.InRound }));
                phase=4;
            }
            // Allow OnStart to create each combatant before disabling AI fire.
            foreach(var bot in Scene.GetAllComponents<ArenaOpponent>())
                if(bot.Components.Get<PaintballCombatant>() is not null) bot.CombatEnabled=false;
            return;
        }
        if(phase==0 && elapsed>.35f){weapon.FireAt(new Vector3(-450,70,65));phase=1;}
        if(phase==1 && elapsed>.7f){cover.TryEnter();weapon.Reload();phase=2;}
        if(phase==2 && elapsed>1.7f)
        {
            Log.Info($"HUD_STATE cover={cover.Attached} reloadProgress={weapon.ReloadProgress} reloadRemaining={weapon.ReloadRemaining} duration={weapon.ActiveReloadDuration}");phase=3;
        }
    }
    protected override void OnDestroy(){if(fixture.IsValid())fixture.Destroy();}
}