relics/RelicTaxi.cs
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

public class RelicTaxi : Relic
{
	public override void Init()
	{
		base.Init();

		MaxLevel = 1;
	}

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Mismatch;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		var card0 = Manager.Instance.ChosenCards[0];
		var card1 = Manager.Instance.ChosenCards[1];

		var matches0 = Manager.Instance.Cards.Where( x => x.CardType == card0.CardType && x != card0 ).OrderBy( x => (x.GridPos - card0.GridPos).ManhattanLength ).ToList();
		var matches1 = Manager.Instance.Cards.Where( x => x.CardType == card1.CardType && x != card1 ).OrderBy( x => (x.GridPos - card1.GridPos).ManhattanLength ).ToList();

		Dictionary<Card, Card> validCards = new();
			
		//Log.Info( $"0: {card0.CardType} - dist: {(matches0.First().GridPos - card0.GridPos).ManhattanLength} - {card0.GridPos} -> {matches0.First().GridPos}" );
		//Log.Info( $"1: {card1.CardType} - dist: {(matches1.First().GridPos - card1.GridPos).ManhattanLength} - {card1.GridPos} -> {matches1.First().GridPos}" );

		if ( matches0.Count > 0 && (matches0.First().GridPos - card0.GridPos).ManhattanLength > 1 )
			validCards.Add( card0, matches0.First() );

		if ( matches1.Count > 0 && (matches1.First().GridPos - card1.GridPos).ManhattanLength > 1 )
			validCards.Add( card1, matches1.First() );

		if ( validCards.Count == 0 )
			return;

		Dictionary<Card, Card> cardsToMove = new();
		if ( Level == 2 ) // max level is 1, moving both cards had some non-ideal behaviour
		{
			foreach( var pair in validCards )
				cardsToMove.Add( pair.Key, pair.Value );
		}
		else
		{
			var pair = validCards.ElementAt( Game.Random.Int( 0, validCards.Count - 1 ) );
			cardsToMove.Add( pair.Key, pair.Value );
		}

		await Task.DelayRealtime( 200 );

		Manager.Instance.PushEventMessage( this, eventType );

		foreach( var pair in cardsToMove )
		{
			var card = pair.Key;
			var match = pair.Value;

			if ( Manager.IsAdjacent( card.GridPos, match.GridPos ) )
				continue;

			var path = Manager.Instance.GetPathTo( card.GridPos, match.GridPos );

			if ( path.Count <= 1 )
				continue;

			await Manager.Instance.ShakeCard( card );

			await Task.DelayRealtime( 450 );

			IntVector2 targetGridPos = path.FirstOrDefault();

			var targetCard = Manager.Instance.GetCardAtGridPos( targetGridPos );

			if ( targetCard != null )
				Manager.Instance.PlayCardSfxBetween( "card_move", card, targetCard, volume: 1.1f, pitch: Game.Random.Float( 0.85f, 1.15f ) );
			else
				Manager.Instance.PlayCardSfx( "card_move", card, volume: 1.1f, pitch: Game.Random.Float( 0.85f, 1.15f ) );

			card.MoveToPos( card.LocalPosition.WithZ( Game.Random.Float( 70f, 90f ) ), 0.3f, EasingType.SineOut );
			targetCard?.MoveToPos( targetCard.LocalPosition.WithZ( Game.Random.Float( 70f, 90f ) ), 0.25f, EasingType.SineOut );

			await Task.DelayRealtime( 250 );

			card.MoveToPos( Manager.GetCardPos( targetGridPos ).WithZ( card.LocalPosition.z ), 0.35f, EasingType.SineInOut );
			targetCard?.MoveToPos( Manager.GetCardPos( card.GridPos ).WithZ( targetCard.LocalPosition.z ), 0.35f, EasingType.SineInOut );

			await Task.DelayRealtime( 350 );

			card.MoveToPos( card.LocalPosition.WithZ( Globals.CARD_DEFAULT_HEIGHT + Globals.CARD_ADD_HEIGHT_REVEALED_OR_HOVERED ), 0.2f, EasingType.SineOut );
			targetCard?.MoveToPos( targetCard.LocalPosition.WithZ( Globals.CARD_DEFAULT_HEIGHT ), 0.2f, EasingType.SineOut );

			await Task.DelayRealtime( 200 );

			if ( targetCard != null )
			{
				await Manager.Instance.SwapCardPositions( card, targetCard );
			}
			else
			{
				Manager.Instance.RemoveCardGridPos( card );
				await Manager.Instance.SetCardGridPos( card, targetGridPos );
			}

			card.IsMovementControlled = false;
			if ( targetCard != null )
				targetCard.IsMovementControlled = false;

			await Task.DelayRealtime( 300 );
		}

		await Task.DelayRealtime( 150 );

		Manager.Instance.PopEventMessage();

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