Cards/DeckDefinition.cs

Asset resource defining a playing-card deck. It declares styling properties (colors, textures, font, resolution), deck kind (standard 52 or custom), holds custom CardFace entries, and builds the resolved list of faces lazily, regenerating standard 52-card faces via enums.

using System.Collections.Generic;
using System.Linq;

namespace CardGames;

/// <summary>
/// A deck asset: what cards exist and how they should look. Authored in the editor as a
/// <c>.deck</c> file. Standard decks are generated in code; Uno/custom decks list their faces.
/// </summary>
[AssetType( Name = "Deck Definition", Extension = "deck", Category = "Card Games" )]
public class DeckDefinition : GameResource
{
	/// <summary>
	/// The kind of deck: a standard 52-card deck, or a custom list of faces.
	/// </summary>
	public enum DeckKind
	{
		Standard52,
		Custom
	}

	[Property] public DeckKind Kind { get; set; } = DeckKind.Standard52;

	[Group( "Style" ), Property] public Color FaceColor { get; set; } = Color.White;
	[Group( "Style" ), Property] public Color BackColor { get; set; } = Color.Parse( "#23408e" ) ?? Color.Blue;

	/// <summary>
	/// Optional card-back texture. If set, it's used instead of the procedurally drawn back.
	/// </summary>
	[Group( "Style" ), Property] public Texture BackImage { get; set; }

	/// <summary>
	/// Treat the back image as a greyscale mask and recolour it from <see cref="BackColor"/>.
	/// </summary>
	[Group( "Style" ), Property] public bool TintBack { get; set; }
	[Group( "Style" ), Property] public Color TextColor { get; set; } = Color.Black;
	[Group( "Style" ), Property] public string Font { get; set; } = "Roboto";

	/// <summary>
	/// Texture height in pixels per face. Width is derived from the card aspect ratio.
	/// </summary>
	[Group( "Style" ), Property, Range( 128, 1024 )] public int Resolution { get; set; } = 512;

	/// <summary>
	/// Corner rounding as a fraction of the card width.
	/// </summary>
	[Group( "Style" ), Property, Range( 0, 0.3f )] public float CornerRadius { get; set; } = 0.08f;

	/// <summary>
	/// How metallic the printed ink/pips look, 0 (matte) to 1 (fully metallic).
	/// </summary>
	[Group( "Style" ), Property, Range( 0, 1 )] public float MetallicInk { get; set; } = 1.0f;

	/// <summary>
	/// Used when <see cref="Kind"/> is <see cref="DeckKind.Custom"/> (e.g. an Uno deck).
	/// </summary>
	[Property, ShowIf( nameof( Kind ), DeckKind.Custom )]
	public List<CardFace> CustomCards { get; set; } = new();

	private List<CardFace> _faces;

	/// <summary>
	/// The resolved, ordered list of faces in this deck.
	/// </summary>
	public IReadOnlyList<CardFace> Faces => _faces ??= BuildFaces();

	private List<CardFace> BuildFaces() => Kind switch
	{
		DeckKind.Standard52 => BuildStandard52(),
		_ => CustomCards ?? new List<CardFace>()
	};

	private static List<CardFace> BuildStandard52()
	{
		var faces = new List<CardFace>( 52 );
		foreach ( var suit in System.Enum.GetValues<Suit>() )
		foreach ( var rank in System.Enum.GetValues<Rank>() )
			faces.Add( CardFace.FromStandard( rank, suit ) );
		return faces;
	}

	protected override void PostLoad() => _faces = null; // rebuild lazily if the asset is hot-reloaded
}