Park/Decoration/Decoration.cs
using HC3;
using HC3.UI;

public sealed partial class Decoration : Component, ISceneMetadata, IInspectable, IPlacementObject, IPathBlocker
{
	[RequireComponent]
	public GridObject GridObject { get; private set; }

	/// <summary>
	/// Cost to place this in the world.
	/// </summary>
	[Property, Feature( "Building" ), Group( "Display" )] public int Cost { get; set; } = 15;

	/// <summary>
	/// The building's name
	/// </summary>
	[Property, Feature( "Building" ), Group( "Display" )] public string Title { get; set; } = "Building";
	[Property, Feature( "Building" ), Group( "Display" ), ImageAssetPath] public string Thumbnail { get; set; } = "textures/thumbnails/missing_thumbnail.png";
	[Property, Feature( "Building" ), Group( "Display" )] public bool SnapToGrid { get; set; } = false;
	[Property, Feature( "Building" ), Group( "Display" )] public DecorationType Type { get; set; } = DecorationType.Prop;
	[Property, Feature( "Building" ), Group( "Display" )] public ObjectMaterial ObjectMaterial { get; set; } = ObjectMaterial.Default;
	[Property, Feature( "Building" ), Group( "Display" )] public int Weight { get; set; } = 0;

	/// <summary>
	/// Which directions should block paths through this decoration
	/// </summary>
	[Property, Feature( "Masks" )]
	public PathMask BlockedDirections { get; set; } = 0;

	/// <summary>
	/// Which directions should block AI navigation (for one-way paths etc)
	/// </summary>
	[Property, Feature( "Masks" )]
	public PathMask BlockedNavigation { get; set; } = 0;

	public Group Group => new( "Decoration", Type.ToString() );

	public TintChannelData RedChannel { get; set; } = new( true, Color.White );
	public TintChannelData GreenChannel { get; set; } = new( false, Color.White );
	public TintChannelData BlueChannel { get; set; } = new( false, Color.White );
	public TintChannelData AlphaChannel { get; set; } = new( false, Color.White );

	/// <summary>
	/// A list of thematic tags
	/// </summary>
	[Property]
	public List<string> ThemeTags { get; set; }

	IEnumerable<string> IPlacementObject.GetTags()
	{
		return ThemeTags;
	}

	public static IEnumerable<GameObject> AllPrefabs
	{
		get
		{
			return ResourceLibrary.GetAll<PrefabFile>()
				.Where( x => x.GetMetadata( "Type", "" ).Equals( "Decoration" ) )
				.ToList()
				.Select( x => GameObject.GetPrefab( x.ResourcePath ) );
		}
	}

	Dictionary<string, string> ISceneMetadata.GetMetadata()
	{
		return new()
		{
			{ "Type", "Decoration" }
		};
	}

	public TintMaskComponent GetTintComponent()
	{
		return GetComponentsInChildren<TintMaskComponent>().FirstOrDefault();
	}

	protected override void OnValidate()
	{
		GridObject ??= GetOrAddComponent<GridObject>( false );
		GridObject.Enabled = false;
	}

	/// <summary>
	/// Check whether or not this building has been placed or is currently being placed.
	/// </summary>
	public bool IsPlaced
	{
		get => _isPlaced;
		set
		{
			_isPlaced = value;

			GridObject.Enabled = value;

			if ( value )
			{
				ActiveDecorations.Add( this );
			}
			else
			{
				ActiveDecorations.Remove( this );
			}
		}
	}

	private bool _isPlaced = false;

	/// <summary>
	/// Right now just a collection of all enabled buildings, we can later what active means, have separate sets for open/closed buildings, etc.
	/// </summary>
	public static HashSet<Decoration> ActiveDecorations = new();

	protected override void OnEnabled()
	{
		base.OnEnabled();
		if ( IsPlaced ) ActiveDecorations.Add( this );
	}

	protected override void OnDisabled()
	{
		base.OnDisabled();
		if ( IsPlaced ) ActiveDecorations.Remove( this );
	}

	protected override void OnStart()
	{
		base.OnStart();

		UpdateColors();
	}

	Window IInspectable.Select()
	{
		var building = this;

		var inspector = new DecorationInspector();
		inspector.Building = building;

		return inspector;
	}

	void ITintMaskEvents.OnColorsChanged( TintMaskComponent component )
	{
		var tintMask = GetTintComponent();

		if ( tintMask != component )
			return;

		var red = RedChannel;
		red.Color = component.PrimaryColor;
		RedChannel = red;

		var green = GreenChannel;
		green.Color = component.SecondaryColor;
		GreenChannel = green;

		var blue = BlueChannel;
		blue.Color = component.AccentColor;
		BlueChannel = blue;

		var alpha = AlphaChannel;
		alpha.Color = component.DetailColor;
		AlphaChannel = alpha;
	}

	void UpdateColors()
	{
		if ( !Networking.IsHost )
			return;

		var tintMask = GetTintComponent();

		if ( tintMask is null )
			return;

		tintMask.PrimaryColor = RedChannel.Color;
		tintMask.SecondaryColor = GreenChannel.Color;
		tintMask.AccentColor = BlueChannel.Color;
		tintMask.DetailColor = AlphaChannel.Color;
	}
}
public enum DecorationType
{
	Nature,
	Building,
	Fence,
	Prop,
	Sign,
	Light,
	Effect
}
public struct TintChannelData
{
	public bool Enabled { get; set; }
	public Color Color { get; set; }

	public TintChannelData( bool enabled, Color color )
	{
		Enabled = enabled;
		Color = color;
	}
}