cards/CardFlyingMoney.cs
using Sandbox;
using System.Globalization;
using System.Net;
using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks;

public class CardFlyingMoney : Card
{
	public override bool ShouldHandleEvent( EventType eventType )
	{
		return (eventType == EventType.Match && Manager.Instance.ChosenCards[1] == this) || (eventType == EventType.Mismatch && Manager.Instance.ChosenCards.Contains( this ) && !Manager.Instance.IsMismatchALockedMatch);
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		if(eventType == EventType.Match)
		{
			Manager.Instance.PushEventMessage( this, eventType );

			await Task.DelayRealtime( 150 );

			Manager.Instance.PlayCardSfxBetween( "flying_money_get", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1f, pitch: Game.Random.Float( 1.25f, 1.3f ) );

			await Task.DelayRealtime( 100 );

			Manager.Instance.PlayCardSfxBetween( "coin", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1.4f, pitch: Game.Random.Float( 1.25f, 1.3f ) );

			await Task.DelayRealtime( 50 );

			int moneyAmount = 3 + (int)Manager.Instance.Stats[StatType.EarnExtraMoney];
			Manager.Instance.SpawnGainMoneyFloater( moneyAmount, WorldPosition );
			await Manager.Instance.GainMoney( moneyAmount );

			await Task.DelayRealtime( 700 );

			Manager.Instance.PopEventMessage();
		}
		else if (eventType == EventType.Mismatch)
		{
			var nearbyCards = Manager.Instance.GetNearbyCards( GridPos );
			nearbyCards.Shuffle();

			if ( nearbyCards.Count == 0 )
				return;

			Manager.Instance.PushEventMessage( this, eventType );

			List<Card> cardsToShuffle = new() { this };

			for ( int i = 0; i < Math.Min( 2, nearbyCards.Count ); i++ )
				cardsToShuffle.Add( nearbyCards[i] );

			await Task.DelayRealtime( 200 );

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

			await Task.DelayRealtime( 100 );

			MoveToPos( Manager.GetCardPos( GridPos ).WithZ( 100f ), 0.2f, EasingType.SineOut );

			await Task.DelayRealtime( 200 );

			// move other cards to self
			int cardNum = 0;
			foreach ( var card in cardsToShuffle )
			{
				if ( card == this )
					continue;

				card.MoveToPos( Manager.GetCardPos( GridPos ).WithZ( 50f - cardNum * 0.2f ), 0.6f, EasingType.SineInOut );
				cardNum++;
			}

			await Task.DelayRealtime( 700 );

			Manager.Instance.HideCard( this );
			Manager.Instance.PlayCardSfx( "card_flip", this, volume: 0.9f, pitch: Game.Random.Float( 0.65f, 0.75f ) );

			await Task.DelayRealtime( 200 );

			MoveToPos( Manager.GetCardPos( GridPos ).WithZ( Game.Random.Float( 48f, 50f ) ), 0.2f, EasingType.SineOut );

			await Task.DelayRealtime( 300 );

			List<IntVector2> newGridPositions = new();
			foreach ( var card in cardsToShuffle )
				newGridPositions.Add( card.GridPos );
			newGridPositions.Shuffle();

			foreach ( var card in cardsToShuffle )
				Manager.Instance.RemoveCardGridPos( card );

			// move self and others to new positions
			int count = 0;
			foreach ( Card card in cardsToShuffle )
			{
				card.MoveToPos( Manager.GetCardPos( newGridPositions[count] ).WithZ( Globals.CARD_DEFAULT_HEIGHT + (card.IsRevealed ? Globals.CARD_ADD_HEIGHT_REVEALED_OR_HOVERED : 0f) ), 0.6f, EasingType.QuadInOut );
				count++;
			}

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

			await Task.DelayRealtime( 700 );

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

			await Task.DelayRealtime( 200 );

			Manager.Instance.PopEventMessage();

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

	public override string GetEventText( EventType eventType )
	{
		if ( eventType == EventType.Match )
			return "✅Match: +$3";
		else
			return "❌Mismatch: shuffle with some nearby cards";
	}
}