public int GetPathCostToRegion(int cellIndex) { var region = regionGrid[cellIndex]; var cell = this.map.cellIndices.IndexToCell(cellIndex); if (rootRegions.Contains(region)) { var dx = Math.Abs(cell.x - targetCell.x); var dz = Math.Abs(cell.z - targetCell.z); return(OctileDistance(dx, dz)); } if (distanceBuilder == null) { NewPathFinder.PfProfilerBeginSample("Distance Map Init"); distanceBuilder = new RegionLinkDijkstra(map, targetRect, rootRegions, startCell, traverseParms, pathCosts); NewPathFinder.PfProfilerEndSample(); } if (region.id != lastRegionId) //Cache the most recently retrieved region, since fetches will tend to be clustered. { NewPathFinder.PfProfilerBeginSample("Get Region Distance"); bestLinkCost = distanceBuilder.GetRegionBestDistances(region, out bestLink, out secondBestLink, out secondBestLinkCost); lastRegionTilePathCost = distanceBuilder.RegionMedianPathCost(region); NewPathFinder.PfProfilerEndSample(); lastRegionId = region.id; } if (bestLink != null) { var costToLink = distanceBuilder.RegionLinkDistance(cell, bestLink, lastRegionTilePathCost); //Diagonal paths in open terrain often have two edges with similar costs; picking the best out of the two improves //the accuracy of the heuristic for the path, reducing reopens when the heuristic was wrong about which edge was really cheaper if (secondBestLink != null) { var costToSecondLink = distanceBuilder.RegionLinkDistance(cell, secondBestLink, lastRegionTilePathCost); return(Math.Min(secondBestLinkCost + costToSecondLink, bestLinkCost + costToLink)); } return(bestLinkCost + costToLink); } return(1000000); //shouldn't happen except for sappers }
public int GetRegionDistance(Region region, out RegionLink minLink) { if (regionMinLink.TryGetValue(region.id, out minLink)) { return(distances[minLink]); } while (queue.Count != 0) { var vertex = queue.Pop(); nodes_popped++; int knownBest = distances[vertex.Link]; if (vertex.Cost == knownBest) { var destRegion = GetLinkOtherRegion(vertex.FromRegion, vertex.Link); //I've never encountered this during testing, but users reported issues resolved by this check. if (destRegion?.valid != true) { continue; } int portalCost = 0; if (destRegion.portal != null) { //Not using Region.Allows because it is not entirely consistent with the pathfinder logic, //resulting in errors when a door is within range of 22 turrets (avoidGrid=255) portalCost = NewPathFinder.GetPathCostForBuilding(destRegion.portal, traverseParms); if (portalCost < 0) { continue; } portalCost = portalCost + OctileDistance(1, 0); } var minPathCost = RegionMedianPathCost(destRegion); foreach (var current2 in destRegion.links) { if (current2 == vertex.Link) { continue; } var addedCost = destRegion.portal != null ? portalCost : RegionLinkDistance(vertex.Link, current2, minPathCost); addedCost = Math.Max(addedCost, 1); //Handle mods with negative path costs int newCost = knownBest + addedCost; int pathCost = MinimumRegionLinkDistance(targetCell, current2) + newCost; int oldCost; if (distances.TryGetValue(current2, out oldCost)) { if (newCost < oldCost) { distances[current2] = newCost; queue.Push(new RegionLinkQueueEntry(destRegion, current2, newCost, pathCost)); } } else { distances.Add(current2, newCost); queue.Push(new RegionLinkQueueEntry(destRegion, current2, newCost, pathCost)); } } //if this is the first vertex popped for the region, we've found the shortest path to that region if (!regionMinLink.ContainsKey(destRegion.id)) { //if (DebugViewSettings.drawPaths && !NewPathFinder.disableDebugFlash) //{ // //NewPathFinder.disableDebugFlash = true; // //var tempPath = debugPathfinder?.FindPathInner(this.rootCell, new LocalTargetInfo(RegionLinkCenter(vertex.Link)), this.traverseParms, Verse.AI.PathEndMode.OnCell); // //NewPathFinder.disableDebugFlash = false; // //var actualCost = tempPath.TotalCost; // //tempPath.Dispose(); // if (regionMinLink.TryGetValue(vertex.FromRegion.id, out minLink)) // NewPathFinder.DebugLine(this.map, RegionLinkCenter(vertex.Link), RegionLinkCenter(minLink)); // NewPathFinder.DebugFlash(this.map, RegionLinkCenter(vertex.Link), knownBest / 1500f, $"{knownBest}\n{nodes_popped}" /*+ "\n(" + actualCost + ")"*/); // //if (actualCost < knownBest) { Log.Warning(vertex.Link + " has actual cost " + actualCost + "with heuristic " + knownBest); } //} regionMinLink[destRegion.id] = vertex.Link; if (destRegion.id == region.id) { minLink = vertex.Link; return(vertex.Cost); } } } } return(100000); }
public PathFinderMapComponent(Map map) : base(map) { PathFinder = new NewPathFinder(map); }