cards/CardJuggler.cs
using Sandbox;
using System.Threading.Tasks;

public class CardJuggler : Card
{
	public override bool IsAlive => true;

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Mismatch && Manager.Instance.ChosenCards.Contains( this ) && !Manager.Instance.IsMismatchALockedMatch;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		List<Card> validCards = new();

		foreach( var card in Manager.Instance.GetNearbyCards( GridPos ) )
		{
			if(!card.CantBeMoved)
				validCards.Add( card );
		}

		if ( validCards.Count < 2 )
			return;

		Manager.Instance.PushEventMessage( this, eventType );

		await Task.DelayRealtime( 400 );

		float JUGGLE_HEIGHT = 120f;
		foreach ( Card card in validCards )
		{
			card.MoveToPos( card.LocalPosition.WithZ( JUGGLE_HEIGHT ), 0.3f, EasingType.QuadOut );
		}

		await Task.DelayRealtime( 350 );

		int numJuggles = Game.Random.Int( 2, 6 );
		List<IntVector2> newGridPositions = new();
		for ( int i = 0; i < numJuggles; i++ )
		{
			float delay = Utils.Map( numJuggles, 2, 6, 0.35f, 0.275f ) * Utils.Map( i, 0, numJuggles - 1, 1f, 0.75f );

			int cardNum = 0;
			foreach ( var card in validCards )
			{
				var targetGridPos = validCards[(cardNum + i + 1) % validCards.Count].GridPos;
				card.MoveToPos( Manager.GetCardPos( targetGridPos ).WithZ( JUGGLE_HEIGHT ), delay, EasingType.QuadInOut );
				cardNum++;

				if ( i == numJuggles - 1 )
					newGridPositions.Add( targetGridPos );
			}

			Manager.Instance.PlayCardSfx( "card_move", this, volume: 1.1f, pitch: Game.Random.Float( 0.85f, 1.15f ) );

			await Task.DelayRealtime( (int)(delay * 1000f) );
		}

		foreach ( Card card in validCards )
			Manager.Instance.RemoveCardGridPos( card );

		int count = 0;
		foreach ( Card card in validCards )
		{
			card.MoveToPos( Manager.GetCardPos( newGridPositions[count] ), 0.15f, EasingType.QuadOut );
			count++;
		}

		await Task.DelayRealtime( 200 );

		count = 0;
		foreach ( Card card in validCards )
		{
			await Manager.Instance.SetCardGridPos( card, newGridPositions[count] );
			card.IsMovementControlled = false;
			count++;
		}

		await Task.DelayRealtime( 200 );

		Manager.Instance.PopEventMessage();

		await Manager.Instance.EventHappened( EventType.AfterCardsMoved );
	}
}