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

/// <summary>Bounded clothing inertia using the engine's damped spring.</summary>
public sealed class CitizenSuitFollowThrough : Component
{
    private SkinnedModelRenderer body,outfit;
    private Vector3 previousPosition,previousVelocity;
    private bool initialized;
    private Model previousModel;
    private Vector3.SpringDamped spring=new(){Frequency=6,Damping=.8f};
    public float CurrentWeight { get; private set; }
    public float PeakWeight { get; private set; }
    public bool MorphsReady => outfit.IsValid() && outfit.Model.IsValid() &&
        outfit.Model.Morphs.GetIndex("SuitFollowThroughLUp")>=0;
    public void Configure(SkinnedModelRenderer skeleton,SkinnedModelRenderer garment)
    { body=skeleton;outfit=garment;initialized=false; }
    protected override void OnPreRender()
    {
        if(!MorphsReady || !body.IsValid() || !body.TryGetBoneTransform("spine_2",out var chest))return;
        float dt=Time.Delta;
        if(dt<=0)return;
        var position=chest.Position;
        if(!initialized || previousModel!=outfit.Model || dt>.1f || (position-previousPosition).Length>32)
        {
            previousModel=outfit.Model;
            initialized=true;previousPosition=position;previousVelocity=Vector3.Zero;
            spring.Current=spring.Target=spring.Velocity=Vector3.Zero;
            Apply(0);return;
        }
        var velocity=(position-previousPosition)/dt;
        var acceleration=(velocity-previousVelocity)/dt;
        previousPosition=position;previousVelocity=velocity;
        spring.Target=Vector3.Up*(-acceleration.z/900).Clamp(-1,1);
        spring.Update(dt);
        Apply(spring.Current.z.Clamp(-1,1));
    }
    private void Apply(float weight)
    {
        CurrentWeight=weight;PeakWeight=MathF.Max(PeakWeight,MathF.Abs(weight));
        var up=MathF.Max(0,weight);var down=MathF.Max(0,-weight);
        outfit.Morphs.Set("SuitFollowThroughLUp",up,0);
        outfit.Morphs.Set("SuitFollowThroughRUp",up,0);
        outfit.Morphs.Set("SuitFollowThroughLDown",down,0);
        outfit.Morphs.Set("SuitFollowThroughRDown",down,0);
    }
    protected override void OnDestroy() { if(MorphsReady)Apply(0); }
}