Framework/Audio/GameSounds.cs

A scene Component that centralizes table-related sound effect slots. It exposes SoundEvent properties for events like CardDraw, CardPickup, Bet, Win, etc., resolves a TableSound enum to the assigned SoundEvent, and plays the resolved sound at a world position on the client.

Native Interop
namespace CardGames;

/// <summary>
/// The single place to wire table sound effects. Holds one <see cref="SoundEvent"/> slot per
/// <see cref="TableSound"/>; drop a sound into a slot in the editor and that moment plays it. A slot
/// left empty simply makes its event silent, so the whole system is inert until sounds are assigned.
///
/// One instance lives in the scene (on the GameDirector object). Consumers resolve it as a cached
/// property (<see cref="CardGame.Sounds"/>, <see cref="CardHover"/>) and call <see cref="Play"/>.
/// </summary>
public sealed class GameSounds : Component
{
	[Property] public SoundEvent CardDraw { get; set; }
	[Property] public SoundEvent CardPickup { get; set; }
	[Property] public SoundEvent CardDrop { get; set; }
	[Property] public SoundEvent Bet { get; set; }
	[Property] public SoundEvent Win { get; set; }
	[Property] public SoundEvent Loss { get; set; }
	[Property] public SoundEvent Push { get; set; }

	/// <summary>The sound assigned to a given event, or null if that slot is empty.</summary>
	public SoundEvent Resolve( TableSound sound ) => sound switch
	{
		TableSound.CardDraw => CardDraw,
		TableSound.CardPickup => CardPickup,
		TableSound.CardDrop => CardDrop,
		TableSound.Bet => Bet,
		TableSound.Win => Win,
		TableSound.Loss => Loss,
		TableSound.Push => Push,
		_ => null,
	};

	/// <summary>Play a sound on this client at a world position. No-ops when the slot is unassigned.</summary>
	public void Play( TableSound sound, Vector3 position )
	{
		if ( Resolve( sound ) is { } evt )
			Sound.Play( evt, position );
	}
}