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

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

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

			await Task.DelayRealtime( 200 );

			Manager.Instance.PlayCardSfx( "dice", this, volume: 1.8f, pitch: Game.Random.Float( 0.97f, 1.03f ) );

			await Task.DelayRealtime( 200 );

			await Manager.Instance.ShakeCard( this, playSfx: false );

			await Task.DelayRealtime( 600 );

			Manager.Instance.PlayCardSfx( "dice_result", this, volume: 1.4f, pitch: Game.Random.Float( 0.8f, 0.83f ) );

			int result = Game.Random.Int( 1, 6 );

			Manager.Instance.SpawnFloaterText(
				pos: WorldPosition.WithZ( 100f ),
				text: $"{result}",
				emojiText: "🎲",
				lifetime: 1.2f,
				color: new Color( 1f, 1f, 1f ),
				velocity: new Vector2( 0f, 35f ),
				deceleration: 2.1f,
				fontSize: 60f,
				startScale: 1f,
				endScale: 1.1f
			);

			await Task.DelayRealtime( 500 );

			if ( result == 1 )
			{
				await Task.DelayRealtime( 400 );

				Manager.Instance.PlayCardSfx( "dice_1", this, volume: 1.6f, pitch: Game.Random.Float( 0.85f, 0.9f ) );

				int loseMoneyAmount = 1;
				Manager.Instance.SpawnLoseMoneyFloater( loseMoneyAmount, WorldPosition );
				await Manager.Instance.LoseMoney( loseMoneyAmount );

				await Task.DelayRealtime( 1000 );
			}
			else if ( result == 6 )
			{
				await Task.DelayRealtime( 400 );

				Manager.Instance.PlayCardSfx( "dice_6", this, volume: 1.4f, pitch: Game.Random.Float( 0.9f, 0.95f ) );

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

				await Task.DelayRealtime( 1000 );
			}

			Manager.Instance.PopEventMessage();
		}
		else if(eventType == EventType.Match)
		{
			var dice0 = Manager.Instance.ChosenCards[0];
			var dice1 = Manager.Instance.ChosenCards[1];

			Manager.Instance.PushEventMessage( this, eventType );

			await Task.DelayRealtime( 200 );

			Manager.Instance.PlayCardSfxBetween( "dice", dice0, dice1, volume: 1.8f, pitch: Game.Random.Float( 0.97f, 1.03f ) );

			await Task.DelayRealtime( 200 );

			await Manager.Instance.ShakeCard( dice0, playSfx: false );
			await Manager.Instance.ShakeCard( dice1, playSfx: false );

			await Task.DelayRealtime( 600 );

			Manager.Instance.PlayCardSfxBetween( "dice_result", dice0, dice1, volume: 1.4f, pitch: Game.Random.Float( 0.8f, 0.83f ) );

			int result0 = Game.Random.Int( 1, 6 );
			int result1 = Game.Random.Int( 1, 6 );

			int result = result0 + result1;

			Manager.Instance.SpawnFloaterText(
				pos: dice0.WorldPosition.WithZ( 100f ),
				text: $"{result0}",
				emojiText: "🎲",
				lifetime: 1.2f,
				color: new Color( 1f, 1f, 1f ),
				velocity: new Vector2( 0f, 35f ),
				deceleration: 2.1f,
				fontSize: 60f,
				startScale: 1f,
				endScale: 1.1f
			);

			Manager.Instance.SpawnFloaterText(
				pos: dice1.WorldPosition.WithZ( 100f ),
				text: $"{result1}",
				emojiText: "🎲",
				lifetime: 1.2f,
				color: new Color( 1f, 1f, 1f ),
				velocity: new Vector2( 0f, 35f ),
				deceleration: 2.1f,
				fontSize: 60f,
				startScale: 1f,
				endScale: 1.1f
			);

			await Task.DelayRealtime( 500 );

			if ( result == 2 )
			{
				await Task.DelayRealtime( 400 );

				Manager.Instance.PlayCardSfx( "dice_1", this, volume: 1.6f, pitch: Game.Random.Float( 0.85f, 0.9f ) );

				int loseMoneyAmount = 2;
				var midpoint = (dice0.WorldPosition + dice1.WorldPosition) / 2f;
				Manager.Instance.SpawnLoseMoneyFloater( loseMoneyAmount, midpoint );
				await Manager.Instance.LoseMoney( loseMoneyAmount );

				await Task.DelayRealtime( 1000 );
			}
			else if ( result == 12 )
			{
				await Task.DelayRealtime( 400 );

				Manager.Instance.PlayCardSfx( "dice_6", this, volume: 1.4f, pitch: Game.Random.Float( 0.9f, 0.95f ) );

				int gainMoneyAmount = 5 + (int)Manager.Instance.Stats[StatType.EarnExtraMoney];
				var midpoint = (dice0.WorldPosition + dice1.WorldPosition) / 2f;
				Manager.Instance.SpawnGainMoneyFloater( gainMoneyAmount, midpoint );
				await Manager.Instance.GainMoney( gainMoneyAmount );

				await Task.DelayRealtime( 1000 );
			}

			Manager.Instance.PopEventMessage();
		}
	}

	public override string GetEventText( EventType eventType )
	{
		if ( eventType == EventType.Mismatch )
		{
			return "❌Mismatch: roll a 6 to get +$3 (rolling 1 loses $1)";
		}
		else
		{
			return "✅Match: Roll a 12 to get +$7 (rolling 2 loses $2)";
		}
	}
}