HotelRatingSystem.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. public class HotelRatingSystem : MonoBehaviour
  5. {
  6. public static HotelRatingSystem Instance { get; private set; }
  7. [Header("Rating Components")]
  8. [SerializeField] private float baseRating = 3f;
  9. [SerializeField] private float currentOverallRating = 3f;
  10. [Header("Rating Weights")]
  11. [SerializeField] private float guestSatisfactionWeight = 0.4f;
  12. [SerializeField] private float facilityQualityWeight = 0.25f;
  13. [SerializeField] private float staffEfficiencyWeight = 0.15f;
  14. [SerializeField] private float roomQualityWeight = 0.1f;
  15. [SerializeField] private float serviceSpeedWeight = 0.1f;
  16. [Header("Rating History")]
  17. [SerializeField] private List<RatingSnapshot> ratingHistory = new List<RatingSnapshot>();
  18. [SerializeField] private int maxHistoryEntries = 100;
  19. private Dictionary<string, float> componentRatings = new Dictionary<string, float>();
  20. private float lastRatingUpdate = 0f;
  21. private float ratingUpdateInterval = 30f; // Update every 30 seconds
  22. // Events
  23. public System.Action<float, float> OnRatingChanged; // old rating, new rating
  24. public System.Action<RatingSnapshot> OnRatingUpdated;
  25. private void Awake()
  26. {
  27. if (Instance != null && Instance != this)
  28. {
  29. Destroy(this.gameObject);
  30. return;
  31. }
  32. Instance = this;
  33. }
  34. private void Start()
  35. {
  36. InitializeRatingSystem();
  37. }
  38. private void Update()
  39. {
  40. if (Time.time - lastRatingUpdate >= ratingUpdateInterval)
  41. {
  42. UpdateOverallRating();
  43. lastRatingUpdate = Time.time;
  44. }
  45. }
  46. #region Initialization
  47. private void InitializeRatingSystem()
  48. {
  49. componentRatings["GuestSatisfaction"] = 0.5f;
  50. componentRatings["FacilityQuality"] = 0.5f;
  51. componentRatings["StaffEfficiency"] = 0.5f;
  52. componentRatings["RoomQuality"] = 0.5f;
  53. componentRatings["ServiceSpeed"] = 0.5f;
  54. currentOverallRating = baseRating;
  55. Debug.Log("Hotel Rating System initialized");
  56. }
  57. #endregion
  58. #region Rating Calculation
  59. public void UpdateOverallRating()
  60. {
  61. float previousRating = currentOverallRating;
  62. // Calculate component ratings
  63. UpdateComponentRatings();
  64. // Calculate weighted overall rating
  65. float newRating = CalculateWeightedRating();
  66. // Apply some smoothing to prevent rapid changes
  67. currentOverallRating = Mathf.Lerp(currentOverallRating, newRating, 0.1f);
  68. currentOverallRating = Mathf.Clamp(currentOverallRating, 1f, 5f);
  69. // Create rating snapshot
  70. RatingSnapshot snapshot = CreateRatingSnapshot();
  71. AddRatingToHistory(snapshot);
  72. // Notify of rating change
  73. if (Mathf.Abs(previousRating - currentOverallRating) > 0.01f)
  74. {
  75. OnRatingChanged?.Invoke(previousRating, currentOverallRating);
  76. OnRatingUpdated?.Invoke(snapshot);
  77. Debug.Log($"Hotel rating updated: {previousRating:F2} → {currentOverallRating:F2}");
  78. }
  79. }
  80. private void UpdateComponentRatings()
  81. {
  82. // Guest Satisfaction
  83. componentRatings["GuestSatisfaction"] = CalculateGuestSatisfactionRating();
  84. // Facility Quality
  85. componentRatings["FacilityQuality"] = CalculateFacilityQualityRating();
  86. // Staff Efficiency
  87. componentRatings["StaffEfficiency"] = CalculateStaffEfficiencyRating();
  88. // Room Quality
  89. componentRatings["RoomQuality"] = CalculateRoomQualityRating();
  90. // Service Speed
  91. componentRatings["ServiceSpeed"] = CalculateServiceSpeedRating();
  92. }
  93. private float CalculateWeightedRating()
  94. {
  95. float weightedSum = 0f;
  96. float totalWeight = 0f;
  97. weightedSum += componentRatings["GuestSatisfaction"] * guestSatisfactionWeight;
  98. totalWeight += guestSatisfactionWeight;
  99. weightedSum += componentRatings["FacilityQuality"] * facilityQualityWeight;
  100. totalWeight += facilityQualityWeight;
  101. weightedSum += componentRatings["StaffEfficiency"] * staffEfficiencyWeight;
  102. totalWeight += staffEfficiencyWeight;
  103. weightedSum += componentRatings["RoomQuality"] * roomQualityWeight;
  104. totalWeight += roomQualityWeight;
  105. weightedSum += componentRatings["ServiceSpeed"] * serviceSpeedWeight;
  106. totalWeight += serviceSpeedWeight;
  107. // Convert from 0-1 scale to 1-5 star scale
  108. float normalizedRating = totalWeight > 0 ? weightedSum / totalWeight : 0.5f;
  109. return 1f + (normalizedRating * 4f);
  110. }
  111. #endregion
  112. #region Component Rating Calculations
  113. private float CalculateGuestSatisfactionRating()
  114. {
  115. if (HotelManager.Instance == null) return 0.5f;
  116. var currentGuests = HotelManager.Instance.GetCurrentGuests();
  117. if (currentGuests.Count == 0) return 0.5f;
  118. // Only count guests who have been assigned rooms
  119. var satisfiedGuests = currentGuests.Where(g => g.AssignedRoom != null).ToList();
  120. if (satisfiedGuests.Count == 0) return 0.5f;
  121. float totalSatisfaction = satisfiedGuests.Sum(g => g.SatisfactionLevel);
  122. return totalSatisfaction / satisfiedGuests.Count;
  123. }
  124. private float CalculateFacilityQualityRating()
  125. {
  126. if (FacilityManager.Instance == null) return 0.5f;
  127. var facilities = FacilityManager.Instance.GetAvailableFacilities();
  128. if (facilities.Count == 0) return 0.3f; // Low rating if no facilities
  129. float totalQuality = facilities.Sum(f => f.QualityRating);
  130. float averageQuality = totalQuality / facilities.Count;
  131. // Bonus for having diverse facilities
  132. var uniqueTypes = facilities.Select(f => f.Type).Distinct().Count();
  133. float diversityBonus = Mathf.Clamp01(uniqueTypes / 6f) * 0.2f; // Max 20% bonus for 6+ facility types
  134. return Mathf.Clamp01(averageQuality + diversityBonus);
  135. }
  136. private float CalculateStaffEfficiencyRating()
  137. {
  138. if (StaffManager.Instance == null) return 0.5f;
  139. float averageEfficiency = StaffManager.Instance.GetAverageStaffEfficiency();
  140. // Factor in staff-to-guest ratio
  141. int totalStaff = StaffManager.Instance.GetTotalStaffCount();
  142. int totalGuests = HotelManager.Instance?.GetTotalGuests() ?? 0;
  143. float idealRatio = 0.3f; // 3 staff per 10 guests
  144. float actualRatio = totalGuests > 0 ? (float)totalStaff / totalGuests : 1f;
  145. float ratioScore = Mathf.Clamp01(actualRatio / idealRatio);
  146. // Combine efficiency and ratio
  147. return (averageEfficiency * 0.7f) + (ratioScore * 0.3f);
  148. }
  149. private float CalculateRoomQualityRating()
  150. {
  151. if (HotelManager.Instance == null) return 0.5f;
  152. var allRooms = HotelManager.Instance.GetAllRooms();
  153. if (allRooms.Count == 0) return 0.2f;
  154. // For now, assume all rooms have base quality
  155. // This could be expanded to include room upgrades, cleanliness, etc.
  156. float baseRoomQuality = 0.6f;
  157. // Factor in room availability vs demand
  158. float occupancyRate = HotelManager.Instance.GetOccupancyRate();
  159. float availabilityBonus = occupancyRate < 0.9f ? 0.1f : 0f; // Bonus for having available rooms
  160. return Mathf.Clamp01(baseRoomQuality + availabilityBonus);
  161. }
  162. private float CalculateServiceSpeedRating()
  163. {
  164. if (StaffManager.Instance == null) return 0.5f;
  165. // Base this on average guest patience levels
  166. if (HotelManager.Instance == null) return 0.5f;
  167. var currentGuests = HotelManager.Instance.GetCurrentGuests();
  168. if (currentGuests.Count == 0) return 0.7f; // Good rating if no complaints
  169. // Calculate average patience (higher patience = better service speed)
  170. float totalPatience = currentGuests.Sum(g => g.Patience);
  171. float averagePatience = totalPatience / currentGuests.Count;
  172. return averagePatience / 100f; // Convert from 0-100 to 0-1 scale
  173. }
  174. #endregion
  175. #region Rating History
  176. private RatingSnapshot CreateRatingSnapshot()
  177. {
  178. return new RatingSnapshot
  179. {
  180. timestamp = System.DateTime.Now,
  181. overallRating = currentOverallRating,
  182. guestSatisfaction = componentRatings["GuestSatisfaction"],
  183. facilityQuality = componentRatings["FacilityQuality"],
  184. staffEfficiency = componentRatings["StaffEfficiency"],
  185. roomQuality = componentRatings["RoomQuality"],
  186. serviceSpeed = componentRatings["ServiceSpeed"],
  187. totalGuests = HotelManager.Instance?.GetTotalGuests() ?? 0,
  188. totalStaff = StaffManager.Instance?.GetTotalStaffCount() ?? 0,
  189. occupancyRate = HotelManager.Instance?.GetOccupancyRate() ?? 0f
  190. };
  191. }
  192. private void AddRatingToHistory(RatingSnapshot snapshot)
  193. {
  194. ratingHistory.Add(snapshot);
  195. // Limit history size
  196. if (ratingHistory.Count > maxHistoryEntries)
  197. {
  198. ratingHistory.RemoveAt(0);
  199. }
  200. }
  201. public List<RatingSnapshot> GetRatingHistory(int entryCount = 10)
  202. {
  203. int startIndex = Mathf.Max(0, ratingHistory.Count - entryCount);
  204. return ratingHistory.GetRange(startIndex, ratingHistory.Count - startIndex);
  205. }
  206. #endregion
  207. #region Public Interface
  208. public float GetOverallRating()
  209. {
  210. return currentOverallRating;
  211. }
  212. public string GetRatingStars()
  213. {
  214. int stars = Mathf.RoundToInt(currentOverallRating);
  215. return new string('★', stars) + new string('☆', 5 - stars);
  216. }
  217. public Dictionary<string, float> GetComponentRatings()
  218. {
  219. return new Dictionary<string, float>(componentRatings);
  220. }
  221. public string GetRatingCategory()
  222. {
  223. if (currentOverallRating >= 4.5f) return "Luxury";
  224. if (currentOverallRating >= 3.5f) return "Premium";
  225. if (currentOverallRating >= 2.5f) return "Standard";
  226. if (currentOverallRating >= 1.5f) return "Budget";
  227. return "Poor";
  228. }
  229. public float GetRatingChangeLastPeriod()
  230. {
  231. if (ratingHistory.Count < 2) return 0f;
  232. var latest = ratingHistory[ratingHistory.Count - 1];
  233. var previous = ratingHistory[ratingHistory.Count - 2];
  234. return latest.overallRating - previous.overallRating;
  235. }
  236. public void ForceRatingUpdate()
  237. {
  238. UpdateOverallRating();
  239. }
  240. #endregion
  241. #region Guest Attraction
  242. public Guest.GuestType GetAttractedGuestType()
  243. {
  244. // Higher rated hotels attract higher profile guests
  245. if (currentOverallRating >= 4.5f && UnityEngine.Random.value < 0.15f)
  246. return Guest.GuestType.Celebrity;
  247. if (currentOverallRating >= 4f && UnityEngine.Random.value < 0.25f)
  248. return Guest.GuestType.VIP;
  249. if (currentOverallRating >= 3.5f && UnityEngine.Random.value < 0.35f)
  250. return Guest.GuestType.Business;
  251. if (UnityEngine.Random.value < 0.4f)
  252. return Guest.GuestType.Family;
  253. return Guest.GuestType.Regular;
  254. }
  255. public float GetGuestArrivalRateMultiplier()
  256. {
  257. // Higher rated hotels get more guests
  258. return Mathf.Clamp(currentOverallRating / 3f, 0.5f, 2f);
  259. }
  260. #endregion
  261. }
  262. // Data structure for rating snapshots
  263. [System.Serializable]
  264. public class RatingSnapshot
  265. {
  266. public System.DateTime timestamp;
  267. public float overallRating;
  268. public float guestSatisfaction;
  269. public float facilityQuality;
  270. public float staffEfficiency;
  271. public float roomQuality;
  272. public float serviceSpeed;
  273. public int totalGuests;
  274. public int totalStaff;
  275. public float occupancyRate;
  276. public override string ToString()
  277. {
  278. return $"Rating: {overallRating:F2} stars at {timestamp:HH:mm:ss} " +
  279. $"(Guests: {totalGuests}, Staff: {totalStaff}, Occupancy: {occupancyRate:P0})";
  280. }
  281. }