ReloadUpperBodyChecks.cs
using Sandbox;
using System;
namespace PaintballBaddies;
/// <summary>Opt-in comparison against the native imported reload animation.</summary>
public sealed class ReloadUpperBodyChecks : Component
{
[Property] public int CharacterIndex {get;set;}
private PlayerController player;
private float elapsed,maxPosition,maxAngle;
private int samples;
private bool selected;
protected override void OnUpdate()
{
player ??= Scene.GetAllComponents<PlayerController>().First();
if(!selected){player.Components.Get<RosterSelection>().Select(CharacterIndex);selected=true;}
elapsed+=Time.Delta;player.UseInputControls=false;player.UseLookControls=false;
player.Components.Get<ViperAvatar>().PreviewSequence="reload_standing";
player.Renderer.PlaybackRate=0;player.Renderer.Sequence.TimeNormalized=(elapsed/3).Clamp(0,1);
}
protected override void OnPreRender()
{
if(!player.IsValid() || elapsed<.15f)return;
var body=player.Renderer;var track=ReloadUpperBodyData.Load(RosterSelection.Names[CharacterIndex].ToLowerInvariant());
for(int i=0;i<track.names.Length;i++)
{
var bone=body.Model.Bones.GetBone(track.names[i]);
if(!body.TryGetBoneTransformAnimation(bone,out var native))continue;
var actual=body.WorldTransform.ToLocal(native);var expected=ReloadUpperBodyData.Sample(track,body.Model,i,body.Sequence.TimeNormalized);
maxPosition=MathF.Max(maxPosition,(actual.Position-expected.Position).Length);
maxAngle=MathF.Max(maxAngle,actual.Rotation.Distance(expected.Rotation));samples++;
}
if(elapsed<3)return;
Log.Info($"RELOAD_UPPER {(samples>500 && maxPosition<.15f && maxAngle<1 ? "PASS" : "FAIL")} samples={samples} position_inches={maxPosition} angle_degrees={maxAngle}");
Destroy();
}
protected override void OnDestroy()
{
if(!player.IsValid())return;
player.UseInputControls=player.UseLookControls=true;player.Components.Get<ViperAvatar>().PreviewSequence="";
}
}