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

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

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Match && (Manager.Instance.ChosenCards[0] == this || (Manager.Instance.ChosenCards[1] == this && Manager.Instance.ChosenCards[0].GridPos.y != Manager.Instance.ChosenCards[1].GridPos.y));
	}

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

		await Task.DelayRealtime( 400 );

		Manager.Instance.PlayCardSfx( "teacher", this, volume: 1.2f, pitch: Game.Random.Float(0.98f, 1.02f) );

		MoveToPos( Manager.GetCardPos(GridPos).WithZ( 100f ), 0.5f, EasingType.SineInOut );

		await Task.DelayRealtime( 500 );

		//Manager.Instance.PlayCardSfx( "ufo_receive", this, volume: 1.2f, pitch: Game.Random.Float( 0.95f, 1.05f ) );

		List<Card> cardsToArrange = new();
		for( int x = 0; x < Manager.Instance.GridWidth; x++ )
		{
			var card = Manager.Instance.GetCardAtGridPos( new IntVector2( x, GridPos.y ) );
			if ( card == null )
				continue;

			cardsToArrange.Add( card );
		}
		cardsToArrange = cardsToArrange.OrderBy( x => Card.GetName( x.CardType ) ).ToList();

		int cardNum = 0;
		foreach ( var card in cardsToArrange )
		{
			if ( card == this )
				continue;

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

		await Task.DelayRealtime( 300 );

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

		// move cards to new positions
		IntVector2 currGridPos = new IntVector2( 0, GridPos.y );
		foreach ( Card card in cardsToArrange )
		{
			Manager.Instance.PlayCardSfx( "card_move", card, volume: 0.7f, pitch: Game.Random.Float( 0.85f, 1.15f ) );
			card.MoveToPos( Manager.GetCardPos( currGridPos ).WithZ( Globals.CARD_DEFAULT_HEIGHT + (card.IsRevealed ? Globals.CARD_ADD_HEIGHT_REVEALED_OR_HOVERED : 0f) ), 0.25f, EasingType.QuadInOut );
			await Task.DelayRealtime( 250 );

			currGridPos += new IntVector2( 1, 0 );
		}

		await Task.DelayRealtime( 200 );

		currGridPos = new IntVector2( 0, GridPos.y );
		foreach ( Card card in cardsToArrange )
		{
			await Manager.Instance.SetCardGridPos( card, currGridPos );
			card.IsMovementControlled = false;

			currGridPos += new IntVector2( 1, 0 );
		}

		await Task.DelayRealtime( 300 );

		Manager.Instance.PopEventMessage();

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