using UnityEngine; using UnityEngine.AI; using System.Collections.Generic; using System; [RequireComponent(typeof(NavMeshAgent))] public class Guest : MonoBehaviour { [Header("Guest Info")] [SerializeField] private string guestName; [SerializeField] private GuestType guestType = GuestType.Regular; [SerializeField] private int stayDuration = 3; // days [Header("Movement")] [SerializeField] private float walkSpeed = 3.5f; [SerializeField] private float runSpeed = 6f; [Header("Mood Visualization")] [SerializeField] private GameObject moodIcon; [SerializeField] private SpriteRenderer moodRenderer; [SerializeField] private Sprite[] moodSprites; // Happy, Neutral, Annoyed, Angry private NavMeshAgent agent; private GuestState currentState = GuestState.Arriving; private GuestMood currentMood = GuestMood.Neutral; private List wishes = new List(); private List moodReasons = new List(); private Room assignedRoom; private Vector3 spawnPosition; private float satisfactionLevel = 0.5f; // 0 to 1 private float patience = 100f; private float maxPatience = 100f; // Navigation targets private Vector3 currentDestination; private bool hasDestination = false; // Events public static event Action OnGuestArrived; public static event Action OnGuestLeft; public static event Action OnGuestAssignedRoom; public enum GuestState { Arriving, LookingForReception, CheckingIn, GoingToRoom, InRoom, Exploring, Leaving, Left } public enum GuestMood { Happy = 0, Neutral = 1, Annoyed = 2, Angry = 3 } public enum GuestType { Regular, Business, Family, VIP, Celebrity } #region Properties public string GuestName { get => string.IsNullOrEmpty(guestName) ? GenerateRandomName() : guestName; set => guestName = value; } public GuestState CurrentState => currentState; public GuestMood CurrentMood => currentMood; public GuestType Type => guestType; public float SatisfactionLevel => satisfactionLevel; public float Patience => patience; public List Wishes => wishes; public List MoodReasons => moodReasons; public Room AssignedRoom => assignedRoom; #endregion private void Awake() { agent = GetComponent(); agent.speed = walkSpeed; SetupMoodIcon(); GenerateWishes(); spawnPosition = transform.position; } private void Start() { SetState(GuestState.Arriving); OnGuestArrived?.Invoke(this); // Look for reception to check in FindAndGoToReception(); } private void Update() { UpdatePatience(); UpdateBehaviorBasedOnState(); UpdateMoodDisplay(); // Check if agent reached destination if (hasDestination && agent.enabled && !agent.pathPending && agent.remainingDistance < 0.5f) { OnReachedDestination(); } } #region State Management private void SetState(GuestState newState) { currentState = newState; Debug.Log($"Guest {GuestName} state changed to: {newState}"); switch (newState) { case GuestState.Arriving: AddMoodReason("Just arrived at the hotel"); break; case GuestState.LookingForReception: AddMoodReason("Looking for reception desk"); break; case GuestState.CheckingIn: AddMoodReason("Checking into the hotel"); break; case GuestState.GoingToRoom: AddMoodReason("Heading to assigned room"); break; case GuestState.InRoom: AddMoodReason("Settling into room"); break; case GuestState.Exploring: AddMoodReason("Exploring hotel facilities"); break; case GuestState.Leaving: AddMoodReason("Checking out and leaving"); break; } } private void UpdateBehaviorBasedOnState() { switch (currentState) { case GuestState.LookingForReception: if (!hasDestination) { FindAndGoToReception(); } break; case GuestState.CheckingIn: // Stay at reception for a moment if (Time.time % 5f < 0.1f) // Every 5 seconds, try to get a room { TryGetRoom(); } break; case GuestState.InRoom: // Randomly decide to explore or stay if (UnityEngine.Random.value < 0.001f) // Small chance per frame { DecideNextActivity(); } break; } } private void UpdatePatience() { if (currentState == GuestState.LookingForReception || currentState == GuestState.CheckingIn) { patience -= Time.deltaTime * 2f; // Lose patience while waiting if (patience <= 0) { SetMood(GuestMood.Angry); AddMoodReason("Waited too long without service"); LeaveHotel(); } else if (patience < maxPatience * 0.3f) { SetMood(GuestMood.Annoyed); AddMoodReason("Taking too long to check in"); } } } #endregion #region Navigation public bool MoveTo(Vector3 destination) { if (!agent.enabled) return false; agent.SetDestination(destination); currentDestination = destination; hasDestination = true; return true; } private void OnReachedDestination() { hasDestination = false; switch (currentState) { case GuestState.LookingForReception: SetState(GuestState.CheckingIn); break; case GuestState.GoingToRoom: SetState(GuestState.InRoom); AddMoodReason("Arrived at assigned room"); break; } } private void FindAndGoToReception() { // Look for reception room or reception desk GameObject[] receptions = GameObject.FindGameObjectsWithTag("Reception"); if (receptions.Length > 0) { MoveTo(receptions[0].transform.position); SetState(GuestState.LookingForReception); } else { // No reception found - get annoyed SetMood(GuestMood.Annoyed); AddMoodReason("Cannot find hotel reception"); // Wander around looking for it WanderRandomly(); } } private void WanderRandomly() { Vector3 randomDirection = UnityEngine.Random.insideUnitSphere * 10f; randomDirection += transform.position; randomDirection.y = transform.position.y; if (NavMesh.SamplePosition(randomDirection, out NavMeshHit hit, 10f, NavMesh.AllAreas)) { MoveTo(hit.position); } } #endregion #region Room Management private void TryGetRoom() { // Try to get a room assignment from hotel manager Room availableRoom = HotelManager.Instance?.GetAvailableRoom(this); if (availableRoom != null) { AssignRoom(availableRoom); } else { // No rooms available SetMood(GuestMood.Annoyed); AddMoodReason("No rooms available"); // Lose more patience patience -= 20f; if (patience <= 20f) { LeaveHotel(); } } } public void AssignRoom(Room room) { assignedRoom = room; SetState(GuestState.GoingToRoom); // Calculate room satisfaction float roomSatisfaction = CalculateRoomSatisfaction(room); satisfactionLevel = Mathf.Clamp01(satisfactionLevel + roomSatisfaction - 0.5f); if (roomSatisfaction > 0.7f) { SetMood(GuestMood.Happy); AddMoodReason("Very satisfied with the room"); } else if (roomSatisfaction < 0.3f) { SetMood(GuestMood.Annoyed); AddMoodReason("Room doesn't meet expectations"); } // Go to room MoveTo(room.roomPoints[0]); // Go to room center OnGuestAssignedRoom?.Invoke(this); } private float CalculateRoomSatisfaction(Room room) { float satisfaction = 0.5f; // Base satisfaction // Check if room type matches guest preferences // This would be expanded based on guest type and wishes return satisfaction; } private void DecideNextActivity() { // Based on wishes, decide what to do next if (wishes.Count > 0) { GuestWish randomWish = wishes[UnityEngine.Random.Range(0, wishes.Count)]; if (TryFulfillWish(randomWish)) { SetState(GuestState.Exploring); } } else { // No specific wishes, just wander WanderRandomly(); SetState(GuestState.Exploring); } } private bool TryFulfillWish(GuestWish wish) { // Look for facilities that can fulfill the wish // This would be expanded with actual facility finding logic return false; } #endregion #region Mood System private void SetMood(GuestMood mood) { currentMood = mood; UpdateMoodDisplay(); } private void AddMoodReason(string reason) { moodReasons.Insert(0, $"{DateTime.Now:HH:mm} - {reason}"); // Keep only recent reasons if (moodReasons.Count > 10) { moodReasons.RemoveAt(moodReasons.Count - 1); } } private void UpdateMoodDisplay() { if (moodRenderer != null && moodSprites != null && moodSprites.Length > (int)currentMood) { moodRenderer.sprite = moodSprites[(int)currentMood]; // Show mood icon only if not neutral or happy moodIcon.SetActive(currentMood != GuestMood.Neutral && currentMood != GuestMood.Happy); } } private void SetupMoodIcon() { if (moodIcon == null) { moodIcon = new GameObject("Mood Icon"); moodIcon.transform.SetParent(transform); moodIcon.transform.localPosition = Vector3.up * 2.5f; moodRenderer = moodIcon.AddComponent(); moodRenderer.sortingOrder = 10; moodIcon.SetActive(false); } } #endregion #region Wish System private void GenerateWishes() { wishes.Clear(); // Generate wishes based on guest type int wishCount = UnityEngine.Random.Range(1, 4); List availableWishes = new List((WishType[])Enum.GetValues(typeof(WishType))); for (int i = 0; i < wishCount && availableWishes.Count > 0; i++) { int randomIndex = UnityEngine.Random.Range(0, availableWishes.Count); WishType wishType = availableWishes[randomIndex]; availableWishes.RemoveAt(randomIndex); wishes.Add(new GuestWish { wishType = wishType, importance = GetWishImportanceForGuestType(wishType), isFulfilled = false }); } } private float GetWishImportanceForGuestType(WishType wishType) { // Different guest types value different wishes switch (guestType) { case GuestType.Business: return wishType == WishType.RoomService || wishType == WishType.CleaningService ? 0.9f : 0.5f; case GuestType.Family: return wishType == WishType.PlayArea || wishType == WishType.Restaurant ? 0.8f : 0.5f; case GuestType.VIP: return wishType == WishType.ValetParking || wishType == WishType.ActivityPlanning ? 0.9f : 0.6f; default: return 0.5f; } } #endregion #region Utility Methods private string GenerateRandomName() { string[] firstNames = { "John", "Jane", "Mike", "Sarah", "David", "Lisa", "Chris", "Amy", "Tom", "Kate" }; string[] lastNames = { "Smith", "Johnson", "Brown", "Davis", "Wilson", "Miller", "Moore", "Taylor", "Anderson", "Thomas" }; return $"{firstNames[UnityEngine.Random.Range(0, firstNames.Length)]} {lastNames[UnityEngine.Random.Range(0, lastNames.Length)]}"; } public void LeaveHotel() { SetState(GuestState.Leaving); MoveTo(spawnPosition); // Go back to entrance OnGuestLeft?.Invoke(this); // Destroy after a delay Destroy(gameObject, 3f); } private void OnMouseDown() { // Show guest info when clicked if (GuestInfoUI.Instance != null) { GuestInfoUI.Instance.ShowGuestInfo(this); } } #endregion } // Supporting classes [System.Serializable] public class GuestWish { public WishType wishType; public float importance; // 0 to 1 public bool isFulfilled; } public enum WishType { Restaurant, ValetParking, ActivityPlanning, RoomService, BarService, PlayArea, CleaningService, WashingService, CloseToEvents }