TownShopManager.cs 41 KB

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