Guest.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. using UnityEngine;
  2. using UnityEngine.AI;
  3. using System.Collections.Generic;
  4. using System;
  5. [RequireComponent(typeof(NavMeshAgent))]
  6. public class Guest : MonoBehaviour
  7. {
  8. [Header("Guest Info")]
  9. [SerializeField] private string guestName;
  10. [SerializeField] private GuestType guestType = GuestType.Regular;
  11. [SerializeField] private int stayDuration = 3; // days
  12. [Header("Movement")]
  13. [SerializeField] private float walkSpeed = 3.5f;
  14. [SerializeField] private float runSpeed = 6f;
  15. [Header("Mood Visualization")]
  16. [SerializeField] private GameObject moodIcon;
  17. [SerializeField] private SpriteRenderer moodRenderer;
  18. [SerializeField] private Sprite[] moodSprites; // Happy, Neutral, Annoyed, Angry
  19. private NavMeshAgent agent;
  20. private GuestState currentState = GuestState.Arriving;
  21. private GuestMood currentMood = GuestMood.Neutral;
  22. private List<GuestWish> wishes = new List<GuestWish>();
  23. private List<string> moodReasons = new List<string>();
  24. private Room assignedRoom;
  25. private Vector3 spawnPosition;
  26. private float satisfactionLevel = 0.5f; // 0 to 1
  27. private float patience = 100f;
  28. private float maxPatience = 100f;
  29. // Navigation targets
  30. private Vector3 currentDestination;
  31. private bool hasDestination = false;
  32. // Events
  33. public static event Action<Guest> OnGuestArrived;
  34. public static event Action<Guest> OnGuestLeft;
  35. public static event Action<Guest> OnGuestAssignedRoom;
  36. public enum GuestState
  37. {
  38. Arriving,
  39. LookingForReception,
  40. CheckingIn,
  41. GoingToRoom,
  42. InRoom,
  43. Exploring,
  44. Leaving,
  45. Left
  46. }
  47. public enum GuestMood
  48. {
  49. Happy = 0,
  50. Neutral = 1,
  51. Annoyed = 2,
  52. Angry = 3
  53. }
  54. public enum GuestType
  55. {
  56. Regular,
  57. Business,
  58. Family,
  59. VIP,
  60. Celebrity
  61. }
  62. #region Properties
  63. public string GuestName
  64. {
  65. get => string.IsNullOrEmpty(guestName) ? GenerateRandomName() : guestName;
  66. set => guestName = value;
  67. }
  68. public GuestState CurrentState => currentState;
  69. public GuestMood CurrentMood => currentMood;
  70. public GuestType Type => guestType;
  71. public float SatisfactionLevel => satisfactionLevel;
  72. public float Patience => patience;
  73. public List<GuestWish> Wishes => wishes;
  74. public List<string> MoodReasons => moodReasons;
  75. public Room AssignedRoom => assignedRoom;
  76. #endregion
  77. private void Awake()
  78. {
  79. agent = GetComponent<NavMeshAgent>();
  80. agent.speed = walkSpeed;
  81. SetupMoodIcon();
  82. GenerateWishes();
  83. spawnPosition = transform.position;
  84. }
  85. private void Start()
  86. {
  87. SetState(GuestState.Arriving);
  88. OnGuestArrived?.Invoke(this);
  89. // Look for reception to check in
  90. FindAndGoToReception();
  91. }
  92. private void Update()
  93. {
  94. UpdatePatience();
  95. UpdateBehaviorBasedOnState();
  96. UpdateMoodDisplay();
  97. // Check if agent reached destination
  98. if (hasDestination && agent.enabled && !agent.pathPending && agent.remainingDistance < 0.5f)
  99. {
  100. OnReachedDestination();
  101. }
  102. }
  103. #region State Management
  104. private void SetState(GuestState newState)
  105. {
  106. currentState = newState;
  107. Debug.Log($"Guest {GuestName} state changed to: {newState}");
  108. switch (newState)
  109. {
  110. case GuestState.Arriving:
  111. AddMoodReason("Just arrived at the hotel");
  112. break;
  113. case GuestState.LookingForReception:
  114. AddMoodReason("Looking for reception desk");
  115. break;
  116. case GuestState.CheckingIn:
  117. AddMoodReason("Checking into the hotel");
  118. break;
  119. case GuestState.GoingToRoom:
  120. AddMoodReason("Heading to assigned room");
  121. break;
  122. case GuestState.InRoom:
  123. AddMoodReason("Settling into room");
  124. break;
  125. case GuestState.Exploring:
  126. AddMoodReason("Exploring hotel facilities");
  127. break;
  128. case GuestState.Leaving:
  129. AddMoodReason("Checking out and leaving");
  130. break;
  131. }
  132. }
  133. private void UpdateBehaviorBasedOnState()
  134. {
  135. switch (currentState)
  136. {
  137. case GuestState.LookingForReception:
  138. if (!hasDestination)
  139. {
  140. FindAndGoToReception();
  141. }
  142. break;
  143. case GuestState.CheckingIn:
  144. // Stay at reception for a moment
  145. if (Time.time % 5f < 0.1f) // Every 5 seconds, try to get a room
  146. {
  147. TryGetRoom();
  148. }
  149. break;
  150. case GuestState.InRoom:
  151. // Randomly decide to explore or stay
  152. if (UnityEngine.Random.value < 0.001f) // Small chance per frame
  153. {
  154. DecideNextActivity();
  155. }
  156. break;
  157. }
  158. }
  159. private void UpdatePatience()
  160. {
  161. if (currentState == GuestState.LookingForReception ||
  162. currentState == GuestState.CheckingIn)
  163. {
  164. patience -= Time.deltaTime * 2f; // Lose patience while waiting
  165. if (patience <= 0)
  166. {
  167. SetMood(GuestMood.Angry);
  168. AddMoodReason("Waited too long without service");
  169. LeaveHotel();
  170. }
  171. else if (patience < maxPatience * 0.3f)
  172. {
  173. SetMood(GuestMood.Annoyed);
  174. AddMoodReason("Taking too long to check in");
  175. }
  176. }
  177. }
  178. #endregion
  179. #region Navigation
  180. public bool MoveTo(Vector3 destination)
  181. {
  182. if (!agent.enabled) return false;
  183. agent.SetDestination(destination);
  184. currentDestination = destination;
  185. hasDestination = true;
  186. return true;
  187. }
  188. private void OnReachedDestination()
  189. {
  190. hasDestination = false;
  191. switch (currentState)
  192. {
  193. case GuestState.LookingForReception:
  194. SetState(GuestState.CheckingIn);
  195. break;
  196. case GuestState.GoingToRoom:
  197. SetState(GuestState.InRoom);
  198. AddMoodReason("Arrived at assigned room");
  199. break;
  200. }
  201. }
  202. private void FindAndGoToReception()
  203. {
  204. // Look for reception room or reception desk
  205. GameObject[] receptions = GameObject.FindGameObjectsWithTag("Reception");
  206. if (receptions.Length > 0)
  207. {
  208. MoveTo(receptions[0].transform.position);
  209. SetState(GuestState.LookingForReception);
  210. }
  211. else
  212. {
  213. // No reception found - get annoyed
  214. SetMood(GuestMood.Annoyed);
  215. AddMoodReason("Cannot find hotel reception");
  216. // Wander around looking for it
  217. WanderRandomly();
  218. }
  219. }
  220. private void WanderRandomly()
  221. {
  222. Vector3 randomDirection = UnityEngine.Random.insideUnitSphere * 10f;
  223. randomDirection += transform.position;
  224. randomDirection.y = transform.position.y;
  225. if (NavMesh.SamplePosition(randomDirection, out NavMeshHit hit, 10f, NavMesh.AllAreas))
  226. {
  227. MoveTo(hit.position);
  228. }
  229. }
  230. #endregion
  231. #region Room Management
  232. private void TryGetRoom()
  233. {
  234. // Try to get a room assignment from hotel manager
  235. Room availableRoom = HotelManager.Instance?.GetAvailableRoom(this);
  236. if (availableRoom != null)
  237. {
  238. AssignRoom(availableRoom);
  239. }
  240. else
  241. {
  242. // No rooms available
  243. SetMood(GuestMood.Annoyed);
  244. AddMoodReason("No rooms available");
  245. // Lose more patience
  246. patience -= 20f;
  247. if (patience <= 20f)
  248. {
  249. LeaveHotel();
  250. }
  251. }
  252. }
  253. public void AssignRoom(Room room)
  254. {
  255. assignedRoom = room;
  256. SetState(GuestState.GoingToRoom);
  257. // Calculate room satisfaction
  258. float roomSatisfaction = CalculateRoomSatisfaction(room);
  259. satisfactionLevel = Mathf.Clamp01(satisfactionLevel + roomSatisfaction - 0.5f);
  260. if (roomSatisfaction > 0.7f)
  261. {
  262. SetMood(GuestMood.Happy);
  263. AddMoodReason("Very satisfied with the room");
  264. }
  265. else if (roomSatisfaction < 0.3f)
  266. {
  267. SetMood(GuestMood.Annoyed);
  268. AddMoodReason("Room doesn't meet expectations");
  269. }
  270. // Go to room
  271. MoveTo(room.roomPoints[0]); // Go to room center
  272. OnGuestAssignedRoom?.Invoke(this);
  273. }
  274. private float CalculateRoomSatisfaction(Room room)
  275. {
  276. float satisfaction = 0.5f; // Base satisfaction
  277. // Check if room type matches guest preferences
  278. // This would be expanded based on guest type and wishes
  279. return satisfaction;
  280. }
  281. private void DecideNextActivity()
  282. {
  283. // Based on wishes, decide what to do next
  284. if (wishes.Count > 0)
  285. {
  286. GuestWish randomWish = wishes[UnityEngine.Random.Range(0, wishes.Count)];
  287. if (TryFulfillWish(randomWish))
  288. {
  289. SetState(GuestState.Exploring);
  290. }
  291. }
  292. else
  293. {
  294. // No specific wishes, just wander
  295. WanderRandomly();
  296. SetState(GuestState.Exploring);
  297. }
  298. }
  299. private bool TryFulfillWish(GuestWish wish)
  300. {
  301. // Look for facilities that can fulfill the wish
  302. // This would be expanded with actual facility finding logic
  303. return false;
  304. }
  305. #endregion
  306. #region Mood System
  307. private void SetMood(GuestMood mood)
  308. {
  309. currentMood = mood;
  310. UpdateMoodDisplay();
  311. }
  312. private void AddMoodReason(string reason)
  313. {
  314. moodReasons.Insert(0, $"{DateTime.Now:HH:mm} - {reason}");
  315. // Keep only recent reasons
  316. if (moodReasons.Count > 10)
  317. {
  318. moodReasons.RemoveAt(moodReasons.Count - 1);
  319. }
  320. }
  321. private void UpdateMoodDisplay()
  322. {
  323. if (moodRenderer != null && moodSprites != null && moodSprites.Length > (int)currentMood)
  324. {
  325. moodRenderer.sprite = moodSprites[(int)currentMood];
  326. // Show mood icon only if not neutral or happy
  327. moodIcon.SetActive(currentMood != GuestMood.Neutral && currentMood != GuestMood.Happy);
  328. }
  329. }
  330. private void SetupMoodIcon()
  331. {
  332. if (moodIcon == null)
  333. {
  334. moodIcon = new GameObject("Mood Icon");
  335. moodIcon.transform.SetParent(transform);
  336. moodIcon.transform.localPosition = Vector3.up * 2.5f;
  337. moodRenderer = moodIcon.AddComponent<SpriteRenderer>();
  338. moodRenderer.sortingOrder = 10;
  339. moodIcon.SetActive(false);
  340. }
  341. }
  342. #endregion
  343. #region Wish System
  344. private void GenerateWishes()
  345. {
  346. wishes.Clear();
  347. // Generate wishes based on guest type
  348. int wishCount = UnityEngine.Random.Range(1, 4);
  349. List<WishType> availableWishes = new List<WishType>((WishType[])Enum.GetValues(typeof(WishType)));
  350. for (int i = 0; i < wishCount && availableWishes.Count > 0; i++)
  351. {
  352. int randomIndex = UnityEngine.Random.Range(0, availableWishes.Count);
  353. WishType wishType = availableWishes[randomIndex];
  354. availableWishes.RemoveAt(randomIndex);
  355. wishes.Add(new GuestWish
  356. {
  357. wishType = wishType,
  358. importance = GetWishImportanceForGuestType(wishType),
  359. isFulfilled = false
  360. });
  361. }
  362. }
  363. private float GetWishImportanceForGuestType(WishType wishType)
  364. {
  365. // Different guest types value different wishes
  366. switch (guestType)
  367. {
  368. case GuestType.Business:
  369. return wishType == WishType.RoomService || wishType == WishType.CleaningService ? 0.9f : 0.5f;
  370. case GuestType.Family:
  371. return wishType == WishType.PlayArea || wishType == WishType.Restaurant ? 0.8f : 0.5f;
  372. case GuestType.VIP:
  373. return wishType == WishType.ValetParking || wishType == WishType.ActivityPlanning ? 0.9f : 0.6f;
  374. default:
  375. return 0.5f;
  376. }
  377. }
  378. #endregion
  379. #region Utility Methods
  380. private string GenerateRandomName()
  381. {
  382. string[] firstNames = { "John", "Jane", "Mike", "Sarah", "David", "Lisa", "Chris", "Amy", "Tom", "Kate" };
  383. string[] lastNames = { "Smith", "Johnson", "Brown", "Davis", "Wilson", "Miller", "Moore", "Taylor", "Anderson", "Thomas" };
  384. return $"{firstNames[UnityEngine.Random.Range(0, firstNames.Length)]} {lastNames[UnityEngine.Random.Range(0, lastNames.Length)]}";
  385. }
  386. public void LeaveHotel()
  387. {
  388. SetState(GuestState.Leaving);
  389. MoveTo(spawnPosition); // Go back to entrance
  390. OnGuestLeft?.Invoke(this);
  391. // Destroy after a delay
  392. Destroy(gameObject, 3f);
  393. }
  394. private void OnMouseDown()
  395. {
  396. // Show guest info when clicked
  397. if (GuestInfoUI.Instance != null)
  398. {
  399. GuestInfoUI.Instance.ShowGuestInfo(this);
  400. }
  401. }
  402. #endregion
  403. }
  404. // Supporting classes
  405. [System.Serializable]
  406. public class GuestWish
  407. {
  408. public WishType wishType;
  409. public float importance; // 0 to 1
  410. public bool isFulfilled;
  411. }
  412. public enum WishType
  413. {
  414. Restaurant,
  415. ValetParking,
  416. ActivityPlanning,
  417. RoomService,
  418. BarService,
  419. PlayArea,
  420. CleaningService,
  421. WashingService,
  422. CloseToEvents
  423. }