CombatEventPopupUXML.cs 23 KB

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