Park/Goals/BaseGoal.cs
using HC3;

/// <summary>
/// A goal that belongs to a <see cref="GoalGroup"/> -- think of this like a Quest, or a Task.
/// </summary>
public partial class Goal : Component, IStatEvents
{
	private static Dictionary<Building, Goal> unlockedBuildingCache = new();

	[Property] public string GoalName { get; set; } = "DefaultGoal";
	[Property, TextArea] public string GoalDescription { get; set; } = "Default goal description";
	[Property, TextArea] public string GoalReward { get; set; } = "Default goal reward";
	[Property, IconName] public string Icon { get; set; }

	[Property, FeatureEnabled( "Stat" )] public bool IsStat { get; set; }
	[Property, Feature( "Stat" )] public string StatName { get; set; } = "StatName";
	[Property, Feature( "Stat" )] public float StatNeededToComplete { get; set; } = 100;

	/// <summary>
	/// A list of buildings to unlock when this goal is reached.
	/// </summary>
	[Property, Feature( "Buildings" ), Title( "Unlock List" )] public List<GameObject> Buildings { get; set; } = new();

	bool IsCompleted { get; set; } = false;

	/// <summary>
	/// Get a goal required to spawn a building
	/// </summary>
	/// <param name="building"></param>
	/// <returns></returns>
	public static Goal GetRequiredForBuilding( Building building )
	{
		if ( unlockedBuildingCache.TryGetValue( building, out var goal ) )
		{
			return goal;
		}
		return null;
	}

	protected override void OnAwake()
	{
		foreach ( var building in Buildings )
		{
			if ( building.IsValid() )
			{
				var component = building.GetComponent<Building>();
				if ( component.IsValid() )
				{
					unlockedBuildingCache[component] = this;
				}
			}
		}
	}

	/// <summary>
	/// Set this goal as completed.
	/// </summary>
	public void Complete()
	{
		IsCompleted = true;

		NotificationPanel.Instance?.Broadcast( $"Goal '{GoalName}' completed!", "emoji_events" );

		GiveReward();
	}

	/// <summary>
	/// Get the stat value for this goal.
	/// </summary>
	public float GetStat()
	{
		return Stats.Get( StatName );
	}

	/// <summary>
	/// Check if this goal is completed.
	/// </summary>
	public bool IsUnlocked()
	{
		if ( GoalGroup.UnlockAll ) return true;
		if ( IsCompleted ) return true;

		if ( IsStat )
		{
			return Stats.Get( StatName ) >= StatNeededToComplete;
		}

		return false;
	}

	public void OnStatChanged( string identifier, float value )
	{
		if ( IsCompleted ) return;

		if ( IsUnlocked() )
		{
			Complete();
		}
	}

	void IStatEvents.OnStatChanged( string identifier, float value )
	{
		OnStatChanged( identifier, value );
	}

	protected virtual void GiveReward()
	{
		// Give moneny or something.
	}
}