UI/Foundation/OMRHUDLayout.cs
using Sandbox;
using System;

public enum OMRHUDPreviewMode
{
	Combat,
	SuddenDeath,
	WinnerDecision,
	ModifierDecision,
	MatchEnd,
	Sandbox,
	Warmup
}

public enum OMRHUDPairAlignment
{
	Split,
	Left,
	Center,
	Right
}

public enum OMRHUDFillDirection
{
	LeftToRight,
	RightToLeft
}

public enum OMRHUDScreenAnchor
{
	TopLeft,
	TopCenter,
	TopRight,
	MiddleLeft,
	Center,
	MiddleRight,
	BottomLeft,
	BottomCenter,
	BottomRight
}

/// <summary>
/// Developer-authored placement, geometry and visibility for the gameplay HUD.
/// OMRHUD owns layout/chrome visibility; OMRTheme owns colors/fonts; gameplay owns values.
/// </summary>
[AssetType(
	Name = "OMR HUD Layout",
	Extension = "omrhud",
	Category = "One More Round/UI"
)]
public sealed partial class OMRHUDLayout : GameResource
{
	// ============================================================
	// HEALTH
	// ============================================================
	[Group( "Health - General" ), Title( "Show Local Health" )]
	public bool ShowLocalHealth { get; set; } = true;
	[Group( "Health - General" ), Title( "Show Opponent Health" )]
	public bool ShowOpponentHealth { get; set; } = true;
	[Group( "Health - General" ), Title( "Show Labels" )]
	public bool ShowHealthLabels { get; set; } = true;
	[Group( "Health - General" ), Title( "Show Numbers" )]
	public bool ShowHealthNumbers { get; set; } = true;
	[Group( "Health - General" ), Title( "Show Bars" )]
	public bool ShowHealthBars { get; set; } = true;

	[Group( "Health - General" ), Range( 0f, 300f ), Title( "Top Inset" )]
	public float HealthTop { get; set; } = 30f;
	[Group( "Health - General" ), Range( 0f, 300f ), Title( "Horizontal Inset" )]
	public float HealthHorizontalInset { get; set; } = 38f;
	[Group( "Health - General" ), Range( 15f, 48f ), Title( "Side Width (%)" )]
	public float HealthSideWidthPercent { get; set; } = 34f;
	[Group( "Health - General" ), Range( 180f, 800f ), Title( "Maximum Side Width" )]
	public float HealthMaximumWidth { get; set; } = 440f;
	[Group( "Health - General" ), Range( 0f, 40f ), Title( "Heading / Bar Gap" )]
	public float HealthHeadingBottomSpacing { get; set; } = 5f;
	// Retained for backwards compatibility with older .omrhud resources.
	// Individual local/opponent grouped-heading gaps now own this behavior.
	[Hide]
	public float HealthHeadingGap { get; set; } = 10f;
	[Group( "Health - General" ), Range( 8f, 32f ), Title( "Label Size" )]
	public float HealthLabelSize { get; set; } = 13f;
	[Group( "Health - General" ), Range( 8f, 36f ), Title( "Number Size" )]
	public float HealthNumberSize { get; set; } = 14f;
	[Group( "Health - General" ), Range( 4f, 42f ), Title( "Bar Height" )]
	public float HealthBarHeight { get; set; } = 15f;
	[Hide]
	public float HealthBarCornerRadius { get; set; }
	[Group( "Health - General" ), Title( "Show Track Background / Border" )]
	public bool ShowHealthTrackChrome { get; set; } = true;
	[Group( "Health - General" ), Range( 0f, 1f ), ShowIf( nameof( ShowHealthTrackChrome ), true ), Title( "Track Chrome Opacity" )]
	public float HealthTrackChromeOpacity { get; set; } = 1f;
	[Group( "Health - General" ), Range( 0f, 1f ), Title( "Health Fill Opacity" )]
	public float HealthBarOpacity { get; set; } = 1f;

	[Group( "Health - Local" ), Range( -300f, 300f ), Title( "X Offset" )]
	public float LocalHealthOffsetX { get; set; }
	[Group( "Health - Local" ), Range( -250f, 250f ), Title( "Y Offset" )]
	public float LocalHealthOffsetY { get; set; }
	[Group( "Health - Local" ), Range( 0f, 1f ), Title( "Opacity" )]
	public float LocalHealthOpacity { get; set; } = 1f;
	[Group( "Health - Local" ), Title( "Heading Alignment" )]
	public OMRHUDPairAlignment LocalHealthHeadingAlignment { get; set; } = OMRHUDPairAlignment.Split;
	[Group( "Health - Local" ), HideIf( nameof( LocalHealthHeadingAlignment ), OMRHUDPairAlignment.Split ), Range( 0f, 80f ), Title( "Heading Item Gap" )]
	public float LocalHealthHeadingGap { get; set; } = 10f;
	[Group( "Health - Local" ), Title( "Fill Direction" )]
	public OMRHUDFillDirection LocalHealthFillDirection { get; set; } = OMRHUDFillDirection.LeftToRight;

	[Group( "Health - Opponent" ), Range( -300f, 300f ), Title( "X Offset" )]
	public float OpponentHealthOffsetX { get; set; }
	[Group( "Health - Opponent" ), Range( -250f, 250f ), Title( "Y Offset" )]
	public float OpponentHealthOffsetY { get; set; }
	[Group( "Health - Opponent" ), Range( 0f, 1f ), Title( "Opacity" )]
	public float OpponentHealthOpacity { get; set; } = 1f;
	[Group( "Health - Opponent" ), Title( "Heading Alignment" )]
	public OMRHUDPairAlignment OpponentHealthHeadingAlignment { get; set; } = OMRHUDPairAlignment.Split;
	[Group( "Health - Opponent" ), HideIf( nameof( OpponentHealthHeadingAlignment ), OMRHUDPairAlignment.Split ), Range( 0f, 80f ), Title( "Heading Item Gap" )]
	public float OpponentHealthHeadingGap { get; set; } = 10f;
	[Group( "Health - Opponent" ), Title( "Fill Direction" )]
	public OMRHUDFillDirection OpponentHealthFillDirection { get; set; } = OMRHUDFillDirection.RightToLeft;

	// ============================================================
	// MATCH HEADER
	// ============================================================
	[Group( "Match Header - General" ), Title( "Show Match Header" )]
	public bool ShowMatchHeader { get; set; } = true;
	[Group( "Match Header - General" ), Range( 0f, 350f ), Title( "Top Inset" )]
	public float MatchHeaderTop { get; set; } = 30f;
	[Group( "Match Header - General" ), Range( 0f, 500f ), Title( "Horizontal Inset" )]
	public float MatchHeaderHorizontalInset { get; set; }
	[Group( "Match Header - General" ), Range( -350f, 350f ), Title( "X Offset" )]
	public float MatchHeaderOffsetX { get; set; }
	[Group( "Match Header - General" ), Range( -300f, 300f ), Title( "Y Offset" )]
	public float MatchHeaderOffsetY { get; set; }
	[Group( "Match Header - General" ), Range( 0f, 1f ), Title( "Opacity" )]
	public float MatchHeaderOpacity { get; set; } = 1f;
	[Group( "Match Header - General" ), Title( "Content Alignment" )]
	public OMRTextHorizontalAlignment MatchHeaderAlignment { get; set; } = OMRTextHorizontalAlignment.Center;
	[Group( "Match Header - General" ), Range( 0f, 60f ), Title( "Element Gap" )]
	public float MatchHeaderElementGap { get; set; } = 5f;

	[Group( "Match Header - Round" ), Title( "Show Round Title" )]
	public bool ShowRoundTitle { get; set; } = true;
	[Group( "Match Header - Round" ), Range( 12f, 70f ), Title( "Size" )]
	public float RoundTitleSize { get; set; } = 28f;
	[Group( "Match Header - Round" ), Range( 0f, 1f ), Title( "Opacity" )]
	public float RoundTitleOpacity { get; set; } = 1f;
	[Group( "Match Header - Round" ), Range( -250f, 250f ), Title( "Y Offset" )]
	public float RoundTitleOffsetY { get; set; }

	[Group( "Match Header - Timer" ), Title( "Show Timer" )]
	public bool ShowTimer { get; set; } = true;
	[Group( "Match Header - Timer" ), Range( 12f, 72f ), Title( "Number Size" )]
	public float TimerSize { get; set; } = 25f;
	[Group( "Match Header - Timer" ), Range( 12f, 72f ), Title( "Sudden Death Number Size" )]
	public float SuddenDeathTimerSize { get; set; } = 30f;
	[Group( "Match Header - Timer" ), Range( -250f, 250f ), Title( "Y Offset" )]
	public float TimerOffsetY { get; set; }
	[Group( "Match Header - Timer" ), Range( 30f, 300f ), Title( "Minimum Width" )]
	public float TimerMinimumWidth { get; set; } = 72f;
	[Group( "Match Header - Timer" ), Range( 0f, 50f ), Title( "Horizontal Padding" )]
	public float TimerPaddingX { get; set; } = 10f;
	[Group( "Match Header - Timer" ), Range( 0f, 30f ), Title( "Vertical Padding" )]
	public float TimerPaddingY { get; set; } = 3f;
	[Group( "Match Header - Timer" ), Title( "Show Background / Border" )]
	public bool ShowTimerChrome { get; set; } = true;
	[Group( "Match Header - Timer" ), Range( 0f, 1f ), ShowIf( nameof( ShowTimerChrome ), true ), Title( "Chrome Opacity" )]
	public float TimerChromeOpacity { get; set; } = 1f;
	[Group( "Match Header - Timer" ), Title( "Show Sudden Death Heading" )]
	public bool ShowTimerMode { get; set; } = true;
	[Group( "Match Header - Timer" ), Title( "Show Sudden Death Note" )]
	public bool ShowTimerNote { get; set; } = true;
	[Group( "Match Header - Timer" ), Range( 6f, 28f ), Title( "Mode / Note Size" )]
	public float TimerMetaSize { get; set; } = 10f;

	[Group( "Match Header - Score" ), Title( "Show Score" )]
	public bool ShowScore { get; set; } = true;
	[Group( "Match Header - Score" ), Title( "Show BANKED Heading" )]
	public bool ShowScoreHeading { get; set; } = true;
	[Group( "Match Header - Score" ), Title( "Show Player Labels" )]
	public bool ShowScorePlayerLabels { get; set; } = true;
	[Group( "Match Header - Score" ), Title( "Show Separator" )]
	public bool ShowScoreSeparator { get; set; } = true;
	[Group( "Match Header - Score" ), Range( 18f, 90f ), Title( "Score Number Size" )]
	public float ScoreNumberSize { get; set; } = 34f;
	[Group( "Match Header - Score" ), Range( 8f, 36f ), Title( "Player Label Size" )]
	public float ScoreLabelSize { get; set; } = 15f;
	[Group( "Match Header - Score" ), Range( 8f, 36f ), Title( "BANKED Heading Size" )]
	public float ScoreHeadingSize { get; set; } = 11f;
	[Group( "Match Header - Score" ), Range( 8f, 60f ), Title( "Separator Size" )]
	public float ScoreSeparatorSize { get; set; } = 28f;
	[Group( "Match Header - Score" ), Range( 0f, 100f ), Title( "Column Gap" )]
	public float ScoreColumnGap { get; set; } = 25f;
	[Group( "Match Header - Score" ), Range( -250f, 250f ), Title( "Y Offset" )]
	public float ScoreOffsetY { get; set; }

	[Group( "Match Header - Pot & Modifier" ), Title( "Show Pot" )]
	public bool ShowPot { get; set; } = true;
	[Group( "Match Header - Pot & Modifier" ), Title( "Show Pot Background / Border" )]
	public bool ShowPotChrome { get; set; } = true;
	[Group( "Match Header - Pot & Modifier" ), Range( 0f, 1f ), ShowIf( nameof( ShowPotChrome ), true ), Title( "Pot Chrome Opacity" )]
	public float PotChromeOpacity { get; set; } = 1f;
	[Group( "Match Header - Pot & Modifier" ), Range( 0f, 40f ), Title( "Pot Horizontal Padding" )]
	public float PotPaddingX { get; set; } = 14f;
	[Group( "Match Header - Pot & Modifier" ), Range( 0f, 30f ), Title( "Pot Vertical Padding" )]
	public float PotPaddingY { get; set; } = 6f;
	[Group( "Match Header - Pot & Modifier" ), Range( 0f, 50f ), Title( "Pot Item Gap" )]
	public float PotGap { get; set; } = 10f;
	[Group( "Match Header - Pot & Modifier" ), Range( 7f, 32f ), Title( "Pot Owner Size" )]
	public float PotOwnerSize { get; set; } = 11f;
	[Group( "Match Header - Pot & Modifier" ), Range( 10f, 42f ), Title( "Pot Value Size" )]
	public float PotValueSize { get; set; } = 17f;
	[Group( "Match Header - Pot & Modifier" ), Range( 0f, 1f ), Title( "Pot Content Opacity" )]
	public float PotOpacity { get; set; } = 1f;
	[Group( "Match Header - Pot & Modifier" ), Title( "Show Modifier Label" )]
	public bool ShowModifierLabel { get; set; } = true;
	[Group( "Match Header - Pot & Modifier" ), Range( 8f, 42f ), Title( "Modifier Size" )]
	public float ModifierLabelSize { get; set; } = 13f;
	[Group( "Match Header - Pot & Modifier" ), Range( 0f, 1f ), Title( "Modifier Opacity" )]
	public float ModifierLabelOpacity { get; set; } = 0.75f;

	// ============================================================
	// CENTER MESSAGE
	// ============================================================
	[Group( "Center Message" ), Title( "Show Center Message" )]
	public bool ShowCenterMessage { get; set; } = true;
	[Group( "Center Message" ), Range( 0f, 100f ), Title( "Vertical Position (%)" )]
	public float CenterMessageTopPercent { get; set; } = 43f;
	[Group( "Center Message" ), Range( -400f, 400f ), Title( "X Offset" )]
	public float CenterMessageOffsetX { get; set; }
	[Group( "Center Message" ), Range( -300f, 300f ), Title( "Y Offset" )]
	public float CenterMessageOffsetY { get; set; }
	[Group( "Center Message" ), Range( 0f, 1f ), Title( "Opacity" )]
	public float CenterMessageOpacity { get; set; } = 1f;
	[Group( "Center Message" ), Range( 18f, 160f ), Title( "Size" )]
	public float CenterMessageSize { get; set; } = 72f;
	[Group( "Center Message" ), Title( "Text Alignment" )]
	public OMRTextHorizontalAlignment CenterMessageAlignment { get; set; } = OMRTextHorizontalAlignment.Center;
	[Group( "Center Message" ), Range( 0f, 400f ), Title( "Text Indent" )]
	public float CenterMessageIndent { get; set; }

	// ============================================================
	// DECISIONS
	// ============================================================
	[Group( "Decisions - Panel" ), Title( "Show Screen Overlay" )]
	public bool ShowDecisionOverlay { get; set; } = true;
	[Group( "Decisions - Panel" ), Range( 0f, 1f ), ShowIf( nameof( ShowDecisionOverlay ), true ), Title( "Screen Overlay Opacity" )]
	public float DecisionScreenOpacity { get; set; } = 1f;
	[Group( "Decisions - Panel" ), Title( "Show Panel Background / Border / Shadow" )]
	public bool ShowDecisionChrome { get; set; } = true;
	[Group( "Decisions - Panel" ), Range( 0f, 1f ), ShowIf( nameof( ShowDecisionChrome ), true ), Title( "Panel Chrome Opacity" )]
	public float DecisionChromeOpacity { get; set; } = 1f;
	[Group( "Decisions - Panel" ), Range( 420f, 1400f ), Title( "Panel Width" )]
	public float DecisionPanelWidth { get; set; } = 760f;
	[Group( "Decisions - Panel" ), Range( 0f, 100f ), Title( "Panel Padding" )]
	public float DecisionPanelPadding { get; set; } = 34f;
	[Group( "Decisions - Panel" ), Range( -500f, 500f ), Step( 1f ), Title( "Panel X Offset" )]
	public float DecisionOffsetX { get; set; }
	[Group( "Decisions - Panel" ), Range( -350f, 350f ), Step( 1f ), Title( "Panel Y Offset" )]
	public float DecisionOffsetY { get; set; }
	[Group( "Decisions - Panel" ), Title( "Content Alignment" )]
	public OMRTextHorizontalAlignment DecisionContentAlignment { get; set; } = OMRTextHorizontalAlignment.Center;
	[Group( "Decisions - Panel" ), Range( 0f, 250f ), Title( "Text Indent" )]
	public float DecisionTextIndent { get; set; }
	[Group( "Decisions - Panel" ), Title( "Show Eyebrow" )]
	public bool ShowDecisionEyebrow { get; set; } = true;
	[Group( "Decisions - Panel" ), Title( "Show Pot / Status Line" )]
	public bool ShowDecisionPot { get; set; } = true;
	[Group( "Decisions - Panel" ), Title( "Show Decision Timer" )]
	public bool ShowDecisionTimer { get; set; } = true;
	[Group( "Decisions - Panel" ), Range( 8f, 36f ), Title( "Eyebrow Size" )]
	public float DecisionEyebrowSize { get; set; } = 13f;
	[Group( "Decisions - Panel" ), Range( 18f, 90f ), Title( "Title Size" )]
	public float DecisionTitleSize { get; set; } = 38f;
	[Group( "Decisions - Panel" ), Range( 8f, 40f ), Title( "Pot / Status Size" )]
	public float DecisionPotSize { get; set; } = 17f;
	[Group( "Decisions - Panel" ), Range( 10f, 60f ), Title( "Timer Size" )]
	public float DecisionTimerSize { get; set; } = 24f;
	[Group( "Decisions - Panel" ), Title( "Show Match End Score" )]
	public bool ShowDecisionFinalScore { get; set; } = true;
	[Group( "Decisions - Panel" ), Range( 18f, 120f ), Title( "Match End Score Size" )]
	public float DecisionFinalScoreSize { get; set; } = 60f;

	[Group( "Decisions - Buttons" ), Title( "Show Button Background / Border" )]
	public bool ShowDecisionButtonChrome { get; set; } = true;
	[Group( "Decisions - Buttons" ), Title( "Show Button Descriptions" )]
	public bool ShowDecisionButtonDescriptions { get; set; } = true;
	[Group( "Decisions - Buttons" ), Range( 45f, 220f ), Title( "Button Height" )]
	public float DecisionButtonHeight { get; set; } = 110f;
	[Group( "Decisions - Buttons" ), Range( 0f, 60f ), Title( "Button Gap" )]
	public float DecisionButtonGap { get; set; } = 16f;
	[Group( "Decisions - Buttons" ), Range( 0f, 60f ), Title( "Button Padding" )]
	public float DecisionButtonPadding { get; set; } = 20f;
	[Group( "Decisions - Buttons" ), Range( 100f, 120f ), Step( 0.1f ), Title( "Hover Scale (%)" )]
	public float DecisionButtonHoverScalePercent { get; set; } = 102f;
	[Group( "Decisions - Buttons" ), Range( -60f, 60f ), Title( "Hover X Shift" )]
	public float DecisionButtonHoverOffsetX { get; set; }
	[Group( "Decisions - Buttons" ), Range( -60f, 60f ), Title( "Hover Y Shift" )]
	public float DecisionButtonHoverOffsetY { get; set; }
	[Group( "Decisions - Buttons" ), Range( 80f, 100f ), Step( 0.1f ), Title( "Pressed Scale (%)" )]
	public float DecisionButtonPressedScalePercent { get; set; } = 98f;
	[Group( "Decisions - Buttons" ), Range( 12f, 60f ), Title( "Title Size" )]
	public float DecisionButtonTitleSize { get; set; } = 24f;
	[Group( "Decisions - Buttons" ), Range( 8f, 36f ), Title( "Description Size" )]
	public float DecisionButtonDescriptionSize { get; set; } = 13f;
	[Group( "Decisions - Buttons" ), Title( "Title Alignment" )]
	public OMRTextHorizontalAlignment DecisionButtonTitleAlignment { get; set; } = OMRTextHorizontalAlignment.Left;
	[Group( "Decisions - Buttons" ), Range( 0f, 160f ), Title( "Title Indent" )]
	public float DecisionButtonTitleIndent { get; set; }
	[Group( "Decisions - Buttons" ), Title( "Description Alignment" )]
	public OMRTextHorizontalAlignment DecisionButtonDescriptionAlignment { get; set; } = OMRTextHorizontalAlignment.Left;
	[Group( "Decisions - Buttons" ), Range( 0f, 160f ), Title( "Description Indent" )]
	public float DecisionButtonDescriptionIndent { get; set; }

	// ============================================================
	// AMMO
	// ============================================================
	[Group( "Ammo" ), Title( "Show Ammo" )]
	public bool ShowAmmo { get; set; } = true;
	[Group( "Ammo" ), Title( "Screen Anchor" )]
	public OMRHUDScreenAnchor AmmoAnchor { get; set; } = OMRHUDScreenAnchor.BottomRight;
	[Group( "Ammo" ), Range( 0f, 500f ), Title( "Horizontal Inset" )]
	public float AmmoRight { get; set; } = 34f;
	[Group( "Ammo" ), Range( 0f, 400f ), Title( "Vertical Inset" )]
	public float AmmoBottom { get; set; } = 30f;
	[Group( "Ammo" ), Range( -300f, 300f ), Title( "X Offset" )]
	public float AmmoOffsetX { get; set; }
	[Group( "Ammo" ), Range( -250f, 250f ), Title( "Y Offset" )]
	public float AmmoOffsetY { get; set; }
	[Group( "Ammo" ), Range( 80f, 500f ), Title( "Minimum Width" )]
	public float AmmoMinimumWidth { get; set; } = 155f;
	[Group( "Ammo" ), Range( 0f, 60f ), Title( "Padding" )]
	public float AmmoPadding { get; set; } = 14f;
	[Group( "Ammo" ), Range( 0f, 1f ), Title( "Content Opacity" )]
	public float AmmoOpacity { get; set; } = 1f;
	[Group( "Ammo" ), Title( "Content Alignment" )]
	public OMRTextHorizontalAlignment AmmoAlignment { get; set; } = OMRTextHorizontalAlignment.Right;
	[Group( "Ammo" ), Title( "Show Panel Background / Border" )]
	public bool ShowAmmoChrome { get; set; } = true;
	[Group( "Ammo" ), Range( 0f, 1f ), ShowIf( nameof( ShowAmmoChrome ), true ), Title( "Panel Chrome Opacity" )]
	public float AmmoChromeOpacity { get; set; } = 1f;
	[Group( "Ammo" ), Title( "Show Weapon Name" )]
	public bool ShowAmmoWeaponName { get; set; } = true;
	[Group( "Ammo" ), Title( "Show Reloading State" )]
	public bool ShowAmmoReloadState { get; set; } = true;
	[Group( "Ammo" ), Title( "Show Divider" )]
	public bool ShowAmmoDivider { get; set; } = true;
	[Group( "Ammo" ), Range( 14f, 72f ), Title( "Current Ammo Size" )]
	public float AmmoCurrentSize { get; set; } = 31f;
	[Group( "Ammo" ), Range( 8f, 48f ), Title( "Capacity Size" )]
	public float AmmoCapacitySize { get; set; } = 16f;
	[Group( "Ammo" ), Range( 7f, 28f ), ShowIf( nameof( ShowAmmoWeaponName ), true ), Title( "Weapon Name Size" )]
	public float AmmoWeaponSize { get; set; } = 10f;
	[Group( "Ammo" ), Range( 7f, 28f ), ShowIf( nameof( ShowAmmoReloadState ), true ), Title( "Reloading Text Size" )]
	public float AmmoReloadSize { get; set; } = 9f;
	[Group( "Ammo" ), Range( 8f, 40f ), ShowIf( nameof( ShowAmmoDivider ), true ), Title( "Divider Size" )]
	public float AmmoDividerSize { get; set; } = 17f;
	[Group( "Ammo" ), Range( 0f, 30f ), Title( "Element Gap" )]
	public float AmmoElementGap { get; set; } = 3f;
	[Group( "Ammo" ), Range( 0f, 30f ), Title( "Count Gap" )]
	public float AmmoCountGap { get; set; } = 5f;

	// ============================================================
	// SANDBOX / WARMUP
	// ============================================================
	[Group( "Sandbox & Warmup" ), Title( "Show Label" )]
	public bool ShowSandboxLabel { get; set; } = true;
	[Group( "Sandbox & Warmup" ), Title( "Screen Anchor" )]
	public OMRHUDScreenAnchor SandboxLabelAnchor { get; set; } = OMRHUDScreenAnchor.TopLeft;
	[Group( "Sandbox & Warmup" ), Range( 0f, 500f ), Title( "Horizontal Inset" )]
	public float SandboxLabelLeft { get; set; } = 30f;
	[Group( "Sandbox & Warmup" ), Range( 0f, 400f ), Title( "Vertical Inset" )]
	public float SandboxLabelTop { get; set; } = 30f;
	[Group( "Sandbox & Warmup" ), Range( -300f, 300f ), Title( "X Offset" )]
	public float SandboxLabelOffsetX { get; set; }
	[Group( "Sandbox & Warmup" ), Range( -250f, 250f ), Title( "Y Offset" )]
	public float SandboxLabelOffsetY { get; set; }
	[Group( "Sandbox & Warmup" ), Range( 0f, 500f ), Title( "Minimum Width" )]
	public float SandboxLabelMinimumWidth { get; set; } = 260f;
	[Group( "Sandbox & Warmup" ), Range( 0f, 50f ), Title( "Padding" )]
	public float SandboxLabelPadding { get; set; } = 10f;
	[Group( "Sandbox & Warmup" ), Range( 0f, 1f ), Title( "Content Opacity" )]
	public float SandboxLabelOpacity { get; set; } = 1f;
	[Group( "Sandbox & Warmup" ), Title( "Content Alignment" )]
	public OMRTextHorizontalAlignment SandboxLabelAlignment { get; set; } = OMRTextHorizontalAlignment.Left;
	[Group( "Sandbox & Warmup" ), Title( "Show Background / Border" )]
	public bool ShowSandboxLabelChrome { get; set; } = true;
	[Group( "Sandbox & Warmup" ), Range( 0f, 1f ), ShowIf( nameof( ShowSandboxLabelChrome ), true ), Title( "Chrome Opacity" )]
	public float SandboxLabelChromeOpacity { get; set; } = 1f;
	[Group( "Sandbox & Warmup" ), Title( "Show Status" )]
	public bool ShowSandboxStatus { get; set; } = true;
	[Group( "Sandbox & Warmup" ), Title( "Show Hint" )]
	public bool ShowSandboxHint { get; set; } = true;
	[Group( "Sandbox & Warmup" ), Range( 10f, 60f ), Title( "Title Size" )]
	public float SandboxTitleSize { get; set; } = 20f;
	[Group( "Sandbox & Warmup" ), Range( 7f, 30f ), Title( "Status Size" )]
	public float SandboxStatusSize { get; set; } = 10f;
	[Group( "Sandbox & Warmup" ), Range( 7f, 30f ), Title( "Hint Size" )]
	public float SandboxHintSize { get; set; } = 12f;
	[Group( "Sandbox & Warmup" ), Range( 0f, 30f ), Title( "Element Gap" )]
	public float SandboxElementGap { get; set; } = 3f;

	public int ContentHash()
	{
		HashCode hash = new();

		hash.Add( ShowLocalHealth ); hash.Add( ShowOpponentHealth ); hash.Add( ShowHealthLabels ); hash.Add( ShowHealthNumbers ); hash.Add( ShowHealthBars );
		hash.Add( HealthTop ); hash.Add( HealthHorizontalInset ); hash.Add( HealthSideWidthPercent ); hash.Add( HealthMaximumWidth );
		hash.Add( HealthHeadingBottomSpacing ); hash.Add( HealthHeadingGap ); hash.Add( HealthLabelSize ); hash.Add( HealthNumberSize );
		hash.Add( LocalHealthHeadingGap ); hash.Add( OpponentHealthHeadingGap );
		hash.Add( HealthBarHeight ); hash.Add( HealthBarCornerRadius ); hash.Add( ShowHealthTrackChrome ); hash.Add( HealthTrackChromeOpacity ); hash.Add( HealthBarOpacity );
		hash.Add( LocalHealthOffsetX ); hash.Add( LocalHealthOffsetY ); hash.Add( LocalHealthOpacity ); hash.Add( LocalHealthHeadingAlignment ); hash.Add( LocalHealthFillDirection );
		hash.Add( OpponentHealthOffsetX ); hash.Add( OpponentHealthOffsetY ); hash.Add( OpponentHealthOpacity ); hash.Add( OpponentHealthHeadingAlignment ); hash.Add( OpponentHealthFillDirection );

		hash.Add( ShowMatchHeader ); hash.Add( MatchHeaderTop ); hash.Add( MatchHeaderHorizontalInset ); hash.Add( MatchHeaderOffsetX ); hash.Add( MatchHeaderOffsetY );
		hash.Add( MatchHeaderOpacity ); hash.Add( MatchHeaderAlignment ); hash.Add( MatchHeaderElementGap );
		hash.Add( ShowRoundTitle ); hash.Add( RoundTitleSize ); hash.Add( RoundTitleOpacity ); hash.Add( RoundTitleOffsetY );
		hash.Add( ShowTimer ); hash.Add( TimerSize ); hash.Add( SuddenDeathTimerSize ); hash.Add( TimerOffsetY ); hash.Add( TimerMinimumWidth ); hash.Add( TimerPaddingX ); hash.Add( TimerPaddingY );
		hash.Add( ShowTimerChrome ); hash.Add( TimerChromeOpacity ); hash.Add( ShowTimerMode ); hash.Add( ShowTimerNote ); hash.Add( TimerMetaSize );
		hash.Add( ShowScore ); hash.Add( ShowScoreHeading ); hash.Add( ShowScorePlayerLabels ); hash.Add( ShowScoreSeparator ); hash.Add( ScoreNumberSize );
		hash.Add( ScoreLabelSize ); hash.Add( ScoreHeadingSize ); hash.Add( ScoreSeparatorSize ); hash.Add( ScoreColumnGap ); hash.Add( ScoreOffsetY );
		hash.Add( ShowPot ); hash.Add( ShowPotChrome ); hash.Add( PotChromeOpacity ); hash.Add( PotPaddingX ); hash.Add( PotPaddingY ); hash.Add( PotGap );
		hash.Add( PotOwnerSize ); hash.Add( PotValueSize ); hash.Add( PotOpacity ); hash.Add( ShowModifierLabel ); hash.Add( ModifierLabelSize ); hash.Add( ModifierLabelOpacity );

		hash.Add( ShowCenterMessage ); hash.Add( CenterMessageTopPercent ); hash.Add( CenterMessageOffsetX ); hash.Add( CenterMessageOffsetY );
		hash.Add( CenterMessageOpacity ); hash.Add( CenterMessageSize ); hash.Add( CenterMessageAlignment ); hash.Add( CenterMessageIndent );

		hash.Add( ShowDecisionOverlay ); hash.Add( DecisionScreenOpacity ); hash.Add( ShowDecisionChrome ); hash.Add( DecisionChromeOpacity );
		hash.Add( DecisionPanelWidth ); hash.Add( DecisionPanelPadding ); hash.Add( DecisionOffsetX ); hash.Add( DecisionOffsetY ); hash.Add( DecisionContentAlignment ); hash.Add( DecisionTextIndent );
		hash.Add( ShowDecisionEyebrow ); hash.Add( ShowDecisionPot ); hash.Add( ShowDecisionTimer ); hash.Add( DecisionEyebrowSize ); hash.Add( DecisionTitleSize ); hash.Add( DecisionPotSize ); hash.Add( DecisionTimerSize ); hash.Add( ShowDecisionFinalScore ); hash.Add( DecisionFinalScoreSize );
		hash.Add( ShowDecisionButtonChrome ); hash.Add( ShowDecisionButtonDescriptions ); hash.Add( DecisionButtonHeight ); hash.Add( DecisionButtonGap ); hash.Add( DecisionButtonPadding );
		hash.Add( DecisionButtonHoverScalePercent ); hash.Add( DecisionButtonHoverOffsetX ); hash.Add( DecisionButtonHoverOffsetY ); hash.Add( DecisionButtonPressedScalePercent );
		hash.Add( DecisionButtonTitleSize ); hash.Add( DecisionButtonDescriptionSize ); hash.Add( DecisionButtonTitleAlignment ); hash.Add( DecisionButtonTitleIndent );
		hash.Add( DecisionButtonDescriptionAlignment ); hash.Add( DecisionButtonDescriptionIndent );

		hash.Add( ShowAmmo ); hash.Add( AmmoAnchor ); hash.Add( AmmoRight ); hash.Add( AmmoBottom ); hash.Add( AmmoOffsetX ); hash.Add( AmmoOffsetY ); hash.Add( AmmoMinimumWidth ); hash.Add( AmmoPadding );
		hash.Add( AmmoOpacity ); hash.Add( AmmoAlignment ); hash.Add( ShowAmmoChrome ); hash.Add( AmmoChromeOpacity ); hash.Add( ShowAmmoWeaponName ); hash.Add( ShowAmmoReloadState ); hash.Add( ShowAmmoDivider );
		hash.Add( AmmoCurrentSize ); hash.Add( AmmoCapacitySize ); hash.Add( AmmoWeaponSize ); hash.Add( AmmoReloadSize ); hash.Add( AmmoDividerSize ); hash.Add( AmmoElementGap ); hash.Add( AmmoCountGap );

		hash.Add( ShowSandboxLabel ); hash.Add( SandboxLabelAnchor ); hash.Add( SandboxLabelLeft ); hash.Add( SandboxLabelTop ); hash.Add( SandboxLabelOffsetX ); hash.Add( SandboxLabelOffsetY );
		hash.Add( SandboxLabelMinimumWidth ); hash.Add( SandboxLabelPadding ); hash.Add( SandboxLabelOpacity ); hash.Add( SandboxLabelAlignment ); hash.Add( ShowSandboxLabelChrome ); hash.Add( SandboxLabelChromeOpacity );
		hash.Add( ShowSandboxStatus ); hash.Add( ShowSandboxHint ); hash.Add( SandboxTitleSize ); hash.Add( SandboxStatusSize ); hash.Add( SandboxHintSize ); hash.Add( SandboxElementGap );

		return hash.ToHashCode();
	}
}