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); }
}