StrideTransitionChecks.cs
using Sandbox;
using System;
namespace PaintballBaddies;
public sealed class StrideTransitionChecks : Component
{
    [Property] public int CharacterIndex {get;set;}
    private PlayerController player;
    private PaintballMarker marker;
    private bool input,look,accept,selected;
    private float elapsed,phase,maxError;
    private string clip;
    private int changes,walkToRun,runToWalk;
    protected override void OnStart()
    {
        player=Scene.GetAllComponents<PlayerController>().First(); marker=player.Components.Get<PaintballMarker>();
        input=player.UseInputControls;look=player.UseLookControls;accept=marker.AcceptInput;
        player.UseInputControls=player.UseLookControls=marker.AcceptInput=false;
        player.WorldPosition=new Vector3(-950,-700,2);player.EyeAngles=new Angles(0,90,0);
        marker.PoseApplied+=Observe;
    }
    private void Observe(SkinnedModelRenderer body)
    {
        var next=body.Sequence.Name;var p=body.Sequence.TimeNormalized;
        if((clip=="walk"&&next=="run")||(clip=="run"&&next=="walk"))
        {
            var expected=(phase+(next=="run"?.875f:.125f))%1f;
            var error=MathF.Abs(p-expected);error=MathF.Min(error,1-error);maxError=MathF.Max(maxError,error);
            changes++;if(next=="run")walkToRun++;else runToWalk++;
            Log.Info($"STRIDE_TRANSITION OBSERVATION {clip}->{next} prior={phase} next={p} error={error}");
        }
        clip=next;phase=p;
    }
    protected override void OnUpdate()
    {
        if(!selected){player.Components.Get<RosterSelection>().Select(CharacterIndex);selected=true;}
        elapsed+=Time.Delta;
        player.WishVelocity=elapsed<6 ? Vector3.Left*((int)(elapsed/.73f)%2==0?85:180) : Vector3.Zero;
        if(elapsed<6.6f)return;
        Check("transitions exercised",changes>=6,$"changes={changes}");
        Check("both directions",walkToRun>=3&&runToWalk>=3,$"up={walkToRun} down={runToWalk}");
        Check("stride phase retained",maxError<.1f,$"max_circular_error={maxError}");
        Check("idle recovery",player.Renderer.Sequence.Name=="idle",player.Renderer.Sequence.Name);
        Log.Info("STRIDE_TRANSITION COMPLETE");Destroy();
    }
    private void Check(string name,bool pass,string detail)=>Log.Info($"STRIDE_TRANSITION {(pass?"PASS":"FAIL")} {name}: {detail}");
    protected override void OnDestroy()
    {
        if(marker.IsValid()){marker.PoseApplied-=Observe;marker.AcceptInput=accept;}
        if(player.IsValid()){player.WishVelocity=Vector3.Zero;player.UseInputControls=input;player.UseLookControls=look;}
    }
}