TravelUI.cs 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. using UnityEngine;
  2. using UnityEngine.UIElements;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. /// <summary>
  6. /// UI Controller for the Travel System with route selection and comparison
  7. /// </summary>
  8. public class TravelUI : MonoBehaviour
  9. {
  10. [Header("UI Document")]
  11. public UIDocument uiDocument;
  12. [Header("UI Files")]
  13. public VisualTreeAsset travelUIAsset;
  14. public StyleSheet travelUIStyleSheet;
  15. // UI Elements
  16. private VisualElement travelContainer;
  17. private VisualElement travelPanel;
  18. private Label distanceLabel;
  19. private Label timeLabel;
  20. private Label specialCostsLabel;
  21. private Button startTravelButton;
  22. private Button cancelTravelButton;
  23. private VisualElement routeList;
  24. // Travel data
  25. private Vector2Int currentDestination;
  26. private List<TravelRoute> availableRoutes = new List<TravelRoute>();
  27. private TravelRoute selectedRoute;
  28. // State
  29. private bool isUIVisible = false;
  30. void Awake()
  31. {
  32. // Find UI Document if not assigned
  33. if (uiDocument == null)
  34. {
  35. uiDocument = GetComponent<UIDocument>();
  36. }
  37. // Load UXML asset from Resources if not assigned
  38. if (travelUIAsset == null)
  39. {
  40. travelUIAsset = Resources.Load<VisualTreeAsset>("UI/Map/TravelUI");
  41. if (travelUIAsset == null)
  42. {
  43. // Try alternative path
  44. travelUIAsset = Resources.Load<VisualTreeAsset>("TravelUI");
  45. }
  46. }
  47. // Load USS style sheet if not assigned
  48. if (travelUIStyleSheet == null)
  49. {
  50. travelUIStyleSheet = Resources.Load<StyleSheet>("UI/Map/TravelUI");
  51. if (travelUIStyleSheet == null)
  52. {
  53. // Try alternative path
  54. travelUIStyleSheet = Resources.Load<StyleSheet>("TravelUI");
  55. }
  56. }
  57. }
  58. void Start()
  59. {
  60. SetupUI();
  61. }
  62. void SetupUI()
  63. {
  64. if (uiDocument == null)
  65. {
  66. Debug.LogError("TravelUI: UIDocument not found!");
  67. return;
  68. }
  69. var root = uiDocument.rootVisualElement;
  70. // Load and instantiate the UXML if we have the asset
  71. if (travelUIAsset != null)
  72. {
  73. // Clear existing content
  74. root.Clear();
  75. // Instantiate from UXML
  76. travelUIAsset.CloneTree(root);
  77. // Add style sheet
  78. if (travelUIStyleSheet != null)
  79. {
  80. root.styleSheets.Add(travelUIStyleSheet);
  81. }
  82. else
  83. {
  84. Debug.LogWarning("TravelUI: No style sheet found");
  85. }
  86. }
  87. else
  88. {
  89. Debug.LogWarning("TravelUI: No UXML asset found");
  90. }
  91. // Find UI elements
  92. travelContainer = root.Q<VisualElement>("TravelContainer");
  93. travelPanel = root.Q<VisualElement>("TravelPanel");
  94. distanceLabel = root.Q<Label>("DistanceLabel");
  95. timeLabel = root.Q<Label>("TimeLabel");
  96. specialCostsLabel = root.Q<Label>("SpecialCostsLabel");
  97. startTravelButton = root.Q<Button>("StartTravelButton");
  98. cancelTravelButton = root.Q<Button>("CancelTravelButton");
  99. // Find draggable header and close button
  100. var travelHeader = root.Q<VisualElement>("TravelHeader");
  101. var closeButton = root.Q<Button>("CloseButton");
  102. // Find or create route list container
  103. routeList = root.Q<VisualElement>("RouteList");
  104. if (routeList == null)
  105. {
  106. Debug.Log("🔨 TravelUI: RouteList not found, creating it...");
  107. // Create route list if it doesn't exist
  108. var routeOptions = root.Q<VisualElement>("RouteOptions");
  109. if (routeOptions != null)
  110. {
  111. routeList = new VisualElement();
  112. routeList.name = "RouteList";
  113. routeList.AddToClassList("route-list");
  114. routeOptions.Add(routeList);
  115. Debug.Log("✅ TravelUI: RouteList created and added to RouteOptions");
  116. }
  117. else
  118. {
  119. Debug.LogWarning("⚠️ TravelUI: RouteOptions container not found, cannot create RouteList");
  120. }
  121. }
  122. else
  123. {
  124. Debug.Log("RouteList found");
  125. }
  126. // Setup dragging functionality
  127. if (travelHeader != null && travelPanel != null)
  128. {
  129. SetupDragFunctionality(travelHeader, travelPanel);
  130. }
  131. // Setup close button
  132. if (closeButton != null)
  133. {
  134. closeButton.clicked += OnCancelTravelClicked;
  135. }
  136. // Setup button callbacks
  137. if (startTravelButton != null)
  138. {
  139. startTravelButton.clicked += OnStartTravelClicked;
  140. }
  141. if (cancelTravelButton != null)
  142. {
  143. cancelTravelButton.clicked += OnCancelTravelClicked;
  144. }
  145. // REMOVE THIS DUPLICATE HANDLER - it's conflicting with SetupClickBlocking()
  146. // travelContainer.RegisterCallback<ClickEvent>(evt =>
  147. // {
  148. // // Allow clicks on buttons and other interactive elements
  149. // if (evt.target is Button || evt.target.GetType().Name.Contains("Button"))
  150. // {
  151. // Debug.Log("🔓 TravelUI: Allowing click on button element");
  152. // return; // Don't stop propagation for buttons
  153. // }
  154. //
  155. // Debug.Log("🛡️ TravelUI: TravelContainer blocking click - stopping propagation");
  156. // evt.StopPropagation(); // Prevent clicks from passing through to the map
  157. // });
  158. // Hide UI initially
  159. HideTravelPanel();
  160. Debug.Log("TravelUI initialized successfully");
  161. }
  162. /// <summary>
  163. /// DEBUG: Force show the TravelUI for testing
  164. /// </summary>
  165. [ContextMenu("DEBUG: Force Show TravelUI")]
  166. public void DebugForceShowTravelUI()
  167. {
  168. Debug.Log("🧪 TravelUI: DEBUG Force showing TravelUI...");
  169. // CHECK FOR DUPLICATE TEAM MARKERS
  170. var allTeamMarkers = GameObject.FindObjectsByType<GameObject>(FindObjectsSortMode.None).Where(g => g.name == "TeamMarker").ToArray();
  171. Debug.Log($"🔍 Found {allTeamMarkers.Length} TeamMarker objects:");
  172. for (int i = 0; i < allTeamMarkers.Length; i++)
  173. {
  174. var marker = allTeamMarkers[i];
  175. bool hasSphere = marker.GetComponentInChildren<Transform>().name.Contains("Sphere");
  176. Debug.Log($" {i + 1}. {marker.name} at {marker.transform.position} - Has Sphere: {hasSphere}");
  177. }
  178. // Prevent multiple instances
  179. if (isUIVisible)
  180. {
  181. Debug.Log("⚠️ TravelUI: Already visible, ignoring duplicate show request");
  182. return;
  183. }
  184. // Create some dummy routes for testing with proper initialization
  185. var dummyRoutes = new List<TravelRoute>
  186. {
  187. new TravelRoute
  188. {
  189. routeType = RouteType.RoadPreferred,
  190. estimatedTime = 10.5f,
  191. totalDistance = 25.0f,
  192. totalCost = 50,
  193. specialCosts = new Dictionary<string, int> { { "Ferry", 10 } },
  194. path = new List<Vector2Int> { new Vector2Int(50, 50), new Vector2Int(75, 75), new Vector2Int(100, 100) }
  195. },
  196. new TravelRoute
  197. {
  198. routeType = RouteType.Standard,
  199. estimatedTime = 12.0f,
  200. totalDistance = 22.0f,
  201. totalCost = 45,
  202. specialCosts = new Dictionary<string, int>(),
  203. path = new List<Vector2Int> { new Vector2Int(50, 50), new Vector2Int(100, 100) }
  204. },
  205. new TravelRoute
  206. {
  207. routeType = RouteType.Special,
  208. estimatedTime = 8.0f,
  209. totalDistance = 30.0f,
  210. totalCost = 35,
  211. specialCosts = new Dictionary<string, int> { { "Tunnel Torch", 2 }, { "Mountain Guide", 15 } },
  212. path = new List<Vector2Int> { new Vector2Int(50, 50), new Vector2Int(60, 80), new Vector2Int(100, 100) }
  213. }
  214. };
  215. ShowTravelUI(new Vector2Int(100, 100), dummyRoutes);
  216. }
  217. /// <summary>
  218. /// Sets up click blocking for the travel UI to prevent click-through
  219. /// </summary>
  220. private void SetupClickBlocking()
  221. {
  222. // Using coordinate-based blocking only - no visual overlay needed
  223. // The IsPointWithinUI() method will handle blocking map clicks
  224. // UI elements will work normally without any overlay interference
  225. Debug.Log("🔧 TravelUI: Using coordinate-based click blocking only - no visual overlay needed");
  226. }
  227. /// <summary>
  228. /// Shows the travel UI with route options - UPDATED
  229. /// </summary>
  230. public void ShowTravelUI(Vector2Int destination, List<TravelRoute> routes)
  231. {
  232. Debug.Log($"🚀 TravelUI: ShowTravelUI called with destination {destination} and {routes.Count} routes");
  233. // If UI is already visible, update with new routes instead of ignoring
  234. if (isUIVisible)
  235. {
  236. Debug.Log("🔄 TravelUI: Already visible, updating with new routes");
  237. UpdateRoutesAndRefreshUI(destination, routes);
  238. return;
  239. }
  240. if (travelContainer == null)
  241. {
  242. Debug.LogWarning("⚠️ TravelUI: Container not found, attempting to setup UI");
  243. SetupUI();
  244. if (travelContainer == null)
  245. {
  246. Debug.LogError("❌ TravelUI: Failed to setup UI - travelContainer still null");
  247. return;
  248. }
  249. Debug.Log("✅ TravelUI: Successfully setup UI after retry");
  250. }
  251. currentDestination = destination;
  252. availableRoutes = new List<TravelRoute>(routes);
  253. // Select the fastest route by default
  254. if (availableRoutes.Count > 0)
  255. {
  256. selectedRoute = availableRoutes.OrderBy(r => r.estimatedTime).First();
  257. }
  258. else
  259. {
  260. Debug.LogWarning("TravelUI: No routes available!");
  261. }
  262. // Update UI content
  263. UpdateTravelInfo();
  264. PopulateRouteList();
  265. // Show the UI
  266. travelContainer.style.display = DisplayStyle.Flex;
  267. isUIVisible = true;
  268. // Set up click blocking when showing the UI
  269. SetupClickBlocking();
  270. // Force a layout update to ensure everything is positioned correctly
  271. travelContainer.MarkDirtyRepaint();
  272. Debug.Log($"📋 TravelUI: Showing UI with {routes.Count} routes to {destination}");
  273. Debug.Log($"🔒 TravelUI: UI panel now blocks clicks within its bounds - isUIVisible: {isUIVisible}");
  274. Debug.Log($"🔍 TravelUI: Container display style: {travelContainer.style.display}");
  275. }
  276. /// <summary>
  277. /// Hides the travel panel - UPDATED
  278. /// </summary>
  279. public void HideTravelPanel()
  280. {
  281. if (travelContainer != null)
  282. {
  283. travelContainer.style.display = DisplayStyle.None;
  284. }
  285. isUIVisible = false;
  286. // Clear route visualization when hiding
  287. var travelSystem = FindFirstObjectByType<TeamTravelSystem>();
  288. if (travelSystem != null)
  289. {
  290. try
  291. {
  292. // Try to clear route visualization
  293. var clearMethod = travelSystem.GetType().GetMethod("ClearRouteVisualization");
  294. if (clearMethod != null)
  295. {
  296. clearMethod.Invoke(travelSystem, null);
  297. Debug.Log("✅ TravelUI: Cleared route visualization");
  298. }
  299. }
  300. catch (System.Exception e)
  301. {
  302. Debug.LogWarning($"⚠️ TravelUI: Failed to clear route visualization: {e.Message}");
  303. }
  304. }
  305. Debug.Log("👻 TravelUI: Panel hidden");
  306. Debug.Log($"🔓 TravelUI: Map clicks now enabled outside panel - isUIVisible: {isUIVisible}");
  307. }
  308. /// <summary>
  309. /// Updates the UI with new routes and forces a complete refresh
  310. /// </summary>
  311. public void UpdateRoutesAndRefreshUI(Vector2Int destination, List<TravelRoute> routes)
  312. {
  313. Debug.Log($"🔄 TravelUI: UpdateRoutesAndRefreshUI called with {routes.Count} routes");
  314. // Update the route data
  315. currentDestination = destination;
  316. availableRoutes = new List<TravelRoute>(routes);
  317. // Select the fastest route by default
  318. if (availableRoutes.Count > 0)
  319. {
  320. selectedRoute = availableRoutes.OrderBy(r => r.estimatedTime).First();
  321. Debug.Log($"🎯 TravelUI: Updated to new default route: {selectedRoute.routeType}");
  322. }
  323. else
  324. {
  325. Debug.LogWarning("⚠️ TravelUI: No routes available in update!");
  326. selectedRoute = null;
  327. }
  328. // Force a complete UI refresh
  329. Debug.Log("🔄 TravelUI: Updating travel info after route refresh...");
  330. UpdateTravelInfo();
  331. Debug.Log("📋 TravelUI: Repopulating route list...");
  332. PopulateRouteList();
  333. // Force the entire container to repaint
  334. if (travelContainer != null)
  335. {
  336. travelContainer.MarkDirtyRepaint();
  337. Debug.Log("🎨 TravelUI: Forced container repaint after route update");
  338. }
  339. Debug.Log($"✅ TravelUI: UI refreshed with {routes.Count} updated routes");
  340. }
  341. /// <summary>
  342. /// DEBUG: Force hide the TravelUI for testing
  343. /// </summary>
  344. [ContextMenu("DEBUG: Force Hide TravelUI")]
  345. public void DebugForceHideTravelUI()
  346. {
  347. Debug.Log("🧪 TravelUI: DEBUG Force hiding TravelUI...");
  348. // Hide UI
  349. HideTravelPanel();
  350. }
  351. /// <summary>
  352. /// DEBUG: Test manual label updates
  353. /// </summary>
  354. [ContextMenu("DEBUG: Test Label Updates")]
  355. public void DebugTestLabelUpdates()
  356. {
  357. Debug.Log("🧪 TravelUI: DEBUG Testing label updates...");
  358. if (distanceLabel != null)
  359. {
  360. distanceLabel.text = $"Distance: {UnityEngine.Random.Range(10f, 100f):F1} leagues [DEBUG]";
  361. distanceLabel.MarkDirtyRepaint(); // Force UI refresh
  362. Debug.Log($"✅ TravelUI: Distance updated to: {distanceLabel.text}");
  363. }
  364. else
  365. {
  366. Debug.LogError("❌ TravelUI: distanceLabel is null!");
  367. }
  368. if (timeLabel != null)
  369. {
  370. timeLabel.text = $"Travel Time: {UnityEngine.Random.Range(5f, 50f):F1} hours [DEBUG]";
  371. timeLabel.MarkDirtyRepaint(); // Force UI refresh
  372. Debug.Log($"✅ TravelUI: Time updated to: {timeLabel.text}");
  373. }
  374. else
  375. {
  376. Debug.LogError("❌ TravelUI: timeLabel is null!");
  377. }
  378. if (specialCostsLabel != null)
  379. {
  380. specialCostsLabel.text = $"Special Costs: {UnityEngine.Random.Range(0, 100)} gold [DEBUG]";
  381. specialCostsLabel.MarkDirtyRepaint(); // Force UI refresh
  382. Debug.Log($"✅ TravelUI: Special costs updated to: {specialCostsLabel.text}");
  383. }
  384. else
  385. {
  386. Debug.LogError("❌ TravelUI: specialCostsLabel is null!");
  387. }
  388. // Force the entire container to refresh
  389. if (travelContainer != null)
  390. {
  391. travelContainer.MarkDirtyRepaint();
  392. }
  393. }
  394. /// <summary>
  395. /// DEBUG: Test route selection programmatically
  396. /// </summary>
  397. [ContextMenu("DEBUG: Test Route Selection")]
  398. public void DebugTestRouteSelection()
  399. {
  400. Debug.Log("🧪 TravelUI: DEBUG Testing route selection...");
  401. if (availableRoutes == null || availableRoutes.Count == 0)
  402. {
  403. Debug.LogError("❌ TravelUI: No available routes for testing!");
  404. return;
  405. }
  406. // Try to select each route programmatically
  407. for (int i = 0; i < availableRoutes.Count; i++)
  408. {
  409. var route = availableRoutes[i];
  410. Debug.Log($"🔄 TravelUI: Testing route {i + 1}: {route.routeType}");
  411. // Find the corresponding UI element
  412. var routeOptions = routeList.Children().ToList();
  413. if (i < routeOptions.Count)
  414. {
  415. var routeElement = routeOptions[i];
  416. Debug.Log($"🎯 TravelUI: Simulating click on route element {i}");
  417. SelectRoute(route, routeElement);
  418. // Wait a bit and show results
  419. Debug.Log($"📊 TravelUI: After selecting {route.routeType}:");
  420. Debug.Log($" Distance: {distanceLabel?.text ?? "NULL"}");
  421. Debug.Log($" Time: {timeLabel?.text ?? "NULL"}");
  422. Debug.Log($" Special: {specialCostsLabel?.text ?? "NULL"}");
  423. }
  424. else
  425. {
  426. Debug.LogError($"❌ TravelUI: Route element {i} not found in UI!");
  427. }
  428. }
  429. }
  430. /// <summary>
  431. /// Updates travel information display
  432. /// </summary>
  433. private void UpdateTravelInfo()
  434. {
  435. Debug.Log($"🔄 TravelUI.UpdateTravelInfo called - selectedRoute: {selectedRoute?.routeType}");
  436. if (selectedRoute == null)
  437. {
  438. Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: selectedRoute is null");
  439. return;
  440. }
  441. Debug.Log($"📊 TravelUI.UpdateTravelInfo: Updating with route {selectedRoute.routeType} - Time: {selectedRoute.estimatedTime:F1}h, Distance: {selectedRoute.totalDistance:F1}, SpecialCosts: {selectedRoute.specialCosts?.Count ?? 0}");
  442. if (distanceLabel != null)
  443. {
  444. distanceLabel.text = $"Distance: {selectedRoute.totalDistance:F1} leagues";
  445. distanceLabel.MarkDirtyRepaint(); // Force UI refresh
  446. Debug.Log($"✅ TravelUI: Updated distance to: {distanceLabel.text}");
  447. }
  448. else
  449. {
  450. Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: distanceLabel is null");
  451. }
  452. if (timeLabel != null)
  453. {
  454. timeLabel.text = $"Travel Time: {selectedRoute.estimatedTime:F1} hours";
  455. timeLabel.MarkDirtyRepaint(); // Force UI refresh
  456. Debug.Log($"✅ TravelUI: Updated time to: {timeLabel.text}");
  457. }
  458. else
  459. {
  460. Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: timeLabel is null");
  461. }
  462. if (specialCostsLabel != null)
  463. {
  464. if (selectedRoute.specialCosts != null && selectedRoute.specialCosts.Count > 0)
  465. {
  466. var costs = selectedRoute.specialCosts.Select(kvp => $"{kvp.Key}: {kvp.Value}");
  467. specialCostsLabel.text = $"Special Costs: {string.Join(", ", costs)}";
  468. specialCostsLabel.style.display = DisplayStyle.Flex;
  469. specialCostsLabel.MarkDirtyRepaint(); // Force UI refresh
  470. Debug.Log($"✅ TravelUI: Updated special costs to: {specialCostsLabel.text}");
  471. }
  472. else
  473. {
  474. specialCostsLabel.text = "Special Costs: None";
  475. specialCostsLabel.style.display = DisplayStyle.Flex;
  476. specialCostsLabel.MarkDirtyRepaint(); // Force UI refresh
  477. Debug.Log($"✅ TravelUI: Updated special costs to: None");
  478. }
  479. }
  480. else
  481. {
  482. Debug.LogWarning("⚠️ TravelUI.UpdateTravelInfo: specialCostsLabel is null");
  483. }
  484. // Force the entire container to refresh
  485. if (travelContainer != null)
  486. {
  487. travelContainer.MarkDirtyRepaint();
  488. }
  489. Debug.Log("✅ TravelUI.UpdateTravelInfo: Complete");
  490. }
  491. /// <summary>
  492. /// Populates the route selection list
  493. /// </summary>
  494. private void PopulateRouteList()
  495. {
  496. if (routeList == null) return;
  497. // Clear existing route options
  498. routeList.Clear();
  499. // Sort routes by estimated time (fastest first)
  500. var sortedRoutes = availableRoutes.OrderBy(r => r.estimatedTime).ToList();
  501. for (int i = 0; i < sortedRoutes.Count; i++)
  502. {
  503. var route = sortedRoutes[i];
  504. var routeElement = CreateRouteOptionElement(route, i == 0); // First route is selected by default
  505. routeList.Add(routeElement);
  506. }
  507. Debug.Log($"📊 TravelUI: Populated route list with {sortedRoutes.Count} options");
  508. }
  509. /// <summary>
  510. /// Creates a clickable route option element
  511. /// </summary>
  512. private VisualElement CreateRouteOptionElement(TravelRoute route, bool isSelected)
  513. {
  514. var routeOption = new VisualElement();
  515. routeOption.AddToClassList("route-option");
  516. if (isSelected)
  517. {
  518. routeOption.AddToClassList("route-selected");
  519. selectedRoute = route;
  520. }
  521. // Color indicator
  522. var colorIndicator = new VisualElement();
  523. colorIndicator.AddToClassList("route-color-indicator");
  524. // Set color based on route type
  525. Color routeColor = GetRouteDisplayColor(route);
  526. colorIndicator.style.backgroundColor = routeColor;
  527. routeOption.Add(colorIndicator);
  528. // Route details container
  529. var routeDetails = new VisualElement();
  530. routeDetails.AddToClassList("route-details");
  531. // Route type and main info
  532. var routeHeader = new Label($"{GetRouteDisplayName(route)}");
  533. routeHeader.AddToClassList("route-header");
  534. routeDetails.Add(routeHeader);
  535. // Time info
  536. var routeTime = new Label($"Time: {route.estimatedTime:F1}h");
  537. routeTime.AddToClassList("route-time");
  538. routeDetails.Add(routeTime);
  539. // Resource costs (gold, torches, etc.)
  540. var resourceCosts = GetResourceCosts(route);
  541. if (resourceCosts.Count > 0)
  542. {
  543. var costText = string.Join(", ", resourceCosts.Select(kvp => $"{kvp.Value} {kvp.Key}"));
  544. var routeCost = new Label($"Resources: {costText}");
  545. routeCost.AddToClassList("route-cost");
  546. routeDetails.Add(routeCost);
  547. }
  548. else
  549. {
  550. var routeCost = new Label("Resources: None");
  551. routeCost.AddToClassList("route-cost");
  552. routeDetails.Add(routeCost);
  553. }
  554. // Special costs if any
  555. if (route.specialCosts.Count > 0)
  556. {
  557. var specialText = string.Join(", ", route.specialCosts.Select(kvp => $"{kvp.Key}: {kvp.Value}"));
  558. var specialLabel = new Label($"Special: {specialText}");
  559. specialLabel.AddToClassList("route-special");
  560. routeDetails.Add(specialLabel);
  561. }
  562. // Add debug information showing path composition
  563. var debugInfo = GetRouteDebugInfo(route);
  564. if (!string.IsNullOrEmpty(debugInfo))
  565. {
  566. var debugLabel = new Label(debugInfo);
  567. debugLabel.AddToClassList("route-debug");
  568. routeDetails.Add(debugLabel);
  569. }
  570. routeOption.Add(routeDetails);
  571. // Click handler with enhanced debugging
  572. routeOption.RegisterCallback<ClickEvent>(evt =>
  573. {
  574. Debug.Log($"🖱️ TravelUI: Route clicked! Event target: {evt.target}, Route: {route.routeType}");
  575. SelectRoute(route, routeOption);
  576. evt.StopPropagation(); // Prevent event bubbling
  577. });
  578. // Also add mouse events for debugging
  579. routeOption.RegisterCallback<MouseEnterEvent>(evt =>
  580. {
  581. Debug.Log($"🐭 TravelUI: Mouse entered route: {route.routeType}");
  582. });
  583. return routeOption;
  584. }
  585. /// <summary>
  586. /// Handles route selection
  587. /// </summary>
  588. private void SelectRoute(TravelRoute route, VisualElement clickedElement)
  589. {
  590. Debug.Log($"🎯 TravelUI: SelectRoute called for {route.routeType} - Time: {route.estimatedTime:F1}h, Distance: {route.totalDistance:F1}");
  591. Debug.Log($"🔍 TravelUI: SelectRoute - Previous selectedRoute: {selectedRoute?.routeType}");
  592. selectedRoute = route;
  593. // Update visual selection
  594. var allRouteOptions = routeList.Children();
  595. Debug.Log($"📋 TravelUI: SelectRoute - Found {allRouteOptions.Count()} route options in list");
  596. foreach (var option in allRouteOptions)
  597. {
  598. option.RemoveFromClassList("route-selected");
  599. }
  600. clickedElement.AddToClassList("route-selected");
  601. Debug.Log($"✅ TravelUI: SelectRoute - Visual selection updated for {route.routeType}");
  602. Debug.Log($"🔄 TravelUI: About to call UpdateTravelInfo for {route.routeType}");
  603. // Update travel info
  604. UpdateTravelInfo();
  605. // Notify the travel system to highlight this route - FIX THIS CALL
  606. var travelSystem = FindFirstObjectByType<TeamTravelSystem>();
  607. if (travelSystem != null)
  608. {
  609. // Use the correct method name from your travel system
  610. try
  611. {
  612. travelSystem.VisualizeSpecificRoute(route);
  613. Debug.Log($"✅ TravelUI: Successfully called VisualizeSpecificRoute for {route.routeType}");
  614. }
  615. catch (System.Exception e)
  616. {
  617. Debug.LogWarning($"⚠️ TravelUI: Failed to visualize route: {e.Message}");
  618. // Try alternative method names
  619. var method = travelSystem.GetType().GetMethod("HighlightRoute");
  620. if (method != null)
  621. {
  622. method.Invoke(travelSystem, new object[] { route });
  623. Debug.Log($"✅ TravelUI: Used HighlightRoute instead");
  624. }
  625. }
  626. }
  627. else
  628. {
  629. Debug.LogWarning("⚠️ TravelUI: TeamTravelSystem not found for route visualization");
  630. }
  631. Debug.Log($"🎯 TravelUI: Selected {route.routeType} route - SelectRoute complete");
  632. }
  633. /// <summary>
  634. /// Gets display color for route based on type (GPS-style)
  635. /// </summary>
  636. private Color GetRouteDisplayColor(TravelRoute route)
  637. {
  638. // Match colors from TeamTravelSystem GPS-style
  639. switch (route.routeType)
  640. {
  641. case RouteType.Standard:
  642. return Color.blue; // Shortest route
  643. case RouteType.RoadPreferred:
  644. return Color.green; // Fastest route
  645. case RouteType.Special:
  646. return Color.yellow; // Cheapest route
  647. default:
  648. return Color.blue;
  649. }
  650. }
  651. /// <summary>
  652. /// Gets GPS-style display name for route type
  653. /// </summary>
  654. private string GetRouteDisplayName(TravelRoute route)
  655. {
  656. switch (route.routeType)
  657. {
  658. case RouteType.Standard:
  659. return "Shortest Route";
  660. case RouteType.RoadPreferred:
  661. return "Fastest Route";
  662. case RouteType.Special:
  663. return "Cheapest Route";
  664. default:
  665. return "Unknown Route";
  666. }
  667. }
  668. /// <summary>
  669. /// Handles start travel button click
  670. /// </summary>
  671. private void OnStartTravelClicked()
  672. {
  673. if (selectedRoute == null)
  674. {
  675. Debug.LogWarning("⚠️ TravelUI: No route selected for travel");
  676. return;
  677. }
  678. Debug.Log($"🚶 TravelUI: Starting travel with {selectedRoute.routeType} route");
  679. // Start travel through the travel system
  680. var travelSystem = TeamTravelSystem.Instance;
  681. if (travelSystem != null)
  682. {
  683. travelSystem.StartTravel(selectedRoute);
  684. }
  685. // Hide UI
  686. HideTravelPanel();
  687. }
  688. /// <summary>
  689. /// Handles cancel button click
  690. /// </summary>
  691. private void OnCancelTravelClicked()
  692. {
  693. Debug.Log("❌ TravelUI: Travel cancelled by user");
  694. // Cancel travel planning
  695. var travelSystem = TeamTravelSystem.Instance;
  696. if (travelSystem != null)
  697. {
  698. travelSystem.CancelTravelPlanning();
  699. }
  700. // Hide UI
  701. HideTravelPanel();
  702. }
  703. /// <summary>
  704. /// Updates UI for a selected route (called by TeamTravelSystem via reflection)
  705. /// </summary>
  706. public void UpdateUIForSelectedRoute(TravelRoute route)
  707. {
  708. if (!isUIVisible)
  709. {
  710. return;
  711. }
  712. selectedRoute = route;
  713. UpdateTravelInfo();
  714. // Update visual selection in route list
  715. if (routeList != null)
  716. {
  717. var routeOptions = routeList.Children().ToList();
  718. for (int i = 0; i < routeOptions.Count && i < availableRoutes.Count; i++)
  719. {
  720. var routeOption = routeOptions[i];
  721. var routeData = availableRoutes.OrderBy(r => r.estimatedTime).ToList()[i];
  722. if (routeData == route)
  723. {
  724. routeOption.AddToClassList("route-selected");
  725. Debug.Log($"🎯 TravelUI.UpdateUIForSelectedRoute: Visual selection updated for route {i}");
  726. }
  727. else
  728. {
  729. routeOption.RemoveFromClassList("route-selected");
  730. }
  731. }
  732. }
  733. else
  734. {
  735. Debug.LogWarning("⚠️ TravelUI.UpdateUIForSelectedRoute: routeList is null");
  736. }
  737. Debug.Log($"🔄 TravelUI: Updated UI for {route.routeType} route");
  738. }
  739. /// <summary>
  740. /// Checks if UI is blocking input (called by TeamTravelSystem via reflection)
  741. /// </summary>
  742. public bool IsUIBlocking()
  743. {
  744. return isUIVisible;
  745. }
  746. /// <summary>
  747. /// Check if a screen position (in Unity's screen coordinates) is within the UI panel
  748. /// </summary>
  749. public bool IsPointWithinUI(Vector2 screenPosition)
  750. {
  751. if (!isUIVisible || travelPanel == null)
  752. {
  753. return false;
  754. }
  755. // Convert screen position to panel-relative position
  756. Vector2 panelPosition = RuntimePanelUtils.ScreenToPanel(travelPanel.panel, screenPosition);
  757. // Check if the position is within the panel bounds
  758. bool withinBounds = travelPanel.worldBound.Contains(panelPosition);
  759. return withinBounds;
  760. }
  761. /// <summary>
  762. /// Public getters for external access
  763. /// </summary>
  764. public bool IsVisible => isUIVisible;
  765. public TravelRoute GetSelectedRoute() => selectedRoute;
  766. /// <summary>
  767. /// Forces the UI to refresh with new route data - called externally when routes are recalculated
  768. /// </summary>
  769. public void RefreshWithNewRoutes(List<TravelRoute> routes)
  770. {
  771. Debug.Log($"🔄 TravelUI: RefreshWithNewRoutes called externally with {routes.Count} routes");
  772. if (!isUIVisible)
  773. {
  774. Debug.LogWarning("⚠️ TravelUI: RefreshWithNewRoutes called but UI is not visible");
  775. return;
  776. }
  777. UpdateRoutesAndRefreshUI(currentDestination, routes);
  778. }
  779. /// <summary>
  780. /// Sets up drag functionality for the travel panel
  781. /// </summary>
  782. private void SetupDragFunctionality(VisualElement dragHandle, VisualElement panelToDrag)
  783. {
  784. bool isDragging = false;
  785. Vector2 startPosition = Vector2.zero;
  786. Vector2 startMousePosition = Vector2.zero;
  787. dragHandle.RegisterCallback<MouseDownEvent>(evt =>
  788. {
  789. if (evt.button == 0) // Left mouse button
  790. {
  791. isDragging = true;
  792. startPosition = new Vector2(panelToDrag.style.left.value.value, panelToDrag.style.top.value.value);
  793. startMousePosition = evt.mousePosition;
  794. dragHandle.CaptureMouse();
  795. evt.StopPropagation();
  796. }
  797. });
  798. dragHandle.RegisterCallback<MouseMoveEvent>(evt =>
  799. {
  800. if (isDragging)
  801. {
  802. Vector2 delta = evt.mousePosition - startMousePosition;
  803. panelToDrag.style.left = startPosition.x + delta.x;
  804. panelToDrag.style.top = startPosition.y + delta.y;
  805. evt.StopPropagation();
  806. }
  807. });
  808. dragHandle.RegisterCallback<MouseUpEvent>(evt =>
  809. {
  810. if (isDragging)
  811. {
  812. isDragging = false;
  813. dragHandle.ReleaseMouse();
  814. evt.StopPropagation();
  815. }
  816. });
  817. }
  818. /// <summary>
  819. /// Gets debug information about route composition
  820. /// </summary>
  821. private string GetRouteDebugInfo(TravelRoute route)
  822. {
  823. if (route?.path == null || route.path.Count == 0) return "";
  824. // Get terrain analysis from TeamTravelSystem
  825. var travelSystem = TeamTravelSystem.Instance;
  826. if (travelSystem == null) return "";
  827. // Count different terrain types in the route
  828. var terrainCounts = new Dictionary<string, int>();
  829. // This is a simplified analysis - in a full implementation,
  830. // you'd access the actual map data through the travel system
  831. float avgCostPerTile = route.totalCost / route.path.Count;
  832. string preference = "";
  833. switch (route.routeType)
  834. {
  835. case RouteType.RoadPreferred:
  836. preference = "Prefers roads";
  837. break;
  838. case RouteType.Special:
  839. preference = "Uses ferries/tunnels";
  840. break;
  841. default:
  842. preference = "Cheapest path";
  843. break;
  844. }
  845. return $"{preference} • Avg: {avgCostPerTile:F1}/tile";
  846. }
  847. /// <summary>
  848. /// Gets resource costs for a route (gold, torches, etc.)
  849. /// </summary>
  850. private Dictionary<string, int> GetResourceCosts(TravelRoute route)
  851. {
  852. var costs = new Dictionary<string, int>();
  853. // Add special costs from the route (with null safety)
  854. if (route?.specialCosts != null)
  855. {
  856. foreach (var specialCost in route.specialCosts)
  857. {
  858. switch (specialCost.Key)
  859. {
  860. case "Ferry":
  861. case "Bridge Toll":
  862. costs["Gold"] = costs.GetValueOrDefault("Gold", 0) + specialCost.Value;
  863. break;
  864. case "Tunnel Torch":
  865. costs["Torches"] = costs.GetValueOrDefault("Torches", 0) + specialCost.Value;
  866. break;
  867. case "Mountain Guide":
  868. costs["Gold"] = costs.GetValueOrDefault("Gold", 0) + specialCost.Value;
  869. break;
  870. default:
  871. costs[specialCost.Key] = specialCost.Value;
  872. break;
  873. }
  874. }
  875. }
  876. return costs;
  877. }
  878. }