CoverConnectionsReview.cs
using Sandbox;
using Sandbox.Navigation;
namespace PaintballBaddies;

/// <summary>Unsaved native attachment and navigation checks for the added cover links.</summary>
public sealed class CoverConnectionsReview : Component
{
    public List<string> Results {get;}=new();
    public bool Done {get;private set;}
    CoverSurface[] links;PlayerController player;CoverController cover;NavMeshAgent agent;
    Vector3 oldPosition;Angles oldAngles;bool oldLook;int index;float elapsed;
    protected override void OnStart()
    {
        Scene.GetAllComponents<PaintballControls>().First().StartPractice();
        Scene.NavMesh.IsEnabled=true;Scene.NavMesh.IncludeStaticBodies=true;Scene.NavMesh.IncludeKeyframedBodies=false;
        Scene.NavMesh.AgentHeight=67;Scene.NavMesh.AgentRadius=17;Scene.NavMesh.SetDirty();
        player=MultiplayerSession.LocalPlayer(Scene);cover=player.Components.Get<CoverController>();
        oldPosition=player.WorldPosition;oldAngles=player.EyeAngles;oldLook=player.UseLookControls;
        player.UseLookControls=false;cover.TestInput=true;
        links=Scene.GetAllComponents<CoverSurface>().Where(x=>x.GameObject.Name.StartsWith("Cover link ")).OrderBy(x=>x.GameObject.Name).ToArray();
        var probe=new GameObject(GameObject){Name="Temporary cover navigation probe",WorldPosition=new(-620,0,2)};
        agent=probe.Components.Create<NavMeshAgent>();agent.Height=67;agent.Radius=17;agent.UpdatePosition=false;agent.UpdateRotation=false;
        Check(links.Length==9,"nine cover links present");
        if(links.Length==0){Done=true;return;}Place();
    }
    void Check(bool ok,string label){Results.Add((ok ? "PASS " : "FAIL ")+label);}
    void Place()
    {
        cover.Leave();var link=links[index];var box=link.Components.Get<BoxCollider>();
        player.WorldPosition=link.WorldPosition-Vector3.Forward*(box.Scale.x*link.WorldScale.x*.5f+45);
        player.WorldPosition=player.WorldPosition.WithZ(2);player.EyeAngles=Angles.Zero;player.Body.Velocity=Vector3.Zero;
        elapsed=0;
    }
    protected override void OnUpdate()
    {
        if(Done)return;elapsed+=Time.Delta;if(elapsed<.8f || Scene.NavMesh.IsGenerating)return;
        var link=links[index];
        Check(cover.TryEnter() && cover.ReservedSurface==link,link.GameObject.Name+" attaches from open approach");
        var start=Scene.NavMesh.GetClosestPoint(new Vector3(-620,0,2),40);
        var finish=Scene.NavMesh.GetClosestPoint(player.WorldPosition,40);
        bool reachable=start is Vector3 from && finish is Vector3 to
            && Scene.NavMesh.CalculatePath(new CalculatePathRequest{Start=from,Target=to,Agent=agent}).Status==NavMeshPathStatus.Complete;
        Check(reachable,link.GameObject.Name+" reachable from main hall on native navmesh");
        cover.Leave();index++;
        if(index==links.Length){Done=true;return;}Place();
    }
    protected override void OnDestroy()
    {
        if(cover.IsValid()){cover.Leave();cover.TestInput=false;}
        if(player.IsValid()){player.WorldPosition=oldPosition;player.EyeAngles=oldAngles;player.UseLookControls=oldLook;player.Body.Velocity=Vector3.Zero;player.WishVelocity=Vector3.Zero;}
    }
}