GameUI.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.UIElements;
  4. using UnityEngine.InputSystem;
  5. public class GameUI : MonoBehaviour
  6. {
  7. [Header("UI Documents")]
  8. public UIDocument mainUIDocument;
  9. private VisualElement root;
  10. private Label woodLabel;
  11. private Label stoneLabel;
  12. private Label foodLabel;
  13. private Label timeSpeedLabel;
  14. private Button pauseButton;
  15. private Button speed1xButton;
  16. private Button speed2xButton;
  17. private Button speed10xButton;
  18. // Villager Panel
  19. private VisualElement villagerPanel;
  20. private Label villagerInfoLabel;
  21. private VisualElement jobButtonsContainer;
  22. private List<Button> jobButtons = new List<Button>();
  23. private Villager currentSelectedVillager; // Store reference to avoid deselection issues
  24. // Selected Object Panel
  25. private VisualElement selectionPanel;
  26. private Label selectionInfoLabel;
  27. void Start()
  28. {
  29. StartCoroutine(InitializeWhenReady());
  30. }
  31. System.Collections.IEnumerator InitializeWhenReady()
  32. {
  33. // Wait for GameManager to be initialized
  34. while (GameManager.Instance == null)
  35. {
  36. yield return null;
  37. }
  38. Debug.Log("GameManager found, initializing UI...");
  39. InitializeUI();
  40. SubscribeToEvents();
  41. // Update initial UI state after a short delay to ensure everything is ready
  42. yield return StartCoroutine(DelayedUIUpdate());
  43. }
  44. System.Collections.IEnumerator DelayedUIUpdate()
  45. {
  46. // Wait a few frames to ensure everything is initialized
  47. yield return new WaitForEndOfFrame();
  48. yield return new WaitForEndOfFrame();
  49. Debug.Log("Performing delayed UI update...");
  50. UpdateAllUI();
  51. // Force update resource displays with current values
  52. if (GameManager.Instance?.resourceManager != null)
  53. {
  54. var resources = GameManager.Instance.resourceManager.GetAllResources();
  55. Debug.Log($"Current resources: Wood={resources.GetValueOrDefault(ResourceType.Wood, 0)}, Stone={resources.GetValueOrDefault(ResourceType.Stone, 0)}, Food={resources.GetValueOrDefault(ResourceType.Food, 0)}");
  56. // Force manual UI updates
  57. foreach (var kvp in resources)
  58. {
  59. UpdateResourceDisplay(kvp.Key, kvp.Value);
  60. }
  61. }
  62. }
  63. void InitializeUI()
  64. {
  65. if (mainUIDocument == null)
  66. {
  67. mainUIDocument = GetComponent<UIDocument>();
  68. }
  69. if (mainUIDocument == null)
  70. {
  71. Debug.LogError("UI Document is required for GameUI! Please assign it in the inspector.");
  72. return;
  73. }
  74. Debug.Log($"UI Document found: {mainUIDocument.name}");
  75. Debug.Log($"UI Document's visual tree asset: {(mainUIDocument.visualTreeAsset != null ? mainUIDocument.visualTreeAsset.name : "NULL")}");
  76. if (mainUIDocument.visualTreeAsset == null)
  77. {
  78. Debug.LogWarning("UIDocument has no Visual Tree Asset assigned. Trying to auto-load...");
  79. // Try to load the UXML file from Resources folder
  80. VisualTreeAsset uxml = Resources.Load<VisualTreeAsset>("GameUI");
  81. if (uxml == null)
  82. {
  83. // Try alternative paths
  84. uxml = Resources.Load<VisualTreeAsset>("UI/GameUI");
  85. }
  86. if (uxml == null)
  87. {
  88. Debug.Log("Trying to load with .uxml extension...");
  89. // Some versions need explicit extension
  90. uxml = Resources.Load<VisualTreeAsset>("GameUI.uxml");
  91. }
  92. if (uxml != null)
  93. {
  94. mainUIDocument.visualTreeAsset = uxml;
  95. Debug.Log($"Auto-loaded UXML asset: {uxml.name}");
  96. }
  97. else
  98. {
  99. Debug.LogError("Could not auto-load GameUI.uxml! Please assign it manually or move it to Resources folder.");
  100. AttemptAutoFixUIDocument();
  101. return;
  102. }
  103. }
  104. root = mainUIDocument.rootVisualElement;
  105. Debug.Log($"Root element found: {root != null}, children count: {root?.childCount ?? 0}");
  106. if (root != null && root.childCount == 0)
  107. {
  108. Debug.LogError($"Root element has no children! The UXML file '{mainUIDocument.visualTreeAsset.name}' may be empty or not loading properly.");
  109. return;
  110. }
  111. // Get resource labels
  112. woodLabel = root.Q<Label>("wood-amount");
  113. stoneLabel = root.Q<Label>("stone-amount");
  114. foodLabel = root.Q<Label>("food-amount");
  115. Debug.Log($"Resource UI found - Wood: {woodLabel != null}, Stone: {stoneLabel != null}, Food: {foodLabel != null}");
  116. // Get time controls
  117. timeSpeedLabel = root.Q<Label>("time-speed");
  118. pauseButton = root.Q<Button>("pause-button");
  119. speed1xButton = root.Q<Button>("speed-1x");
  120. speed2xButton = root.Q<Button>("speed-2x");
  121. speed10xButton = root.Q<Button>("speed-10x");
  122. Debug.Log($"Time UI found - Speed: {timeSpeedLabel != null}, Pause: {pauseButton != null}, 1x: {speed1xButton != null}, 2x: {speed2xButton != null}, 10x: {speed10xButton != null}");
  123. // Get villager panel
  124. villagerPanel = root.Q<VisualElement>("villager-panel");
  125. villagerInfoLabel = root.Q<Label>("villager-info");
  126. jobButtonsContainer = root.Q<VisualElement>("job-buttons");
  127. Debug.Log($"Villager UI found - Panel: {villagerPanel != null}, Info: {villagerInfoLabel != null}, Jobs: {jobButtonsContainer != null}");
  128. // Get selection panel
  129. selectionPanel = root.Q<VisualElement>("selection-panel");
  130. selectionInfoLabel = root.Q<Label>("selection-info");
  131. Debug.Log($"Selection UI found - Panel: {selectionPanel != null}, Info: {selectionInfoLabel != null}");
  132. // Set up button callbacks
  133. SetupButtons();
  134. // Create job buttons
  135. CreateJobButtons();
  136. // Debug: List all found UI elements
  137. Debug.Log($"UI Initialization Summary:");
  138. Debug.Log($" Resource Labels - Wood: {woodLabel != null}, Stone: {stoneLabel != null}, Food: {foodLabel != null}");
  139. Debug.Log($" Time Controls - Speed Label: {timeSpeedLabel != null}, Buttons: {pauseButton != null}");
  140. Debug.Log($" Villager Panel - Panel: {villagerPanel != null}, Info: {villagerInfoLabel != null}");
  141. Debug.Log($" Selection Panel - Panel: {selectionPanel != null}, Info: {selectionInfoLabel != null}");
  142. // If no elements found, try to auto-fix by loading the UXML file
  143. if (woodLabel == null && stoneLabel == null && foodLabel == null)
  144. {
  145. AttemptAutoFixUIDocument();
  146. }
  147. }
  148. void AttemptAutoFixUIDocument()
  149. {
  150. Debug.LogError("=== UI SETUP ISSUE DETECTED ===");
  151. Debug.LogError("The UIDocument has no Visual Tree Asset assigned or the UXML file is empty.");
  152. Debug.LogError("To fix this:");
  153. Debug.LogError("1. Select the GameManager GameObject in the scene");
  154. Debug.LogError("2. Look at the UIDocument component in the inspector");
  155. Debug.LogError("3. Assign the GameUI.uxml file to the 'Visual Tree Asset' field");
  156. Debug.LogError("4. The GameUI.uxml file should be located at Assets/UI/GameUI.uxml");
  157. Debug.LogError("================================");
  158. }
  159. void SetupButtons()
  160. {
  161. // Time control buttons
  162. if (pauseButton != null)
  163. {
  164. pauseButton.RegisterCallback<ClickEvent>(evt =>
  165. {
  166. if (GameManager.Instance != null)
  167. {
  168. GameManager.Instance.TogglePause();
  169. Debug.Log("Pause button clicked");
  170. }
  171. });
  172. }
  173. if (speed1xButton != null)
  174. {
  175. speed1xButton.RegisterCallback<ClickEvent>(evt =>
  176. {
  177. if (GameManager.Instance?.timeManager != null)
  178. {
  179. GameManager.Instance.timeManager.SetSpeed(0);
  180. Debug.Log("1x speed button clicked");
  181. }
  182. });
  183. }
  184. if (speed2xButton != null)
  185. {
  186. speed2xButton.RegisterCallback<ClickEvent>(evt =>
  187. {
  188. if (GameManager.Instance?.timeManager != null)
  189. {
  190. GameManager.Instance.timeManager.SetSpeed(1);
  191. Debug.Log("2x speed button clicked");
  192. }
  193. });
  194. }
  195. if (speed10xButton != null)
  196. {
  197. speed10xButton.RegisterCallback<ClickEvent>(evt =>
  198. {
  199. if (GameManager.Instance?.timeManager != null)
  200. {
  201. GameManager.Instance.timeManager.SetSpeed(2);
  202. Debug.Log("10x speed button clicked");
  203. }
  204. });
  205. }
  206. }
  207. void CreateJobButtons()
  208. {
  209. if (jobButtonsContainer == null)
  210. {
  211. Debug.LogWarning("Job buttons container not found!");
  212. return;
  213. }
  214. // Clear existing buttons
  215. jobButtonsContainer.Clear();
  216. jobButtons.Clear();
  217. // Create job buttons (skip None job type)
  218. foreach (JobType job in System.Enum.GetValues(typeof(JobType)))
  219. {
  220. if (job == JobType.None) continue; // Skip "None" job
  221. Button jobButton = new Button();
  222. jobButton.text = GetJobDisplayName(job);
  223. jobButton.AddToClassList("job-button");
  224. jobButton.RegisterCallback<ClickEvent>(evt => AssignJobToSelectedVillager(job));
  225. // Initially disable all job buttons
  226. jobButton.SetEnabled(false);
  227. jobButtonsContainer.Add(jobButton);
  228. jobButtons.Add(jobButton);
  229. }
  230. Debug.Log($"Created {jobButtons.Count} job buttons (initially disabled)");
  231. }
  232. string GetJobDisplayName(JobType job)
  233. {
  234. return job switch
  235. {
  236. JobType.Woodcutter => "Woodcutter",
  237. JobType.Stonecutter => "Stonecutter",
  238. JobType.Farmer => "Farmer",
  239. JobType.Builder => "Builder",
  240. _ => job.ToString()
  241. };
  242. }
  243. void AssignJobToSelectedVillager(JobType job)
  244. {
  245. // Use stored reference instead of real-time selection to avoid deselection issues
  246. if (currentSelectedVillager == null)
  247. {
  248. Debug.Log("No villager stored for job assignment - this shouldn't happen if buttons are disabled properly");
  249. return;
  250. }
  251. if (GameManager.Instance?.villagerManager == null)
  252. {
  253. Debug.LogWarning("VillagerManager not found!");
  254. return;
  255. }
  256. Debug.Log($"Assigning job {job} to stored villager: {currentSelectedVillager.name}");
  257. // Find the closest available workplace for this job
  258. GameObject workplace = GameManager.Instance.villagerManager.FindWorkplaceForJob(job, currentSelectedVillager.transform.position);
  259. if (workplace != null)
  260. {
  261. // Assign villager to specific workplace
  262. GameManager.Instance.villagerManager.AssignVillagerToSpecificWorkplace(currentSelectedVillager, job, workplace);
  263. Debug.Log($"Assigned {currentSelectedVillager.name} to {job} at {workplace.name}");
  264. }
  265. else
  266. {
  267. // Fallback to general job assignment if no specific workplace found
  268. GameManager.Instance.villagerManager.AssignVillagerToJob(currentSelectedVillager, job);
  269. Debug.Log($"Assigned {currentSelectedVillager.name} to {job} (no specific workplace found)");
  270. }
  271. // Update the villager panel to show new job
  272. UpdateVillagerPanel(currentSelectedVillager);
  273. }
  274. void SubscribeToEvents()
  275. {
  276. // Subscribe to resource changes
  277. ResourceManager.OnResourceChanged += UpdateResourceDisplay;
  278. // Subscribe to time changes
  279. TimeManager.OnTimeSpeedChanged += UpdateTimeDisplay;
  280. TimeManager.OnPauseStateChanged += UpdatePauseDisplay;
  281. // Subscribe to villager selection
  282. Villager.OnVillagerSelected += UpdateVillagerPanel;
  283. }
  284. void OnDestroy()
  285. {
  286. // Unsubscribe from events
  287. ResourceManager.OnResourceChanged -= UpdateResourceDisplay;
  288. TimeManager.OnTimeSpeedChanged -= UpdateTimeDisplay;
  289. TimeManager.OnPauseStateChanged -= UpdatePauseDisplay;
  290. Villager.OnVillagerSelected -= UpdateVillagerPanel;
  291. }
  292. void UpdateAllUI()
  293. {
  294. // Update resource displays
  295. if (GameManager.Instance?.resourceManager != null)
  296. {
  297. var resources = GameManager.Instance.resourceManager.GetAllResources();
  298. foreach (var kvp in resources)
  299. {
  300. UpdateResourceDisplay(kvp.Key, kvp.Value);
  301. }
  302. }
  303. // Update time display
  304. if (GameManager.Instance?.timeManager != null)
  305. {
  306. UpdateTimeDisplay(GameManager.Instance.timeManager.CurrentTimeScale);
  307. UpdatePauseDisplay(GameManager.Instance.timeManager.IsPaused);
  308. }
  309. // Update selection
  310. UpdateSelectionPanel();
  311. }
  312. void UpdateResourceDisplay(ResourceType type, int amount)
  313. {
  314. Label targetLabel = null;
  315. string elementName = "";
  316. switch (type)
  317. {
  318. case ResourceType.Wood:
  319. targetLabel = woodLabel;
  320. elementName = "wood-amount";
  321. break;
  322. case ResourceType.Stone:
  323. targetLabel = stoneLabel;
  324. elementName = "stone-amount";
  325. break;
  326. case ResourceType.Food:
  327. targetLabel = foodLabel;
  328. elementName = "food-amount";
  329. break;
  330. }
  331. if (targetLabel != null)
  332. {
  333. targetLabel.text = amount.ToString();
  334. Debug.Log($"Updated {type} to {amount} successfully");
  335. }
  336. else
  337. {
  338. Debug.LogWarning($"Could not find UI label for {type} (looking for '{elementName}')");
  339. // Try to re-find the element
  340. if (root != null)
  341. {
  342. targetLabel = root.Q<Label>(elementName);
  343. if (targetLabel != null)
  344. {
  345. targetLabel.text = amount.ToString();
  346. Debug.Log($"Re-found and updated {type} to {amount}");
  347. // Update the cached reference
  348. switch (type)
  349. {
  350. case ResourceType.Wood: woodLabel = targetLabel; break;
  351. case ResourceType.Stone: stoneLabel = targetLabel; break;
  352. case ResourceType.Food: foodLabel = targetLabel; break;
  353. }
  354. }
  355. else
  356. {
  357. Debug.LogError($"Element '{elementName}' not found in UXML! Check that the UXML file contains an element with name='{elementName}'");
  358. }
  359. }
  360. }
  361. }
  362. void UpdateTimeDisplay(float timeScale)
  363. {
  364. if (timeSpeedLabel != null)
  365. {
  366. if (timeScale == 0f)
  367. {
  368. timeSpeedLabel.text = "Paused";
  369. }
  370. else if (timeScale == 1f)
  371. {
  372. timeSpeedLabel.text = "1x";
  373. }
  374. else if (timeScale == 2f)
  375. {
  376. timeSpeedLabel.text = "2x";
  377. }
  378. else if (timeScale == 10f)
  379. {
  380. timeSpeedLabel.text = "10x";
  381. }
  382. else
  383. {
  384. timeSpeedLabel.text = $"{timeScale}x";
  385. }
  386. Debug.Log($"Updated time speed to {timeSpeedLabel.text}");
  387. }
  388. else
  389. {
  390. Debug.LogWarning("Time speed label not found!");
  391. }
  392. }
  393. void UpdatePauseDisplay(bool isPaused)
  394. {
  395. if (pauseButton != null)
  396. {
  397. pauseButton.text = isPaused ? "Resume" : "Pause";
  398. Debug.Log($"Updated pause button to: {pauseButton.text}");
  399. }
  400. if (timeSpeedLabel != null && isPaused)
  401. {
  402. timeSpeedLabel.text = "Paused";
  403. }
  404. }
  405. public void UpdateVillagerPanel(Villager villager)
  406. {
  407. Debug.Log($"UpdateVillagerPanel called for: {(villager != null ? villager.name : "null")}");
  408. // Store the villager reference for job assignment
  409. currentSelectedVillager = villager;
  410. Debug.Log($"Stored villager reference: {(currentSelectedVillager != null ? currentSelectedVillager.name : "null")}");
  411. if (villagerInfoLabel == null)
  412. {
  413. Debug.LogWarning("villagerInfoLabel is null - cannot show villager info");
  414. return;
  415. }
  416. // Update job button states
  417. UpdateJobButtonStates(villager);
  418. if (villager == null)
  419. {
  420. villagerInfoLabel.text = "No villager selected";
  421. Debug.Log("Updated villager panel: No villager selected");
  422. }
  423. else
  424. {
  425. string jobText = villager.currentJob == JobType.None ? "Unemployed" : GetJobDisplayName(villager.currentJob);
  426. string info = $"Name: {villager.name}\n" +
  427. $"Job: {jobText}\n" +
  428. $"Experience: {villager.experience.GetExperienceForJob(villager.currentJob):F1}\n" +
  429. $"State: {villager.state}";
  430. villagerInfoLabel.text = info;
  431. Debug.Log($"Updated villager panel with info: {info}");
  432. }
  433. }
  434. void UpdateJobButtonStates(Villager selectedVillager)
  435. {
  436. bool hasSelection = selectedVillager != null;
  437. // Enable/disable all job buttons based on villager selection
  438. foreach (var button in jobButtons)
  439. {
  440. button.SetEnabled(hasSelection);
  441. // Optional: Change button appearance based on villager's current job
  442. if (hasSelection)
  443. {
  444. string buttonJobName = button.text;
  445. string currentJobName = GetJobDisplayName(selectedVillager.currentJob);
  446. if (buttonJobName == currentJobName)
  447. {
  448. button.AddToClassList("current-job"); // You can style this in USS
  449. }
  450. else
  451. {
  452. button.RemoveFromClassList("current-job");
  453. }
  454. }
  455. }
  456. Debug.Log($"Job buttons {(hasSelection ? "enabled" : "disabled")} - {jobButtons.Count} buttons updated");
  457. }
  458. // Public method to manually refresh all UI elements
  459. public void ManualUIRefresh()
  460. {
  461. Debug.Log("Manual UI refresh requested");
  462. // Re-find all UI elements
  463. if (root != null)
  464. {
  465. woodLabel = root.Q<Label>("wood-amount");
  466. stoneLabel = root.Q<Label>("stone-amount");
  467. foodLabel = root.Q<Label>("food-amount");
  468. villagerInfoLabel = root.Q<Label>("villager-info");
  469. selectionInfoLabel = root.Q<Label>("selection-info");
  470. Debug.Log($"Re-found UI elements - Wood: {woodLabel != null}, Stone: {stoneLabel != null}, Food: {foodLabel != null}, VillagerInfo: {villagerInfoLabel != null}");
  471. // Force update all displays
  472. UpdateAllUI();
  473. }
  474. }
  475. void Update()
  476. {
  477. // Check for debug key to manually refresh UI
  478. if (Keyboard.current != null && Keyboard.current.f5Key.wasPressedThisFrame)
  479. {
  480. Debug.Log("F5 pressed - Manual UI refresh");
  481. ManualUIRefresh();
  482. }
  483. }
  484. void UpdateSelectionPanel()
  485. {
  486. if (selectionInfoLabel == null) return;
  487. var selectedObject = GameManager.Instance?.selectionManager?.SelectedObject;
  488. var selectedVillager = GameManager.Instance?.selectionManager?.SelectedVillager;
  489. if (selectedObject == null && selectedVillager == null)
  490. {
  491. selectionInfoLabel.text = "Nothing selected";
  492. return;
  493. }
  494. string info = "";
  495. if (selectedVillager != null)
  496. {
  497. info = $"Selected: {selectedVillager.name}";
  498. }
  499. else if (selectedObject != null)
  500. {
  501. // Add specific info based on object type
  502. Building building = selectedObject.GetComponent<Building>();
  503. if (building != null)
  504. {
  505. info = building.GetBuildingInfo();
  506. }
  507. else
  508. {
  509. ResourceNode node = selectedObject.GetComponent<ResourceNode>();
  510. if (node != null)
  511. {
  512. info = node.GetNodeInfo();
  513. }
  514. else
  515. {
  516. info = "Selected: " + selectedObject.name;
  517. }
  518. }
  519. }
  520. selectionInfoLabel.text = info;
  521. }
  522. }