River.cs

A Component named River that tracks Thing instances touching the river using a Dictionary<Thing,int>. On each update it iterates touching Things, removes invalid ones, skips those with IgnoreCollision, and damps their Velocity toward zero. It also exposes Restart, OnTriggerEnter and OnTriggerExit to manage the touch counts.

Native Interop
using Sandbox;
using System;
using static Sandbox.VertexLayout;

public sealed class River : Component
{
	public Dictionary<Thing, int> Touching = new();

	protected override void OnUpdate()
	{
		//Gizmo.Draw.Color = Color.White;
		//Gizmo.Draw.Text( $"Touching: {Touching.Count}", new global::Transform( WorldPosition ) );

		var dt = Time.Delta;
		for ( int i = Touching.Keys.Count - 1; i >= 0; i-- )
		{
			var other = Touching.Keys.ElementAt( i );
			if ( !other.IsValid() )
			{
				Touching.Remove( other );
				continue;
			}

			if ( other.IgnoreCollision )
				continue;

			other.Velocity = Utils.DynamicEaseTo( other.Velocity, Vector3.Zero, 0.06f, dt );
		}
	}

	public void Restart()
	{
		Touching.Clear();
	}

	public void OnTriggerEnter( Thing thing )
	{
		if ( Touching.ContainsKey( thing ) )
			Touching[thing]++;
		else
			Touching.Add( thing, 1 );
	}

	public void OnTriggerExit( Thing thing )
	{
		if ( !Touching.ContainsKey( thing ) )
			return;

		Touching[thing]--;
		if( Touching[thing] <= 0 )
			Touching.Remove( thing );
	}
}