| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045 |
- using UnityEngine;
- using UnityEngine.UIElements;
- using System.Collections.Generic;
- using System.Collections;
- using System.Linq;
- public class TownShopManager : MonoBehaviour
- {
- [Header("Shop Configuration")]
- public TownShop[] allShops;
- [Header("UI References")]
- public UIDocument townUI;
- private Dictionary<string, TownShop> shopLookup = new Dictionary<string, TownShop>();
- private Dictionary<string, EventCallback<ClickEvent>> clickCallbacks = new Dictionary<string, EventCallback<ClickEvent>>();
- private VisualElement rootElement;
- // Singleton pattern to prevent multiple instances
- private static TownShopManager instance;
- public static TownShopManager Instance => instance;
- // Prevent multiple shop UI creation
- private bool isShopUIOpen = false;
- void Awake()
- {
- Debug.Log($"TownShopManager.Awake() called on {gameObject.name}");
- // Check if another instance already exists
- if (instance != null)
- {
- if (instance == this)
- {
- Debug.Log($"TownShopManager: Same instance re-awakening on {gameObject.name}");
- }
- else
- {
- Debug.LogWarning($"Duplicate TownShopManager detected! Existing: {instance.gameObject.name}, New: {gameObject.name}");
- // Before destroying, transfer any UI reference to the existing instance
- if (townUI != null && instance.townUI == null)
- {
- Debug.Log("Transferring UI reference to existing singleton");
- instance.townUI = townUI;
- // Also transfer rootElement if available
- if (instance.rootElement == null && townUI != null)
- {
- instance.rootElement = townUI.rootVisualElement;
- }
- }
- Debug.LogWarning($"Destroying duplicate on {gameObject.name}");
- Destroy(gameObject);
- return;
- }
- }
- else
- {
- instance = this;
- Debug.Log($"TownShopManager singleton established on {gameObject.name}");
- }
- // Configure shops immediately in Awake to ensure they're ready before Start()
- StartCoroutine(ConfigureShopsEarly());
- }
- private System.Collections.IEnumerator ConfigureShopsEarly()
- {
- Debug.Log($"ConfigureShopsEarly starting on {gameObject.name}");
- // Wait a few frames for SettlementContext to be available and TownShop components to start
- yield return null;
- yield return null;
- yield return null;
- Debug.Log($"TownShopManager: Configuring shops early in Awake on {gameObject.name}");
- // Always create default shops first (creates the GameObjects)
- if (allShops == null || allShops.Length == 0)
- {
- Debug.Log($"Creating default shops on {gameObject.name}");
- CreateDefaultShops();
- Debug.Log($"After CreateDefaultShops: allShops.Length = {allShops?.Length ?? 0}");
- }
- else
- {
- Debug.Log($"Shops already exist: {allShops.Length} shops");
- }
- // Wait another frame for shops to finish their Start() methods
- yield return null;
- // Then configure them based on SettlementContext if available
- if (SettlementContext.Instance != null)
- {
- Debug.Log($"=== SETTLEMENT DEBUG on {gameObject.name} ===");
- Debug.Log($"Settlement Name: {SettlementContext.Instance.settlementName}");
- Debug.Log($"Settlement Type: {SettlementContext.Instance.settlementType}");
- Debug.Log($"Settlement Seed: {SettlementContext.Instance.settlementSeed}");
- Debug.Log($"=== Configuring shops for {SettlementContext.Instance.settlementType} ===");
- ConfigureShopsFromSettlement();
- UpdateAllBuildingNames();
- }
- else
- {
- Debug.LogWarning("TownShopManager: SettlementContext.Instance is NULL in Awake - shops will use default names");
- // Configure with default names when no SettlementContext (for testing)
- ConfigureShopsWithDefaults();
- }
- // Build the shop lookup table BEFORE setting up UI
- Debug.Log($"ConfigureShopsEarly: Building shop lookup table for {allShops?.Length ?? 0} shops");
- shopLookup.Clear();
- if (allShops != null)
- {
- foreach (var shop in allShops)
- {
- if (shop != null)
- {
- string elementName = GetUIElementNameForShop(shop);
- shopLookup[elementName] = shop;
- Debug.Log($"Added to lookup: {shop.buildingName} -> {elementName}");
- }
- }
- }
- // NOW setup UI after shops are properly configured AND lookup is built
- Debug.Log($"ConfigureShopsEarly: Setting up UI with {allShops?.Length ?? 0} shops, lookup: {shopLookup.Count}");
- if (rootElement != null && allShops != null && allShops.Length > 0 && shopLookup.Count > 0)
- {
- SetupUIClickHandlers();
- UpdateMoneyDisplay();
- UpdateBuildingVisibility();
- UpdateAllBuildingNames();
- Debug.Log("ConfigureShopsEarly: UI setup completed");
- }
- else
- {
- Debug.LogWarning($"ConfigureShopsEarly: Cannot setup UI - rootElement: {rootElement != null}, allShops: {allShops?.Length ?? 0}, lookup: {shopLookup.Count}");
- }
- }
- private void ConfigureShopsWithDefaults()
- {
- Debug.Log("TownShopManager: Using default shop configuration");
- if (allShops == null) return;
- foreach (var shop in allShops)
- {
- if (shop != null)
- {
- // Use shop type as default name if not already set properly
- string defaultName = $"{shop.shopType} Shop";
- string defaultKeeper = "Merchant";
- shop.UpdateNames(defaultName, defaultKeeper);
- Debug.Log($"Set default name: {shop.buildingName}");
- }
- }
- }
- void OnDestroy()
- {
- // Clear singleton reference when destroyed
- if (instance == this)
- {
- instance = null;
- Debug.Log("TownShopManager singleton cleared");
- }
- }
- void Start()
- {
- Debug.Log($"TownShopManager.Start() called on {gameObject.name}, instance == this: {instance == this}");
- Debug.Log($"Current singleton instance: {(instance != null ? instance.gameObject.name : "NULL")}");
- // Only start if this is the active instance
- if (instance == this)
- {
- Debug.Log($"TownShopManager.Start() - Active instance on {gameObject.name}");
- Debug.Log($"allShops status: {(allShops != null ? $"Array with {allShops.Length} items" : "NULL")}");
- // Check if townUI is assigned
- if (townUI == null)
- {
- Debug.LogError("TownShopManager: townUI is not assigned in the inspector!");
- return;
- }
- // Get root element from townUI
- rootElement = townUI.rootVisualElement;
- if (rootElement == null)
- {
- Debug.LogError("TownShopManager: Could not get rootVisualElement from townUI!");
- return;
- }
- Debug.Log($"TownShopManager: UI initialized - townUI: {townUI != null}, rootElement: {rootElement != null}");
- // NOTE: Shop configuration and UI setup will happen in ConfigureShopsEarly coroutine
- // Don't setup UI here since shops might not be ready yet
- Debug.Log("TownShopManager: Waiting for ConfigureShopsEarly to complete before setting up UI");
- }
- else
- {
- Debug.LogWarning($"TownShopManager.Start() - Inactive instance on {gameObject.name}, skipping initialization");
- }
- }
- /// <summary>
- /// Public method to reinitialize shops and UI (for testing)
- /// </summary>
- public void ReinitializeShopsAndUI()
- {
- Debug.Log("Reinitializing shops and UI...");
- // Clear existing UI event handlers to prevent duplicates
- ClearUIClickHandlers();
- // Reinitialize shops
- InitializeShops();
- // Setup UI again
- SetupUIClickHandlers();
- UpdateMoneyDisplay();
- }
- /// <summary>
- /// Clear all UI click handlers to prevent duplicates
- /// </summary>
- private void ClearUIClickHandlers()
- {
- if (rootElement == null) return;
- // Clear stored callbacks first
- foreach (var kvp in clickCallbacks)
- {
- var element = rootElement.Q<VisualElement>(kvp.Key);
- if (element != null)
- {
- element.UnregisterCallback<ClickEvent>(kvp.Value);
- Debug.Log($"Cleared stored callback for {kvp.Key}");
- }
- }
- clickCallbacks.Clear();
- // Also clear any OnAnyShopClicked callbacks as fallback
- var shopElements = new string[] { "WeaponShop", "ArmorShop", "PotionShop", "GeneralStore" };
- foreach (var elementName in shopElements)
- {
- var element = rootElement.Q<VisualElement>(elementName);
- if (element != null)
- {
- element.UnregisterCallback<ClickEvent>(OnAnyShopClicked);
- Debug.Log($"Cleared fallback callbacks for {elementName}");
- }
- }
- }
- public void InitializeShops()
- {
- shopLookup.Clear();
- // Ensure shops exist before trying to initialize them
- if (allShops == null || allShops.Length == 0)
- {
- Debug.LogWarning("TownShopManager.InitializeShops() - allShops is null or empty, creating default shops");
- CreateDefaultShops();
- }
- if (allShops == null)
- {
- Debug.LogError("TownShopManager.InitializeShops() - Failed to create shops, cannot continue");
- return;
- }
- // Shops should already be configured by ConfigureShopsEarly() in Awake
- Debug.Log($"TownShopManager.InitializeShops() - Building shop lookup table for {allShops.Length} shops");
- foreach (var shop in allShops)
- {
- if (shop != null)
- {
- // Map shop to UI element by name
- string elementName = GetUIElementNameForShop(shop);
- shopLookup[elementName] = shop;
- Debug.Log($"Registered shop: {shop.buildingName} -> {elementName}");
- }
- }
- }
- private void CreateDefaultShops()
- {
- Debug.Log($"Creating default shops on {gameObject.name}...");
- var setupHelper = GetComponent<TownSetupHelper>();
- if (setupHelper == null)
- {
- setupHelper = gameObject.AddComponent<TownSetupHelper>();
- Debug.Log($"Added TownSetupHelper to {gameObject.name}");
- }
- setupHelper.SetupSampleTown();
- // Collect the created shops
- List<TownShop> shopList = new List<TownShop>();
- if (setupHelper.weaponShop != null) shopList.Add(setupHelper.weaponShop);
- if (setupHelper.armorShop != null) shopList.Add(setupHelper.armorShop);
- if (setupHelper.potionShop != null) shopList.Add(setupHelper.potionShop);
- if (setupHelper.generalStore != null) shopList.Add(setupHelper.generalStore);
- allShops = shopList.ToArray();
- Debug.Log($"CreateDefaultShops completed on {gameObject.name}: {allShops.Length} shops created");
- foreach (var shop in allShops)
- {
- Debug.Log($" - {shop.buildingName} ({shop.shopType}) on {shop.gameObject.name}");
- }
- }
- private void ConfigureShopsFromSettlement()
- {
- if (SettlementContext.Instance == null || allShops == null)
- {
- Debug.LogError("ConfigureShopsFromSettlement: Missing SettlementContext or allShops!");
- return;
- }
- var settlementType = SettlementContext.Instance.settlementType;
- var settlementName = SettlementContext.Instance.settlementName;
- var mapPosition = SettlementContext.Instance.mapPosition;
- Debug.Log($"=== CONFIGURE SHOPS DEBUG ===");
- Debug.Log($"Settlement: {settlementName} ({settlementType})");
- Debug.Log($"Total shops to configure: {allShops.Length}");
- // First, randomize all shop names based on settlement
- foreach (var shop in allShops)
- {
- if (shop != null)
- {
- string newBuildingName = ShopNameGenerator.GenerateUniqueShopName(shop.shopType, settlementName, mapPosition);
- string newShopkeeperName = ShopNameGenerator.GenerateUniqueShopkeeperName(shop.shopType, settlementName, mapPosition);
- // Update names using the shop's UpdateNames method
- shop.UpdateNames(newBuildingName, newShopkeeperName);
- Debug.Log($"Configured shop: {shop.buildingName} run by {shop.shopkeeperName} (GameObject: {shop.gameObject.name}, Active: {shop.gameObject.activeInHierarchy})");
- }
- }
- // For villages, disable some shops
- if (settlementType == SettlementType.Village)
- {
- Debug.Log($"=== VILLAGE CONFIGURATION START ===");
- Debug.Log($"Configuring for village: {settlementName}");
- Debug.Log($"Total shops before: {allShops?.Length ?? 0}");
- // Count shops that are actually active before we start
- int activeShopsBefore = 0;
- foreach (var shop in allShops)
- {
- if (shop != null && shop.gameObject.activeInHierarchy)
- {
- activeShopsBefore++;
- }
- }
- Debug.Log($"Active shops before village config: {activeShopsBefore}");
- // First, disable all shops
- foreach (var shop in allShops)
- {
- if (shop != null)
- {
- bool wasPreviouslyActive = shop.gameObject.activeInHierarchy;
- shop.gameObject.SetActive(false);
- Debug.Log($"Village: Disabled {shop.buildingName} ({shop.shopType}) - Was active: {wasPreviouslyActive}, Now active: {shop.gameObject.activeInHierarchy}");
- }
- }
- // Count shops that are actually disabled after we disable them
- int activeShopsAfterDisable = 0;
- foreach (var shop in allShops)
- {
- if (shop != null && shop.gameObject.activeInHierarchy)
- {
- activeShopsAfterDisable++;
- }
- }
- Debug.Log($"Active shops after disabling all: {activeShopsAfterDisable}");
- var random = new System.Random(SettlementContext.Instance.settlementSeed);
- Debug.Log($"Using village seed: {SettlementContext.Instance.settlementSeed}");
- // Always keep general store
- var generalStore = allShops.FirstOrDefault(s => s != null && s.shopType == ShopType.General);
- if (generalStore != null)
- {
- bool wasActive = generalStore.gameObject.activeInHierarchy;
- generalStore.gameObject.SetActive(true);
- Debug.Log($"Village: Enabled General Store - Was active: {wasActive}, Now active: {generalStore.gameObject.activeInHierarchy}, Building: {generalStore.buildingName}");
- }
- else
- {
- Debug.LogError("Village: No General Store found!");
- }
- // Maybe keep one other shop (60% chance)
- double randomValue = random.NextDouble();
- Debug.Log($"Village: Random value for second shop: {randomValue} (>0.4 = keep second shop)");
- if (randomValue > 0.4)
- {
- var eligibleShops = allShops.Where(s => s != null && s.shopType != ShopType.General).ToArray();
- Debug.Log($"Village: Found {eligibleShops.Length} eligible shops for second shop");
- if (eligibleShops.Length > 0)
- {
- var selectedShop = eligibleShops[random.Next(eligibleShops.Length)];
- bool wasActive = selectedShop.gameObject.activeInHierarchy;
- selectedShop.gameObject.SetActive(true);
- Debug.Log($"Village: Also enabled {selectedShop.buildingName} ({selectedShop.shopType}) - Was active: {wasActive}, Now active: {selectedShop.gameObject.activeInHierarchy}");
- }
- }
- else
- {
- Debug.Log("Village: Only keeping General Store (random value <= 0.4)");
- }
- // Final count of active shops
- int finalActiveShops = 0;
- foreach (var shop in allShops)
- {
- if (shop != null && shop.gameObject.activeInHierarchy)
- {
- finalActiveShops++;
- Debug.Log($"Village Final: {shop.buildingName} ({shop.shopType}) is ACTIVE");
- }
- else if (shop != null)
- {
- Debug.Log($"Village Final: {shop.buildingName} ({shop.shopType}) is DISABLED");
- }
- }
- Debug.Log($"=== VILLAGE CONFIGURATION END - Final active shops: {finalActiveShops} ===");
- }
- else // Town
- {
- Debug.Log("Configuring for town - enabling all shops");
- // Towns have all shops available
- foreach (var shop in allShops)
- {
- if (shop != null)
- {
- shop.gameObject.SetActive(true);
- Debug.Log($"Town: Enabling {shop.buildingName}");
- }
- }
- }
- UpdateAllBuildingNames();
- }
- private void UpdateBuildingVisibility()
- {
- if (rootElement == null)
- {
- Debug.LogWarning("UpdateBuildingVisibility: rootElement is null!");
- return;
- }
- Debug.Log("=== UPDATE BUILDING VISIBILITY START ===");
- // Check if allShops is null or empty
- if (allShops == null || allShops.Length == 0)
- {
- Debug.LogWarning("UpdateBuildingVisibility: allShops is null or empty!");
- return;
- }
- // Get all shop types that are available and active
- var availableShopTypes = new System.Collections.Generic.HashSet<ShopType>();
- foreach (var shop in allShops)
- {
- if (shop != null && shop.gameObject.activeInHierarchy)
- {
- availableShopTypes.Add(shop.shopType);
- Debug.Log($"Available shop type: {shop.shopType} ({shop.buildingName})");
- }
- else if (shop != null)
- {
- Debug.Log($"Unavailable shop type: {shop.shopType} ({shop.buildingName}) - GameObject active: {shop.gameObject.activeInHierarchy}");
- }
- }
- Debug.Log($"Total available shop types: {availableShopTypes.Count}");
- // Show/hide buildings based on what's available
- var allShopElements = new string[] { "WeaponShop", "ArmorShop", "PotionShop", "GeneralStore" };
- var correspondingTypes = new ShopType[] { ShopType.Weapons, ShopType.Armor, ShopType.Potions, ShopType.General };
- for (int i = 0; i < allShopElements.Length; i++)
- {
- var element = rootElement.Q<VisualElement>(allShopElements[i]);
- if (element != null)
- {
- bool shouldShow = availableShopTypes.Contains(correspondingTypes[i]);
- var previousDisplay = element.style.display.value;
- if (shouldShow)
- {
- element.style.display = DisplayStyle.Flex;
- UpdateBuildingNameDisplay(element, correspondingTypes[i]);
- Debug.Log($"UI Element {allShopElements[i]} ({correspondingTypes[i]}): SHOWING (was {previousDisplay})");
- }
- else
- {
- element.style.display = DisplayStyle.None;
- Debug.Log($"UI Element {allShopElements[i]} ({correspondingTypes[i]}): HIDING (was {previousDisplay})");
- }
- }
- else
- {
- Debug.LogWarning($"UI Element {allShopElements[i]} not found in root element!");
- }
- }
- // Handle Harbor visibility based on settlement context
- var harborElement = rootElement.Q<VisualElement>("Harbor");
- if (harborElement != null)
- {
- bool shouldShowHarbor = true; // Default to show
- if (SettlementContext.Instance != null)
- {
- shouldShowHarbor = SettlementContext.Instance.hasHarbor;
- Debug.Log($"Harbor visibility: {shouldShowHarbor} (hasHarbor: {SettlementContext.Instance.hasHarbor})");
- }
- harborElement.style.display = shouldShowHarbor ? DisplayStyle.Flex : DisplayStyle.None;
- }
- }
- private void UpdateAllBuildingNames()
- {
- if (rootElement == null) return;
- var allShopElements = new string[] { "WeaponShop", "ArmorShop", "PotionShop", "GeneralStore" };
- var correspondingTypes = new ShopType[] { ShopType.Weapons, ShopType.Armor, ShopType.Potions, ShopType.General };
- for (int i = 0; i < allShopElements.Length; i++)
- {
- var element = rootElement.Q<VisualElement>(allShopElements[i]);
- if (element != null)
- {
- UpdateBuildingNameDisplay(element, correspondingTypes[i]);
- }
- }
- Debug.Log("Updated all building names to match shop data");
- }
- private void UpdateBuildingNameDisplay(VisualElement buildingElement, ShopType shopType)
- {
- // Check if allShops is available
- if (allShops == null || allShops.Length == 0)
- {
- Debug.LogWarning($"UpdateBuildingNameDisplay: allShops is null or empty for {shopType}");
- return;
- }
- // Find the shop with this type
- var shop = allShops.FirstOrDefault(s => s != null && s.shopType == shopType && s.gameObject.activeInHierarchy);
- if (shop == null) return;
- // Look for various possible label elements that might contain the building name
- var possibleLabelSelectors = new string[]
- {
- "building-name",
- "shop-name",
- "label",
- "name-label",
- "title"
- };
- bool nameUpdated = false;
- foreach (var selector in possibleLabelSelectors)
- {
- var nameLabel = buildingElement.Q<Label>(selector);
- if (nameLabel != null)
- {
- string oldText = nameLabel.text;
- nameLabel.text = shop.buildingName;
- Debug.Log($"Updated building display name: {selector} '{oldText}' -> '{shop.buildingName}'");
- nameUpdated = true;
- break;
- }
- }
- // Try to find any Label element within the building element
- if (!nameUpdated)
- {
- var allLabels = buildingElement.Query<Label>().ToList();
- foreach (var label in allLabels)
- {
- // Skip if label text looks like it might be something else (like "WEAPONS", "ARMOR", etc)
- if (!string.IsNullOrEmpty(label.text) &&
- !label.text.ToUpper().Contains("WEAPON") &&
- !label.text.ToUpper().Contains("ARMOR") &&
- !label.text.ToUpper().Contains("POTION") &&
- !label.text.ToUpper().Contains("GENERAL"))
- {
- string oldText = label.text;
- label.text = shop.buildingName;
- Debug.Log($"Updated building label: '{oldText}' -> '{shop.buildingName}'");
- nameUpdated = true;
- break;
- }
- }
- }
- // If still no update, log the building structure for debugging
- if (!nameUpdated)
- {
- Debug.Log($"Could not find name label for {shopType} building (shop: {shop.buildingName})");
- LogBuildingStructure(buildingElement, shopType);
- }
- }
- private void LogBuildingStructure(VisualElement buildingElement, ShopType shopType)
- {
- Debug.Log($"=== Building Structure for {shopType} ===");
- Debug.Log($"Element name: {buildingElement.name}");
- var labels = buildingElement.Query<Label>().ToList();
- Debug.Log($"Found {labels.Count} labels:");
- for (int i = 0; i < labels.Count; i++)
- {
- Debug.Log($" Label {i}: name='{labels[i].name}', text='{labels[i].text}'");
- }
- }
- private string GetUIElementNameForShop(TownShop shop)
- {
- switch (shop.shopType)
- {
- case ShopType.Weapons: return "WeaponShop";
- case ShopType.Armor: return "ArmorShop";
- case ShopType.Potions: return "PotionShop";
- case ShopType.General: return "GeneralStore";
- default: return shop.buildingName.Replace(" ", "");
- }
- }
- private void SetupUIClickHandlers()
- {
- if (townUI == null)
- {
- Debug.LogError("TownUI is null!");
- return;
- }
- rootElement = townUI.rootVisualElement;
- if (rootElement == null)
- {
- Debug.LogError("Root element is null!");
- return;
- }
- Debug.Log($"Setting up click handlers for {shopLookup.Count} shops");
- // Setup click handlers for each shop
- foreach (var kvp in shopLookup)
- {
- var shopElement = rootElement.Q<VisualElement>(kvp.Key);
- if (shopElement != null)
- {
- // Create and store the callback so we can unregister it later
- var shop = kvp.Value;
- EventCallback<ClickEvent> callback = evt => OnShopClicked(shop);
- // Store the callback for later cleanup
- clickCallbacks[kvp.Key] = callback;
- // Register the callback
- shopElement.RegisterCallback<ClickEvent>(callback);
- // Add visual feedback for interactable elements
- shopElement.AddToClassList("interactable-building");
- Debug.Log($"✓ Setup click handler for {kvp.Key} -> {shop.buildingName}");
- }
- else
- {
- Debug.LogWarning($"✗ Could not find UI element: {kvp.Key}");
- // List all available elements for debugging
- DebugListChildElements(rootElement, kvp.Key);
- }
- }
- // Setup other building click handlers
- SetupSpecialBuildingHandlers();
- }
- private void DebugListChildElements(VisualElement parent, string searchName)
- {
- Debug.Log($"Looking for '{searchName}' in available elements:");
- foreach (var child in parent.Children())
- {
- Debug.Log($" - Found element: {child.name} (type: {child.GetType().Name})");
- if (child.childCount > 0)
- {
- DebugListChildElementsRecursive(child, 1);
- }
- }
- }
- private void DebugListChildElementsRecursive(VisualElement parent, int depth)
- {
- string indent = new string(' ', depth * 4);
- foreach (var child in parent.Children())
- {
- Debug.Log($"{indent}- {child.name} (type: {child.GetType().Name})");
- if (child.childCount > 0 && depth < 3) // Limit recursion
- {
- DebugListChildElementsRecursive(child, depth + 1);
- }
- }
- }
- // This method handles all shop clicks to avoid closure issues
- private void OnAnyShopClicked(ClickEvent evt)
- {
- var element = evt.target as VisualElement;
- if (element != null && shopLookup.ContainsKey(element.name))
- {
- OnShopClicked(shopLookup[element.name]);
- }
- }
- private void SetupSpecialBuildingHandlers()
- {
- // Adventurer's Guild
- var guild = rootElement.Q<VisualElement>("AdventurersGuild");
- if (guild != null)
- {
- guild.RegisterCallback<ClickEvent>(evt => OnAdventurersGuildClicked());
- guild.AddToClassList("interactable-building");
- Debug.Log("✓ Setup Adventurer's Guild click handler");
- }
- else
- {
- Debug.LogWarning("✗ Could not find AdventurersGuild element");
- }
- // Harbor
- var harbor = rootElement.Q<VisualElement>("Harbor");
- if (harbor != null)
- {
- harbor.RegisterCallback<ClickEvent>(evt => OnHarborClicked());
- harbor.AddToClassList("interactable-building");
- Debug.Log("✓ Setup Harbor click handler");
- }
- else
- {
- Debug.LogWarning("✗ Could not find Harbor element");
- }
- // Inn
- var inn = rootElement.Q<VisualElement>("Inn");
- if (inn != null)
- {
- inn.RegisterCallback<ClickEvent>(evt => OnInnClicked());
- inn.AddToClassList("interactable-building");
- Debug.Log("✓ Setup Inn click handler");
- }
- else
- {
- Debug.LogWarning("✗ Could not find Inn element");
- }
- // Map button
- var mapButton = rootElement.Q<Button>("MapButton");
- if (mapButton != null)
- {
- mapButton.clicked += OnReturnToMapClicked;
- Debug.Log("✓ Setup Map button click handler");
- }
- else
- {
- Debug.LogWarning("✗ Could not find MapButton element");
- }
- // Inventory button
- var inventoryButton = rootElement.Q<Button>("InventoryButton");
- if (inventoryButton != null)
- {
- inventoryButton.clicked += OnInventoryClicked;
- Debug.Log("✓ Setup Inventory button click handler");
- }
- else
- {
- Debug.LogWarning("✗ Could not find InventoryButton element");
- }
- }
- private void OnShopClicked(TownShop shop)
- {
- // Prevent multiple shop UIs from opening simultaneously
- if (isShopUIOpen)
- {
- Debug.Log($"Shop UI already open, ignoring click on {shop.buildingName}");
- return;
- }
- Debug.Log($"=== SHOP CLICK DEBUG ===");
- Debug.Log($"Clicked shop: {shop.buildingName} ({shop.shopType})");
- Debug.Log($"Shop keeper: {shop.shopkeeperName}");
- Debug.Log($"GameObject name: {shop.gameObject.name}");
- Debug.Log($"=== Opening shop UI ===");
- // Handle shop opening directly in TownShopManager
- // Don't delegate to TownManager to avoid duplicate UI creation
- OpenShopUI(shop);
- }
- private void OpenShopUI(TownShop shop)
- {
- // Set flag to prevent multiple openings
- isShopUIOpen = true;
- // Close any existing shop UIs first
- var existingShopUIs = FindObjectsByType<TownShopUI>(FindObjectsSortMode.None);
- foreach (var existingUI in existingShopUIs)
- {
- if (existingUI.gameObject != null)
- {
- Debug.Log($"Closing existing shop UI: {existingUI.gameObject.name}");
- Destroy(existingUI.gameObject);
- }
- }
- // Create shop UI GameObject as a separate top-level object
- var shopUIObject = new GameObject("TownShopUI");
- // Add UIDocument component and load the UXML
- var uiDocument = shopUIObject.AddComponent<UIDocument>();
- // Copy settings from town UI document if available
- if (townUI != null)
- {
- uiDocument.panelSettings = townUI.panelSettings;
- uiDocument.sortingOrder = townUI.sortingOrder + 100; // Ensure it's on top
- }
- uiDocument.visualTreeAsset = Resources.Load<VisualTreeAsset>("UI/TownShopUI");
- Debug.Log($"Loaded UXML asset: {uiDocument.visualTreeAsset != null}");
- // If not found in Resources, try direct path
- if (uiDocument.visualTreeAsset == null)
- {
- #if UNITY_EDITOR
- uiDocument.visualTreeAsset = UnityEditor.AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/UI/TownShopUI.uxml");
- Debug.Log($"Loaded UXML from Assets: {uiDocument.visualTreeAsset != null}");
- #endif
- }
- if (uiDocument.visualTreeAsset == null)
- {
- Debug.LogError("Failed to load TownShopUI UXML!");
- Destroy(shopUIObject);
- return;
- }
- // Add TownShopUI component
- var shopUI = shopUIObject.AddComponent<TownShopUI>();
- // Set the UI document reference
- shopUI.uiDocument = uiDocument;
- // Wait for proper initialization then set shop data
- StartCoroutine(InitializeShopUIDelayed(shopUI, shop));
- // Reset flag when this specific shop UI is destroyed
- StartCoroutine(WatchForShopUIDestruction(shopUIObject));
- Debug.Log($"Opened shop UI for {shop.buildingName} with sortOrder: {uiDocument.sortingOrder}");
- }
- private System.Collections.IEnumerator InitializeShopUIDelayed(TownShopUI shopUI, TownShop shop)
- {
- // Wait a frame to ensure Start() is called
- yield return null;
- // Now set the shop data and open
- shopUI.SetShop(shop);
- shopUI.OpenShop();
- Debug.Log($"Shop UI initialization completed for {shop.buildingName}");
- }
- private System.Collections.IEnumerator WatchForShopUIDestruction(GameObject shopUIObject)
- {
- while (shopUIObject != null)
- {
- yield return null;
- }
- // Shop UI was destroyed, reset flag
- isShopUIOpen = false;
- Debug.Log("Shop UI closed - ready for new shop");
- }
- private void OnAdventurersGuildClicked()
- {
- Debug.Log("Adventurer's Guild clicked - Quest system not implemented yet");
- ShowNotImplementedMessage("Adventurer's Guild", "Quest and job board system");
- }
- private void OnHarborClicked()
- {
- Debug.Log("Harbor clicked - Travel system not implemented yet");
- ShowNotImplementedMessage("Harbor", "Sea travel and shipping");
- }
- private void OnInnClicked()
- {
- Debug.Log("Inn clicked - Rest system not implemented yet");
- ShowNotImplementedMessage("The Wanderer's Rest", "Rest, heal, and save progress");
- }
- private void OnReturnToMapClicked()
- {
- Debug.Log("Returning to map");
- UnityEngine.SceneManagement.SceneManager.LoadScene("MapScene2");
- }
- private void OnInventoryClicked()
- {
- Debug.Log("Team inventory clicked - not implemented yet");
- ShowNotImplementedMessage("Team Inventory", "View and manage party equipment");
- }
- private void ShowNotImplementedMessage(string buildingName, string description)
- {
- // Create a simple popup message
- var popup = new VisualElement();
- popup.style.position = Position.Absolute;
- popup.style.width = Length.Percent(100);
- popup.style.height = Length.Percent(100);
- popup.style.backgroundColor = new Color(0, 0, 0, 0.7f);
- popup.style.justifyContent = Justify.Center;
- popup.style.alignItems = Align.Center;
- var messageBox = new VisualElement();
- messageBox.style.width = 400;
- messageBox.style.height = 200;
- messageBox.style.backgroundColor = new Color(0.9f, 0.9f, 0.9f);
- messageBox.style.borderLeftWidth = 2;
- messageBox.style.borderRightWidth = 2;
- messageBox.style.borderTopWidth = 2;
- messageBox.style.borderBottomWidth = 2;
- messageBox.style.borderLeftColor = Color.black;
- messageBox.style.borderRightColor = Color.black;
- messageBox.style.borderTopColor = Color.black;
- messageBox.style.borderBottomColor = Color.black;
- messageBox.style.borderTopLeftRadius = 10;
- messageBox.style.borderTopRightRadius = 10;
- messageBox.style.borderBottomLeftRadius = 10;
- messageBox.style.borderBottomRightRadius = 10;
- messageBox.style.paddingLeft = 20;
- messageBox.style.paddingRight = 20;
- messageBox.style.paddingTop = 20;
- messageBox.style.paddingBottom = 20;
- messageBox.style.justifyContent = Justify.Center;
- messageBox.style.alignItems = Align.Center;
- var titleLabel = new Label(buildingName);
- titleLabel.style.fontSize = 20;
- titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
- titleLabel.style.color = Color.black;
- titleLabel.style.marginBottom = 10;
- var descLabel = new Label($"Coming Soon!\n\n{description}");
- descLabel.style.fontSize = 14;
- descLabel.style.color = new Color(0.3f, 0.3f, 0.3f);
- descLabel.style.whiteSpace = WhiteSpace.Normal;
- descLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
- descLabel.style.marginBottom = 20;
- var closeButton = new Button { text = "Close" };
- closeButton.style.width = 100;
- closeButton.style.height = 30;
- closeButton.clicked += () => rootElement.Remove(popup);
- messageBox.Add(titleLabel);
- messageBox.Add(descLabel);
- messageBox.Add(closeButton);
- popup.Add(messageBox);
- rootElement.Add(popup);
- // Auto-close after 3 seconds
- StartCoroutine(ClosePopupAfterDelay(popup, 3f));
- }
- private System.Collections.IEnumerator ClosePopupAfterDelay(VisualElement popup, float delay)
- {
- yield return new WaitForSeconds(delay);
- if (popup.parent != null)
- rootElement.Remove(popup);
- }
- public void UpdateMoneyDisplay()
- {
- var moneyAmount = rootElement?.Q<Label>("MoneyAmount");
- if (moneyAmount != null)
- {
- var townManager = FindFirstObjectByType<TownManager>();
- if (townManager?.currentTeam?[0] != null)
- {
- var character = townManager.currentTeam[0];
- string moneyText = "";
- if (character.gold > 0) moneyText += $"{character.gold}g ";
- if (character.silver > 0) moneyText += $"{character.silver}s ";
- if (character.copper > 0) moneyText += $"{character.copper}c";
- moneyAmount.text = moneyText.Trim();
- }
- }
- }
- void Update()
- {
- // Update money display periodically (could be optimized with events)
- if (Time.frameCount % 60 == 0) // Update once per second at 60fps
- {
- UpdateMoneyDisplay();
- }
- }
- }
|