AI/Staff/StaffManager.cs
using Sandbox.Diagnostics;
using System;

namespace HC3;

public partial class StaffManager : Component, IParkEvents
{
	/// <summary>
	/// Singleton
	/// </summary>
	public static StaffManager Instance { get; private set; }

	/// <summary>
	/// All staff members currently employed and working in the park.
	/// </summary>
	public IEnumerable<Staff> StaffList => _staff;
	private IEnumerable<Staff> _staff => Scene.GetAll<Staff>();

	/// <summary>
	/// How many staff are currently in the park?
	/// </summary>
	public int StaffCount => _staff.Count();

	/// <summary>
	/// A quick reference to the <see cref="ParkManager"/>
	/// </summary>
	public ParkManager ParkManager => ParkManager.Instance;

	/// <summary>
	/// The park entrance, just find one in the scene
	/// </summary>
	private ParkEntrance _cachedEntrance;
	private ParkEntrance Entrance => _cachedEntrance ??= Scene.GetAll<ParkEntrance>().FirstOrDefault();

	/// <summary>
	/// Get existing staff of type
	/// </summary>
	/// <param name="def"></param>
	/// <returns></returns>
	public IEnumerable<Staff> OfType( StaffDefinition def )
	{
		return _staff.Where( x => x.Definition == def );
	}

	public Staff CreateStaffMember( StaffDefinition definition, Action<Staff> onInit = null )
	{
		var target = Scene.GetAll<SpawnPoint>().OrderBy( x => Random.Shared.Next() ).FirstOrDefault()?.GameObject.WorldPosition ?? Entrance.WorldPosition;

		var staffObject = definition.Prefab?.Clone( new CloneConfig()
		{
			Transform = new Transform( target, Entrance.WorldRotation, 1f ),
			StartEnabled = false
		} );

		Assert.True( staffObject.IsValid(), "Staff prefab not hooked up to StaffManager" );

		var staff = staffObject.GetComponent<Staff>( true );

		Assert.True( staff.IsValid(), "Staff prefab does not have a Staff component" );

		staffObject.Enabled = true;

		onInit?.Invoke( staff );

		staffObject.NetworkSpawn();

		return staff;
	}

	[Rpc.Host]
	public void Hire( StaffDefinition definition )
	{
		if ( !ParkManager.Instance.TakeMoney( definition.HireCost, "Staff Wages" ) )
			return;

		var isParkOpen = ParkManager.IsOpen();

		var staff = CreateStaffMember( definition, x =>
		{
			x.SetWorkingState( isParkOpen );
		} );

		SelectionSystem.Instance.Select( staff.GameObject );
		CameraPanning.Instance.Follow( staff.GameObject );

		NotificationPanel.Instance?.Broadcast( $"You hired a {definition.DisplayName}!" );

		Stats.Increment( "staff.hired" );
		Stats.Increment( $"staff.hired.{definition.DisplayName.ToIdentifier()}" );

		// Conna: we probably want some feedback here. Do we play a money sound to the user?
		// Also, should we limit how many staff of each type can exist at once?
	}

	protected override void OnStart()
	{
		Instance = this;
	}

	void IParkEvents.OnParkOpened()
	{
		foreach ( var staff in _staff )
		{
			staff.SetWorkingState( true );
		}
	}
}