GearsControlsReview.cs
using Sandbox;
using System;
namespace PaintballBaddies;

/// <summary>Opt-in native geometry/action regression. Never saved in a playable scene.</summary>
public sealed class GearsControlsReview : Component
{
    public List<string> Results {get;}=new();
    public bool Done {get;private set;}
    public int Stage {get;private set;}
    PlayerController player;PaintballMarker marker;CloseCombat contact;CoverController cover;
    PaintballCombatant victim;GameObject obstacle;
    Vector3 oldPosition;Angles oldAngles;bool oldLook;float elapsed;int contacts,shots;
    IDisposable hook,fixedHook;bool primary,focus,coverPress,previousCover,previousPrimary,previousFocus;
    void Check(bool ok,string label){Results.Add((ok ? "PASS " : "FAIL ")+label);Log.Info("GEARS_CONTROLS "+Results.Last());}
    void Next(){Stage++;elapsed=0;primary=false;focus=false;}
    void Place(float target=42)
    {
        contact.ResetState();victim.Components.Get<CloseCombat>()?.ResetState();
        cover.Leave();cover.TestAim=false;cover.TestMovement=Vector3.Zero;
        player.WorldPosition=new(0,0,1202);player.EyeAngles=Angles.Zero;
        player.Body.Velocity=Vector3.Zero;player.WishVelocity=Vector3.Zero;
        victim.WorldPosition=new(target,0,1200);
        contacts=contact.Contacts;shots=marker.Shots;
    }
    protected override void OnStart()
    {
        Scene.GetAllComponents<PaintballControls>().First().StartPractice();
        player=MultiplayerSession.LocalPlayer(Scene);marker=player.Components.Get<PaintballMarker>();
        contact=player.Components.Get<CloseCombat>();cover=player.Components.Get<CoverController>();
        oldPosition=player.WorldPosition;oldAngles=player.EyeAngles;oldLook=player.UseLookControls;
        player.UseLookControls=false;
        TrainingRange.Box(GameObject,"Gears review platform",new(0,0,1198),new(750,700,4),Color.Gray,true);
        var target=new GameObject(GameObject){Name="Gears review target",WorldPosition=new(42,0,1200)};
        var hitbox=target.Components.Create<BoxCollider>();hitbox.Scale=new(28,28,65);hitbox.Center=new(0,0,32.5f);
        victim=target.Components.Create<PaintballCombatant>();victim.Team=91;victim.StayInMatch=true;
        hook=Scene.AddHook(GameObjectSystem.Stage.StartUpdate,-35,()=>
        {
            Input.AnalogMove=Vector3.Zero;Input.AnalogLook=Angles.Zero;
            Input.SetAction("attack1",primary);Input.SetLastAction("attack1",previousPrimary);previousPrimary=primary;
            Input.SetAction("attack2",focus);Input.SetLastAction("attack2",previousFocus);previousFocus=focus;
            Input.SetAction("cover",coverPress);Input.SetLastAction("cover",previousCover);previousCover=coverPress;
        },nameof(GearsControlsReview),"Temporary native action edges");
        fixedHook=Scene.AddHook(GameObjectSystem.Stage.StartFixedUpdate,-35,()=>{Input.SetAction("attack2",focus);Input.AnalogMove=Vector3.Zero;},nameof(GearsControlsReview),"Hold test focus through physics");
        Place();
    }
    protected override void OnUpdate()
    {
        if(Done)return;elapsed+=Time.Delta;
        switch(Stage)
        {
            case 0:
                if(elapsed<.4f)return;
                Check(contact.MeleeAvailable,"close frontal target advertises melee");focus=true;Stage=1;elapsed=0;break;
            case 1:
                if(elapsed<.4f)return;
                Check(contact.Contacts==contacts && !contact.Attacking && marker.Aiming,"right mouse focuses without striking");
                primary=true;Stage=2;elapsed=0;break;
            case 2:
                if(elapsed<.4f)return;
                Check(marker.Shots>shots && contact.Contacts==contacts,"focused left click fires at point blank");Next();break;
            case 3:
                if(elapsed<.4f)return;Place();primary=true;Stage=4;elapsed=0;break;
            case 4:
                if(elapsed<.7f)return;
                Check(contact.Contacts==contacts+1 && victim.Components.Get<CloseCombat>().Downed,"fresh close left click knocks down once");
                Check(marker.Shots==shots,"melee click emits no paintball");
                victim.Components.Get<CloseCombat>().ResetState();victim.WorldPosition=player.WorldPosition+Vector3.Forward*42;
                Stage=5;elapsed=0;break;
            case 5:
                if(elapsed<.8f)return;
                Check(contact.Contacts==contacts+1 && marker.Shots==shots,"held melee neither repeats nor becomes a shot");Next();break;
            case 6:
                if(elapsed<.3f)return;Place(100);primary=true;Stage=7;elapsed=0;break;
            case 7:
                if(elapsed<.25f)return;victim.WorldPosition=player.WorldPosition+Vector3.Forward*42;Stage=8;elapsed=0;break;
            case 8:
                if(elapsed<.5f)return;
                Check(marker.Shots>shots && contact.Contacts==contacts,"held shooting stays shooting when target approaches");Next();break;
            case 9:
                if(elapsed<.25f)return;Place();
                victim.WorldPosition=player.WorldPosition+Vector3.Forward*49;Check(!contact.MeleeAvailable,"target beyond 1.2m does not select melee");
                victim.WorldPosition=player.WorldPosition+Vector3.Left*42;Check(!contact.MeleeAvailable,"target beside player does not steal attack");
                victim.WorldPosition=player.WorldPosition-Vector3.Forward*42;Check(!contact.MeleeAvailable,"target behind player rejected");
                victim.WorldPosition=player.WorldPosition+Vector3.Forward*42;
                obstacle=TrainingRange.Box(GameObject,"Melee obstruction",new(21,0,1235),new(5,90,70),Color.Gray,true);
                Next();break;
            case 10:
                if(elapsed<.2f)return;
                Check(!contact.MeleeAvailable && !contact.TryAttack(),"solid wall prevents melee selection and hit");
                obstacle.Destroy();victim.WorldPosition=new(300,200,1200);
                obstacle=TrainingRange.Box(GameObject,"Angled cover",new(65,0,1236),new(20,160,72),Color.Gray,true);
                obstacle.Components.Create<CoverSurface>();player.EyeAngles=new(0,50,0);cover.TestInput=true;Next();break;
            case 11:
                if(elapsed<.25f)return;
                Check(cover.Activate(false) && cover.Attached,"forgiving cone finds cover missed by central ray");
                Check(cover.Activate(true) && !cover.Attached && !player.Components.Get<VaultController>().IsVaulting,"forward plus Space still leaves and never vaults");
                obstacle.WorldPosition=new(85,0,1236);player.WorldPosition=new(0,0,1202);player.EyeAngles=Angles.Zero;Next();break;
            case 12:
                if(elapsed<.25f)return;
                Check(!cover.RequestCover(false),"early cover press waits while outside reach");
                player.WorldPosition=new(10,0,1202);Next();break;
            case 13:
                if(elapsed<.12f)return;
                Check(cover.Attached,"buffered Space attaches after entering range");cover.Leave();
                player.WorldPosition=new(0,0,1202);player.EyeAngles=Angles.Zero;Next();break;
            case 14:
                if(elapsed<.25f)return;
                cover.RequestCover(false);player.EyeAngles=new(0,180,0);player.WorldPosition=new(10,0,1202);Next();break;
            case 15:
                if(elapsed<.25f)return;
                Check(!cover.Attached && player.UseInputControls,"changing direction cancels buffered cover");
                player.WorldPosition=new(0,0,1202);player.EyeAngles=Angles.Zero;obstacle.WorldPosition=new(180,0,1224);
                obstacle.LocalScale=new Vector3(20,160,48)/50;Next();break;
            case 16:
                player.WorldPosition=new(0,0,1202);player.Body.Velocity=Vector3.Forward*190;
                if(elapsed<.3f)return;
                player.Body.Velocity=Vector3.Forward*190;
                Check(cover.Activate(true)&&cover.Sliding,"running Space starts native slide");
                Check(!marker.FireAt(new(300,0,1240)),"slide cannot fire through traversal");Next();break;
            case 17:
                if(elapsed<.12f)return;
                Check(cover.Activate(false)&&!cover.Sliding&&player.UseInputControls,"second Space cancels slide and restores movement");
                player.WorldPosition=new(0,0,1202);player.EyeAngles=Angles.Zero;Next();break;
            case 18:
                player.WorldPosition=new(0,0,1202);player.Body.Velocity=Vector3.Forward*190;
                if(elapsed<.35f)return;
                player.Body.Velocity=Vector3.Forward*190;Check(cover.TrySlide(),"slide can restart after cancellation");
                cover.TestMovement=Vector3.Backward;Next();break;
            case 19:
                if(elapsed<.2f)return;
                Check(!cover.Sliding&&player.UseInputControls,"backward intent cancels slide without locking input");
                cover.TestMovement=Vector3.Zero;cover.Leave();Place();obstacle.WorldPosition=new(60,0,1222);
                victim.WorldPosition=new(300,200,1200);
                obstacle.LocalScale=new Vector3(20,160,44)/50;cover.TestInput=false;Next();break;
            case 20:
                if(elapsed<.3f)return;focus=true;Stage++;elapsed=0;break;
            case 21:
                if(elapsed<.07f)return;focus=false;Stage++;elapsed=0;break;
            case 22:
                if(elapsed<.4f)return;
                Check(!cover.Attached,"right click never takes cover");
                coverPress=true;Stage++;elapsed=0;break;
            case 23:
                if(elapsed<.5f)return;coverPress=false;
                Check(cover.Attached&&cover.LowCover&&player.IsDucking,"stationary Space takes crouched cover without movement");
                focus=true;Stage++;elapsed=0;break;
            case 24:
                if(elapsed<.5f)return;
                Check(cover.Attached&&cover.Peeking&&!player.IsDucking,"holding right mouse peeks from low cover");
                focus=false;Stage++;elapsed=0;break;
            case 25:
                if(elapsed<.5f)return;
                Check(cover.Attached&&!cover.Peeking&&player.IsDucking,"releasing right mouse returns to shelter without leaving");
                cover.Leave();Place();victim.WorldPosition=new(300,200,1200);focus=true;Stage++;elapsed=0;break;
            case 26:
                if(elapsed<.4f)return;
                Check(!cover.Attached&&marker.Aiming,"holding focus near cover does not attach");focus=false;Stage++;elapsed=0;break;
            case 27:
                if(elapsed<.2f)return;
                Check(!cover.Attached,"releasing a held focus does not attach");
                focus=primary=true;Stage++;elapsed=0;break;
            case 28:
                if(elapsed<.07f)return;focus=primary=false;Stage++;elapsed=0;break;
            case 29:
                if(elapsed<.2f)return;
                Check(!cover.Attached,"a quick focused shot does not take cover on release");
                obstacle.WorldPosition=new(250,0,1222);focus=true;Stage++;elapsed=0;break;
            case 30:
                if(elapsed<.07f)return;focus=false;Stage++;elapsed=0;break;
            case 31:
                if(elapsed<.2f)return;
                Check(!cover.Attached&&player.UseInputControls,"right tap on open ground leaves movement free");
                Place();victim.WorldPosition=new(300,200,1200);obstacle.WorldPosition=new(60,0,1222);
                player.WorldPosition=new(34.05f,0,1202);Stage++;elapsed=0;break;
            case 32:
                if(elapsed<.5f)return;coverPress=true;Stage++;elapsed=0;break;
            case 33:
                if(elapsed<.35f)return;coverPress=false;
                Check(cover.Attached&&cover.LowCover,"Space attaches at body-contact distance while stationary");
                Results.Add($"DIAGNOSTIC contact position={player.WorldPosition} grounded={player.IsOnGround}");
                Stage++;elapsed=0;break;
            case 34:
                if(elapsed<.2f)return;coverPress=true;Stage++;elapsed=0;break;
            case 35:
                if(elapsed<.2f)return;coverPress=false;
                Check(!cover.Attached&&player.UseInputControls,"second Space leaves contact cover and restores movement");
                obstacle.WorldPosition=new(60,0,1248);obstacle.LocalScale=new Vector3(20,160,96)/50;
                player.WorldPosition=new(34.05f,0,1202);Stage++;elapsed=0;break;
            case 36:
                if(elapsed<.5f)return;coverPress=true;Stage++;elapsed=0;break;
            case 37:
                if(elapsed<.35f)return;coverPress=false;
                Check(cover.Attached&&!cover.LowCover&&!player.IsDucking,"stationary Space takes standing cover at a tall wall");
                primary=focus=false;cover.Leave();player.WorldPosition=new(0,0,1202);obstacle.WorldPosition=new(280,0,1248);Next();break;
            case 38:
                if(elapsed<.35f)return;coverPress=true;Next();break;
            case 39:
                if(elapsed<.3f)return;coverPress=false;
                Check(cover.FreeCrouched&&player.IsDucking&&!cover.Attached,"Space crouches on open ground without attaching");
                Check(!player.Components.Get<VaultController>().IsVaulting,"open-ground Space never starts a vault");Next();break;
            case 40:
                if(elapsed<.5f)return;
                Check(cover.FreeCrouched&&player.IsDucking,"open-ground crouch remains after Space release");
                shots=marker.Shots;primary=true;Stage++;elapsed=0;break;
            case 41:
                if(elapsed<.45f)return;
                Check(marker.Shots>shots&&player.IsDucking,"open-ground crouching allows ordinary firing");
                primary=false;coverPress=true;Next();break;
            case 42:
                if(elapsed<.3f)return;coverPress=false;
                Check(!cover.FreeCrouched&&!player.IsDucking,"second open-ground Space stands up");
                cover.RequestCoverOrCrouch(false);Next();break;
            case 43:
                if(elapsed<.35f)return;
                obstacle.WorldPosition=new(60,0,1222);obstacle.LocalScale=new Vector3(20,160,44)/50;
                coverPress=true;Next();break;
            case 44:
                if(elapsed<.4f)return;coverPress=false;
                Check(cover.Attached&&cover.LowCover&&!cover.FreeCrouched,"nearby low cover takes priority over free crouch");
                Next();break;
            case 45:
                if(elapsed<.2f)return;coverPress=true;Next();break;
            case 46:
                if(elapsed<.3f)return;coverPress=false;
                Check(!cover.Attached&&!cover.FreeCrouched&&!player.IsDucking,"Space leaves low cover standing without an extra crouch toggle");
                Done=true;primary=focus=false;break;
        }
    }
    protected override void OnDestroy()
    {
        hook?.Dispose();fixedHook?.Dispose();Input.ReleaseActions();Scene.TimeScale=1;
        if(cover.IsValid()){cover.Leave();cover.TestInput=false;cover.TestMovement=Vector3.Zero;}
        contact?.ResetState();
        if(player.IsValid()){player.WorldPosition=oldPosition;player.EyeAngles=oldAngles;player.UseLookControls=oldLook;player.Body.Velocity=Vector3.Zero;player.WishVelocity=Vector3.Zero;}
    }
}