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

// TODO: Move Decoration to namespace

partial class Decoration : IPrefabSourceComponent<Decoration.SaveData>, ITintMaskEvents, IPlacementObject
{
	/// <summary>
	/// Hack so we don't forget which prefab this came from when it spawns.
	/// </summary>
	[Property]
	public string PrefabSource { get; private set; }

	public sealed record SaveData(
		Vector3 Position,
		float Rotation,
		float Pitch,
		TintChannelData RedColor,
		TintChannelData GreenColor,
		TintChannelData BlueColor,
		TintChannelData AlphaColor );


	SaveData IPrefabSourceComponent<SaveData>.GetSaveData() => new(
		Position: GridManager.Instance.Terrain.WorldToGrid( WorldPosition ),
		Rotation: WorldRotation.Yaw(),
		Pitch: WorldRotation.Pitch(),
		RedColor: RedChannel,
		GreenColor: GreenChannel,
		BlueColor: BlueChannel,
		AlphaColor: AlphaChannel );

	void IPrefabSourceComponent<SaveData>.Spawn( SaveData saveData )
	{
		WorldPosition = GridManager.Instance.Terrain.GridToWorld( saveData.Position );
		WorldRotation = new Angles( saveData.Pitch, saveData.Rotation, 0f );

		IsPlaced = true;
		Tags.Add( "decoration" );
		Tags.Add( "selectable" );

		RedChannel = saveData.RedColor;
		GreenChannel = saveData.GreenColor;
		BlueChannel = saveData.BlueColor;
		AlphaChannel = saveData.AlphaColor;

		// Apply to tint component if it exists
		var tint = GetTintComponent();
		if ( tint.IsValid() )
		{
			tint.PrimaryColor = RedChannel.Color;
			tint.SecondaryColor = GreenChannel.Color;
			tint.AccentColor = BlueChannel.Color;
			tint.DetailColor = AlphaChannel.Color;
		}

		GameObject.Root.NetworkSpawn();
	}

	protected override void OnAwake()
	{
		if ( GameObject.IsPrefabInstanceRoot )
		{
			PrefabSource = GameObject.PrefabInstanceSource;
		}
	}
}

file sealed class DecorationsSaveData : SpawnedPrefabSaveData<Decoration, Decoration.SaveData>
{
	public override string PropertyName => "Decorations";
}