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);
 }