CloseCameraVisibilityReview.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Opt-in checks using the real wall-clipped native player camera.</summary>
public sealed class CloseCameraVisibilityReview : Component
{
    public bool Done {get;private set;}
    public int Stage {get;private set;}
    public List<string> Results {get;}=new();
    PlayerController player;PaintballMarker marker;CloseCameraVisibility visibility;
    Vector3 original;Angles angles;bool input,look;float elapsed;GameObject witness;
    void Check(bool ok,string text)=>Results.Add((ok?"PASS ":"FAIL ")+text);
    protected override void OnStart()
    {
        Scene.GetAllComponents<PaintballControls>().First().StartPractice();
        player=MultiplayerSession.LocalPlayer(Scene);marker=player.Components.Get<PaintballMarker>();visibility=player.Components.Get<CloseCameraVisibility>();
        original=player.WorldPosition;angles=player.EyeAngles;input=player.UseInputControls;look=player.UseLookControls;
        player.Components.Get<CoverController>()?.Leave();player.UseInputControls=player.UseLookControls=false;
        player.WorldPosition=new(-1000,1250,2);player.EyeAngles=Angles.Zero;player.Body.Velocity=Vector3.Zero;marker.ReviewAimOverride=false;
        witness=TrainingRange.Box(GameObject,"Camera visibility unrelated prop",new(-1050,1400,25),new(12,12,50),Color.Green,false);
    }
    protected override void OnUpdate()
    {
        if(Done)return;elapsed+=Time.Delta;player.WishVelocity=Vector3.Zero;
        if(elapsed<1.5f)return;elapsed=0;
        var camera=Scene.Camera;
        switch(Stage++)
        {
            case 0:
                Check(!visibility.Hidden&&visibility.EyeDistance>70,"open third-person view keeps own character visible");
                player.WorldPosition=new(-1155,1250,2);player.Body.Velocity=Vector3.Zero;break;
            case 1:
                Check(visibility.Hidden&&visibility.EyeDistance<44,"wall-compressed camera hides own obstructing geometry");
                var missing=player.Components.GetAll<ModelRenderer>(FindMode.EverythingInSelfAndDescendants).Where(x=>x.Enabled&&!visibility.Filters(camera,x.GameObject)).Select(x=>x.GameObject.Name).ToArray();
                Check(missing.Length==0,"separate body, helmet, suit and marker all use camera filter; missing: "+string.Join(", ",missing));
                Check(!visibility.Filters(camera,witness),"unrelated scenery is not hidden");
                Check(camera.WorldPosition.x>-1174,"native camera stays inside arena wall");
                Check(marker.FireAt(player.EyePosition+Vector3.Forward*400),"close camera still permits shooting");
                marker.ReviewAimOverride=true;break;
            case 2:
                Check(marker.Aiming&&visibility.Hidden,"focused wall camera remains unobstructed");
                player.UpdateDucking(true);break;
            case 3:
                Check(visibility.Hidden,"close crouched camera filters obstructing outfit");
                player.UpdateDucking(false);player.WorldPosition=new(-1000,1250,2);player.Body.Velocity=Vector3.Zero;break;
            case 4:
                Check(!visibility.Hidden&&visibility.HiddenPieces==0,"moving away restores all owned geometry");
                Check(marker.Aiming&&visibility.EyeDistance>52,"normal focused shoulder view remains visible");
                // Test only the filter boundaries; camera transform stays native.
                visibility.Apply(camera,player.EyePosition+Vector3.Backward*40,player.EyePosition);
                visibility.Apply(camera,player.EyePosition+Vector3.Backward*48,player.EyePosition);
                Check(visibility.Hidden,"hysteresis keeps visibility stable near close threshold");
                visibility.Apply(camera,player.EyePosition+Vector3.Backward*56,player.EyePosition);
                Check(!visibility.Hidden,"visibility returns outside hysteresis range");
                Done=true;break;
        }
    }
    protected override void OnDestroy()
    {
        visibility?.Restore();if(witness.IsValid())witness.Destroy();
        if(marker.IsValid())marker.ReviewAimOverride=null;
        if(player.IsValid()){player.WorldPosition=original;player.EyeAngles=angles;player.UseInputControls=input;player.UseLookControls=look;player.UpdateDucking(false);player.Body.Velocity=Vector3.Zero;}
    }
}