CombatEventPopupUXML.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. using UnityEngine;
  2. using UnityEngine.UIElements;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. /// <summary>
  6. /// Beautiful UI popup for combat event encounters
  7. /// Shows enemy details and provides options to attack or run away
  8. /// Uses UXML template for cleaner UI structure
  9. /// </summary>
  10. public class CombatEventPopupUXML : MonoBehaviour, IClickBlocker
  11. {
  12. [Header("UI References")]
  13. public UIDocument uiDocument;
  14. public VisualTreeAsset popupTemplate; // UXML template
  15. public StyleSheet popupStyleSheet; // USS styles
  16. [Header("Popup Settings")]
  17. public float animationDuration = 0.3f;
  18. public float backgroundBlurIntensity = 0.5f;
  19. [Header("Debug")]
  20. public bool debugMode = false;
  21. // Events
  22. public System.Action<bool> OnCombatDecision; // true = attack, false = run away
  23. // UI Elements (from UXML)
  24. private VisualElement popupContainer;
  25. private VisualElement backgroundOverlay;
  26. private VisualElement popupPanel;
  27. private Label eventTitleLabel;
  28. private Label eventDescriptionLabel;
  29. private VisualElement enemyListContainer;
  30. private VisualElement enemyItemTemplate;
  31. private Button attackButton;
  32. private Button runAwayButton;
  33. // Current combat data
  34. private BattleEventData currentBattleData;
  35. private TravelEventContext currentContext;
  36. private bool isPopupActive = false;
  37. void Awake()
  38. {
  39. // Find UI Document if not assigned
  40. if (uiDocument == null)
  41. uiDocument = GetComponent<UIDocument>();
  42. if (uiDocument == null)
  43. {
  44. Debug.LogError("CombatEventPopupUXML: No UIDocument found! Please assign one.");
  45. return;
  46. }
  47. SetupUI();
  48. }
  49. void OnEnable()
  50. {
  51. // Register with ClickManager when this component is enabled
  52. if (ClickManager.Instance != null)
  53. {
  54. ClickManager.Instance.RegisterClickBlocker(this);
  55. }
  56. }
  57. void OnDisable()
  58. {
  59. // Unregister from ClickManager when this component is disabled
  60. if (ClickManager.Instance != null)
  61. {
  62. ClickManager.Instance.UnregisterClickBlocker(this);
  63. }
  64. }
  65. void OnDestroy()
  66. {
  67. // Ensure we're unregistered when destroyed
  68. if (ClickManager.Instance != null)
  69. {
  70. ClickManager.Instance.UnregisterClickBlocker(this);
  71. }
  72. }
  73. void SetupUI()
  74. {
  75. var root = uiDocument.rootVisualElement;
  76. if (debugMode) Debug.Log($"🔧 SetupUI: Root element size: {root.resolvedStyle.width}x{root.resolvedStyle.height}");
  77. // Load and instantiate UXML template
  78. if (popupTemplate != null)
  79. {
  80. // Clone the template
  81. var template = popupTemplate.Instantiate();
  82. root.Add(template);
  83. if (debugMode) Debug.Log($"✅ UXML template instantiated and added to root");
  84. // Apply style sheet
  85. if (popupStyleSheet != null)
  86. {
  87. root.styleSheets.Add(popupStyleSheet);
  88. if (debugMode) Debug.Log($"✅ USS stylesheet applied");
  89. }
  90. else
  91. {
  92. Debug.LogWarning("⚠️ No USS stylesheet assigned");
  93. }
  94. }
  95. else
  96. {
  97. Debug.LogWarning("No UXML template assigned, creating UI programmatically as fallback");
  98. CreateUIFallback(root);
  99. }
  100. // Cache UI element references
  101. CacheUIElements(root);
  102. // Setup event handlers
  103. if (backgroundOverlay != null)
  104. {
  105. // Block all mouse events from reaching the map below
  106. backgroundOverlay.RegisterCallback<ClickEvent>(OnBackgroundClick);
  107. backgroundOverlay.RegisterCallback<MouseDownEvent>(OnBackgroundMouseDown);
  108. backgroundOverlay.RegisterCallback<MouseUpEvent>(OnBackgroundMouseUp);
  109. backgroundOverlay.RegisterCallback<MouseMoveEvent>(OnBackgroundMouseMove);
  110. backgroundOverlay.RegisterCallback<WheelEvent>(OnBackgroundWheelEvent);
  111. }
  112. if (attackButton != null)
  113. attackButton.clicked += OnAttackClicked;
  114. if (runAwayButton != null)
  115. runAwayButton.clicked += OnRunAwayClicked;
  116. // Initially hide the popup
  117. HidePopup();
  118. }
  119. void CacheUIElements(VisualElement root)
  120. {
  121. // Cache references to UI elements by name/class
  122. popupContainer = root.Q<VisualElement>("combat-event-popup");
  123. backgroundOverlay = root.Q<VisualElement>("background-overlay");
  124. popupPanel = root.Q<VisualElement>("popup-panel");
  125. eventTitleLabel = root.Q<Label>("event-title");
  126. eventDescriptionLabel = root.Q<Label>("event-description");
  127. enemyListContainer = root.Q<VisualElement>("enemy-list");
  128. enemyItemTemplate = root.Q<VisualElement>("enemy-item-template");
  129. attackButton = root.Q<Button>("attack-button");
  130. runAwayButton = root.Q<Button>("run-away-button");
  131. // Debug what we found
  132. if (debugMode)
  133. {
  134. Debug.Log($"🔍 UI Elements found:");
  135. Debug.Log($" popupContainer: {(popupContainer != null ? "✅" : "❌")}");
  136. Debug.Log($" backgroundOverlay: {(backgroundOverlay != null ? "✅" : "❌")}");
  137. Debug.Log($" popupPanel: {(popupPanel != null ? "✅" : "❌")}");
  138. Debug.Log($" eventTitleLabel: {(eventTitleLabel != null ? "✅" : "❌")}");
  139. Debug.Log($" attackButton: {(attackButton != null ? "✅" : "❌")}");
  140. Debug.Log($" runAwayButton: {(runAwayButton != null ? "✅" : "❌")}");
  141. }
  142. // Hide the template
  143. if (enemyItemTemplate != null)
  144. enemyItemTemplate.style.display = DisplayStyle.None;
  145. // Debug missing elements
  146. if (popupContainer == null) Debug.LogError("Could not find 'combat-event-popup' element");
  147. if (attackButton == null) Debug.LogError("Could not find 'attack-button' element");
  148. if (runAwayButton == null) Debug.LogError("Could not find 'run-away-button' element");
  149. }
  150. /// <summary>
  151. /// Fallback UI creation if UXML template is not available
  152. /// </summary>
  153. void CreateUIFallback(VisualElement root)
  154. {
  155. // Create basic popup structure programmatically
  156. popupContainer = new VisualElement();
  157. popupContainer.name = "combat-event-popup";
  158. popupContainer.style.position = Position.Absolute;
  159. popupContainer.style.top = 0;
  160. popupContainer.style.left = 0;
  161. popupContainer.style.right = 0;
  162. popupContainer.style.bottom = 0;
  163. popupContainer.style.justifyContent = Justify.Center;
  164. popupContainer.style.alignItems = Align.Center;
  165. backgroundOverlay = new VisualElement();
  166. backgroundOverlay.name = "background-overlay";
  167. backgroundOverlay.style.position = Position.Absolute;
  168. backgroundOverlay.style.top = 0;
  169. backgroundOverlay.style.left = 0;
  170. backgroundOverlay.style.right = 0;
  171. backgroundOverlay.style.bottom = 0;
  172. backgroundOverlay.style.backgroundColor = new Color(0, 0, 0, 0.7f);
  173. popupPanel = new VisualElement();
  174. popupPanel.name = "popup-panel";
  175. popupPanel.style.backgroundColor = new Color(0.15f, 0.15f, 0.2f, 0.95f);
  176. popupPanel.style.borderTopLeftRadius = 10;
  177. popupPanel.style.borderTopRightRadius = 10;
  178. popupPanel.style.borderBottomLeftRadius = 10;
  179. popupPanel.style.borderBottomRightRadius = 10;
  180. popupPanel.style.minWidth = 400;
  181. popupPanel.style.paddingTop = 20;
  182. popupPanel.style.paddingBottom = 20;
  183. popupPanel.style.paddingLeft = 20;
  184. popupPanel.style.paddingRight = 20;
  185. eventTitleLabel = new Label("*** COMBAT ENCOUNTER! ***");
  186. eventTitleLabel.name = "event-title";
  187. eventTitleLabel.style.fontSize = 24;
  188. eventTitleLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
  189. eventDescriptionLabel = new Label();
  190. eventDescriptionLabel.name = "event-description";
  191. enemyListContainer = new VisualElement();
  192. enemyListContainer.name = "enemy-list";
  193. var buttonContainer = new VisualElement();
  194. buttonContainer.style.flexDirection = FlexDirection.Row;
  195. buttonContainer.style.justifyContent = Justify.SpaceAround;
  196. buttonContainer.style.marginTop = 20;
  197. runAwayButton = new Button();
  198. runAwayButton.name = "run-away-button";
  199. runAwayButton.text = "[FLEE] RUN AWAY";
  200. attackButton = new Button();
  201. attackButton.name = "attack-button";
  202. attackButton.text = "[FIGHT] ATTACK!";
  203. // Assemble the UI
  204. buttonContainer.Add(runAwayButton);
  205. buttonContainer.Add(attackButton);
  206. popupPanel.Add(eventTitleLabel);
  207. popupPanel.Add(eventDescriptionLabel);
  208. popupPanel.Add(enemyListContainer);
  209. popupPanel.Add(buttonContainer);
  210. popupContainer.Add(backgroundOverlay);
  211. popupContainer.Add(popupPanel);
  212. root.Add(popupContainer);
  213. }
  214. /// <summary>
  215. /// Show the combat encounter popup with battle data
  216. /// </summary>
  217. public void ShowCombatEncounter(BattleEventData battleData, TravelEventContext context, string description)
  218. {
  219. if (isPopupActive)
  220. {
  221. Debug.LogWarning("Combat popup is already active!");
  222. return;
  223. }
  224. currentBattleData = battleData;
  225. currentContext = context;
  226. isPopupActive = true;
  227. if (debugMode) Debug.Log($"🎭 Showing combat popup with {battleData.enemyCount} {battleData.enemyType}(s)");
  228. // Update UI content
  229. UpdatePopupContent(battleData, description);
  230. // Show popup with animation
  231. ShowPopup();
  232. }
  233. void UpdatePopupContent(BattleEventData battleData, string description)
  234. {
  235. // Update description
  236. if (eventDescriptionLabel != null)
  237. eventDescriptionLabel.text = description;
  238. // Clear existing enemy items
  239. if (enemyListContainer != null)
  240. {
  241. var existingItems = enemyListContainer.Children().Where(child =>
  242. child.name != "enemy-item-template").ToList();
  243. foreach (var item in existingItems)
  244. {
  245. enemyListContainer.Remove(item);
  246. }
  247. // Create enemy display items
  248. CreateEnemyDisplays(battleData);
  249. }
  250. }
  251. void CreateEnemyDisplays(BattleEventData battleData)
  252. {
  253. if (battleData.enemyCharacterData == null)
  254. {
  255. Debug.LogWarning("No enemy character data available for display");
  256. return;
  257. }
  258. var enemyData = battleData.enemyCharacterData;
  259. // Create enemy item element
  260. VisualElement enemyItem;
  261. if (enemyItemTemplate != null)
  262. {
  263. // Clone the template by creating a new instance from the UXML
  264. enemyItem = CreateEnemyItemFromTemplate();
  265. }
  266. else
  267. {
  268. // Create manually if no template
  269. enemyItem = CreateEnemyItemFallback();
  270. }
  271. // Update enemy information
  272. var nameLabel = enemyItem.Q<Label>("enemy-name");
  273. var statsLabel = enemyItem.Q<Label>("enemy-stats");
  274. var weaponLabel = enemyItem.Q<Label>("enemy-weapon");
  275. var threatIndicator = enemyItem.Q<VisualElement>("threat-indicator");
  276. var threatLabel = enemyItem.Q<Label>("threat-label");
  277. if (nameLabel != null)
  278. nameLabel.text = $"{battleData.enemyCount}x {enemyData.enemyName}";
  279. if (statsLabel != null)
  280. statsLabel.text = $"HP: {enemyData.maxHealth} | AC: {enemyData.armorClass} | Threat: {enemyData.threatLevel}";
  281. if (weaponLabel != null && enemyData.preferredWeapon != null)
  282. weaponLabel.text = $"[WEAPON] {enemyData.preferredWeapon.itemName}";
  283. // Update threat indicator
  284. if (threatIndicator != null && threatLabel != null)
  285. {
  286. threatLabel.text = $"T{enemyData.threatLevel}";
  287. // Remove existing threat classes
  288. threatIndicator.RemoveFromClassList("threat-low");
  289. threatIndicator.RemoveFromClassList("threat-medium");
  290. threatIndicator.RemoveFromClassList("threat-high");
  291. threatIndicator.RemoveFromClassList("threat-extreme");
  292. // Add appropriate threat class
  293. if (enemyData.threatLevel <= 2)
  294. threatIndicator.AddToClassList("threat-low");
  295. else if (enemyData.threatLevel <= 4)
  296. threatIndicator.AddToClassList("threat-medium");
  297. else if (enemyData.threatLevel <= 6)
  298. threatIndicator.AddToClassList("threat-high");
  299. else
  300. threatIndicator.AddToClassList("threat-extreme");
  301. }
  302. // Add hover effect
  303. enemyItem.RegisterCallback<MouseEnterEvent>(evt =>
  304. {
  305. enemyItem.AddToClassList("enemy-item-hover");
  306. });
  307. enemyItem.RegisterCallback<MouseLeaveEvent>(evt =>
  308. {
  309. enemyItem.RemoveFromClassList("enemy-item-hover");
  310. });
  311. enemyListContainer.Add(enemyItem);
  312. }
  313. VisualElement CreateEnemyItemFromTemplate()
  314. {
  315. // Create a new enemy item based on the template structure
  316. var item = new VisualElement();
  317. item.style.flexDirection = FlexDirection.Row;
  318. item.style.justifyContent = Justify.SpaceBetween;
  319. item.style.alignItems = Align.Center;
  320. item.style.marginBottom = 8;
  321. item.style.paddingTop = 8;
  322. item.style.paddingBottom = 8;
  323. item.style.paddingLeft = 10;
  324. item.style.paddingRight = 10;
  325. item.style.backgroundColor = new Color(0.2f, 0.2f, 0.25f, 0.6f);
  326. item.style.borderTopLeftRadius = 4;
  327. item.style.borderTopRightRadius = 4;
  328. item.style.borderBottomLeftRadius = 4;
  329. item.style.borderBottomRightRadius = 4;
  330. item.AddToClassList("enemy-item");
  331. // Enemy info container
  332. var infoContainer = new VisualElement();
  333. infoContainer.name = "enemy-info";
  334. infoContainer.AddToClassList("enemy-info");
  335. infoContainer.style.flexDirection = FlexDirection.Column;
  336. infoContainer.style.flexGrow = 1;
  337. // Enemy name label
  338. var nameLabel = new Label();
  339. nameLabel.name = "enemy-name";
  340. nameLabel.AddToClassList("enemy-name");
  341. nameLabel.style.fontSize = 16;
  342. nameLabel.style.color = new Color(1f, 0.9f, 0.7f, 1f);
  343. // Enemy stats label
  344. var statsLabel = new Label();
  345. statsLabel.name = "enemy-stats";
  346. statsLabel.AddToClassList("enemy-stats");
  347. statsLabel.style.fontSize = 12;
  348. statsLabel.style.color = new Color(0.8f, 0.8f, 0.8f, 1f);
  349. // Enemy weapon label
  350. var weaponLabel = new Label();
  351. weaponLabel.name = "enemy-weapon";
  352. weaponLabel.AddToClassList("enemy-weapon");
  353. weaponLabel.style.fontSize = 11;
  354. weaponLabel.style.color = new Color(0.7f, 0.7f, 1f, 1f);
  355. // Threat indicator
  356. var threatIndicator = new VisualElement();
  357. threatIndicator.name = "threat-indicator";
  358. threatIndicator.AddToClassList("threat-indicator");
  359. threatIndicator.style.width = 60;
  360. threatIndicator.style.height = 20;
  361. threatIndicator.style.borderTopLeftRadius = 10;
  362. threatIndicator.style.borderTopRightRadius = 10;
  363. threatIndicator.style.borderBottomLeftRadius = 10;
  364. threatIndicator.style.borderBottomRightRadius = 10;
  365. threatIndicator.style.justifyContent = Justify.Center;
  366. threatIndicator.style.alignItems = Align.Center;
  367. threatIndicator.style.backgroundColor = new Color(0.3f, 0.8f, 0.3f, 1f);
  368. // Threat label
  369. var threatLabel = new Label();
  370. threatLabel.name = "threat-label";
  371. threatLabel.AddToClassList("threat-label");
  372. threatLabel.style.fontSize = 11;
  373. threatLabel.style.color = Color.white;
  374. // Assemble the structure
  375. threatIndicator.Add(threatLabel);
  376. infoContainer.Add(nameLabel);
  377. infoContainer.Add(statsLabel);
  378. infoContainer.Add(weaponLabel);
  379. item.Add(infoContainer);
  380. item.Add(threatIndicator);
  381. return item;
  382. }
  383. VisualElement CreateEnemyItemFallback()
  384. {
  385. var item = new VisualElement();
  386. item.style.flexDirection = FlexDirection.Row;
  387. item.style.justifyContent = Justify.SpaceBetween;
  388. item.style.alignItems = Align.Center;
  389. item.style.marginBottom = 8;
  390. item.style.paddingTop = 8;
  391. item.style.paddingBottom = 8;
  392. item.style.paddingLeft = 10;
  393. item.style.paddingRight = 10;
  394. item.style.backgroundColor = new Color(0.2f, 0.2f, 0.25f, 0.6f);
  395. item.style.borderTopLeftRadius = 4;
  396. item.style.borderTopRightRadius = 4;
  397. item.style.borderBottomLeftRadius = 4;
  398. item.style.borderBottomRightRadius = 4;
  399. var infoContainer = new VisualElement();
  400. infoContainer.name = "enemy-info";
  401. infoContainer.style.flexDirection = FlexDirection.Column;
  402. infoContainer.style.flexGrow = 1;
  403. var nameLabel = new Label();
  404. nameLabel.name = "enemy-name";
  405. nameLabel.style.fontSize = 16;
  406. nameLabel.style.color = new Color(1f, 0.9f, 0.7f, 1f);
  407. var statsLabel = new Label();
  408. statsLabel.name = "enemy-stats";
  409. statsLabel.style.fontSize = 12;
  410. statsLabel.style.color = new Color(0.8f, 0.8f, 0.8f, 1f);
  411. var weaponLabel = new Label();
  412. weaponLabel.name = "enemy-weapon";
  413. weaponLabel.style.fontSize = 11;
  414. weaponLabel.style.color = new Color(0.7f, 0.7f, 1f, 1f);
  415. var threatIndicator = new VisualElement();
  416. threatIndicator.name = "threat-indicator";
  417. threatIndicator.style.width = 60;
  418. threatIndicator.style.height = 20;
  419. threatIndicator.style.borderTopLeftRadius = 10;
  420. threatIndicator.style.borderTopRightRadius = 10;
  421. threatIndicator.style.borderBottomLeftRadius = 10;
  422. threatIndicator.style.borderBottomRightRadius = 10;
  423. threatIndicator.style.justifyContent = Justify.Center;
  424. threatIndicator.style.alignItems = Align.Center;
  425. threatIndicator.style.backgroundColor = new Color(0.3f, 0.8f, 0.3f, 1f);
  426. var threatLabel = new Label();
  427. threatLabel.name = "threat-label";
  428. threatLabel.style.fontSize = 11;
  429. threatLabel.style.color = Color.white;
  430. threatIndicator.Add(threatLabel);
  431. infoContainer.Add(nameLabel);
  432. infoContainer.Add(statsLabel);
  433. infoContainer.Add(weaponLabel);
  434. item.Add(infoContainer);
  435. item.Add(threatIndicator);
  436. return item;
  437. }
  438. void ShowPopup()
  439. {
  440. if (popupContainer != null)
  441. {
  442. if (debugMode) Debug.Log($"🎭 ShowPopup: Setting display to Flex, current display: {popupContainer.style.display}");
  443. popupContainer.style.display = DisplayStyle.Flex;
  444. // Add animation classes
  445. popupContainer.RemoveFromClassList("popup-hidden");
  446. popupContainer.RemoveFromClassList("popup-exit");
  447. popupContainer.AddToClassList("popup-enter");
  448. // Check if UI Document is active and panel settings are configured
  449. if (uiDocument != null && uiDocument.panelSettings != null)
  450. {
  451. if (debugMode) Debug.Log($"✅ UIDocument active: {uiDocument.enabled}, Panel Settings: {uiDocument.panelSettings.name}");
  452. }
  453. else
  454. {
  455. Debug.LogError("❌ UIDocument or Panel Settings not configured properly!");
  456. }
  457. Debug.Log("✅ Combat popup displayed");
  458. }
  459. else
  460. {
  461. Debug.LogError("❌ Cannot show popup - popupContainer is null!");
  462. }
  463. }
  464. public void HidePopup()
  465. {
  466. if (popupContainer != null)
  467. {
  468. // Add exit animation
  469. popupContainer.RemoveFromClassList("popup-enter");
  470. popupContainer.AddToClassList("popup-exit");
  471. // Hide after animation
  472. this.ExecuteAfterDelay(() =>
  473. {
  474. popupContainer.style.display = DisplayStyle.None;
  475. popupContainer.AddToClassList("popup-hidden");
  476. }, animationDuration);
  477. isPopupActive = false;
  478. if (debugMode) Debug.Log("✅ Combat popup hidden");
  479. }
  480. }
  481. void OnBackgroundClick(ClickEvent evt)
  482. {
  483. // Block the click event from reaching elements below
  484. evt.StopPropagation();
  485. // Optionally allow closing popup by clicking background
  486. // Uncomment if you want this behavior:
  487. // OnRunAwayClicked();
  488. }
  489. void OnBackgroundMouseDown(MouseDownEvent evt)
  490. {
  491. // Block mouse down events from reaching the map below
  492. evt.StopPropagation();
  493. }
  494. void OnBackgroundMouseUp(MouseUpEvent evt)
  495. {
  496. // Block mouse up events from reaching the map below
  497. evt.StopPropagation();
  498. }
  499. void OnBackgroundMouseMove(MouseMoveEvent evt)
  500. {
  501. // Block mouse move events from reaching the map below
  502. evt.StopPropagation();
  503. }
  504. void OnBackgroundWheelEvent(WheelEvent evt)
  505. {
  506. // Block wheel/scroll events from reaching the map below
  507. evt.StopPropagation();
  508. }
  509. void OnAttackClicked()
  510. {
  511. Debug.Log("🗡️ Player chose to ATTACK!");
  512. HidePopup();
  513. OnCombatDecision?.Invoke(true);
  514. }
  515. void OnRunAwayClicked()
  516. {
  517. Debug.Log("🏃 Player chose to RUN AWAY!");
  518. HidePopup();
  519. OnCombatDecision?.Invoke(false);
  520. }
  521. /// <summary>
  522. /// IClickBlocker implementation: Check if this UI should block clicks at the given position
  523. /// </summary>
  524. public bool IsBlockingClick(Vector2 screenPosition)
  525. {
  526. if (!isPopupActive || popupContainer == null || uiDocument == null)
  527. {
  528. return false;
  529. }
  530. // Convert screen position to panel-relative position
  531. Vector2 panelPosition = RuntimePanelUtils.ScreenToPanel(uiDocument.rootVisualElement.panel, screenPosition);
  532. // Check if the position is within the popup container bounds
  533. bool withinBounds = popupContainer.worldBound.Contains(panelPosition);
  534. return withinBounds;
  535. }
  536. /// <summary>
  537. /// Legacy method for backward compatibility - now delegates to IsBlockingClick
  538. /// Check if a screen position is within the UI bounds to block map interaction
  539. /// </summary>
  540. public bool IsPointWithinUI(Vector2 screenPosition)
  541. {
  542. return IsBlockingClick(screenPosition);
  543. }
  544. /// <summary>
  545. /// Public getters for external access
  546. /// </summary>
  547. public bool IsVisible => isPopupActive;
  548. }
  549. /// <summary>
  550. /// Extension method for delayed execution
  551. /// </summary>
  552. public static class MonoBehaviourExtensions
  553. {
  554. public static void ExecuteAfterDelay(this MonoBehaviour mono, System.Action action, float delay)
  555. {
  556. mono.StartCoroutine(ExecuteAfterDelayCoroutine(action, delay));
  557. }
  558. private static System.Collections.IEnumerator ExecuteAfterDelayCoroutine(System.Action action, float delay)
  559. {
  560. yield return new WaitForSeconds(delay);
  561. action?.Invoke();
  562. }
  563. }