AiTacticsReview.cs
using Sandbox;
namespace PaintballBaddies;
public sealed class AiTacticsReview : Component
{
    PlayerController player;ArenaOpponent bot;float elapsed,next;int hiddenShots,peekShots,lastShots;bool sheltered,shifted;ArenaShield shield;bool stowed,exposed;float firstShot=-1;
    protected override void OnStart()
    {
        player=Scene.GetAllComponents<PlayerController>().First();player.UseInputControls=player.UseLookControls=false;
        player.WorldPosition=new(-630,100,1);
        Scene.GetAllComponents<TrainingRange>().First().SetTargetsVisible(false);
        Scene.NavMesh.IsEnabled=true;Scene.NavMesh.IncludeStaticBodies=true;Scene.NavMesh.IncludeKeyframedBodies=false;Scene.NavMesh.AgentHeight=67;Scene.NavMesh.AgentRadius=17;Scene.NavMesh.SetDirty();
        var go=new GameObject(true,"AI tactics review opponent");go.WorldPosition=new(-190,-40,1);go.WorldRotation=Rotation.LookAt(player.WorldPosition-go.WorldPosition);
        go.Components.Create<CitizenOpponentPresentation>();bot=go.Components.Create<ArenaOpponent>();bot.Character="imani";bot.CombatEnabled=true;bot.ContestMode=true;
    }
    protected override void OnUpdate()
    {
        elapsed+=Time.Delta;
        if(elapsed>next){next=elapsed+1;Log.Info($"AI_TRACE t={elapsed} pos={bot.WorldPosition} cover={bot.UsingCover} peek={bot.CoverPeeking} sees={bot.SeesPlayer} plan={bot.HasCoverPlan} destination={bot.CoverDestination} nav={bot.Components.Get<NavMeshAgent>()?.TargetPosition} block={bot.ShotBlockReason}");}
        if(shield is null && elapsed>1){shield=Scene.GetAllComponents<ArenaShield>().First();shield.WorldPosition=bot.WorldPosition+new Vector3(0,0,24);}
        if(shield is not null && shield.Owner.IsValid()){stowed|=bot.UsingCover&&shield.Stowed;exposed|=bot.CoverPeeking&&!shield.Stowed;}
        if(bot.ShotsFired>0&&firstShot<0)firstShot=elapsed;
        var state=player.Components.Get<PaintballCombatant>();if(state.IsValid())state.StayInMatch=true;
        if(bot.UsingCover){sheltered=true;hiddenShots+=bot.ShotsFired-lastShots;}
        if(bot.CoverPeeking)peekShots+=bot.ShotsFired-lastShots;
        lastShots=bot.ShotsFired;
        if(elapsed>17&&!shifted){shifted=true;player.WorldPosition=new(180,0,1);}
        if(elapsed>31)
        {
            Log.Info("AI_TACTICS "+Json.Serialize(new{bot.CoverSelections,firstShot,ShieldCollected=shield?.Owner?.GameObject==bot.GameObject,stowed,exposed,sheltered,bot.CoverBursts,bot.ShotsFromCover,bot.ShotsWhileSheltered,bot.FlankSelections,bot.ObstructedMoves,bot.ShotsFired,bot.DistanceTravelled,PlayerHits=state?.PaintHits}));Destroy();
        }
    }
    protected override void OnDestroy(){if(bot.IsValid())bot.GameObject.Destroy();if(player.IsValid())player.UseInputControls=player.UseLookControls=true;}
}