AiIntelligenceReview.cs
using Sandbox;
namespace PaintballBaddies;

/// <summary>Opt-in live perception/decision regressions, never saved in the arena.</summary>
public sealed class AiIntelligenceReview : Component
{
    private PlayerController player;
    private ArenaOpponent bot;
    private GameObject wall, rival;
    private Vector3 original,remembered;
    private bool input,look;
    private float elapsed,stageTime;
    private int stage,shots,changes,heard;
    public bool Done { get; private set; }
    public List<string> Results { get; }=new();
    public string State=>Json.Serialize(new{stage,stageTime,Target=bot?.TargetName,Sees=bot?.SeesPlayer,Memory=bot?.LastKnownPlayerPosition,Shots=bot?.ShotsFired});
    private void Check(bool okay,string label){var text=(okay ? "PASS " : "FAIL ")+label;Results.Add(text);Log.Info("AI_SMART "+text);}
    private void Next(){stage++;stageTime=0;}
    protected override void OnStart()
    {
        Scene.NavMesh.IsEnabled=true;Scene.NavMesh.IncludeStaticBodies=true;
        Scene.NavMesh.IncludeKeyframedBodies=false;Scene.NavMesh.AgentHeight=67;Scene.NavMesh.AgentRadius=17;Scene.NavMesh.SetDirty();
        player=Scene.GetAllComponents<PlayerController>().First();
        original=player.WorldPosition;input=player.UseInputControls;look=player.UseLookControls;
        player.UseInputControls=player.UseLookControls=false;
        var go=new GameObject(GameObject){Name="Smart AI review opponent",WorldPosition=new(-900,1250,1)};
        go.Components.Create<CitizenOpponentPresentation>();
        bot=go.Components.Create<ArenaOpponent>();bot.Character="imani";bot.CombatEnabled=true;
    }
    protected override void OnUpdate()
    {
        if(Done)return;
        elapsed+=Time.Delta;stageTime+=Time.Delta;
        if(elapsed>28){Check(false,"timeout: "+State);Done=true;return;}
        bot.WorldPosition=new(-900,1250,1);bot.WorldRotation=Rotation.Identity;
        var nav=bot.Components.Get<NavMeshAgent>();if(nav.IsValid()){nav.UpdatePosition=false;nav.MaxSpeed=0;nav.Stop();}
        var contact=bot.Components.Get<CloseCombat>();if(contact.IsValid())contact.Enabled=false;
        var actor=bot.Components.Get<PaintballCombatant>();if(actor.IsValid())actor.StayInMatch=true;
        var human=player.Components.Get<PaintballCombatant>();human.StayInMatch=true;
        player.WorldPosition=stage==0 ? new(-1080,1250,4) : stage==4 || stage==5 ? new(-640,1250,4) : stage==7 ? new(-940,1250,4) : new(-700,1250,4);
        player.Body.Velocity=player.WishVelocity=Vector3.Zero;
        if(stage==0 && stageTime>1){Check(!bot.SeesPlayer,"unseen rear opponent not acquired");Next();}
        else if(stage==1 && stageTime>1.2f)
        {
            Check(bot.SeesPlayer,"front opponent acquired");
            wall=new GameObject(GameObject){Name="Smart AI sight blocker",WorldPosition=new(-750,1250,24)};
            var box=wall.Components.Create<BoxCollider>();box.Static=true;box.Scale=new(12,220,48);
            Next();
        }
        else if(stage==2 && stageTime>1.2f)
        {
            Check(bot.SeesPlayer && bot.CombatAimPoint.z>54,"exposed head noticed above chest-height cover");
            wall.WorldPosition=new(-750,1250,60);wall.Components.Get<BoxCollider>().Scale=new(12,220,120);Next();
        }
        else if(stage==3 && stageTime>1)
        {Check(!bot.SeesPlayer,"full cover blocks sight");shots=bot.ShotsFired;remembered=bot.LastKnownPlayerPosition;Next();}
        else if(stage==4 && stageTime>1)
        {
            Check((bot.LastKnownPlayerPosition-remembered).Length<2,"hidden movement does not update visual memory");
            Check(bot.ShotsFired==shots,"no shots continue through fully blocking cover");
            // Hearing is deliberately lower priority than a committed cover
            // plan; release that unrelated tactic for this isolated check.
            bot.InterruptForContact();
            heard=bot.HeardShots;bot.HearMarker(human.GameObject,new(-660,1250,60));Next();
        }
        else if(stage==5 && stageTime>.1f)
        {
            Check(bot.HeardShots==heard+1 && !bot.SeesPlayer,"nearby occluded shot heard without granting sight");
            Check((bot.LastKnownPlayerPosition-new Vector3(-630,1260,1)).Length<2,"hearing stores approximate launch location");
            heard=bot.HeardShots;bot.HearMarker(human.GameObject,new(1500,1250,60));
            Check(bot.HeardShots==heard,"distant shots do not grant awareness");wall.Destroy();Next();
        }
        else if(stage==6 && stageTime>1)
        {
            Check(bot.SeesPlayer,"target reacquired after obstacle removed");Next();
        }
        else if(stage==7 && stageTime>.8f)
        {
            Check(bot.CanObserve(human,out _),"unobstructed opponent inside personal space noticed behind");Next();
        }
        else if(stage==8 && stageTime>1)
        {
            bot.ContestMode=true;
            rival=new GameObject(GameObject){Name="Smart AI alternative target",WorldPosition=new(-710,1290,1)};
            var rivalState=rival.Components.Create<PaintballCombatant>();rivalState.Team=89;rivalState.StayInMatch=true;
            var hull=rival.Components.Create<BoxCollider>();hull.Scale=new(28,28,66);hull.Center=new(0,0,33);
            changes=bot.TargetChanges;Next();
        }
        else if(stage==9)
        {
            rival.WorldPosition=new(-710+System.MathF.Sin(stageTime*12)*12,1290,1);
            if(stageTime<2)return;
            Check(bot.TargetChanges-changes<=2,"weighted choices retain target commitment without thrashing");
            Check(!bot.Reachable(new Vector3(5000,5000,1),750,out _,out _),"off-arena flank destination rejected by native navmesh");
            Check(bot.Reachable(new Vector3(-900,1400,1),750,out _,out _),"nearby clear route accepted by native navmesh");
            actor.RegisterHit(0,new Vector3(-650,1200,50),human,new Vector3(-620,1190,50));
            Check(actor.LastHitOrigin==new Vector3(-620,1190,50),"hit memory uses recorded launch origin");
            Done=true;
        }
    }
    protected override void OnDestroy()
    {
        if(player.IsValid()){player.WorldPosition=original;player.UseInputControls=input;player.UseLookControls=look;player.Body.Velocity=Vector3.Zero;}
    }
}