/// <summary> /// Calculates the distance between START and DESTINATION if the travel must be conducted /// over a surface (i.e., instead of flying). This is most helpful in tunnels where a mob /// can be within X feet of you, but above or below you. For such mobs, the direct distance /// is X feet, but the path you must take through the tunnels may be much much longer. /// </summary> /// <param name="start"></param> /// <param name="destination"></param> /// <returns></returns> /// <remarks>17Apr2011-12:16UTC chinajade</remarks> public static float SurfacePathDistance(this WoWPoint start, WoWPoint destination) { float pathDistance; if (SurfacePathDistanceCache.TryGet(start, destination, out pathDistance)) { return(pathDistance); } var groundPath = new WoWPoint[] { }; bool canFullyNavigate; // Note: Use the Navigate.GeneratePath that outs a 'isPartial' boolean once it's available. var meshNavigator = Navigator.NavigationProvider as MeshNavigator; if (meshNavigator != null) { var pathResult = meshNavigator.Nav.FindPath(start, destination); canFullyNavigate = pathResult.Succeeded && !pathResult.IsPartialPath; if (canFullyNavigate) { groundPath = pathResult.Points.Select(v => (WoWPoint)v).ToArray(); } } else { groundPath = Navigator.GeneratePath(start, destination) ?? new WoWPoint[0]; canFullyNavigate = groundPath.Length > 0; } if (!canFullyNavigate || groundPath.Length <= 0) { SurfacePathDistanceCache.Add(start, destination, float.NaN); return(float.NaN); } // Include distance it takes us to get from start point to first point in path... pathDistance = start.Distance(groundPath[0]); // Include distance it takes us to get from last point in path to destination... pathDistance += groundPath[groundPath.Length - 1].Distance(destination); // Include distance for each point in path... for (int i = 0; i < (groundPath.Length - 1); ++i) { pathDistance += groundPath[i].Distance(groundPath[i + 1]); } // Sanity check... Contract.Provides( pathDistance >= start.Distance(destination), context => "Surface path distance must be equal to or greater than straight-line distance."); SurfacePathDistanceCache.Add(start, destination, pathDistance); return(pathDistance); }
/// <summary> /// Tries to find a cached surface path distance between the start/destination pair and returns <c>true</c> if successful /// </summary> /// <param name="start">The start.</param> /// <param name="destination">The destination.</param> /// <param name="distance">The surface path distance, zero if no cache found or NaN if no path could be fully generated</param> /// <returns><c>true</c> if a cache was found, <c>false</c> otherwise.</returns> internal static bool TryGet(WoWPoint start, WoWPoint destination, out float distance) { SurfacePathDistanceCache match = null; var now = DateTime.Now; // do we need to cleanup old caches? var doCleanup = now - s_lastCleanupTime > s_maxCacheTimeSpan; // iterate the path cache in revere so we can remove entries safely for (int idx = s_pathDistanceCache.Count - 1; idx >= 0; idx--) { var entry = s_pathDistanceCache[idx]; // check if we need the entry if (doCleanup && now - entry.TimeStamp > s_maxCacheTimeSpan) { s_pathDistanceCache.RemoveAt(idx); continue; } // check if we have a match if (match == null && Navigator.AtLocation(start, entry.Start) && Navigator.AtLocation(destination, entry.Destination)) { match = entry; // exit for loop now if not doing a cleanup pass if (!doCleanup) { break; } } } if (doCleanup) { s_lastCleanupTime = now; } if (match == null) { distance = 0; return(false); } distance = match.Distance; return(true); }