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 shopLookup = new Dictionary(); private Dictionary> clickCallbacks = new Dictionary>(); 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"); } } /// /// Public method to reinitialize shops and UI (for testing) /// 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(); } /// /// Clear all UI click handlers to prevent duplicates /// private void ClearUIClickHandlers() { if (rootElement == null) return; // Clear stored callbacks first foreach (var kvp in clickCallbacks) { var element = rootElement.Q(kvp.Key); if (element != null) { element.UnregisterCallback(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(elementName); if (element != null) { element.UnregisterCallback(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(); if (setupHelper == null) { setupHelper = gameObject.AddComponent(); Debug.Log($"Added TownSetupHelper to {gameObject.name}"); } setupHelper.SetupSampleTown(); // Collect the created shops List shopList = new List(); 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(); 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(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("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(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