Utility static class that computes a soft-body separation vector for nearby pushable actors (players and NPCs). It samples Player and NPC components in the same Scene and accumulates a falloff-weighted push away from each within radius*2 using MathE.SmoothKernel.
using Sandbox;
namespace BrickJam;
/// <summary>
/// Soft-body separation for <see cref="IPushable"/> actors (players + NPCs). Scene-System replacement for
/// the legacy <c>StartTouch</c>/<c>EndTouch</c> "toPush" list: rather than tracking physics touches, each
/// fixed update we sample nearby pushables and accumulate a falloff-weighted push away from them. Keeps the
/// legacy formula - <c>MathE.SmoothKernel( radius*2, distance ) * PushForce</c> - so two actors gently shove
/// apart instead of stacking (player movement ignores the "player"/"npc" collision tags).
/// </summary>
public static class Pushables
{
/// <summary>
/// Accumulated push velocity (pre-<c>Time.Delta</c>) separating <paramref name="self"/> from every other
/// pushable within <c>radius*2</c>. Caller scales by <c>Time.Delta</c> and adds it to its own velocity.
/// </summary>
public static Vector3 ComputeSeparation( Component self, Vector3 position, float radius, float pushForce )
{
var scene = self.Scene;
if ( !scene.IsValid() )
return Vector3.Zero;
var reach = radius * 2f;
var push = Vector3.Zero;
void Consider( Component other, Vector3 otherPos )
{
if ( other == self || !other.IsValid() )
return;
var delta = (position - otherPos).WithZ( 0 );
var distance = delta.Length;
if ( distance >= reach || distance <= 0.001f )
return;
push += delta.Normal * MathE.SmoothKernel( reach, distance ) * pushForce;
}
foreach ( var p in scene.GetAllComponents<Player>() )
Consider( p, p.WorldPosition );
foreach ( var n in scene.GetAllComponents<NPC>() )
Consider( n, n.WorldPosition );
return push;
}
}