| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064 |
- using UnityEngine;
- using UnityEngine.UIElements;
- using System.Collections.Generic;
- using System.Linq;
- /// <summary>
- /// UI Controller for the Travel System with route selection and comparison
- /// </summary>
- public class TravelUI : MonoBehaviour, IClickBlocker
- {
- [Header("UI Document")]
- public UIDocument uiDocument;
- [Header("UI Files")]
- public VisualTreeAsset travelUIAsset;
- public StyleSheet travelUIStyleSheet;
- [Header("Debug")]
- public bool debugMode = false;
- // UI Elements
- private VisualElement travelContainer;
- private VisualElement travelPanel;
- private VisualElement clickBlocker;
- private Label distanceLabel;
- private Label timeLabel;
- private Label specialCostsLabel;
- private Button startTravelButton;
- private Button cancelTravelButton;
- private VisualElement routeList;
- // Travel data
- private Vector2Int currentDestination;
- private List<TravelRoute> availableRoutes = new List<TravelRoute>();
- private TravelRoute selectedRoute;
- // State
- private bool isUIVisible = false;
- void Awake()
- {
- // Find UI Document if not assigned
- if (uiDocument == null)
- {
- uiDocument = GetComponent<UIDocument>();
- }
- // Load UXML asset from Resources if not assigned
- if (travelUIAsset == null)
- {
- travelUIAsset = Resources.Load<VisualTreeAsset>("UI/Map/TravelUI");
- if (travelUIAsset == null)
- {
- // Try alternative path
- travelUIAsset = Resources.Load<VisualTreeAsset>("TravelUI");
- }
- }
- // Load USS style sheet if not assigned
- if (travelUIStyleSheet == null)
- {
- travelUIStyleSheet = Resources.Load<StyleSheet>("UI/Map/TravelUI");
- if (travelUIStyleSheet == null)
- {
- // Try alternative path
- travelUIStyleSheet = Resources.Load<StyleSheet>("TravelUI");
- }
- }
- }
- void Start()
- {
- SetupUI();
- }
- void OnEnable()
- {
- // Register with ClickManager when this component is enabled
- if (ClickManager.Instance != null)
- {
- ClickManager.Instance.RegisterClickBlocker(this);
- }
- }
- void OnDisable()
- {
- // Unregister from ClickManager when this component is disabled
- if (ClickManager.Instance != null)
- {
- ClickManager.Instance.UnregisterClickBlocker(this);
- }
- }
- void OnDestroy()
- {
- // Ensure we're unregistered when destroyed
- if (ClickManager.Instance != null)
- {
- ClickManager.Instance.UnregisterClickBlocker(this);
- }
- }
- void SetupUI()
- {
- if (uiDocument == null)
- {
- Debug.LogError("TravelUI: UIDocument not found!");
- return;
- }
- var root = uiDocument.rootVisualElement;
- // Load and instantiate the UXML if we have the asset
- if (travelUIAsset != null)
- {
- // Clear existing content
- root.Clear();
- // Instantiate from UXML
- travelUIAsset.CloneTree(root);
- // Add style sheet
- if (travelUIStyleSheet != null)
- {
- root.styleSheets.Add(travelUIStyleSheet);
- }
- else
- {
- Debug.LogWarning("TravelUI: No style sheet found");
- }
- }
- else
- {
- Debug.LogWarning("TravelUI: No UXML asset found");
- }
- // Find UI elements
- travelContainer = root.Q<VisualElement>("TravelContainer");
- travelPanel = root.Q<VisualElement>("TravelPanel");
- clickBlocker = root.Q<VisualElement>("ClickBlocker");
- distanceLabel = root.Q<Label>("DistanceLabel");
- timeLabel = root.Q<Label>("TimeLabel");
- specialCostsLabel = root.Q<Label>("SpecialCostsLabel");
- startTravelButton = root.Q<Button>("StartTravelButton");
- cancelTravelButton = root.Q<Button>("CancelTravelButton");
- // Find draggable header and close button
- var travelHeader = root.Q<VisualElement>("TravelHeader");
- var closeButton = root.Q<Button>("CloseButton");
- // Find or create route list container
- routeList = root.Q<VisualElement>("RouteList");
- if (routeList == null)
- {
- Debug.Log("🔨 TravelUI: RouteList not found, creating it...");
- // Create route list if it doesn't exist
- var routeOptions = root.Q<VisualElement>("RouteOptions");
- if (routeOptions != null)
- {
- routeList = new VisualElement();
- routeList.name = "RouteList";
- routeList.AddToClassList("route-list");
- routeOptions.Add(routeList);
- Debug.Log("✅ TravelUI: RouteList created and added to RouteOptions");
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI: RouteOptions container not found, cannot create RouteList");
- }
- }
- else
- {
- if (debugMode) Debug.Log("RouteList found");
- }
- // Setup dragging functionality
- if (travelHeader != null && travelPanel != null)
- {
- SetupDragFunctionality(travelHeader, travelPanel);
- }
- // Setup close button
- if (closeButton != null)
- {
- closeButton.clicked += OnCancelTravelClicked;
- }
- // Setup button callbacks
- if (startTravelButton != null)
- {
- startTravelButton.clicked += OnStartTravelClicked;
- }
- if (cancelTravelButton != null)
- {
- cancelTravelButton.clicked += OnCancelTravelClicked;
- }
- // Setup click blocking on the invisible overlay
- if (clickBlocker != null)
- {
- clickBlocker.RegisterCallback<ClickEvent>(evt =>
- {
- Debug.Log("�️ TravelUI: Click blocked by overlay - stopping propagation");
- evt.StopPropagation(); // Prevent clicks from passing through to the map
- });
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI: ClickBlocker element not found!");
- }
- // Hide UI initially
- HideTravelPanel();
- if (debugMode) Debug.Log("TravelUI initialized successfully");
- }
- /// <summary>
- /// DEBUG: Force show the TravelUI for testing
- /// </summary>
- [ContextMenu("DEBUG: Force Show TravelUI")]
- public void DebugForceShowTravelUI()
- {
- Debug.Log("🧪 TravelUI: DEBUG Force showing TravelUI...");
- // CHECK FOR DUPLICATE TEAM MARKERS
- var allTeamMarkers = GameObject.FindObjectsByType<GameObject>(FindObjectsSortMode.None).Where(g => g.name == "TeamMarker").ToArray();
- Debug.Log($"🔍 Found {allTeamMarkers.Length} TeamMarker objects:");
- for (int i = 0; i < allTeamMarkers.Length; i++)
- {
- var marker = allTeamMarkers[i];
- bool hasSphere = marker.GetComponentInChildren<Transform>().name.Contains("Sphere");
- Debug.Log($" {i + 1}. {marker.name} at {marker.transform.position} - Has Sphere: {hasSphere}");
- }
- // Prevent multiple instances
- if (isUIVisible)
- {
- Debug.Log("⚠️ TravelUI: Already visible, ignoring duplicate show request");
- return;
- }
- // Create some dummy routes for testing with proper initialization
- var dummyRoutes = new List<TravelRoute>
- {
- new TravelRoute
- {
- routeType = RouteType.RoadPreferred,
- estimatedTime = 10.5f,
- totalDistance = 25.0f,
- totalCost = 50,
- specialCosts = new Dictionary<string, int> { { "Ferry", 10 } },
- path = new List<Vector2Int> { new Vector2Int(50, 50), new Vector2Int(75, 75), new Vector2Int(100, 100) }
- },
- new TravelRoute
- {
- routeType = RouteType.Standard,
- estimatedTime = 12.0f,
- totalDistance = 22.0f,
- totalCost = 45,
- specialCosts = new Dictionary<string, int>(),
- path = new List<Vector2Int> { new Vector2Int(50, 50), new Vector2Int(100, 100) }
- },
- new TravelRoute
- {
- routeType = RouteType.Special,
- estimatedTime = 8.0f,
- totalDistance = 30.0f,
- totalCost = 35,
- specialCosts = new Dictionary<string, int> { { "Tunnel Torch", 2 }, { "Mountain Guide", 15 } },
- path = new List<Vector2Int> { new Vector2Int(50, 50), new Vector2Int(60, 80), new Vector2Int(100, 100) }
- }
- };
- ShowTravelUI(new Vector2Int(100, 100), dummyRoutes);
- }
- /// <summary>
- /// Shows the travel UI with route options - UPDATED
- /// </summary>
- public void ShowTravelUI(Vector2Int destination, List<TravelRoute> routes)
- {
- Debug.Log($"🚀 TravelUI: ShowTravelUI called with destination {destination} and {routes.Count} routes");
- // If UI is already visible, update with new routes instead of ignoring
- if (isUIVisible)
- {
- Debug.Log("🔄 TravelUI: Already visible, updating with new routes");
- UpdateRoutesAndRefreshUI(destination, routes);
- return;
- }
- if (travelContainer == null)
- {
- Debug.LogWarning("⚠️ TravelUI: Container not found, attempting to setup UI");
- SetupUI();
- if (travelContainer == null)
- {
- Debug.LogError("❌ TravelUI: Failed to setup UI - travelContainer still null");
- return;
- }
- Debug.Log("✅ TravelUI: Successfully setup UI after retry");
- }
- currentDestination = destination;
- availableRoutes = new List<TravelRoute>(routes);
- // Select the fastest route by default
- if (availableRoutes.Count > 0)
- {
- selectedRoute = availableRoutes.OrderBy(r => r.estimatedTime).First();
- }
- else
- {
- Debug.LogWarning("TravelUI: No routes available!");
- }
- // Update UI content
- UpdateTravelInfo();
- PopulateRouteList();
- // Show the UI
- travelContainer.style.display = DisplayStyle.Flex;
- isUIVisible = true;
- // Ensure travel UI doesn't overlap with team overview panel
- EnsureNoTeamOverviewOverlap();
- // Force a layout update to ensure everything is positioned correctly
- travelContainer.MarkDirtyRepaint();
- }
- /// <summary>
- /// Hides the travel panel - UPDATED
- /// </summary>
- public void HideTravelPanel()
- {
- if (travelContainer != null)
- {
- travelContainer.style.display = DisplayStyle.None;
- }
- isUIVisible = false;
- // Clear route visualization when hiding
- var travelSystem = FindFirstObjectByType<TeamTravelSystem>();
- if (travelSystem != null)
- {
- try
- {
- // Try to clear route visualization
- var clearMethod = travelSystem.GetType().GetMethod("ClearRouteVisualization");
- if (clearMethod != null)
- {
- clearMethod.Invoke(travelSystem, null);
- Debug.Log("✅ TravelUI: Cleared route visualization");
- }
- }
- catch (System.Exception e)
- {
- Debug.LogWarning($"⚠️ TravelUI: Failed to clear route visualization: {e.Message}");
- }
- }
- if (debugMode) Debug.Log("👻 TravelUI: Panel hidden");
- if (debugMode) Debug.Log($"🔓 TravelUI: Map clicks now enabled outside panel - isUIVisible: {isUIVisible}");
- }
- /// <summary>
- /// Updates the UI with new routes and forces a complete refresh
- /// </summary>
- public void UpdateRoutesAndRefreshUI(Vector2Int destination, List<TravelRoute> routes)
- {
- Debug.Log($"🔄 TravelUI: UpdateRoutesAndRefreshUI called with {routes.Count} routes");
- // Update the route data
- currentDestination = destination;
- availableRoutes = new List<TravelRoute>(routes);
- // Select the fastest route by default
- if (availableRoutes.Count > 0)
- {
- selectedRoute = availableRoutes.OrderBy(r => r.estimatedTime).First();
- Debug.Log($"🎯 TravelUI: Updated to new default route: {selectedRoute.routeType}");
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI: No routes available in update!");
- selectedRoute = null;
- }
- // Force a complete UI refresh
- Debug.Log("🔄 TravelUI: Updating travel info after route refresh...");
- UpdateTravelInfo();
- Debug.Log("📋 TravelUI: Repopulating route list...");
- PopulateRouteList();
- // Force the entire container to repaint
- if (travelContainer != null)
- {
- travelContainer.MarkDirtyRepaint();
- Debug.Log("🎨 TravelUI: Forced container repaint after route update");
- }
- Debug.Log($"✅ TravelUI: UI refreshed with {routes.Count} updated routes");
- }
- /// <summary>
- /// DEBUG: Force hide the TravelUI for testing
- /// </summary>
- [ContextMenu("DEBUG: Force Hide TravelUI")]
- public void DebugForceHideTravelUI()
- {
- Debug.Log("🧪 TravelUI: DEBUG Force hiding TravelUI...");
- // Hide UI
- HideTravelPanel();
- }
- /// <summary>
- /// DEBUG: Test manual label updates
- /// </summary>
- [ContextMenu("DEBUG: Test Label Updates")]
- public void DebugTestLabelUpdates()
- {
- Debug.Log("🧪 TravelUI: DEBUG Testing label updates...");
- if (distanceLabel != null)
- {
- distanceLabel.text = $"Distance: {UnityEngine.Random.Range(10f, 100f):F1} leagues [DEBUG]";
- distanceLabel.MarkDirtyRepaint(); // Force UI refresh
- Debug.Log($"✅ TravelUI: Distance updated to: {distanceLabel.text}");
- }
- else
- {
- Debug.LogError("❌ TravelUI: distanceLabel is null!");
- }
- if (timeLabel != null)
- {
- timeLabel.text = $"Travel Time: {UnityEngine.Random.Range(5f, 50f):F1} hours [DEBUG]";
- timeLabel.MarkDirtyRepaint(); // Force UI refresh
- Debug.Log($"✅ TravelUI: Time updated to: {timeLabel.text}");
- }
- else
- {
- Debug.LogError("❌ TravelUI: timeLabel is null!");
- }
- if (specialCostsLabel != null)
- {
- specialCostsLabel.text = $"Special Costs: {UnityEngine.Random.Range(0, 100)} gold [DEBUG]";
- specialCostsLabel.MarkDirtyRepaint(); // Force UI refresh
- Debug.Log($"✅ TravelUI: Special costs updated to: {specialCostsLabel.text}");
- }
- else
- {
- Debug.LogError("❌ TravelUI: specialCostsLabel is null!");
- }
- // Force the entire container to refresh
- if (travelContainer != null)
- {
- travelContainer.MarkDirtyRepaint();
- }
- }
- /// <summary>
- /// DEBUG: Test route selection programmatically
- /// </summary>
- [ContextMenu("DEBUG: Test Route Selection")]
- public void DebugTestRouteSelection()
- {
- Debug.Log("🧪 TravelUI: DEBUG Testing route selection...");
- if (availableRoutes == null || availableRoutes.Count == 0)
- {
- Debug.LogError("❌ TravelUI: No available routes for testing!");
- return;
- }
- // Try to select each route programmatically
- for (int i = 0; i < availableRoutes.Count; i++)
- {
- var route = availableRoutes[i];
- Debug.Log($"🔄 TravelUI: Testing route {i + 1}: {route.routeType}");
- // Find the corresponding UI element
- var routeOptions = routeList.Children().ToList();
- if (i < routeOptions.Count)
- {
- var routeElement = routeOptions[i];
- Debug.Log($"🎯 TravelUI: Simulating click on route element {i}");
- SelectRoute(route, routeElement);
- // Wait a bit and show results
- Debug.Log($"📊 TravelUI: After selecting {route.routeType}:");
- Debug.Log($" Distance: {distanceLabel?.text ?? "NULL"}");
- Debug.Log($" Time: {timeLabel?.text ?? "NULL"}");
- Debug.Log($" Special: {specialCostsLabel?.text ?? "NULL"}");
- }
- else
- {
- Debug.LogError($"❌ TravelUI: Route element {i} not found in UI!");
- }
- }
- }
- /// <summary>
- /// Updates travel information display
- /// </summary>
- private void UpdateTravelInfo()
- {
- Debug.Log($"🔄 TravelUI.UpdateTravelInfo called - selectedRoute: {selectedRoute?.routeType}");
- if (selectedRoute == null)
- {
- Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: selectedRoute is null");
- return;
- }
- Debug.Log($"📊 TravelUI.UpdateTravelInfo: Updating with route {selectedRoute.routeType} - Time: {selectedRoute.estimatedTime:F1}h, Distance: {selectedRoute.totalDistance:F1}, SpecialCosts: {selectedRoute.specialCosts?.Count ?? 0}");
- if (distanceLabel != null)
- {
- distanceLabel.text = $"Distance: {selectedRoute.totalDistance:F1} leagues";
- distanceLabel.MarkDirtyRepaint(); // Force UI refresh
- Debug.Log($"✅ TravelUI: Updated distance to: {distanceLabel.text}");
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: distanceLabel is null");
- }
- if (timeLabel != null)
- {
- timeLabel.text = $"Travel Time: {selectedRoute.estimatedTime:F1} hours";
- timeLabel.MarkDirtyRepaint(); // Force UI refresh
- Debug.Log($"✅ TravelUI: Updated time to: {timeLabel.text}");
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: timeLabel is null");
- }
- if (specialCostsLabel != null)
- {
- if (selectedRoute.specialCosts != null && selectedRoute.specialCosts.Count > 0)
- {
- var costs = selectedRoute.specialCosts.Select(kvp => $"{kvp.Key}: {kvp.Value}");
- specialCostsLabel.text = $"Special Costs: {string.Join(", ", costs)}";
- specialCostsLabel.style.display = DisplayStyle.Flex;
- specialCostsLabel.MarkDirtyRepaint(); // Force UI refresh
- Debug.Log($"✅ TravelUI: Updated special costs to: {specialCostsLabel.text}");
- }
- else
- {
- specialCostsLabel.text = "Special Costs: None";
- specialCostsLabel.style.display = DisplayStyle.Flex;
- specialCostsLabel.MarkDirtyRepaint(); // Force UI refresh
- Debug.Log($"✅ TravelUI: Updated special costs to: None");
- }
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: specialCostsLabel is null");
- }
- // Force the entire container to refresh
- if (travelContainer != null)
- {
- travelContainer.MarkDirtyRepaint();
- }
- Debug.Log("✅ TravelUI.UpdateTravelInfo: Complete");
- }
- /// <summary>
- /// Populates the route selection list
- /// </summary>
- private void PopulateRouteList()
- {
- if (routeList == null) return;
- // Clear existing route options
- routeList.Clear();
- // Sort routes by estimated time (fastest first)
- var sortedRoutes = availableRoutes.OrderBy(r => r.estimatedTime).ToList();
- for (int i = 0; i < sortedRoutes.Count; i++)
- {
- var route = sortedRoutes[i];
- var routeElement = CreateRouteOptionElement(route, i == 0); // First route is selected by default
- routeList.Add(routeElement);
- }
- Debug.Log($"📊 TravelUI: Populated route list with {sortedRoutes.Count} options");
- }
- /// <summary>
- /// Creates a clickable route option element
- /// </summary>
- private VisualElement CreateRouteOptionElement(TravelRoute route, bool isSelected)
- {
- var routeOption = new VisualElement();
- routeOption.AddToClassList("route-option");
- if (isSelected)
- {
- routeOption.AddToClassList("route-selected");
- selectedRoute = route;
- }
- // Color indicator
- var colorIndicator = new VisualElement();
- colorIndicator.AddToClassList("route-color-indicator");
- // Set color based on route type
- Color routeColor = GetRouteDisplayColor(route);
- colorIndicator.style.backgroundColor = routeColor;
- routeOption.Add(colorIndicator);
- // Route details container
- var routeDetails = new VisualElement();
- routeDetails.AddToClassList("route-details");
- // Route type and main info
- var routeHeader = new Label($"{GetRouteDisplayName(route)}");
- routeHeader.AddToClassList("route-header");
- routeDetails.Add(routeHeader);
- // Time info
- var routeTime = new Label($"Time: {route.estimatedTime:F1}h");
- routeTime.AddToClassList("route-time");
- routeDetails.Add(routeTime);
- // Resource costs (gold, torches, etc.)
- var resourceCosts = GetResourceCosts(route);
- if (resourceCosts.Count > 0)
- {
- var costText = string.Join(", ", resourceCosts.Select(kvp => $"{kvp.Value} {kvp.Key}"));
- var routeCost = new Label($"Resources: {costText}");
- routeCost.AddToClassList("route-cost");
- routeDetails.Add(routeCost);
- }
- else
- {
- var routeCost = new Label("Resources: None");
- routeCost.AddToClassList("route-cost");
- routeDetails.Add(routeCost);
- }
- // Special costs if any
- if (route.specialCosts.Count > 0)
- {
- var specialText = string.Join(", ", route.specialCosts.Select(kvp => $"{kvp.Key}: {kvp.Value}"));
- var specialLabel = new Label($"Special: {specialText}");
- specialLabel.AddToClassList("route-special");
- routeDetails.Add(specialLabel);
- }
- // Add debug information showing path composition
- var debugInfo = GetRouteDebugInfo(route);
- if (!string.IsNullOrEmpty(debugInfo))
- {
- var debugLabel = new Label(debugInfo);
- debugLabel.AddToClassList("route-debug");
- routeDetails.Add(debugLabel);
- }
- routeOption.Add(routeDetails);
- // Click handler with enhanced debugging
- routeOption.RegisterCallback<ClickEvent>(evt =>
- {
- Debug.Log($"🖱️ TravelUI: Route clicked! Event target: {evt.target}, Route: {route.routeType}");
- SelectRoute(route, routeOption);
- evt.StopPropagation(); // Prevent event bubbling
- });
- // Also add mouse events for debugging
- routeOption.RegisterCallback<MouseEnterEvent>(evt =>
- {
- Debug.Log($"🐭 TravelUI: Mouse entered route: {route.routeType}");
- });
- return routeOption;
- }
- /// <summary>
- /// Handles route selection
- /// </summary>
- private void SelectRoute(TravelRoute route, VisualElement clickedElement)
- {
- Debug.Log($"🎯 TravelUI: SelectRoute called for {route.routeType} - Time: {route.estimatedTime:F1}h, Distance: {route.totalDistance:F1}");
- Debug.Log($"🔍 TravelUI: SelectRoute - Previous selectedRoute: {selectedRoute?.routeType}");
- selectedRoute = route;
- // Update visual selection
- var allRouteOptions = routeList.Children();
- Debug.Log($"📋 TravelUI: SelectRoute - Found {allRouteOptions.Count()} route options in list");
- foreach (var option in allRouteOptions)
- {
- option.RemoveFromClassList("route-selected");
- }
- clickedElement.AddToClassList("route-selected");
- Debug.Log($"✅ TravelUI: SelectRoute - Visual selection updated for {route.routeType}");
- Debug.Log($"🔄 TravelUI: About to call UpdateTravelInfo for {route.routeType}");
- // Update travel info
- UpdateTravelInfo();
- // Notify the travel system to highlight this route - FIX THIS CALL
- var travelSystem = FindFirstObjectByType<TeamTravelSystem>();
- if (travelSystem != null)
- {
- // Use the correct method name from your travel system
- try
- {
- travelSystem.VisualizeSpecificRoute(route);
- Debug.Log($"✅ TravelUI: Successfully called VisualizeSpecificRoute for {route.routeType}");
- }
- catch (System.Exception e)
- {
- Debug.LogWarning($"⚠️ TravelUI: Failed to visualize route: {e.Message}");
- // Try alternative method names
- var method = travelSystem.GetType().GetMethod("HighlightRoute");
- if (method != null)
- {
- method.Invoke(travelSystem, new object[] { route });
- Debug.Log($"✅ TravelUI: Used HighlightRoute instead");
- }
- }
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI: TeamTravelSystem not found for route visualization");
- }
- Debug.Log($"🎯 TravelUI: Selected {route.routeType} route - SelectRoute complete");
- }
- /// <summary>
- /// Gets display color for route based on type (GPS-style)
- /// </summary>
- private Color GetRouteDisplayColor(TravelRoute route)
- {
- // Match colors from TeamTravelSystem GPS-style
- switch (route.routeType)
- {
- case RouteType.Standard:
- return Color.blue; // Shortest route
- case RouteType.RoadPreferred:
- return Color.green; // Fastest route
- case RouteType.Special:
- return Color.yellow; // Cheapest route
- default:
- return Color.blue;
- }
- }
- /// <summary>
- /// Gets GPS-style display name for route type
- /// </summary>
- private string GetRouteDisplayName(TravelRoute route)
- {
- switch (route.routeType)
- {
- case RouteType.Standard:
- return "Shortest Route";
- case RouteType.RoadPreferred:
- return "Fastest Route";
- case RouteType.Special:
- return "Cheapest Route";
- default:
- return "Unknown Route";
- }
- }
- /// <summary>
- /// Handles start travel button click
- /// </summary>
- private void OnStartTravelClicked()
- {
- if (selectedRoute == null)
- {
- Debug.LogWarning("⚠️ TravelUI: No route selected for travel");
- return;
- }
- Debug.Log($"🚶 TravelUI: Starting travel with {selectedRoute.routeType} route");
- // Start travel through the travel system
- var travelSystem = TeamTravelSystem.Instance;
- if (travelSystem != null)
- {
- travelSystem.StartTravel(selectedRoute);
- }
- // Hide UI
- HideTravelPanel();
- }
- /// <summary>
- /// Handles cancel button click
- /// </summary>
- private void OnCancelTravelClicked()
- {
- Debug.Log("❌ TravelUI: Travel cancelled by user");
- // Cancel travel planning
- var travelSystem = TeamTravelSystem.Instance;
- if (travelSystem != null)
- {
- travelSystem.CancelTravelPlanning();
- }
- // Hide UI
- HideTravelPanel();
- }
- /// <summary>
- /// Updates UI for a selected route (called by TeamTravelSystem via reflection)
- /// </summary>
- public void UpdateUIForSelectedRoute(TravelRoute route)
- {
- if (!isUIVisible)
- {
- return;
- }
- selectedRoute = route;
- UpdateTravelInfo();
- // Update visual selection in route list
- if (routeList != null)
- {
- var routeOptions = routeList.Children().ToList();
- for (int i = 0; i < routeOptions.Count && i < availableRoutes.Count; i++)
- {
- var routeOption = routeOptions[i];
- var routeData = availableRoutes.OrderBy(r => r.estimatedTime).ToList()[i];
- if (routeData == route)
- {
- routeOption.AddToClassList("route-selected");
- Debug.Log($"🎯 TravelUI.UpdateUIForSelectedRoute: Visual selection updated for route {i}");
- }
- else
- {
- routeOption.RemoveFromClassList("route-selected");
- }
- }
- }
- else
- {
- Debug.LogWarning("⚠️ TravelUI.UpdateUIForSelectedRoute: routeList is null");
- }
- Debug.Log($"🔄 TravelUI: Updated UI for {route.routeType} route");
- }
- /// <summary>
- /// Checks if UI is blocking input (called by TeamTravelSystem via reflection)
- /// </summary>
- public bool IsUIBlocking()
- {
- return isUIVisible;
- }
- /// <summary>
- /// <summary>
- /// IClickBlocker implementation: Check if this UI should block clicks at the given position
- /// </summary>
- public bool IsBlockingClick(Vector2 screenPosition)
- {
- if (!isUIVisible || travelPanel == null)
- {
- return false;
- }
- // Convert screen position to panel-relative position
- Vector2 panelPosition = RuntimePanelUtils.ScreenToPanel(travelPanel.panel, screenPosition);
- // Check if the position is within the panel bounds
- bool withinBounds = travelPanel.worldBound.Contains(panelPosition);
- return withinBounds;
- }
- /// <summary>
- /// Legacy method for backward compatibility - now delegates to IsBlockingClick
- /// Check if a screen position (in Unity's screen coordinates) is within the UI panel
- /// </summary>
- public bool IsPointWithinUI(Vector2 screenPosition)
- {
- return IsBlockingClick(screenPosition);
- }
- /// <summary>
- /// Public getters for external access
- /// </summary>
- public bool IsVisible => isUIVisible;
- public TravelRoute GetSelectedRoute() => selectedRoute;
- /// <summary>
- /// Forces the UI to refresh with new route data - called externally when routes are recalculated
- /// </summary>
- public void RefreshWithNewRoutes(List<TravelRoute> routes)
- {
- Debug.Log($"🔄 TravelUI: RefreshWithNewRoutes called externally with {routes.Count} routes");
- if (!isUIVisible)
- {
- Debug.LogWarning("⚠️ TravelUI: RefreshWithNewRoutes called but UI is not visible");
- return;
- }
- UpdateRoutesAndRefreshUI(currentDestination, routes);
- }
- /// <summary>
- /// Sets up drag functionality for the travel panel
- /// </summary>
- private void SetupDragFunctionality(VisualElement dragHandle, VisualElement panelToDrag)
- {
- bool isDragging = false;
- Vector2 startPosition = Vector2.zero;
- Vector2 startMousePosition = Vector2.zero;
- dragHandle.RegisterCallback<MouseDownEvent>(evt =>
- {
- if (evt.button == 0) // Left mouse button
- {
- isDragging = true;
- startPosition = new Vector2(panelToDrag.style.left.value.value, panelToDrag.style.top.value.value);
- startMousePosition = evt.mousePosition;
- dragHandle.CaptureMouse();
- evt.StopPropagation();
- }
- });
- dragHandle.RegisterCallback<MouseMoveEvent>(evt =>
- {
- if (isDragging)
- {
- Vector2 delta = evt.mousePosition - startMousePosition;
- panelToDrag.style.left = startPosition.x + delta.x;
- panelToDrag.style.top = startPosition.y + delta.y;
- evt.StopPropagation();
- }
- });
- dragHandle.RegisterCallback<MouseUpEvent>(evt =>
- {
- if (isDragging)
- {
- isDragging = false;
- dragHandle.ReleaseMouse();
- evt.StopPropagation();
- }
- });
- }
- /// <summary>
- /// Gets debug information about route composition
- /// </summary>
- private string GetRouteDebugInfo(TravelRoute route)
- {
- if (route?.path == null || route.path.Count == 0) return "";
- // Get terrain analysis from TeamTravelSystem
- var travelSystem = TeamTravelSystem.Instance;
- if (travelSystem == null) return "";
- // Count different terrain types in the route
- var terrainCounts = new Dictionary<string, int>();
- // This is a simplified analysis - in a full implementation,
- // you'd access the actual map data through the travel system
- float avgCostPerTile = route.totalCost / route.path.Count;
- string preference = "";
- switch (route.routeType)
- {
- case RouteType.RoadPreferred:
- preference = "Prefers roads";
- break;
- case RouteType.Special:
- preference = "Uses ferries/tunnels";
- break;
- default:
- preference = "Cheapest path";
- break;
- }
- return $"{preference} • Avg: {avgCostPerTile:F1}/tile";
- }
- /// <summary>
- /// Gets resource costs for a route (gold, torches, etc.)
- /// </summary>
- private Dictionary<string, int> GetResourceCosts(TravelRoute route)
- {
- var costs = new Dictionary<string, int>();
- // Add special costs from the route (with null safety)
- if (route?.specialCosts != null)
- {
- foreach (var specialCost in route.specialCosts)
- {
- switch (specialCost.Key)
- {
- case "Ferry":
- case "Bridge Toll":
- costs["Gold"] = costs.GetValueOrDefault("Gold", 0) + specialCost.Value;
- break;
- case "Tunnel Torch":
- costs["Torches"] = costs.GetValueOrDefault("Torches", 0) + specialCost.Value;
- break;
- case "Mountain Guide":
- costs["Gold"] = costs.GetValueOrDefault("Gold", 0) + specialCost.Value;
- break;
- default:
- costs[specialCost.Key] = specialCost.Value;
- break;
- }
- }
- }
- return costs;
- }
- /// <summary>
- /// Ensures the travel UI doesn't overlap with the team overview panel on the right side
- /// </summary>
- private void EnsureNoTeamOverviewOverlap()
- {
- if (travelContainer == null) return;
- // Check if team overview exists
- var teamOverview = FindFirstObjectByType<TeamOverviewController>();
- if (teamOverview != null)
- {
- // The CSS already centers the travel UI, but we can add extra margin if needed
- // This is a safeguard to ensure proper spacing
- var panel = travelContainer.Q("TravelPanel");
- if (panel != null)
- {
- // Add some extra margin to avoid any potential overlap
- panel.style.marginRight = 320; // Team overview width + padding
- }
- }
- }
- }
|