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

public class CardBomb : Card
{
	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Match && Manager.Instance.ChosenCards[0] == this;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		var nearbyCards0 = Manager.Instance.GetNearbyCards( GridPos ).Where(x => x != Manager.Instance.ChosenCards[1]);
		var nearbyCards1 = Manager.Instance.GetNearbyCards( Manager.Instance.ChosenCards[1].GridPos ).Where( x => x != this && !nearbyCards0.Contains(x) );

		if ( nearbyCards0.Count() == 0 && nearbyCards1.Count() == 0 )
			return;

		Manager.Instance.PushEventMessage( this, eventType );

		Manager.Instance.PlayCardSfx( "bomb", this, volume: 0.85f, pitch: Game.Random.Float( 0.95f, 1.05f ) );

		await LiftCards( nearbyCards0, height: 70f );

		Manager.Instance.PlayCardSfx( "bomb", Manager.Instance.ChosenCards[1], volume: 0.85f, pitch: Game.Random.Float( 0.95f, 1.05f ) );

		await LiftCards( nearbyCards1, height: 90f );

		await Task.DelayRealtime( 900 );

		await RearrangeCards( nearbyCards0 );
		await RearrangeCards( nearbyCards1 );

		Manager.Instance.PopEventMessage();

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

	async Task LiftCards( IEnumerable<Card> cards, float height)
	{
		if ( cards.Count() > 0 )
		{
			foreach ( Card card in cards )
			{
				if ( card.CantBeMoved )
					continue;

				card.MoveToPos( (card.LocalPosition + (card.LocalPosition - LocalPosition).Normal * Game.Random.Float( 6f, 9f )).WithZ( height ), 
					Game.Random.Float( 0.2f, 0.4f ), EasingType.QuadOut );
			}

			await Task.DelayRealtime( 350 );

			foreach ( Card card in cards )
			{
				Manager.Instance.PlayCardSfx( "card_flip", card, volume: 0.6f, pitch: Game.Random.Float( 1.15f, 1.2f ) );
				await Manager.Instance.RevealCard( card );
				await Task.DelayRealtime( 50 );
			}

			await Task.DelayRealtime( 250 );
		}
	}

	async Task RearrangeCards(IEnumerable<Card> cards)
	{
		List<Card> validCards = new();
		foreach(var card in cards)
		{
			if ( !card.CantBeMoved )
				validCards.Add( card );
		}

		List<IntVector2> newGridPositions0 = new();
		foreach ( Card card in validCards )
			newGridPositions0.Add( card.GridPos );
		newGridPositions0.Shuffle();

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

		foreach ( Card card in cards )
		{
			Manager.Instance.HideCard( card );
			Manager.Instance.PlayCardSfx( "card_flip", card, volume: 0.7f, pitch: Game.Random.Float( 0.65f, 0.75f ) );

			await Task.DelayRealtime( 50 );
		}

		int count = 0;
		foreach ( Card card in validCards )
		{
			card.MoveToPos( Manager.GetCardPos( newGridPositions0[count] ), 0.5f, EasingType.QuadOut );

			await Task.DelayRealtime( 100 );

			count++;
		}

		await Task.DelayRealtime( 200 );

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