TownShopManager.cs 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. using UnityEngine;
  2. using UnityEngine.UIElements;
  3. using System.Collections.Generic;
  4. using System.Collections;
  5. using System.Linq;
  6. public class TownShopManager : MonoBehaviour
  7. {
  8. [Header("Shop Configuration")]
  9. public TownShop[] allShops;
  10. [Header("UI References")]
  11. public UIDocument townUI;
  12. private Dictionary<string, TownShop> shopLookup = new Dictionary<string, TownShop>();
  13. private Dictionary<string, EventCallback<ClickEvent>> clickCallbacks = new Dictionary<string, EventCallback<ClickEvent>>();
  14. private VisualElement rootElement;
  15. // Singleton pattern to prevent multiple instances
  16. private static TownShopManager instance;
  17. public static TownShopManager Instance => instance;
  18. // Prevent multiple shop UI creation
  19. private bool isShopUIOpen = false;
  20. void Awake()
  21. {
  22. Debug.Log($"TownShopManager.Awake() called on {gameObject.name}");
  23. // Check if another instance already exists
  24. if (instance != null)
  25. {
  26. if (instance == this)
  27. {
  28. Debug.Log($"TownShopManager: Same instance re-awakening on {gameObject.name}");
  29. }
  30. else
  31. {
  32. Debug.LogWarning($"Duplicate TownShopManager detected! Existing: {instance.gameObject.name}, New: {gameObject.name}");
  33. // Before destroying, transfer any UI reference to the existing instance
  34. if (townUI != null && instance.townUI == null)
  35. {
  36. Debug.Log("Transferring UI reference to existing singleton");
  37. instance.townUI = townUI;
  38. // Also transfer rootElement if available
  39. if (instance.rootElement == null && townUI != null)
  40. {
  41. instance.rootElement = townUI.rootVisualElement;
  42. }
  43. }
  44. Debug.LogWarning($"Destroying duplicate on {gameObject.name}");
  45. Destroy(gameObject);
  46. return;
  47. }
  48. }
  49. else
  50. {
  51. instance = this;
  52. Debug.Log($"TownShopManager singleton established on {gameObject.name}");
  53. }
  54. // Configure shops immediately in Awake to ensure they're ready before Start()
  55. StartCoroutine(ConfigureShopsEarly());
  56. }
  57. private System.Collections.IEnumerator ConfigureShopsEarly()
  58. {
  59. Debug.Log($"ConfigureShopsEarly starting on {gameObject.name}");
  60. // Wait a few frames for SettlementContext to be available and TownShop components to start
  61. yield return null;
  62. yield return null;
  63. yield return null;
  64. Debug.Log($"TownShopManager: Configuring shops early in Awake on {gameObject.name}");
  65. // Always create default shops first (creates the GameObjects)
  66. if (allShops == null || allShops.Length == 0)
  67. {
  68. Debug.Log($"Creating default shops on {gameObject.name}");
  69. CreateDefaultShops();
  70. Debug.Log($"After CreateDefaultShops: allShops.Length = {allShops?.Length ?? 0}");
  71. }
  72. else
  73. {
  74. Debug.Log($"Shops already exist: {allShops.Length} shops");
  75. }
  76. // Wait another frame for shops to finish their Start() methods
  77. yield return null;
  78. // Then configure them based on SettlementContext if available
  79. if (SettlementContext.Instance != null)
  80. {
  81. Debug.Log($"=== SETTLEMENT DEBUG on {gameObject.name} ===");
  82. Debug.Log($"Settlement Name: {SettlementContext.Instance.settlementName}");
  83. Debug.Log($"Settlement Type: {SettlementContext.Instance.settlementType}");
  84. Debug.Log($"Settlement Seed: {SettlementContext.Instance.settlementSeed}");
  85. Debug.Log($"=== Configuring shops for {SettlementContext.Instance.settlementType} ===");
  86. ConfigureShopsFromSettlement();
  87. UpdateAllBuildingNames();
  88. }
  89. else
  90. {
  91. Debug.LogWarning("TownShopManager: SettlementContext.Instance is NULL in Awake - shops will use default names");
  92. // Configure with default names when no SettlementContext (for testing)
  93. ConfigureShopsWithDefaults();
  94. }
  95. // Build the shop lookup table BEFORE setting up UI
  96. Debug.Log($"ConfigureShopsEarly: Building shop lookup table for {allShops?.Length ?? 0} shops");
  97. shopLookup.Clear();
  98. if (allShops != null)
  99. {
  100. foreach (var shop in allShops)
  101. {
  102. if (shop != null)
  103. {
  104. string elementName = GetUIElementNameForShop(shop);
  105. shopLookup[elementName] = shop;
  106. Debug.Log($"Added to lookup: {shop.buildingName} -> {elementName}");
  107. }
  108. }
  109. }
  110. // NOW setup UI after shops are properly configured AND lookup is built
  111. Debug.Log($"ConfigureShopsEarly: Setting up UI with {allShops?.Length ?? 0} shops, lookup: {shopLookup.Count}");
  112. if (rootElement != null && allShops != null && allShops.Length > 0 && shopLookup.Count > 0)
  113. {
  114. SetupUIClickHandlers();
  115. UpdateMoneyDisplay();
  116. UpdateBuildingVisibility();
  117. UpdateAllBuildingNames();
  118. Debug.Log("ConfigureShopsEarly: UI setup completed");
  119. }
  120. else
  121. {
  122. Debug.LogWarning($"ConfigureShopsEarly: Cannot setup UI - rootElement: {rootElement != null}, allShops: {allShops?.Length ?? 0}, lookup: {shopLookup.Count}");
  123. }
  124. }
  125. private void ConfigureShopsWithDefaults()
  126. {
  127. Debug.Log("TownShopManager: Using default shop configuration");
  128. if (allShops == null) return;
  129. foreach (var shop in allShops)
  130. {
  131. if (shop != null)
  132. {
  133. // Use shop type as default name if not already set properly
  134. string defaultName = $"{shop.shopType} Shop";
  135. string defaultKeeper = "Merchant";
  136. shop.UpdateNames(defaultName, defaultKeeper);
  137. Debug.Log($"Set default name: {shop.buildingName}");
  138. }
  139. }
  140. }
  141. void OnDestroy()
  142. {
  143. // Clear singleton reference when destroyed
  144. if (instance == this)
  145. {
  146. instance = null;
  147. Debug.Log("TownShopManager singleton cleared");
  148. }
  149. }
  150. void Start()
  151. {
  152. Debug.Log($"TownShopManager.Start() called on {gameObject.name}, instance == this: {instance == this}");
  153. Debug.Log($"Current singleton instance: {(instance != null ? instance.gameObject.name : "NULL")}");
  154. // Only start if this is the active instance
  155. if (instance == this)
  156. {
  157. Debug.Log($"TownShopManager.Start() - Active instance on {gameObject.name}");
  158. Debug.Log($"allShops status: {(allShops != null ? $"Array with {allShops.Length} items" : "NULL")}");
  159. // Check if townUI is assigned
  160. if (townUI == null)
  161. {
  162. Debug.LogError("TownShopManager: townUI is not assigned in the inspector!");
  163. return;
  164. }
  165. // Get root element from townUI
  166. rootElement = townUI.rootVisualElement;
  167. if (rootElement == null)
  168. {
  169. Debug.LogError("TownShopManager: Could not get rootVisualElement from townUI!");
  170. return;
  171. }
  172. Debug.Log($"TownShopManager: UI initialized - townUI: {townUI != null}, rootElement: {rootElement != null}");
  173. // NOTE: Shop configuration and UI setup will happen in ConfigureShopsEarly coroutine
  174. // Don't setup UI here since shops might not be ready yet
  175. Debug.Log("TownShopManager: Waiting for ConfigureShopsEarly to complete before setting up UI");
  176. }
  177. else
  178. {
  179. Debug.LogWarning($"TownShopManager.Start() - Inactive instance on {gameObject.name}, skipping initialization");
  180. }
  181. }
  182. /// <summary>
  183. /// Public method to reinitialize shops and UI (for testing)
  184. /// </summary>
  185. public void ReinitializeShopsAndUI()
  186. {
  187. Debug.Log("Reinitializing shops and UI...");
  188. // Clear existing UI event handlers to prevent duplicates
  189. ClearUIClickHandlers();
  190. // Reinitialize shops
  191. InitializeShops();
  192. // Setup UI again
  193. SetupUIClickHandlers();
  194. UpdateMoneyDisplay();
  195. }
  196. /// <summary>
  197. /// Clear all UI click handlers to prevent duplicates
  198. /// </summary>
  199. private void ClearUIClickHandlers()
  200. {
  201. if (rootElement == null) return;
  202. // Clear stored callbacks first
  203. foreach (var kvp in clickCallbacks)
  204. {
  205. var element = rootElement.Q<VisualElement>(kvp.Key);
  206. if (element != null)
  207. {
  208. element.UnregisterCallback<ClickEvent>(kvp.Value);
  209. Debug.Log($"Cleared stored callback for {kvp.Key}");
  210. }
  211. }
  212. clickCallbacks.Clear();
  213. // Also clear any OnAnyShopClicked callbacks as fallback
  214. var shopElements = new string[] { "WeaponShop", "ArmorShop", "PotionShop", "GeneralStore" };
  215. foreach (var elementName in shopElements)
  216. {
  217. var element = rootElement.Q<VisualElement>(elementName);
  218. if (element != null)
  219. {
  220. element.UnregisterCallback<ClickEvent>(OnAnyShopClicked);
  221. Debug.Log($"Cleared fallback callbacks for {elementName}");
  222. }
  223. }
  224. }
  225. public void InitializeShops()
  226. {
  227. shopLookup.Clear();
  228. // Ensure shops exist before trying to initialize them
  229. if (allShops == null || allShops.Length == 0)
  230. {
  231. Debug.LogWarning("TownShopManager.InitializeShops() - allShops is null or empty, creating default shops");
  232. CreateDefaultShops();
  233. }
  234. if (allShops == null)
  235. {
  236. Debug.LogError("TownShopManager.InitializeShops() - Failed to create shops, cannot continue");
  237. return;
  238. }
  239. // Shops should already be configured by ConfigureShopsEarly() in Awake
  240. Debug.Log($"TownShopManager.InitializeShops() - Building shop lookup table for {allShops.Length} shops");
  241. foreach (var shop in allShops)
  242. {
  243. if (shop != null)
  244. {
  245. // Map shop to UI element by name
  246. string elementName = GetUIElementNameForShop(shop);
  247. shopLookup[elementName] = shop;
  248. Debug.Log($"Registered shop: {shop.buildingName} -> {elementName}");
  249. }
  250. }
  251. }
  252. private void CreateDefaultShops()
  253. {
  254. Debug.Log($"Creating default shops on {gameObject.name}...");
  255. var setupHelper = GetComponent<TownSetupHelper>();
  256. if (setupHelper == null)
  257. {
  258. setupHelper = gameObject.AddComponent<TownSetupHelper>();
  259. Debug.Log($"Added TownSetupHelper to {gameObject.name}");
  260. }
  261. setupHelper.SetupSampleTown();
  262. // Collect the created shops
  263. List<TownShop> shopList = new List<TownShop>();
  264. if (setupHelper.weaponShop != null) shopList.Add(setupHelper.weaponShop);
  265. if (setupHelper.armorShop != null) shopList.Add(setupHelper.armorShop);
  266. if (setupHelper.potionShop != null) shopList.Add(setupHelper.potionShop);
  267. if (setupHelper.generalStore != null) shopList.Add(setupHelper.generalStore);
  268. allShops = shopList.ToArray();
  269. Debug.Log($"CreateDefaultShops completed on {gameObject.name}: {allShops.Length} shops created");
  270. foreach (var shop in allShops)
  271. {
  272. Debug.Log($" - {shop.buildingName} ({shop.shopType}) on {shop.gameObject.name}");
  273. }
  274. }
  275. private void ConfigureShopsFromSettlement()
  276. {
  277. if (SettlementContext.Instance == null || allShops == null)
  278. {
  279. Debug.LogError("ConfigureShopsFromSettlement: Missing SettlementContext or allShops!");
  280. return;
  281. }
  282. var settlementType = SettlementContext.Instance.settlementType;
  283. var settlementName = SettlementContext.Instance.settlementName;
  284. var mapPosition = SettlementContext.Instance.mapPosition;
  285. Debug.Log($"=== CONFIGURE SHOPS DEBUG ===");
  286. Debug.Log($"Settlement: {settlementName} ({settlementType})");
  287. Debug.Log($"Total shops to configure: {allShops.Length}");
  288. // First, randomize all shop names based on settlement
  289. foreach (var shop in allShops)
  290. {
  291. if (shop != null)
  292. {
  293. string newBuildingName = ShopNameGenerator.GenerateUniqueShopName(shop.shopType, settlementName, mapPosition);
  294. string newShopkeeperName = ShopNameGenerator.GenerateUniqueShopkeeperName(shop.shopType, settlementName, mapPosition);
  295. // Update names using the shop's UpdateNames method
  296. shop.UpdateNames(newBuildingName, newShopkeeperName);
  297. Debug.Log($"Configured shop: {shop.buildingName} run by {shop.shopkeeperName} (GameObject: {shop.gameObject.name}, Active: {shop.gameObject.activeInHierarchy})");
  298. }
  299. }
  300. // For villages, disable some shops
  301. if (settlementType == SettlementType.Village)
  302. {
  303. Debug.Log($"=== VILLAGE CONFIGURATION START ===");
  304. Debug.Log($"Configuring for village: {settlementName}");
  305. Debug.Log($"Total shops before: {allShops?.Length ?? 0}");
  306. // Count shops that are actually active before we start
  307. int activeShopsBefore = 0;
  308. foreach (var shop in allShops)
  309. {
  310. if (shop != null && shop.gameObject.activeInHierarchy)
  311. {
  312. activeShopsBefore++;
  313. }
  314. }
  315. Debug.Log($"Active shops before village config: {activeShopsBefore}");
  316. // First, disable all shops
  317. foreach (var shop in allShops)
  318. {
  319. if (shop != null)
  320. {
  321. bool wasPreviouslyActive = shop.gameObject.activeInHierarchy;
  322. shop.gameObject.SetActive(false);
  323. Debug.Log($"Village: Disabled {shop.buildingName} ({shop.shopType}) - Was active: {wasPreviouslyActive}, Now active: {shop.gameObject.activeInHierarchy}");
  324. }
  325. }
  326. // Count shops that are actually disabled after we disable them
  327. int activeShopsAfterDisable = 0;
  328. foreach (var shop in allShops)
  329. {
  330. if (shop != null && shop.gameObject.activeInHierarchy)
  331. {
  332. activeShopsAfterDisable++;
  333. }
  334. }
  335. Debug.Log($"Active shops after disabling all: {activeShopsAfterDisable}");
  336. var random = new System.Random(SettlementContext.Instance.settlementSeed);
  337. Debug.Log($"Using village seed: {SettlementContext.Instance.settlementSeed}");
  338. // Always keep general store
  339. var generalStore = allShops.FirstOrDefault(s => s != null && s.shopType == ShopType.General);
  340. if (generalStore != null)
  341. {
  342. bool wasActive = generalStore.gameObject.activeInHierarchy;
  343. generalStore.gameObject.SetActive(true);
  344. Debug.Log($"Village: Enabled General Store - Was active: {wasActive}, Now active: {generalStore.gameObject.activeInHierarchy}, Building: {generalStore.buildingName}");
  345. }
  346. else
  347. {
  348. Debug.LogError("Village: No General Store found!");
  349. }
  350. // Maybe keep one other shop (60% chance)
  351. double randomValue = random.NextDouble();
  352. Debug.Log($"Village: Random value for second shop: {randomValue} (>0.4 = keep second shop)");
  353. if (randomValue > 0.4)
  354. {
  355. var eligibleShops = allShops.Where(s => s != null && s.shopType != ShopType.General).ToArray();
  356. Debug.Log($"Village: Found {eligibleShops.Length} eligible shops for second shop");
  357. if (eligibleShops.Length > 0)
  358. {
  359. var selectedShop = eligibleShops[random.Next(eligibleShops.Length)];
  360. bool wasActive = selectedShop.gameObject.activeInHierarchy;
  361. selectedShop.gameObject.SetActive(true);
  362. Debug.Log($"Village: Also enabled {selectedShop.buildingName} ({selectedShop.shopType}) - Was active: {wasActive}, Now active: {selectedShop.gameObject.activeInHierarchy}");
  363. }
  364. }
  365. else
  366. {
  367. Debug.Log("Village: Only keeping General Store (random value <= 0.4)");
  368. }
  369. // Final count of active shops
  370. int finalActiveShops = 0;
  371. foreach (var shop in allShops)
  372. {
  373. if (shop != null && shop.gameObject.activeInHierarchy)
  374. {
  375. finalActiveShops++;
  376. Debug.Log($"Village Final: {shop.buildingName} ({shop.shopType}) is ACTIVE");
  377. }
  378. else if (shop != null)
  379. {
  380. Debug.Log($"Village Final: {shop.buildingName} ({shop.shopType}) is DISABLED");
  381. }
  382. }
  383. Debug.Log($"=== VILLAGE CONFIGURATION END - Final active shops: {finalActiveShops} ===");
  384. }
  385. else // Town
  386. {
  387. Debug.Log("Configuring for town - enabling all shops");
  388. // Towns have all shops available
  389. foreach (var shop in allShops)
  390. {
  391. if (shop != null)
  392. {
  393. shop.gameObject.SetActive(true);
  394. Debug.Log($"Town: Enabling {shop.buildingName}");
  395. }
  396. }
  397. }
  398. UpdateAllBuildingNames();
  399. }
  400. private void UpdateBuildingVisibility()
  401. {
  402. if (rootElement == null)
  403. {
  404. Debug.LogWarning("UpdateBuildingVisibility: rootElement is null!");
  405. return;
  406. }
  407. Debug.Log("=== UPDATE BUILDING VISIBILITY START ===");
  408. // Check if allShops is null or empty
  409. if (allShops == null || allShops.Length == 0)
  410. {
  411. Debug.LogWarning("UpdateBuildingVisibility: allShops is null or empty!");
  412. return;
  413. }
  414. // Get all shop types that are available and active
  415. var availableShopTypes = new System.Collections.Generic.HashSet<ShopType>();
  416. foreach (var shop in allShops)
  417. {
  418. if (shop != null && shop.gameObject.activeInHierarchy)
  419. {
  420. availableShopTypes.Add(shop.shopType);
  421. Debug.Log($"Available shop type: {shop.shopType} ({shop.buildingName})");
  422. }
  423. else if (shop != null)
  424. {
  425. Debug.Log($"Unavailable shop type: {shop.shopType} ({shop.buildingName}) - GameObject active: {shop.gameObject.activeInHierarchy}");
  426. }
  427. }
  428. Debug.Log($"Total available shop types: {availableShopTypes.Count}");
  429. // Show/hide buildings based on what's available
  430. var allShopElements = new string[] { "WeaponShop", "ArmorShop", "PotionShop", "GeneralStore" };
  431. var correspondingTypes = new ShopType[] { ShopType.Weapons, ShopType.Armor, ShopType.Potions, ShopType.General };
  432. for (int i = 0; i < allShopElements.Length; i++)
  433. {
  434. var element = rootElement.Q<VisualElement>(allShopElements[i]);
  435. if (element != null)
  436. {
  437. bool shouldShow = availableShopTypes.Contains(correspondingTypes[i]);
  438. var previousDisplay = element.style.display.value;
  439. if (shouldShow)
  440. {
  441. element.style.display = DisplayStyle.Flex;
  442. UpdateBuildingNameDisplay(element, correspondingTypes[i]);
  443. Debug.Log($"UI Element {allShopElements[i]} ({correspondingTypes[i]}): SHOWING (was {previousDisplay})");
  444. }
  445. else
  446. {
  447. element.style.display = DisplayStyle.None;
  448. Debug.Log($"UI Element {allShopElements[i]} ({correspondingTypes[i]}): HIDING (was {previousDisplay})");
  449. }
  450. }
  451. else
  452. {
  453. Debug.LogWarning($"UI Element {allShopElements[i]} not found in root element!");
  454. }
  455. }
  456. // Handle Harbor visibility based on settlement context
  457. var harborElement = rootElement.Q<VisualElement>("Harbor");
  458. if (harborElement != null)
  459. {
  460. bool shouldShowHarbor = true; // Default to show
  461. if (SettlementContext.Instance != null)
  462. {
  463. shouldShowHarbor = SettlementContext.Instance.hasHarbor;
  464. Debug.Log($"Harbor visibility: {shouldShowHarbor} (hasHarbor: {SettlementContext.Instance.hasHarbor})");
  465. }
  466. harborElement.style.display = shouldShowHarbor ? DisplayStyle.Flex : DisplayStyle.None;
  467. }
  468. }
  469. private void UpdateAllBuildingNames()
  470. {
  471. if (rootElement == null) return;
  472. var allShopElements = new string[] { "WeaponShop", "ArmorShop", "PotionShop", "GeneralStore" };
  473. var correspondingTypes = new ShopType[] { ShopType.Weapons, ShopType.Armor, ShopType.Potions, ShopType.General };
  474. for (int i = 0; i < allShopElements.Length; i++)
  475. {
  476. var element = rootElement.Q<VisualElement>(allShopElements[i]);
  477. if (element != null)
  478. {
  479. UpdateBuildingNameDisplay(element, correspondingTypes[i]);
  480. }
  481. }
  482. Debug.Log("Updated all building names to match shop data");
  483. }
  484. private void UpdateBuildingNameDisplay(VisualElement buildingElement, ShopType shopType)
  485. {
  486. // Check if allShops is available
  487. if (allShops == null || allShops.Length == 0)
  488. {
  489. Debug.LogWarning($"UpdateBuildingNameDisplay: allShops is null or empty for {shopType}");
  490. return;
  491. }
  492. // Find the shop with this type
  493. var shop = allShops.FirstOrDefault(s => s != null && s.shopType == shopType && s.gameObject.activeInHierarchy);
  494. if (shop == null) return;
  495. // Look for various possible label elements that might contain the building name
  496. var possibleLabelSelectors = new string[]
  497. {
  498. "building-name",
  499. "shop-name",
  500. "label",
  501. "name-label",
  502. "title"
  503. };
  504. bool nameUpdated = false;
  505. foreach (var selector in possibleLabelSelectors)
  506. {
  507. var nameLabel = buildingElement.Q<Label>(selector);
  508. if (nameLabel != null)
  509. {
  510. string oldText = nameLabel.text;
  511. nameLabel.text = shop.buildingName;
  512. Debug.Log($"Updated building display name: {selector} '{oldText}' -> '{shop.buildingName}'");
  513. nameUpdated = true;
  514. break;
  515. }
  516. }
  517. // Try to find any Label element within the building element
  518. if (!nameUpdated)
  519. {
  520. var allLabels = buildingElement.Query<Label>().ToList();
  521. foreach (var label in allLabels)
  522. {
  523. // Skip if label text looks like it might be something else (like "WEAPONS", "ARMOR", etc)
  524. if (!string.IsNullOrEmpty(label.text) &&
  525. !label.text.ToUpper().Contains("WEAPON") &&
  526. !label.text.ToUpper().Contains("ARMOR") &&
  527. !label.text.ToUpper().Contains("POTION") &&
  528. !label.text.ToUpper().Contains("GENERAL"))
  529. {
  530. string oldText = label.text;
  531. label.text = shop.buildingName;
  532. Debug.Log($"Updated building label: '{oldText}' -> '{shop.buildingName}'");
  533. nameUpdated = true;
  534. break;
  535. }
  536. }
  537. }
  538. // If still no update, log the building structure for debugging
  539. if (!nameUpdated)
  540. {
  541. Debug.Log($"Could not find name label for {shopType} building (shop: {shop.buildingName})");
  542. LogBuildingStructure(buildingElement, shopType);
  543. }
  544. }
  545. private void LogBuildingStructure(VisualElement buildingElement, ShopType shopType)
  546. {
  547. Debug.Log($"=== Building Structure for {shopType} ===");
  548. Debug.Log($"Element name: {buildingElement.name}");
  549. var labels = buildingElement.Query<Label>().ToList();
  550. Debug.Log($"Found {labels.Count} labels:");
  551. for (int i = 0; i < labels.Count; i++)
  552. {
  553. Debug.Log($" Label {i}: name='{labels[i].name}', text='{labels[i].text}'");
  554. }
  555. }
  556. private string GetUIElementNameForShop(TownShop shop)
  557. {
  558. switch (shop.shopType)
  559. {
  560. case ShopType.Weapons: return "WeaponShop";
  561. case ShopType.Armor: return "ArmorShop";
  562. case ShopType.Potions: return "PotionShop";
  563. case ShopType.General: return "GeneralStore";
  564. default: return shop.buildingName.Replace(" ", "");
  565. }
  566. }
  567. private void SetupUIClickHandlers()
  568. {
  569. if (townUI == null)
  570. {
  571. Debug.LogError("TownUI is null!");
  572. return;
  573. }
  574. rootElement = townUI.rootVisualElement;
  575. if (rootElement == null)
  576. {
  577. Debug.LogError("Root element is null!");
  578. return;
  579. }
  580. Debug.Log($"Setting up click handlers for {shopLookup.Count} shops");
  581. // Setup click handlers for each shop
  582. foreach (var kvp in shopLookup)
  583. {
  584. var shopElement = rootElement.Q<VisualElement>(kvp.Key);
  585. if (shopElement != null)
  586. {
  587. // Create and store the callback so we can unregister it later
  588. var shop = kvp.Value;
  589. EventCallback<ClickEvent> callback = evt => OnShopClicked(shop);
  590. // Store the callback for later cleanup
  591. clickCallbacks[kvp.Key] = callback;
  592. // Register the callback
  593. shopElement.RegisterCallback<ClickEvent>(callback);
  594. // Add visual feedback for interactable elements
  595. shopElement.AddToClassList("interactable-building");
  596. Debug.Log($"✓ Setup click handler for {kvp.Key} -> {shop.buildingName}");
  597. }
  598. else
  599. {
  600. Debug.LogWarning($"✗ Could not find UI element: {kvp.Key}");
  601. // List all available elements for debugging
  602. DebugListChildElements(rootElement, kvp.Key);
  603. }
  604. }
  605. // Setup other building click handlers
  606. SetupSpecialBuildingHandlers();
  607. }
  608. private void DebugListChildElements(VisualElement parent, string searchName)
  609. {
  610. Debug.Log($"Looking for '{searchName}' in available elements:");
  611. foreach (var child in parent.Children())
  612. {
  613. Debug.Log($" - Found element: {child.name} (type: {child.GetType().Name})");
  614. if (child.childCount > 0)
  615. {
  616. DebugListChildElementsRecursive(child, 1);
  617. }
  618. }
  619. }
  620. private void DebugListChildElementsRecursive(VisualElement parent, int depth)
  621. {
  622. string indent = new string(' ', depth * 4);
  623. foreach (var child in parent.Children())
  624. {
  625. Debug.Log($"{indent}- {child.name} (type: {child.GetType().Name})");
  626. if (child.childCount > 0 && depth < 3) // Limit recursion
  627. {
  628. DebugListChildElementsRecursive(child, depth + 1);
  629. }
  630. }
  631. }
  632. // This method handles all shop clicks to avoid closure issues
  633. private void OnAnyShopClicked(ClickEvent evt)
  634. {
  635. var element = evt.target as VisualElement;
  636. if (element != null && shopLookup.ContainsKey(element.name))
  637. {
  638. OnShopClicked(shopLookup[element.name]);
  639. }
  640. }
  641. private void SetupSpecialBuildingHandlers()
  642. {
  643. // Adventurer's Guild
  644. var guild = rootElement.Q<VisualElement>("AdventurersGuild");
  645. if (guild != null)
  646. {
  647. guild.RegisterCallback<ClickEvent>(evt => OnAdventurersGuildClicked());
  648. guild.AddToClassList("interactable-building");
  649. Debug.Log("✓ Setup Adventurer's Guild click handler");
  650. }
  651. else
  652. {
  653. Debug.LogWarning("✗ Could not find AdventurersGuild element");
  654. }
  655. // Harbor
  656. var harbor = rootElement.Q<VisualElement>("Harbor");
  657. if (harbor != null)
  658. {
  659. harbor.RegisterCallback<ClickEvent>(evt => OnHarborClicked());
  660. harbor.AddToClassList("interactable-building");
  661. Debug.Log("✓ Setup Harbor click handler");
  662. }
  663. else
  664. {
  665. Debug.LogWarning("✗ Could not find Harbor element");
  666. }
  667. // Inn
  668. var inn = rootElement.Q<VisualElement>("Inn");
  669. if (inn != null)
  670. {
  671. inn.RegisterCallback<ClickEvent>(evt => OnInnClicked());
  672. inn.AddToClassList("interactable-building");
  673. Debug.Log("✓ Setup Inn click handler");
  674. }
  675. else
  676. {
  677. Debug.LogWarning("✗ Could not find Inn element");
  678. }
  679. // Map button
  680. var mapButton = rootElement.Q<Button>("MapButton");
  681. if (mapButton != null)
  682. {
  683. mapButton.clicked += OnReturnToMapClicked;
  684. Debug.Log("✓ Setup Map button click handler");
  685. }
  686. else
  687. {
  688. Debug.LogWarning("✗ Could not find MapButton element");
  689. }
  690. // Inventory button
  691. var inventoryButton = rootElement.Q<Button>("InventoryButton");
  692. if (inventoryButton != null)
  693. {
  694. inventoryButton.clicked += OnInventoryClicked;
  695. Debug.Log("✓ Setup Inventory button click handler");
  696. }
  697. else
  698. {
  699. Debug.LogWarning("✗ Could not find InventoryButton element");
  700. }
  701. }
  702. private void OnShopClicked(TownShop shop)
  703. {
  704. // Prevent multiple shop UIs from opening simultaneously
  705. if (isShopUIOpen)
  706. {
  707. Debug.Log($"Shop UI already open, ignoring click on {shop.buildingName}");
  708. return;
  709. }
  710. Debug.Log($"=== SHOP CLICK DEBUG ===");
  711. Debug.Log($"Clicked shop: {shop.buildingName} ({shop.shopType})");
  712. Debug.Log($"Shop keeper: {shop.shopkeeperName}");
  713. Debug.Log($"GameObject name: {shop.gameObject.name}");
  714. Debug.Log($"=== Opening shop UI ===");
  715. // Handle shop opening directly in TownShopManager
  716. // Don't delegate to TownManager to avoid duplicate UI creation
  717. OpenShopUI(shop);
  718. }
  719. private void OpenShopUI(TownShop shop)
  720. {
  721. // Set flag to prevent multiple openings
  722. isShopUIOpen = true;
  723. // Close any existing shop UIs first
  724. var existingShopUIs = FindObjectsByType<TownShopUI>(FindObjectsSortMode.None);
  725. foreach (var existingUI in existingShopUIs)
  726. {
  727. if (existingUI.gameObject != null)
  728. {
  729. Debug.Log($"Closing existing shop UI: {existingUI.gameObject.name}");
  730. Destroy(existingUI.gameObject);
  731. }
  732. }
  733. // Create shop UI GameObject as a separate top-level object
  734. var shopUIObject = new GameObject("TownShopUI");
  735. // Add UIDocument component and load the UXML
  736. var uiDocument = shopUIObject.AddComponent<UIDocument>();
  737. // Copy settings from town UI document if available
  738. if (townUI != null)
  739. {
  740. uiDocument.panelSettings = townUI.panelSettings;
  741. uiDocument.sortingOrder = townUI.sortingOrder + 100; // Ensure it's on top
  742. }
  743. uiDocument.visualTreeAsset = Resources.Load<VisualTreeAsset>("UI/TownShopUI");
  744. Debug.Log($"Loaded UXML asset: {uiDocument.visualTreeAsset != null}");
  745. // If not found in Resources, try direct path
  746. if (uiDocument.visualTreeAsset == null)
  747. {
  748. #if UNITY_EDITOR
  749. uiDocument.visualTreeAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UI/TownShopUI.uxml");
  750. Debug.Log($"Loaded UXML from Assets: {uiDocument.visualTreeAsset != null}");
  751. #endif
  752. }
  753. if (uiDocument.visualTreeAsset == null)
  754. {
  755. Debug.LogError("Failed to load TownShopUI UXML!");
  756. Destroy(shopUIObject);
  757. return;
  758. }
  759. // Add TownShopUI component
  760. var shopUI = shopUIObject.AddComponent<TownShopUI>();
  761. // Set the UI document reference
  762. shopUI.uiDocument = uiDocument;
  763. // Wait for proper initialization then set shop data
  764. StartCoroutine(InitializeShopUIDelayed(shopUI, shop));
  765. // Reset flag when this specific shop UI is destroyed
  766. StartCoroutine(WatchForShopUIDestruction(shopUIObject));
  767. Debug.Log($"Opened shop UI for {shop.buildingName} with sortOrder: {uiDocument.sortingOrder}");
  768. }
  769. private System.Collections.IEnumerator InitializeShopUIDelayed(TownShopUI shopUI, TownShop shop)
  770. {
  771. // Wait a frame to ensure Start() is called
  772. yield return null;
  773. // Now set the shop data and open
  774. shopUI.SetShop(shop);
  775. shopUI.OpenShop();
  776. Debug.Log($"Shop UI initialization completed for {shop.buildingName}");
  777. }
  778. private System.Collections.IEnumerator WatchForShopUIDestruction(GameObject shopUIObject)
  779. {
  780. while (shopUIObject != null)
  781. {
  782. yield return null;
  783. }
  784. // Shop UI was destroyed, reset flag
  785. isShopUIOpen = false;
  786. Debug.Log("Shop UI closed - ready for new shop");
  787. }
  788. private void OnAdventurersGuildClicked()
  789. {
  790. Debug.Log("Adventurer's Guild clicked - Quest system not implemented yet");
  791. ShowNotImplementedMessage("Adventurer's Guild", "Quest and job board system");
  792. }
  793. private void OnHarborClicked()
  794. {
  795. Debug.Log("Harbor clicked - Travel system not implemented yet");
  796. ShowNotImplementedMessage("Harbor", "Sea travel and shipping");
  797. }
  798. private void OnInnClicked()
  799. {
  800. Debug.Log("Inn clicked - Rest system not implemented yet");
  801. ShowNotImplementedMessage("The Wanderer's Rest", "Rest, heal, and save progress");
  802. }
  803. private void OnReturnToMapClicked()
  804. {
  805. Debug.Log("Returning to map");
  806. UnityEngine.SceneManagement.SceneManager.LoadScene("MapScene2");
  807. }
  808. private void OnInventoryClicked()
  809. {
  810. Debug.Log("Team inventory clicked - not implemented yet");
  811. ShowNotImplementedMessage("Team Inventory", "View and manage party equipment");
  812. }
  813. private void ShowNotImplementedMessage(string buildingName, string description)
  814. {
  815. // Create a simple popup message
  816. var popup = new VisualElement();
  817. popup.style.position = Position.Absolute;
  818. popup.style.width = Length.Percent(100);
  819. popup.style.height = Length.Percent(100);
  820. popup.style.backgroundColor = new Color(0, 0, 0, 0.7f);
  821. popup.style.justifyContent = Justify.Center;
  822. popup.style.alignItems = Align.Center;
  823. var messageBox = new VisualElement();
  824. messageBox.style.width = 400;
  825. messageBox.style.height = 200;
  826. messageBox.style.backgroundColor = new Color(0.9f, 0.9f, 0.9f);
  827. messageBox.style.borderLeftWidth = 2;
  828. messageBox.style.borderRightWidth = 2;
  829. messageBox.style.borderTopWidth = 2;
  830. messageBox.style.borderBottomWidth = 2;
  831. messageBox.style.borderLeftColor = Color.black;
  832. messageBox.style.borderRightColor = Color.black;
  833. messageBox.style.borderTopColor = Color.black;
  834. messageBox.style.borderBottomColor = Color.black;
  835. messageBox.style.borderTopLeftRadius = 10;
  836. messageBox.style.borderTopRightRadius = 10;
  837. messageBox.style.borderBottomLeftRadius = 10;
  838. messageBox.style.borderBottomRightRadius = 10;
  839. messageBox.style.paddingLeft = 20;
  840. messageBox.style.paddingRight = 20;
  841. messageBox.style.paddingTop = 20;
  842. messageBox.style.paddingBottom = 20;
  843. messageBox.style.justifyContent = Justify.Center;
  844. messageBox.style.alignItems = Align.Center;
  845. var titleLabel = new Label(buildingName);
  846. titleLabel.style.fontSize = 20;
  847. titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
  848. titleLabel.style.color = Color.black;
  849. titleLabel.style.marginBottom = 10;
  850. var descLabel = new Label($"Coming Soon!\n\n{description}");
  851. descLabel.style.fontSize = 14;
  852. descLabel.style.color = new Color(0.3f, 0.3f, 0.3f);
  853. descLabel.style.whiteSpace = WhiteSpace.Normal;
  854. descLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
  855. descLabel.style.marginBottom = 20;
  856. var closeButton = new Button { text = "Close" };
  857. closeButton.style.width = 100;
  858. closeButton.style.height = 30;
  859. closeButton.clicked += () => rootElement.Remove(popup);
  860. messageBox.Add(titleLabel);
  861. messageBox.Add(descLabel);
  862. messageBox.Add(closeButton);
  863. popup.Add(messageBox);
  864. rootElement.Add(popup);
  865. // Auto-close after 3 seconds
  866. StartCoroutine(ClosePopupAfterDelay(popup, 3f));
  867. }
  868. private System.Collections.IEnumerator ClosePopupAfterDelay(VisualElement popup, float delay)
  869. {
  870. yield return new WaitForSeconds(delay);
  871. if (popup.parent != null)
  872. rootElement.Remove(popup);
  873. }
  874. public void UpdateMoneyDisplay()
  875. {
  876. var moneyAmount = rootElement?.Q<Label>("MoneyAmount");
  877. if (moneyAmount != null)
  878. {
  879. var townManager = FindFirstObjectByType<TownManager>();
  880. if (townManager?.currentTeam?[0] != null)
  881. {
  882. var character = townManager.currentTeam[0];
  883. string moneyText = "";
  884. if (character.gold > 0) moneyText += $"{character.gold}g ";
  885. if (character.silver > 0) moneyText += $"{character.silver}s ";
  886. if (character.copper > 0) moneyText += $"{character.copper}c";
  887. moneyAmount.text = moneyText.Trim();
  888. }
  889. }
  890. }
  891. void Update()
  892. {
  893. // Update money display periodically (could be optimized with events)
  894. if (Time.frameCount % 60 == 0) // Update once per second at 60fps
  895. {
  896. UpdateMoneyDisplay();
  897. }
  898. }
  899. }