using UnityEngine; using System.Collections.Generic; using System.Linq; public class HotelRatingSystem : MonoBehaviour { public static HotelRatingSystem Instance { get; private set; } [Header("Rating Components")] [SerializeField] private float baseRating = 3f; [SerializeField] private float currentOverallRating = 3f; [Header("Rating Weights")] [SerializeField] private float guestSatisfactionWeight = 0.4f; [SerializeField] private float facilityQualityWeight = 0.25f; [SerializeField] private float staffEfficiencyWeight = 0.15f; [SerializeField] private float roomQualityWeight = 0.1f; [SerializeField] private float serviceSpeedWeight = 0.1f; [Header("Rating History")] [SerializeField] private List ratingHistory = new List(); [SerializeField] private int maxHistoryEntries = 100; private Dictionary componentRatings = new Dictionary(); private float lastRatingUpdate = 0f; private float ratingUpdateInterval = 30f; // Update every 30 seconds // Events public System.Action OnRatingChanged; // old rating, new rating public System.Action OnRatingUpdated; private void Awake() { if (Instance != null && Instance != this) { Destroy(this.gameObject); return; } Instance = this; } private void Start() { InitializeRatingSystem(); } private void Update() { if (Time.time - lastRatingUpdate >= ratingUpdateInterval) { UpdateOverallRating(); lastRatingUpdate = Time.time; } } #region Initialization private void InitializeRatingSystem() { componentRatings["GuestSatisfaction"] = 0.5f; componentRatings["FacilityQuality"] = 0.5f; componentRatings["StaffEfficiency"] = 0.5f; componentRatings["RoomQuality"] = 0.5f; componentRatings["ServiceSpeed"] = 0.5f; currentOverallRating = baseRating; Debug.Log("Hotel Rating System initialized"); } #endregion #region Rating Calculation public void UpdateOverallRating() { float previousRating = currentOverallRating; // Calculate component ratings UpdateComponentRatings(); // Calculate weighted overall rating float newRating = CalculateWeightedRating(); // Apply some smoothing to prevent rapid changes currentOverallRating = Mathf.Lerp(currentOverallRating, newRating, 0.1f); currentOverallRating = Mathf.Clamp(currentOverallRating, 1f, 5f); // Create rating snapshot RatingSnapshot snapshot = CreateRatingSnapshot(); AddRatingToHistory(snapshot); // Notify of rating change if (Mathf.Abs(previousRating - currentOverallRating) > 0.01f) { OnRatingChanged?.Invoke(previousRating, currentOverallRating); OnRatingUpdated?.Invoke(snapshot); Debug.Log($"Hotel rating updated: {previousRating:F2} → {currentOverallRating:F2}"); } } private void UpdateComponentRatings() { // Guest Satisfaction componentRatings["GuestSatisfaction"] = CalculateGuestSatisfactionRating(); // Facility Quality componentRatings["FacilityQuality"] = CalculateFacilityQualityRating(); // Staff Efficiency componentRatings["StaffEfficiency"] = CalculateStaffEfficiencyRating(); // Room Quality componentRatings["RoomQuality"] = CalculateRoomQualityRating(); // Service Speed componentRatings["ServiceSpeed"] = CalculateServiceSpeedRating(); } private float CalculateWeightedRating() { float weightedSum = 0f; float totalWeight = 0f; weightedSum += componentRatings["GuestSatisfaction"] * guestSatisfactionWeight; totalWeight += guestSatisfactionWeight; weightedSum += componentRatings["FacilityQuality"] * facilityQualityWeight; totalWeight += facilityQualityWeight; weightedSum += componentRatings["StaffEfficiency"] * staffEfficiencyWeight; totalWeight += staffEfficiencyWeight; weightedSum += componentRatings["RoomQuality"] * roomQualityWeight; totalWeight += roomQualityWeight; weightedSum += componentRatings["ServiceSpeed"] * serviceSpeedWeight; totalWeight += serviceSpeedWeight; // Convert from 0-1 scale to 1-5 star scale float normalizedRating = totalWeight > 0 ? weightedSum / totalWeight : 0.5f; return 1f + (normalizedRating * 4f); } #endregion #region Component Rating Calculations private float CalculateGuestSatisfactionRating() { if (HotelManager.Instance == null) return 0.5f; var currentGuests = HotelManager.Instance.GetCurrentGuests(); if (currentGuests.Count == 0) return 0.5f; // Only count guests who have been assigned rooms var satisfiedGuests = currentGuests.Where(g => g.AssignedRoom != null).ToList(); if (satisfiedGuests.Count == 0) return 0.5f; float totalSatisfaction = satisfiedGuests.Sum(g => g.SatisfactionLevel); return totalSatisfaction / satisfiedGuests.Count; } private float CalculateFacilityQualityRating() { if (FacilityManager.Instance == null) return 0.5f; var facilities = FacilityManager.Instance.GetAvailableFacilities(); if (facilities.Count == 0) return 0.3f; // Low rating if no facilities float totalQuality = facilities.Sum(f => f.QualityRating); float averageQuality = totalQuality / facilities.Count; // Bonus for having diverse facilities var uniqueTypes = facilities.Select(f => f.Type).Distinct().Count(); float diversityBonus = Mathf.Clamp01(uniqueTypes / 6f) * 0.2f; // Max 20% bonus for 6+ facility types return Mathf.Clamp01(averageQuality + diversityBonus); } private float CalculateStaffEfficiencyRating() { if (StaffManager.Instance == null) return 0.5f; float averageEfficiency = StaffManager.Instance.GetAverageStaffEfficiency(); // Factor in staff-to-guest ratio int totalStaff = StaffManager.Instance.GetTotalStaffCount(); int totalGuests = HotelManager.Instance?.GetTotalGuests() ?? 0; float idealRatio = 0.3f; // 3 staff per 10 guests float actualRatio = totalGuests > 0 ? (float)totalStaff / totalGuests : 1f; float ratioScore = Mathf.Clamp01(actualRatio / idealRatio); // Combine efficiency and ratio return (averageEfficiency * 0.7f) + (ratioScore * 0.3f); } private float CalculateRoomQualityRating() { if (HotelManager.Instance == null) return 0.5f; var allRooms = HotelManager.Instance.GetAllRooms(); if (allRooms.Count == 0) return 0.2f; // For now, assume all rooms have base quality // This could be expanded to include room upgrades, cleanliness, etc. float baseRoomQuality = 0.6f; // Factor in room availability vs demand float occupancyRate = HotelManager.Instance.GetOccupancyRate(); float availabilityBonus = occupancyRate < 0.9f ? 0.1f : 0f; // Bonus for having available rooms return Mathf.Clamp01(baseRoomQuality + availabilityBonus); } private float CalculateServiceSpeedRating() { if (StaffManager.Instance == null) return 0.5f; // Base this on average guest patience levels if (HotelManager.Instance == null) return 0.5f; var currentGuests = HotelManager.Instance.GetCurrentGuests(); if (currentGuests.Count == 0) return 0.7f; // Good rating if no complaints // Calculate average patience (higher patience = better service speed) float totalPatience = currentGuests.Sum(g => g.Patience); float averagePatience = totalPatience / currentGuests.Count; return averagePatience / 100f; // Convert from 0-100 to 0-1 scale } #endregion #region Rating History private RatingSnapshot CreateRatingSnapshot() { return new RatingSnapshot { timestamp = System.DateTime.Now, overallRating = currentOverallRating, guestSatisfaction = componentRatings["GuestSatisfaction"], facilityQuality = componentRatings["FacilityQuality"], staffEfficiency = componentRatings["StaffEfficiency"], roomQuality = componentRatings["RoomQuality"], serviceSpeed = componentRatings["ServiceSpeed"], totalGuests = HotelManager.Instance?.GetTotalGuests() ?? 0, totalStaff = StaffManager.Instance?.GetTotalStaffCount() ?? 0, occupancyRate = HotelManager.Instance?.GetOccupancyRate() ?? 0f }; } private void AddRatingToHistory(RatingSnapshot snapshot) { ratingHistory.Add(snapshot); // Limit history size if (ratingHistory.Count > maxHistoryEntries) { ratingHistory.RemoveAt(0); } } public List GetRatingHistory(int entryCount = 10) { int startIndex = Mathf.Max(0, ratingHistory.Count - entryCount); return ratingHistory.GetRange(startIndex, ratingHistory.Count - startIndex); } #endregion #region Public Interface public float GetOverallRating() { return currentOverallRating; } public string GetRatingStars() { int stars = Mathf.RoundToInt(currentOverallRating); return new string('★', stars) + new string('☆', 5 - stars); } public Dictionary GetComponentRatings() { return new Dictionary(componentRatings); } public string GetRatingCategory() { if (currentOverallRating >= 4.5f) return "Luxury"; if (currentOverallRating >= 3.5f) return "Premium"; if (currentOverallRating >= 2.5f) return "Standard"; if (currentOverallRating >= 1.5f) return "Budget"; return "Poor"; } public float GetRatingChangeLastPeriod() { if (ratingHistory.Count < 2) return 0f; var latest = ratingHistory[ratingHistory.Count - 1]; var previous = ratingHistory[ratingHistory.Count - 2]; return latest.overallRating - previous.overallRating; } public void ForceRatingUpdate() { UpdateOverallRating(); } #endregion #region Guest Attraction public Guest.GuestType GetAttractedGuestType() { // Higher rated hotels attract higher profile guests if (currentOverallRating >= 4.5f && UnityEngine.Random.value < 0.15f) return Guest.GuestType.Celebrity; if (currentOverallRating >= 4f && UnityEngine.Random.value < 0.25f) return Guest.GuestType.VIP; if (currentOverallRating >= 3.5f && UnityEngine.Random.value < 0.35f) return Guest.GuestType.Business; if (UnityEngine.Random.value < 0.4f) return Guest.GuestType.Family; return Guest.GuestType.Regular; } public float GetGuestArrivalRateMultiplier() { // Higher rated hotels get more guests return Mathf.Clamp(currentOverallRating / 3f, 0.5f, 2f); } #endregion } // Data structure for rating snapshots [System.Serializable] public class RatingSnapshot { public System.DateTime timestamp; public float overallRating; public float guestSatisfaction; public float facilityQuality; public float staffEfficiency; public float roomQuality; public float serviceSpeed; public int totalGuests; public int totalStaff; public float occupancyRate; public override string ToString() { return $"Rating: {overallRating:F2} stars at {timestamp:HH:mm:ss} " + $"(Guests: {totalGuests}, Staff: {totalStaff}, Occupancy: {occupancyRate:P0})"; } }