AgentInfoPanel.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. using UnityEngine;
  2. using UnityEngine.InputSystem;
  3. using UnityEngine.UIElements;
  4. /// <summary>
  5. /// Displays agent information in a popup panel using UI Toolkit
  6. /// Singleton pattern to ensure only one panel is shown at a time
  7. /// </summary>
  8. public class AgentInfoPanel : MonoBehaviour
  9. {
  10. private static AgentInfoPanel instance;
  11. private static UIDocument uiDocument;
  12. private static VisualElement panelRoot;
  13. private static AIAgent currentAgent;
  14. private static AgentGroup currentGroup;
  15. void Awake()
  16. {
  17. if (instance != null && instance != this)
  18. {
  19. Destroy(gameObject);
  20. return;
  21. }
  22. instance = this;
  23. uiDocument = GetComponent<UIDocument>();
  24. if (uiDocument == null)
  25. {
  26. Debug.LogError("AgentInfoPanel: UIDocument component not found!");
  27. return;
  28. }
  29. }
  30. void OnEnable()
  31. {
  32. if (uiDocument == null) return;
  33. panelRoot = uiDocument.rootVisualElement.Q<VisualElement>("AgentInfoPanelRoot");
  34. if (panelRoot == null)
  35. {
  36. Debug.LogError("AgentInfoPanel: AgentInfoPanelRoot not found in UXML!");
  37. return;
  38. }
  39. // Hide panel initially
  40. panelRoot.style.display = DisplayStyle.None;
  41. // Setup close button
  42. var closeButton = panelRoot.Q<Button>("CloseButton");
  43. if (closeButton != null)
  44. {
  45. closeButton.clicked += HidePanel;
  46. }
  47. }
  48. /// <summary>
  49. /// Shows agent information in the info panel (solo agent).
  50. /// </summary>
  51. public static void ShowAgentInfo(AIAgent agent)
  52. {
  53. if (instance == null) { Debug.LogWarning("AgentInfoPanel: Instance not found!"); return; }
  54. if (agent == null) return;
  55. currentAgent = agent;
  56. currentGroup = null;
  57. instance.DisplayAgentInfo(agent, null);
  58. }
  59. /// <summary>
  60. /// Shows combined group information – called when clicking any member of a group.
  61. /// </summary>
  62. public static void ShowGroupInfo(AgentGroup group)
  63. {
  64. if (instance == null) { Debug.LogWarning("AgentInfoPanel: Instance not found!"); return; }
  65. if (group == null) return;
  66. currentGroup = group;
  67. currentAgent = group.Leader;
  68. instance.DisplayAgentInfo(group.Leader, group);
  69. }
  70. /// <summary>
  71. /// Displays agent or group information in the panel.
  72. /// When group is non-null, shows combined (max) stats and the member roster.
  73. /// </summary>
  74. private void DisplayAgentInfo(AIAgent agent, AgentGroup group)
  75. {
  76. if (panelRoot == null) return;
  77. panelRoot.style.display = DisplayStyle.Flex;
  78. bool isGroup = group != null && group.Size > 1;
  79. // ---- Header name ----
  80. var nameLabel = panelRoot.Q<Label>("AgentNameLabel");
  81. if (nameLabel != null)
  82. nameLabel.text = isGroup ? $"{agent.AgentName}'s Group" : agent.AgentName;
  83. // ---- Group banner ----
  84. var groupBanner = panelRoot.Q<VisualElement>("GroupBanner");
  85. if (groupBanner != null)
  86. {
  87. groupBanner.style.display = isGroup ? DisplayStyle.Flex : DisplayStyle.None;
  88. if (isGroup)
  89. {
  90. var sizeLabel = panelRoot.Q<Label>("GroupSizeLabel");
  91. if (sizeLabel != null)
  92. sizeLabel.text = $"Group of {group.Size} (max {AgentGroup.MAX_SIZE})";
  93. var membersLabel = panelRoot.Q<Label>("GroupMembersLabel");
  94. if (membersLabel != null)
  95. membersLabel.text = string.Join(", ", System.Linq.Enumerable.Select(group.Members, m => m.AgentName));
  96. }
  97. }
  98. // ---- Stats section title ----
  99. var statsTitle = panelRoot.Q<Label>("StatsTitle");
  100. if (statsTitle != null)
  101. statsTitle.text = isGroup ? "Combined Stats (best in group)" : "Stats";
  102. // ---- Stat bars ----
  103. if (isGroup)
  104. {
  105. UpdateStatDisplay("StrengthBar", "StrengthValue", group.CombinedStrength);
  106. UpdateStatDisplay("SpeedBar", "SpeedValue", group.CombinedSpeed);
  107. UpdateStatDisplay("MagicBar", "MagicValue", group.CombinedMagic);
  108. UpdateStatDisplay("DexterityBar", "DexterityValue", group.CombinedDexterity);
  109. UpdateStatDisplay("IntelligenceBar", "IntelligenceValue", group.CombinedIntelligence);
  110. UpdateStatDisplay("ConstitutionBar", "ConstitutionValue", group.CombinedConstitution);
  111. UpdateStatDisplay("HealthBar", "HealthValue", group.CombinedHealth, maxValue: group.Size * 110);
  112. var totalLabel = panelRoot.Q<Label>("TotalStatsValue");
  113. if (totalLabel != null)
  114. totalLabel.text = (group.CombinedStrength + group.CombinedSpeed + group.CombinedMagic
  115. + group.CombinedDexterity + group.CombinedIntelligence + group.CombinedConstitution).ToString();
  116. }
  117. else
  118. {
  119. var stats = agent.Stats;
  120. UpdateStatDisplay("StrengthBar", "StrengthValue", stats.Strength);
  121. UpdateStatDisplay("SpeedBar", "SpeedValue", stats.Speed);
  122. UpdateStatDisplay("MagicBar", "MagicValue", stats.Magic);
  123. UpdateStatDisplay("DexterityBar", "DexterityValue", stats.Dexterity);
  124. UpdateStatDisplay("IntelligenceBar", "IntelligenceValue", stats.Intelligence);
  125. UpdateStatDisplay("ConstitutionBar", "ConstitutionValue", stats.Constitution);
  126. UpdateStatDisplay("HealthBar", "HealthValue", stats.Health, maxValue: 110);
  127. var totalLabel = panelRoot.Q<Label>("TotalStatsValue");
  128. if (totalLabel != null)
  129. totalLabel.text = stats.GetTotalStats().ToString();
  130. }
  131. // ---- Status ----
  132. var statusLabel = panelRoot.Q<Label>("StatusLabel");
  133. if (statusLabel != null)
  134. {
  135. if (isGroup)
  136. statusLabel.text = $"Travelling together ({group.Size} members)";
  137. else if (agent.HasReachedGoal)
  138. statusLabel.text = "✓ Reached Exit";
  139. else if (agent.KnowsExitLocation)
  140. statusLabel.text = agent.GroupUpAffinity > 0.5f
  141. ? "Spotted exit — seeking allies..."
  142. : "Spotted exit — heading in!";
  143. else
  144. statusLabel.text = "Exploring maze...";
  145. }
  146. }
  147. /// <summary>
  148. /// Updates a stat display (progress bar and label)
  149. /// </summary>
  150. private void UpdateStatDisplay(string barName, string labelName, int value, int maxValue = 100)
  151. {
  152. var bar = panelRoot.Q<ProgressBar>(barName);
  153. if (bar != null)
  154. {
  155. bar.highValue = maxValue;
  156. bar.value = value;
  157. }
  158. var label = panelRoot.Q<Label>(labelName);
  159. if (label != null)
  160. {
  161. label.text = value.ToString();
  162. }
  163. }
  164. /// <summary>
  165. /// Hides the info panel
  166. /// </summary>
  167. private static void HidePanel()
  168. {
  169. if (panelRoot != null)
  170. {
  171. panelRoot.style.display = DisplayStyle.None;
  172. currentAgent = null;
  173. currentGroup = null;
  174. }
  175. }
  176. /// <summary>
  177. /// Hides panel when clicking outside of it
  178. /// </summary>
  179. void Update()
  180. {
  181. if (panelRoot == null || panelRoot.style.display == DisplayStyle.None) return;
  182. if (Mouse.current.leftButton.wasPressedThisFrame)
  183. {
  184. // Check if click is outside the panel
  185. var clickPos = Input.mousePosition;
  186. var panelWorldRect = panelRoot.worldBound;
  187. if (!panelWorldRect.Contains(new Vector2(clickPos.x, Screen.height - clickPos.y)))
  188. {
  189. HidePanel();
  190. }
  191. }
  192. }
  193. }