MapLegendUI.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. using UnityEngine;
  2. using UnityEngine.UIElements;
  3. using System.Reflection;
  4. public class MapSceneLegendUI : MonoBehaviour, IClickBlocker
  5. {
  6. [Header("UI References")]
  7. public UIDocument uiDocument;
  8. [Header("Settings")]
  9. public bool startVisible = true;
  10. public KeyCode toggleKey = KeyCode.L;
  11. public bool debugMode = true;
  12. // UI Elements
  13. private VisualElement legendContainer;
  14. private Button toggleButton;
  15. private VisualElement legendContent;
  16. private VisualElement nameToggleSection;
  17. // Name toggle buttons
  18. private Toggle settlementNamesToggle;
  19. private Toggle forestNamesToggle;
  20. private Toggle lakeNamesToggle;
  21. private Toggle plainNamesToggle;
  22. private Toggle mountainNamesToggle;
  23. private Toggle riverNamesToggle;
  24. // References
  25. private MapLocationNameDisplay nameDisplay;
  26. private bool isLegendVisible;
  27. // Helper method to call methods on WorldSpaceLocationNames using reflection
  28. private void TryCallWorldSpaceMethod(string methodName, bool parameter)
  29. {
  30. var allMonoBehaviours = FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None);
  31. foreach (var mb in allMonoBehaviours)
  32. {
  33. if (mb.GetType().Name == "WorldSpaceLocationNames")
  34. {
  35. var method = mb.GetType().GetMethod(methodName);
  36. if (method != null)
  37. {
  38. method.Invoke(mb, new object[] { parameter });
  39. break;
  40. }
  41. }
  42. }
  43. }
  44. private void Awake()
  45. {
  46. if (uiDocument == null)
  47. uiDocument = GetComponent<UIDocument>();
  48. }
  49. private void Start()
  50. {
  51. nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  52. if (debugMode)
  53. {
  54. Debug.Log($"MapSceneLegendUI: Starting setup. UIDocument: {(uiDocument != null ? "Found" : "NULL")}");
  55. Debug.Log($"MapSceneLegendUI: NameDisplay: {(nameDisplay != null ? "Found" : "NULL")}");
  56. Debug.Log($"MapSceneLegendUI: Root Visual Element: {(uiDocument?.rootVisualElement != null ? "Found" : "NULL")}");
  57. }
  58. SetupUI();
  59. SetLegendVisible(startVisible);
  60. if (debugMode)
  61. {
  62. Debug.Log($"MapSceneLegendUI: Setup complete. Legend visible: {isLegendVisible}");
  63. Debug.Log($"MapSceneLegendUI: Legend container: {(legendContainer != null ? "Found" : "NULL")}");
  64. }
  65. // Register with ClickManager
  66. if (ClickManager.Instance != null)
  67. {
  68. ClickManager.Instance.RegisterClickBlocker(this);
  69. if (debugMode) Debug.Log("MapSceneLegendUI: Registered with ClickManager");
  70. }
  71. else if (debugMode)
  72. {
  73. Debug.LogWarning("MapSceneLegendUI: ClickManager not found");
  74. }
  75. }
  76. private void OnDestroy()
  77. {
  78. // Unregister from ClickManager
  79. if (ClickManager.Instance != null)
  80. {
  81. ClickManager.Instance.UnregisterClickBlocker(this);
  82. }
  83. }
  84. private void Update()
  85. {
  86. if (Input.GetKeyDown(toggleKey))
  87. {
  88. ToggleLegend();
  89. }
  90. }
  91. private void SetupUI()
  92. {
  93. if (uiDocument == null)
  94. {
  95. if (debugMode) Debug.LogError("MapSceneLegendUI: UIDocument is null! Cannot setup UI.");
  96. return;
  97. }
  98. var root = uiDocument.rootVisualElement;
  99. if (root == null)
  100. {
  101. if (debugMode) Debug.LogError("MapSceneLegendUI: Root visual element is null!");
  102. return;
  103. }
  104. if (debugMode) Debug.Log("MapSceneLegendUI: Setting up UI...");
  105. // Try to find existing legend container from UXML first
  106. legendContainer = root.Q<VisualElement>("map-legend-container");
  107. if (legendContainer != null)
  108. {
  109. // UXML is loaded, use existing structure
  110. if (debugMode) Debug.Log("MapSceneLegendUI: Using UXML structure");
  111. legendContent = legendContainer;
  112. // Find the name toggle section
  113. nameToggleSection = root.Q<VisualElement>("name-toggle-section");
  114. if (nameToggleSection == null)
  115. {
  116. // Create it if not found
  117. nameToggleSection = new VisualElement();
  118. nameToggleSection.name = "name-toggle-section";
  119. nameToggleSection.AddToClassList("legend-section");
  120. var sectionTitle = new Label("Location Names");
  121. sectionTitle.AddToClassList("legend-section-title");
  122. nameToggleSection.Add(sectionTitle);
  123. legendContainer.Add(nameToggleSection);
  124. }
  125. // Create name toggles
  126. CreateNameTogglesSection();
  127. }
  128. else
  129. {
  130. // Fallback to programmatic creation
  131. if (debugMode) Debug.Log("MapSceneLegendUI: Using programmatic UI creation");
  132. // Create main legend container
  133. legendContainer = new VisualElement();
  134. legendContainer.name = "map-legend-container";
  135. legendContainer.AddToClassList("map-legend");
  136. // Create toggle button
  137. toggleButton = new Button(() => ToggleLegend());
  138. toggleButton.text = "Map Legend";
  139. toggleButton.name = "legend-toggle-button";
  140. toggleButton.AddToClassList("legend-toggle-btn");
  141. // Create legend content
  142. legendContent = new VisualElement();
  143. legendContent.name = "legend-content";
  144. legendContent.AddToClassList("legend-content");
  145. // Add title
  146. var title = new Label("Map Legend");
  147. title.AddToClassList("legend-title");
  148. legendContent.Add(title);
  149. // Create terrain legend section
  150. CreateTerrainLegend();
  151. // Create name toggles section
  152. CreateNameTogglesSection();
  153. // Create controls info
  154. CreateControlsInfo();
  155. // Assemble the UI
  156. legendContainer.Add(toggleButton);
  157. legendContainer.Add(legendContent);
  158. root.Add(legendContainer);
  159. }
  160. // Apply styles
  161. ApplyStyles();
  162. if (debugMode) Debug.Log("MapSceneLegendUI: UI setup complete");
  163. }
  164. private void CreateTerrainLegend()
  165. {
  166. var terrainSection = new VisualElement();
  167. terrainSection.AddToClassList("legend-section");
  168. var terrainTitle = new Label("Terrain");
  169. terrainTitle.AddToClassList("legend-section-title");
  170. terrainSection.Add(terrainTitle);
  171. // Terrain items
  172. var terrainItems = new[]
  173. {
  174. ("Ocean", "ocean-color"),
  175. ("Rivers / Lakes", "river-color"),
  176. ("Plains / Grasslands", "plain-color"),
  177. ("Forests", "forest-color"),
  178. ("Mountains", "mountain-color"),
  179. ("Towns / Cities", "town-color"),
  180. ("Villages", "village-color"),
  181. ("Roads", "road-color")
  182. };
  183. foreach (var (label, colorClass) in terrainItems)
  184. {
  185. var item = CreateLegendItem(label, colorClass);
  186. terrainSection.Add(item);
  187. }
  188. legendContent.Add(terrainSection);
  189. }
  190. private void CreateNameTogglesSection()
  191. {
  192. nameToggleSection = new VisualElement();
  193. nameToggleSection.AddToClassList("legend-section");
  194. var nameTitle = new Label("Location Names");
  195. nameTitle.AddToClassList("legend-section-title");
  196. nameToggleSection.Add(nameTitle);
  197. // Create toggle switches for each name type
  198. settlementNamesToggle = CreateNameToggle("Settlements", true, (show) =>
  199. {
  200. // Try both old and new name display systems
  201. if (nameDisplay == null) nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  202. if (nameDisplay != null) nameDisplay.ToggleSettlementNames(show);
  203. // Also try to find WorldSpaceLocationNames component using reflection
  204. TryCallWorldSpaceMethod("ToggleSettlementNames", show);
  205. });
  206. forestNamesToggle = CreateNameToggle("Forests", true, (show) =>
  207. {
  208. if (nameDisplay == null) nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  209. if (nameDisplay != null) nameDisplay.ToggleForestNames(show);
  210. TryCallWorldSpaceMethod("ToggleForestNames", show);
  211. });
  212. lakeNamesToggle = CreateNameToggle("Lakes", true, (show) =>
  213. {
  214. if (nameDisplay == null) nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  215. if (nameDisplay != null) nameDisplay.ToggleLakeNames(show);
  216. TryCallWorldSpaceMethod("ToggleLakeNames", show);
  217. });
  218. plainNamesToggle = CreateNameToggle("Plains", true, (show) =>
  219. {
  220. if (nameDisplay == null) nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  221. if (nameDisplay != null) nameDisplay.TogglePlainNames(show);
  222. TryCallWorldSpaceMethod("TogglePlainNames", show);
  223. });
  224. mountainNamesToggle = CreateNameToggle("Mountains", true, (show) =>
  225. {
  226. if (nameDisplay == null) nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  227. if (nameDisplay != null) nameDisplay.ToggleMountainNames(show);
  228. TryCallWorldSpaceMethod("ToggleMountainNames", show);
  229. });
  230. riverNamesToggle = CreateNameToggle("Rivers", true, (show) =>
  231. {
  232. if (nameDisplay == null) nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  233. if (nameDisplay != null) nameDisplay.ToggleRiverNames(show);
  234. TryCallWorldSpaceMethod("ToggleRiverNames", show);
  235. });
  236. nameToggleSection.Add(settlementNamesToggle);
  237. nameToggleSection.Add(forestNamesToggle);
  238. nameToggleSection.Add(lakeNamesToggle);
  239. nameToggleSection.Add(plainNamesToggle);
  240. nameToggleSection.Add(mountainNamesToggle);
  241. nameToggleSection.Add(riverNamesToggle);
  242. legendContent.Add(nameToggleSection);
  243. }
  244. private Toggle CreateNameToggle(string labelText, bool defaultValue, System.Action<bool> onValueChanged)
  245. {
  246. var toggle = new Toggle();
  247. toggle.text = labelText;
  248. toggle.value = defaultValue;
  249. toggle.AddToClassList("name-toggle");
  250. toggle.RegisterValueChangedCallback(evt => onValueChanged(evt.newValue));
  251. return toggle;
  252. }
  253. private void CreateControlsInfo()
  254. {
  255. var controlsSection = new VisualElement();
  256. controlsSection.AddToClassList("legend-section");
  257. controlsSection.AddToClassList("controls-info");
  258. var controlsTitle = new Label("Controls");
  259. controlsTitle.AddToClassList("legend-section-title");
  260. controlsSection.Add(controlsTitle);
  261. var controls = new[]
  262. {
  263. "WASD / Arrow Keys: Move Camera",
  264. "Mouse Wheel: Zoom In/Out",
  265. $"{toggleKey}: Toggle Legend",
  266. "Click: Select Location",
  267. "Mouse Hover: Show Details"
  268. };
  269. foreach (var controlText in controls)
  270. {
  271. var controlLabel = new Label(controlText);
  272. controlLabel.AddToClassList("controls-text");
  273. controlsSection.Add(controlLabel);
  274. }
  275. legendContent.Add(controlsSection);
  276. }
  277. private VisualElement CreateLegendItem(string labelText, string colorClass)
  278. {
  279. var item = new VisualElement();
  280. item.AddToClassList("legend-item");
  281. var colorBox = new VisualElement();
  282. colorBox.AddToClassList("color-box");
  283. colorBox.AddToClassList(colorClass);
  284. var label = new Label(labelText);
  285. label.AddToClassList("legend-text");
  286. item.Add(colorBox);
  287. item.Add(label);
  288. return item;
  289. }
  290. private void ApplyStyles()
  291. {
  292. var styleSheet = Resources.Load<StyleSheet>("UI/MapLegend");
  293. if (styleSheet != null)
  294. {
  295. uiDocument.rootVisualElement.styleSheets.Add(styleSheet);
  296. }
  297. else
  298. {
  299. // Apply inline styles if stylesheet not found
  300. ApplyInlineStyles();
  301. }
  302. }
  303. private void ApplyInlineStyles()
  304. {
  305. // Legend container positioning
  306. legendContainer.style.position = Position.Absolute;
  307. legendContainer.style.top = 20;
  308. legendContainer.style.left = 20;
  309. legendContainer.style.width = 250;
  310. // Toggle button
  311. toggleButton.style.backgroundColor = new Color(0.2f, 0.2f, 0.2f, 0.9f);
  312. toggleButton.style.color = Color.white;
  313. toggleButton.style.borderTopLeftRadius = 5;
  314. toggleButton.style.borderTopRightRadius = 5;
  315. toggleButton.style.borderBottomLeftRadius = 0;
  316. toggleButton.style.borderBottomRightRadius = 0;
  317. toggleButton.style.paddingTop = 8;
  318. toggleButton.style.paddingBottom = 8;
  319. toggleButton.style.paddingLeft = 12;
  320. toggleButton.style.paddingRight = 12;
  321. // Legend content
  322. legendContent.style.backgroundColor = new Color(0.1f, 0.1f, 0.1f, 0.9f);
  323. legendContent.style.borderBottomLeftRadius = 5;
  324. legendContent.style.borderBottomRightRadius = 5;
  325. legendContent.style.borderTopWidth = 1;
  326. legendContent.style.borderTopColor = new Color(0.4f, 0.4f, 0.4f, 0.8f);
  327. legendContent.style.paddingTop = 10;
  328. legendContent.style.paddingBottom = 10;
  329. legendContent.style.paddingLeft = 12;
  330. legendContent.style.paddingRight = 12;
  331. // Apply basic color classes
  332. ApplyTerrainColors();
  333. }
  334. private void ApplyTerrainColors()
  335. {
  336. // This would ideally be done via USS, but for now we'll handle it in the legend creation
  337. // The actual color styling should be handled by the MapLegend.uss file
  338. }
  339. public void ToggleLegend()
  340. {
  341. if (debugMode) Debug.Log($"MapSceneLegendUI: Toggling legend. Current state: {isLegendVisible}");
  342. SetLegendVisible(!isLegendVisible);
  343. }
  344. public void SetLegendVisible(bool visible)
  345. {
  346. isLegendVisible = visible;
  347. if (legendContent != null)
  348. {
  349. legendContent.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
  350. if (debugMode) Debug.Log($"MapSceneLegendUI: Set legend visible: {visible}");
  351. }
  352. else if (debugMode)
  353. {
  354. Debug.LogWarning("MapSceneLegendUI: Cannot set visibility - legendContent is null");
  355. }
  356. if (toggleButton != null)
  357. {
  358. toggleButton.text = visible ? "Hide Legend" : "Map Legend";
  359. }
  360. }
  361. public void OnFeaturesGenerated()
  362. {
  363. // Refresh the toggle states to match the current display settings
  364. if (nameDisplay != null)
  365. {
  366. settlementNamesToggle?.SetValueWithoutNotify(nameDisplay.showSettlementNames);
  367. forestNamesToggle?.SetValueWithoutNotify(nameDisplay.showForestNames);
  368. lakeNamesToggle?.SetValueWithoutNotify(nameDisplay.showLakeNames);
  369. plainNamesToggle?.SetValueWithoutNotify(nameDisplay.showPlainNames);
  370. mountainNamesToggle?.SetValueWithoutNotify(nameDisplay.showMountainNames);
  371. riverNamesToggle?.SetValueWithoutNotify(nameDisplay.showRiverNames);
  372. }
  373. }
  374. // IClickBlocker implementation
  375. public bool IsBlockingClick(Vector2 screenPosition)
  376. {
  377. if (!isLegendVisible || legendContainer == null)
  378. return false;
  379. // Convert screen position to UI coordinates and check if it's within the legend bounds
  380. var panelGeometry = legendContainer.worldBound;
  381. return panelGeometry.Contains(screenPosition);
  382. }
  383. [ContextMenu("Toggle Legend")]
  384. public void ToggleLegendManual()
  385. {
  386. ToggleLegend();
  387. }
  388. [ContextMenu("Show Legend")]
  389. public void ShowLegend()
  390. {
  391. SetLegendVisible(true);
  392. }
  393. [ContextMenu("Hide Legend")]
  394. public void HideLegend()
  395. {
  396. SetLegendVisible(false);
  397. }
  398. [ContextMenu("Debug Legend")]
  399. public void RefreshNameDisplayConnection()
  400. {
  401. // Try to find the name display if it wasn't found initially
  402. if (nameDisplay == null)
  403. {
  404. nameDisplay = FindFirstObjectByType<MapLocationNameDisplay>();
  405. if (debugMode && nameDisplay != null)
  406. {
  407. Debug.Log("MapSceneLegendUI: Successfully connected to MapLocationNameDisplay");
  408. }
  409. }
  410. }
  411. public void DebugLegend()
  412. {
  413. Debug.Log($"=== MapSceneLegendUI Debug Info ===");
  414. Debug.Log($"UIDocument: {(uiDocument != null ? "Found" : "NULL")}");
  415. Debug.Log($"LegendContainer: {(legendContainer != null ? "Found" : "NULL")}");
  416. Debug.Log($"LegendContent: {(legendContent != null ? "Found" : "NULL")}");
  417. Debug.Log($"IsLegendVisible: {isLegendVisible}");
  418. Debug.Log($"NameDisplay: {(nameDisplay != null ? "Found" : "NULL")}");
  419. if (uiDocument?.rootVisualElement != null)
  420. {
  421. Debug.Log($"Root element children: {uiDocument.rootVisualElement.childCount}");
  422. }
  423. }
  424. }