StaffManager.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. public class StaffManager : MonoBehaviour
  5. {
  6. public static StaffManager Instance { get; private set; }
  7. [Header("Staff Management")]
  8. [SerializeField] private List<Staff> allStaff = new List<Staff>();
  9. [SerializeField] private int maxStaffCount = 50;
  10. [Header("Hiring Settings")]
  11. [SerializeField] private float baseHiringCost = 1000f;
  12. [SerializeField] private float dailyWageCost = 100f;
  13. [Header("Staff Prefabs")]
  14. [SerializeField] private GameObject receptionistPrefab;
  15. [SerializeField] private GameObject housekeeperPrefab;
  16. [SerializeField] private GameObject chefPrefab;
  17. [SerializeField] private GameObject janitorPrefab;
  18. [SerializeField] private GameObject bellhopPrefab;
  19. [SerializeField] private GameObject valetPrefab;
  20. [SerializeField] private GameObject defaultStaffPrefab;
  21. [Header("Spawn Points")]
  22. [SerializeField] private Transform staffSpawnPoint;
  23. private Queue<StaffTask> pendingTasks = new Queue<StaffTask>();
  24. private Dictionary<Staff.StaffType, List<Staff>> staffByType = new Dictionary<Staff.StaffType, List<Staff>>();
  25. // Events
  26. public System.Action<Staff> OnStaffHired;
  27. public System.Action<Staff> OnStaffFired;
  28. public System.Action<StaffTask> OnTaskCreated;
  29. public System.Action<StaffTask> OnTaskCompleted;
  30. private void Awake()
  31. {
  32. if (Instance != null && Instance != this)
  33. {
  34. Destroy(this.gameObject);
  35. return;
  36. }
  37. Instance = this;
  38. }
  39. private void Start()
  40. {
  41. InitializeStaffCategories();
  42. SubscribeToEvents();
  43. SetupSpawnPoint();
  44. // Hire initial staff
  45. HireInitialStaff();
  46. }
  47. private void OnDestroy()
  48. {
  49. UnsubscribeFromEvents();
  50. }
  51. private void Update()
  52. {
  53. ProcessPendingTasks();
  54. AutoAssignTasks();
  55. }
  56. #region Initialization
  57. private void InitializeStaffCategories()
  58. {
  59. foreach (Staff.StaffType staffType in System.Enum.GetValues(typeof(Staff.StaffType)))
  60. {
  61. staffByType[staffType] = new List<Staff>();
  62. }
  63. }
  64. private void SubscribeToEvents()
  65. {
  66. Staff.OnStaffHired += OnStaffHiredHandler;
  67. Staff.OnStaffCompleteTask += OnStaffCompleteTaskHandler;
  68. Staff.OnTaskAssigned += OnTaskAssignedHandler;
  69. // Subscribe to guest events to create tasks
  70. if (HotelManager.Instance != null)
  71. {
  72. HotelManager.Instance.OnGuestArrived += OnGuestArrived;
  73. }
  74. }
  75. private void UnsubscribeFromEvents()
  76. {
  77. Staff.OnStaffHired -= OnStaffHiredHandler;
  78. Staff.OnStaffCompleteTask -= OnStaffCompleteTaskHandler;
  79. Staff.OnTaskAssigned -= OnTaskAssignedHandler;
  80. if (HotelManager.Instance != null)
  81. {
  82. HotelManager.Instance.OnGuestArrived -= OnGuestArrived;
  83. }
  84. }
  85. private void SetupSpawnPoint()
  86. {
  87. if (staffSpawnPoint == null)
  88. {
  89. GameObject spawn = new GameObject("Staff Spawn Point");
  90. spawn.transform.position = new Vector3(-5f, 0f, -5f);
  91. staffSpawnPoint = spawn.transform;
  92. spawn.transform.SetParent(this.transform);
  93. }
  94. }
  95. private void HireInitialStaff()
  96. {
  97. // Hire minimum required staff
  98. HireStaff(Staff.StaffType.Receptionist);
  99. HireStaff(Staff.StaffType.Housekeeper);
  100. }
  101. #endregion
  102. #region Staff Hiring and Management
  103. public bool HireStaff(Staff.StaffType staffType)
  104. {
  105. if (allStaff.Count >= maxStaffCount)
  106. {
  107. Debug.LogWarning("Cannot hire more staff: Maximum capacity reached");
  108. return false;
  109. }
  110. GameObject staffPrefab = GetStaffPrefab(staffType);
  111. if (staffPrefab == null)
  112. {
  113. Debug.LogWarning($"No prefab available for staff type: {staffType}");
  114. return false;
  115. }
  116. // Spawn staff member
  117. Vector3 spawnPosition = staffSpawnPoint != null ? staffSpawnPoint.position : Vector3.zero;
  118. GameObject staffObj = Instantiate(staffPrefab, spawnPosition, Quaternion.identity);
  119. Staff staffComponent = staffObj.GetComponent<Staff>();
  120. if (staffComponent == null)
  121. {
  122. staffComponent = staffObj.AddComponent<Staff>();
  123. }
  124. // Set staff properties
  125. ConfigureNewStaff(staffComponent, staffType);
  126. Debug.Log($"Hired new {staffType}: {staffComponent.StaffName}");
  127. return true;
  128. }
  129. private GameObject GetStaffPrefab(Staff.StaffType staffType)
  130. {
  131. switch (staffType)
  132. {
  133. case Staff.StaffType.Receptionist:
  134. return receptionistPrefab ?? CreateDefaultStaffPrefab("Receptionist", Color.blue);
  135. case Staff.StaffType.Housekeeper:
  136. return housekeeperPrefab ?? CreateDefaultStaffPrefab("Housekeeper", Color.green);
  137. case Staff.StaffType.Chef:
  138. return chefPrefab ?? CreateDefaultStaffPrefab("Chef", Color.red);
  139. case Staff.StaffType.Janitor:
  140. return janitorPrefab ?? CreateDefaultStaffPrefab("Janitor", Color.yellow);
  141. case Staff.StaffType.Bellhop:
  142. return bellhopPrefab ?? CreateDefaultStaffPrefab("Bellhop", Color.cyan);
  143. case Staff.StaffType.Valet:
  144. return valetPrefab ?? CreateDefaultStaffPrefab("Valet", Color.magenta);
  145. default:
  146. return defaultStaffPrefab ?? CreateDefaultStaffPrefab("Staff", Color.gray);
  147. }
  148. }
  149. private GameObject CreateDefaultStaffPrefab(string name, Color color)
  150. {
  151. GameObject staff = GameObject.CreatePrimitive(PrimitiveType.Capsule);
  152. staff.name = name;
  153. // Add NavMeshAgent with error handling
  154. try
  155. {
  156. var agent = staff.AddComponent<UnityEngine.AI.NavMeshAgent>();
  157. agent.enabled = false; // Disable until NavMesh is available
  158. }
  159. catch (System.Exception e)
  160. {
  161. Debug.LogWarning($"Failed to add NavMeshAgent to {name}: {e.Message}. NavMesh needs to be baked first.");
  162. }
  163. // Set color
  164. staff.GetComponent<Renderer>().material.color = color;
  165. // Add Staff script will be added by the HireStaff method
  166. return staff;
  167. }
  168. private void ConfigureNewStaff(Staff staff, Staff.StaffType staffType)
  169. {
  170. // Set random efficiency
  171. float efficiency = UnityEngine.Random.Range(0.6f, 1f);
  172. // Configure based on type (using reflection or direct property access)
  173. staff.name = $"{staffType}_{allStaff.Count + 1}";
  174. }
  175. public bool FireStaff(Staff staff)
  176. {
  177. if (staff == null || !allStaff.Contains(staff))
  178. {
  179. return false;
  180. }
  181. // Clear all tasks
  182. staff.ClearAllTasks();
  183. // Remove from lists
  184. allStaff.Remove(staff);
  185. foreach (var staffList in staffByType.Values)
  186. {
  187. staffList.Remove(staff);
  188. }
  189. OnStaffFired?.Invoke(staff);
  190. // Destroy the game object
  191. Destroy(staff.gameObject);
  192. Debug.Log($"Fired staff member: {staff.StaffName}");
  193. return true;
  194. }
  195. public List<Staff> GetStaffByType(Staff.StaffType staffType)
  196. {
  197. return staffByType.ContainsKey(staffType) ? new List<Staff>(staffByType[staffType]) : new List<Staff>();
  198. }
  199. public List<Staff> GetAvailableStaff(Staff.StaffType staffType)
  200. {
  201. return GetStaffByType(staffType).Where(s => s.IsAvailable).ToList();
  202. }
  203. public List<Staff> GetAllStaff()
  204. {
  205. return new List<Staff>(allStaff);
  206. }
  207. public int GetStaffCount(Staff.StaffType staffType)
  208. {
  209. return GetStaffByType(staffType).Count;
  210. }
  211. #endregion
  212. #region Task Management
  213. public void CreateTask(TaskType taskType, Vector3 location, GameObject target = null, Room targetRoom = null, int priority = 1)
  214. {
  215. StaffTask task = new StaffTask
  216. {
  217. taskType = taskType,
  218. location = location,
  219. targetGuest = target,
  220. targetRoom = targetRoom,
  221. priority = priority,
  222. duration = GetTaskDuration(taskType),
  223. isUrgent = priority >= 8,
  224. description = GetTaskDescription(taskType)
  225. };
  226. pendingTasks.Enqueue(task);
  227. OnTaskCreated?.Invoke(task);
  228. Debug.Log($"Created task: {taskType} at {location}");
  229. }
  230. private void ProcessPendingTasks()
  231. {
  232. while (pendingTasks.Count > 0)
  233. {
  234. StaffTask task = pendingTasks.Dequeue();
  235. if (AssignTaskToAvailableStaff(task))
  236. {
  237. Debug.Log($"Task assigned: {task.taskType}");
  238. }
  239. else
  240. {
  241. // Re-queue task if no available staff
  242. pendingTasks.Enqueue(task);
  243. break; // Avoid infinite loop
  244. }
  245. }
  246. }
  247. private bool AssignTaskToAvailableStaff(StaffTask task)
  248. {
  249. // Find staff members who can handle this task
  250. List<Staff> capableStaff = allStaff.Where(s => s.CanHandleTask(task.taskType)).ToList();
  251. if (capableStaff.Count == 0)
  252. {
  253. Debug.LogWarning($"No staff available to handle task: {task.taskType}");
  254. return false;
  255. }
  256. // Find the best available staff member
  257. Staff bestStaff = capableStaff
  258. .Where(s => s.IsAvailable || s.TaskCount < 3) // Allow up to 3 queued tasks
  259. .OrderBy(s => s.TaskCount)
  260. .ThenByDescending(s => s.Efficiency)
  261. .FirstOrDefault();
  262. if (bestStaff == null)
  263. {
  264. return false; // No available staff
  265. }
  266. bestStaff.AssignTask(task);
  267. return true;
  268. }
  269. private void AutoAssignTasks()
  270. {
  271. // Automatically create tasks based on hotel needs
  272. // Check if guests need check-in assistance
  273. if (HotelManager.Instance != null)
  274. {
  275. var waitingGuests = HotelManager.Instance.GetWaitingGuests();
  276. foreach (var guest in waitingGuests)
  277. {
  278. if (guest.CurrentState == Guest.GuestState.LookingForReception)
  279. {
  280. CreateTask(TaskType.CheckInGuest, guest.transform.position, guest.gameObject, null, 7);
  281. }
  282. }
  283. }
  284. // Create random maintenance tasks
  285. if (UnityEngine.Random.value < 0.001f) // Very low chance per frame
  286. {
  287. CreateRandomMaintenanceTask();
  288. }
  289. }
  290. private void CreateRandomMaintenanceTask()
  291. {
  292. TaskType[] maintenanceTasks = { TaskType.CleanArea, TaskType.Maintenance };
  293. TaskType randomTask = maintenanceTasks[UnityEngine.Random.Range(0, maintenanceTasks.Length)];
  294. Vector3 randomLocation = new Vector3(
  295. UnityEngine.Random.Range(-10f, 10f),
  296. 0f,
  297. UnityEngine.Random.Range(-10f, 10f)
  298. );
  299. CreateTask(randomTask, randomLocation, null, null, 3);
  300. }
  301. private float GetTaskDuration(TaskType taskType)
  302. {
  303. switch (taskType)
  304. {
  305. case TaskType.CheckInGuest: return 15f;
  306. case TaskType.CleanRoom: return 30f;
  307. case TaskType.DeliverService: return 10f;
  308. case TaskType.PrepareFood: return 20f;
  309. case TaskType.AnswerPhone: return 5f;
  310. case TaskType.CarryLuggage: return 8f;
  311. case TaskType.ParkCar: return 12f;
  312. case TaskType.CleanArea: return 25f;
  313. case TaskType.Maintenance: return 45f;
  314. case TaskType.LaundryService: return 60f;
  315. default: return 15f;
  316. }
  317. }
  318. private string GetTaskDescription(TaskType taskType)
  319. {
  320. switch (taskType)
  321. {
  322. case TaskType.CheckInGuest: return "Check in arriving guest";
  323. case TaskType.CleanRoom: return "Clean and prepare guest room";
  324. case TaskType.DeliverService: return "Deliver room service";
  325. case TaskType.PrepareFood: return "Prepare meal in kitchen";
  326. case TaskType.AnswerPhone: return "Answer reception phone";
  327. case TaskType.CarryLuggage: return "Assist guest with luggage";
  328. case TaskType.ParkCar: return "Park guest's vehicle";
  329. case TaskType.CleanArea: return "Clean common area";
  330. case TaskType.Maintenance: return "Perform maintenance work";
  331. case TaskType.LaundryService: return "Handle laundry service";
  332. default: return "General hotel task";
  333. }
  334. }
  335. #endregion
  336. #region Event Handlers
  337. private void OnStaffHiredHandler(Staff staff)
  338. {
  339. if (!allStaff.Contains(staff))
  340. {
  341. allStaff.Add(staff);
  342. if (staffByType.ContainsKey(staff.Type))
  343. {
  344. staffByType[staff.Type].Add(staff);
  345. }
  346. }
  347. OnStaffHired?.Invoke(staff);
  348. }
  349. private void OnStaffCompleteTaskHandler(Staff staff)
  350. {
  351. // Handle task completion effects
  352. Debug.Log($"{staff.StaffName} completed a task");
  353. }
  354. private void OnTaskAssignedHandler(Staff staff, StaffTask task)
  355. {
  356. Debug.Log($"Task {task.taskType} assigned to {staff.StaffName}");
  357. }
  358. private void OnGuestArrived(Guest guest)
  359. {
  360. // Create check-in task for arriving guests
  361. CreateTask(TaskType.CheckInGuest, guest.transform.position, guest.gameObject, null, 6);
  362. }
  363. #endregion
  364. #region Statistics and Information
  365. public int GetTotalStaffCount()
  366. {
  367. return allStaff.Count;
  368. }
  369. public float GetTotalDailyWages()
  370. {
  371. return allStaff.Count * dailyWageCost;
  372. }
  373. public Dictionary<Staff.StaffType, int> GetStaffCountByType()
  374. {
  375. Dictionary<Staff.StaffType, int> counts = new Dictionary<Staff.StaffType, int>();
  376. foreach (var kvp in staffByType)
  377. {
  378. counts[kvp.Key] = kvp.Value.Count;
  379. }
  380. return counts;
  381. }
  382. public List<Staff> GetBusiestStaff()
  383. {
  384. return allStaff.OrderByDescending(s => s.TaskCount).Take(5).ToList();
  385. }
  386. public float GetAverageStaffEfficiency()
  387. {
  388. if (allStaff.Count == 0) return 0f;
  389. return allStaff.Average(s => s.Efficiency);
  390. }
  391. #endregion
  392. #region Debug and Visualization
  393. private void OnDrawGizmosSelected()
  394. {
  395. if (staffSpawnPoint != null)
  396. {
  397. Gizmos.color = Color.yellow;
  398. Gizmos.DrawWireSphere(staffSpawnPoint.position, 1f);
  399. Gizmos.DrawWireCube(staffSpawnPoint.position + Vector3.up, Vector3.one * 0.8f);
  400. }
  401. }
  402. #endregion
  403. }