public static DijkstraNode FindRoute(Node start, Node goal) { // Dijkstra algorithm // We use Dijkstra instead of A* because there are random teleports in Tibia (e.g. boats) // And it is difficult to create a good A* heuristic that takes random teleports into account // and Dijkstra has more consistently good performance than A* with a bad heuristic List <DijkstraNode> openSet = new List <DijkstraNode> { new DijkstraNode(null, start, 0) }; HashSet <Node> closedSet = new HashSet <Node>(); while (openSet.Count > 0) { // Extract path with current minimal cost DijkstraNode current = GetMinimum(openSet); if (current.node == goal) { return(current); } // Add node to closed set openSet.Remove(current); closedSet.Add(current.node); // Iterate over all neighboring nodes foreach (Node neighbor in current.node.neighbors) { if (closedSet.Contains(neighbor)) { continue; } // If node is not in closed set, create a new path, we use conn.settings.cost for 'special' connections (e.g. teleports, stairs) double newCost = current.cost + Distance(current.node, neighbor); DijkstraNode neighborAStar = openSet.Find(o => o.node == neighbor); if (neighborAStar == null) { // The node is not in the open set; add it openSet.Add(new DijkstraNode(current, neighbor, newCost)); } else if (neighborAStar.cost < newCost) { // If the node is already in the open set and with a lower cost than this path, this path cannot be optimal, so skip it continue; } else { // If the node is already in the open set but with a higher cost, remove it and add this node openSet.Remove(neighborAStar); openSet.Add(new DijkstraNode(current, neighbor, newCost)); } } } return(null); }
public static DijkstraNode GetMinimum(List <DijkstraNode> list) { double minValue = double.MaxValue; DijkstraNode min = null; foreach (var node in list) { double nodeValue = node.cost; if (nodeValue < minValue) { min = node; minValue = nodeValue; } } return(min); }
public static Tuple <DijkstraPoint, List <Rectangle3D> > FindRoute(Point3D point, Point3D target, Bitmap image) { Node n1 = Pathfinder.GetNode(Tibia_Path_Finder.Form1.beginx, Tibia_Path_Finder.Form1.beginy, 6); Node n2 = Pathfinder.GetNode(Tibia_Path_Finder.Form1.beginx, Tibia_Path_Finder.Form1.beginy, 7); Node beginNode = Pathfinder.GetNode(point.X, point.Y, point.Z); Node endNode = Pathfinder.GetNode(target.X, target.Y, target.Z); List <Rectangle3D> collisionBounds = null; DijkstraNode highresult = Dijkstra.FindRoute(beginNode, endNode, target); SpecialConnection connection = null; if (highresult != null) { collisionBounds = new List <Rectangle3D>(); while (highresult != null) { highresult.rect.Inflate(5, 5); collisionBounds.Add(new Rectangle3D(highresult.rect, highresult.node.z)); /*Point tl = new Point(convertx(highresult.rect.X), converty(highresult.rect.Y)); * Point tr = new Point(convertx(highresult.rect.X + highresult.rect.Width), converty(highresult.rect.Y + highresult.rect.Height)); * gr.DrawRectangle(Pens.Yellow, new Rectangle(tl.X, tl.Y, (tr.X - tl.X), (tr.Y - tl.Y)));*/ if (highresult.connection.connection != null) { connection = highresult.connection.connection; } highresult = highresult.previous; } if (collisionBounds.Count == 0) { collisionBounds = null; } } return(new Tuple <DijkstraPoint, List <Rectangle3D> >(Dijkstra.FindRoute(image, point, target, collisionBounds, null, connection), collisionBounds)); }
public void UpdateMap(bool periodicUpdate = false) { lock (mapBoxLock) { int PlayerX = 0, PlayerY = 0, PlayerZ = 0; bool recomputeRoute = true; if (targetCoordinate != null) { MemoryReader.UpdateBattleList(); PlayerX = MemoryReader.X; PlayerY = MemoryReader.Y; PlayerZ = MemoryReader.Z; Point3D playerCoordinate = new Point3D(PlayerX, PlayerY, PlayerZ); if (previousCoordinate != playerCoordinate) { previousCoordinate = playerCoordinate; mapCoordinate = new Coordinate(PlayerX, PlayerY, PlayerZ); } else { if (FakePlayerData.X >= 0) { PlayerX = FakePlayerData.X; PlayerY = FakePlayerData.Y; PlayerZ = FakePlayerData.Z; mapCoordinate = new Coordinate(PlayerX, PlayerY, PlayerZ); FakePlayerData = new Point3D(-1, -1, -1); } else { if (periodicUpdate) return; recomputeRoute = false; } } } if (beginCoordinate == null) { beginCoordinate = new Coordinate(mapCoordinate); beginWidth = sourceWidth; } if (beginCoordinate.x == Coordinate.MaxWidth / 2 && beginCoordinate.y == Coordinate.MaxHeight / 2 && beginCoordinate.z == 7) { Image oldImage = this.Image; this.Image = StyleManager.GetImage("nomapavailable.png"); if (oldImage != StyleManager.GetImage("nomapavailable.png") && oldImage != null) { oldImage.Dispose(); } this.SizeMode = PictureBoxSizeMode.Zoom; return; } if (mapCoordinate.z < 0) { mapCoordinate.z = 0; } else if (mapCoordinate.z >= StorageManager.mapFilesCount) { mapCoordinate.z = StorageManager.mapFilesCount - 1; } if (mapCoordinate.x - sourceWidth / 2 < 0) { mapCoordinate.x = sourceWidth / 2; } if (mapCoordinate.x + sourceWidth / 2 > Coordinate.MaxWidth) { mapCoordinate.x = Coordinate.MaxWidth - sourceWidth / 2; } if (mapCoordinate.y - sourceWidth / 2 < 0) { mapCoordinate.y = sourceWidth / 2; } if (mapCoordinate.y + sourceWidth / 2 > Coordinate.MaxHeight) { mapCoordinate.y = Coordinate.MaxHeight - sourceWidth / 2; } Image image; if (mapCoordinate.z == zCoordinate) { image = map != null ? map.GetImage() : mapImage; } else { Map m = StorageManager.getMap(mapCoordinate.z); if (otherMap != null && m != otherMap) { otherMap.Dispose(); } otherMap = m; image = m.GetImage(); } lock (image) { sourceWidth = Math.Min(Math.Max(sourceWidth, minWidth), maxWidth); Rectangle sourceRectangle = new Rectangle(mapCoordinate.x - sourceWidth / 2, mapCoordinate.y - sourceWidth / 2, sourceWidth, sourceWidth); Bitmap bitmap = new Bitmap(this.Width, this.Height); using (Graphics gr = Graphics.FromImage(bitmap)) { gr.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), sourceRectangle, GraphicsUnit.Pixel); if (targetCoordinate != null && recomputeRoute) { Coordinate beginCoordinate = new Coordinate(PlayerX, PlayerY, PlayerZ); Node beginNode = Pathfinder.GetNode(beginCoordinate.x, beginCoordinate.y, beginCoordinate.z); Node endNode = Pathfinder.GetNode(targetCoordinate.x, targetCoordinate.y, targetCoordinate.z); List<Rectangle3D> collisionBounds = null; DijkstraNode highresult = Dijkstra.FindRoute(beginNode, endNode, new Point3D(targetCoordinate), previousResult); previousResult = highresult; SpecialConnection connection = null; nextConnectionPoint = new Point3D(-1, -1, -1); nextImportantTarget = null; nextTarget = "Head to the destination."; if (highresult != null) { collisionBounds = new List<Rectangle3D>(); while (highresult != null) { highresult.rect.Inflate(5, 5); collisionBounds.Add(new Rectangle3D(highresult.rect, highresult.node.z)); /*if (highresult.node.z == beginCoordinate.z) { Point tl = new Point(convertx(highresult.rect.X), converty(highresult.rect.Y)); Point tr = new Point(convertx(highresult.rect.X + highresult.rect.Width), converty(highresult.rect.Y + highresult.rect.Height)); gr.DrawRectangle(Pens.Yellow, new Rectangle(tl.X, tl.Y, (tr.X - tl.X), (tr.Y - tl.Y))); }*/ if (highresult.connection.connection != null) { connection = highresult.connection.connection; if (connection.name.Equals("stairs", StringComparison.InvariantCultureIgnoreCase)) { nextTarget = connection.destination.z > connection.source.z ? "Go down the stairs." : "Go up the stairs."; } else if (connection.name.Equals("levitate", StringComparison.InvariantCultureIgnoreCase)) { nextTarget = connection.destination.z > connection.source.z ? "Levitate down." : "Levitate up."; } else { nextImportantTarget = String.Format("Take the {0}.", connection.name); nextTarget = null; } nextConnectionPoint = new Point3D(connection.destination.x, connection.destination.y, connection.destination.z); } highresult = highresult.previous; } if (collisionBounds.Count == 0) collisionBounds = null; } Map m = StorageManager.getMap(beginCoordinate.z); DijkstraPoint result = Dijkstra.FindRoute(image as Bitmap, new Point3D(beginCoordinate), new Point3D(targetCoordinate), collisionBounds, null, connection); if (result != null) { playerPath = new TibiaPath(); playerPath.path = result; playerPath.begin = beginCoordinate; playerPath.end = targetCoordinate; DrawPath(gr, playerPath); } } else if (!recomputeRoute && playerPath != null) { DrawPath(gr, playerPath); } foreach (TibiaPath path in paths) { DrawPath(gr, path); } foreach (Target target in targets) { if (target.coordinate.z == mapCoordinate.z) { int x = target.coordinate.x - (mapCoordinate.x - sourceWidth / 2); int y = target.coordinate.y - (mapCoordinate.y - sourceWidth / 2); if (x >= 0 && y >= 0 && x < sourceWidth && y < sourceWidth) { x = (int)((double)x / sourceWidth * bitmap.Width); y = (int)((double)y / sourceWidth * bitmap.Height); lock (target.image) { int targetWidth = (int)((double)target.size / target.image.Height * target.image.Width); gr.DrawImage(target.image, new Rectangle(x - targetWidth, y - target.size, targetWidth * 2, target.size * 2)); } } } } } Image oldImage = this.Image; this.Image = bitmap; if (oldImage != null) { oldImage.Dispose(); } } if (MapUpdated != null) MapUpdated(); } }
public DijkstraNode(DijkstraNode previous, Node node, double cost) { this.previous = previous; this.node = node; this.cost = cost; }
public void UpdateMap(bool periodicUpdate = false) { lock (mapBoxLock) { int PlayerX = 0, PlayerY = 0, PlayerZ = 0; bool recomputeRoute = true; if (targetCoordinate != null) { MemoryReader.UpdateBattleList(); PlayerX = MemoryReader.X; PlayerY = MemoryReader.Y; PlayerZ = MemoryReader.Z; Point3D playerCoordinate = new Point3D(PlayerX, PlayerY, PlayerZ); if (previousCoordinate != playerCoordinate) { previousCoordinate = playerCoordinate; mapCoordinate = new Coordinate(PlayerX, PlayerY, PlayerZ); } else { if (FakePlayerData.X >= 0) { PlayerX = FakePlayerData.X; PlayerY = FakePlayerData.Y; PlayerZ = FakePlayerData.Z; mapCoordinate = new Coordinate(PlayerX, PlayerY, PlayerZ); FakePlayerData = new Point3D(-1, -1, -1); } else { if (periodicUpdate) { return; } recomputeRoute = false; } } } if (beginCoordinate == null) { beginCoordinate = new Coordinate(mapCoordinate); beginWidth = sourceWidth; } if (beginCoordinate.x == Coordinate.MaxWidth / 2 && beginCoordinate.y == Coordinate.MaxHeight / 2 && beginCoordinate.z == 7) { Image oldImage = this.Image; this.Image = StyleManager.GetImage("nomapavailable.png"); if (oldImage != StyleManager.GetImage("nomapavailable.png") && oldImage != null) { oldImage.Dispose(); } this.SizeMode = PictureBoxSizeMode.Zoom; return; } if (mapCoordinate.z < 0) { mapCoordinate.z = 0; } else if (mapCoordinate.z >= StorageManager.mapFilesCount) { mapCoordinate.z = StorageManager.mapFilesCount - 1; } if (mapCoordinate.x - sourceWidth / 2 < 0) { mapCoordinate.x = sourceWidth / 2; } if (mapCoordinate.x + sourceWidth / 2 > Coordinate.MaxWidth) { mapCoordinate.x = Coordinate.MaxWidth - sourceWidth / 2; } if (mapCoordinate.y - sourceWidth / 2 < 0) { mapCoordinate.y = sourceWidth / 2; } if (mapCoordinate.y + sourceWidth / 2 > Coordinate.MaxHeight) { mapCoordinate.y = Coordinate.MaxHeight - sourceWidth / 2; } Image image; if (mapCoordinate.z == zCoordinate) { image = map != null?map.GetImage() : mapImage; } else { Map m = StorageManager.getMap(mapCoordinate.z); if (otherMap != null && m != otherMap) { otherMap.Dispose(); } otherMap = m; image = m.GetImage(); } lock (image) { sourceWidth = Math.Min(Math.Max(sourceWidth, minWidth), maxWidth); Rectangle sourceRectangle = new Rectangle(mapCoordinate.x - sourceWidth / 2, mapCoordinate.y - sourceWidth / 2, sourceWidth, sourceWidth); Bitmap bitmap = new Bitmap(this.Width, this.Height); using (Graphics gr = Graphics.FromImage(bitmap)) { gr.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), sourceRectangle, GraphicsUnit.Pixel); if (targetCoordinate != null && recomputeRoute) { Coordinate beginCoordinate = new Coordinate(PlayerX, PlayerY, PlayerZ); Node beginNode = Pathfinder.GetNode(beginCoordinate.x, beginCoordinate.y, beginCoordinate.z); Node endNode = Pathfinder.GetNode(targetCoordinate.x, targetCoordinate.y, targetCoordinate.z); List <Rectangle3D> collisionBounds = null; DijkstraNode highresult = Dijkstra.FindRoute(beginNode, endNode, new Point3D(targetCoordinate), previousResult); previousResult = highresult; SpecialConnection connection = null; nextConnectionPoint = new Point3D(-1, -1, -1); nextImportantTarget = null; nextTarget = "Head to the destination."; if (highresult != null) { collisionBounds = new List <Rectangle3D>(); while (highresult != null) { highresult.rect.Inflate(5, 5); collisionBounds.Add(new Rectangle3D(highresult.rect, highresult.node.z)); /*if (highresult.node.z == beginCoordinate.z) { * Point tl = new Point(convertx(highresult.rect.X), converty(highresult.rect.Y)); * Point tr = new Point(convertx(highresult.rect.X + highresult.rect.Width), converty(highresult.rect.Y + highresult.rect.Height)); * gr.DrawRectangle(Pens.Yellow, new Rectangle(tl.X, tl.Y, (tr.X - tl.X), (tr.Y - tl.Y))); * }*/ if (highresult.connection.connection != null) { connection = highresult.connection.connection; if (connection.name.Equals("stairs", StringComparison.InvariantCultureIgnoreCase)) { nextTarget = connection.destination.z > connection.source.z ? "Go down the stairs." : "Go up the stairs."; } else if (connection.name.Equals("levitate", StringComparison.InvariantCultureIgnoreCase)) { nextTarget = connection.destination.z > connection.source.z ? "Levitate down." : "Levitate up."; } else { nextImportantTarget = String.Format("Take the {0}.", connection.name); nextTarget = null; } nextConnectionPoint = new Point3D(connection.destination.x, connection.destination.y, connection.destination.z); } highresult = highresult.previous; } if (collisionBounds.Count == 0) { collisionBounds = null; } } Map m = StorageManager.getMap(beginCoordinate.z); DijkstraPoint result = Dijkstra.FindRoute(image as Bitmap, new Point3D(beginCoordinate), new Point3D(targetCoordinate), collisionBounds, null, connection); if (result != null) { playerPath = new TibiaPath(); playerPath.path = result; playerPath.begin = beginCoordinate; playerPath.end = targetCoordinate; DrawPath(gr, playerPath); } } else if (!recomputeRoute && playerPath != null) { DrawPath(gr, playerPath); } foreach (TibiaPath path in paths) { DrawPath(gr, path); } foreach (Target target in targets) { if (target.coordinate.z == mapCoordinate.z) { int x = target.coordinate.x - (mapCoordinate.x - sourceWidth / 2); int y = target.coordinate.y - (mapCoordinate.y - sourceWidth / 2); if (x >= 0 && y >= 0 && x < sourceWidth && y < sourceWidth) { x = (int)((double)x / sourceWidth * bitmap.Width); y = (int)((double)y / sourceWidth * bitmap.Height); lock (target.image) { int targetWidth = (int)((double)target.size / target.image.Height * target.image.Width); gr.DrawImage(target.image, new Rectangle(x - targetWidth, y - target.size, targetWidth * 2, target.size * 2)); } } } } } Image oldImage = this.Image; this.Image = bitmap; if (oldImage != null) { oldImage.Dispose(); } } if (MapUpdated != null) { MapUpdated(); } } }
public DijkstraNode(DijkstraNode previous, Connection connection, double cost) { this.previous = previous; this.connection = connection; this.cost = cost; }
public static MapPictureBox DrawRoute(Coordinate begin, Coordinate end, Size pictureBoxSize, Size minSize, Size maxSize, List <Color> additionalWalkableColors, List <Target> targetList = null) { if (end.x >= 0 && begin.z != end.z) { throw new Exception("Can't draw route with different z-coordinates"); } Rectangle sourceRectangle; MapPictureBox pictureBox = new MapPictureBox(); if (pictureBoxSize.Width != 0) { pictureBox.Size = pictureBoxSize; } pictureBox.SizeMode = PictureBoxSizeMode.Zoom; if (targetList != null) { foreach (Target target in targetList) { pictureBox.targets.Add(target); } if (end.x < 0) { if (pictureBoxSize.Width == 0) { pictureBoxSize = new Size(Math.Min(Math.Max(end.z, minSize.Width), maxSize.Width), Math.Min(Math.Max(end.z, minSize.Height), maxSize.Height)); pictureBox.Size = pictureBoxSize; } Map map = StorageManager.getMap(begin.z); pictureBox.map = map; pictureBox.sourceWidth = end.z; pictureBox.mapCoordinate = new Coordinate(begin.x, begin.y, begin.z); pictureBox.zCoordinate = begin.z; pictureBox.UpdateMap(); return(pictureBox); } } // First find the route at a high level Node beginNode = Pathfinder.GetNode(begin.x, begin.y, begin.z); Node endNode = Pathfinder.GetNode(end.x, end.y, end.z); List <Rectangle> collisionBounds = null; DijkstraNode highresult = Dijkstra.FindRoute(beginNode, endNode); if (highresult != null) { collisionBounds = new List <Rectangle>(); while (highresult != null) { highresult.rect.Inflate(5, 5); collisionBounds.Add(highresult.rect); highresult = highresult.previous; } if (collisionBounds.Count == 0) { collisionBounds = null; } } Map m = StorageManager.getMap(begin.z); DijkstraPoint result = Dijkstra.FindRoute(m.image, new Point(begin.x, begin.y), new Point(end.x, end.y), collisionBounds, additionalWalkableColors); if (result == null) { throw new Exception("Couldn't find route."); } // create a rectangle from the result double minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue; DijkstraPoint node = result; while (node != null) { if (node.point.X < minX) { minX = node.point.X; } if (node.point.Y < minY) { minY = node.point.Y; } if (node.point.X > maxX) { maxX = node.point.X; } if (node.point.Y > maxY) { maxY = node.point.Y; } node = node.previous; } minX -= 10; minY -= 10; maxX += 10; maxY += 10; int size = (int)Math.Max(maxX - minX, maxY - minY); sourceRectangle = new Rectangle((int)minX, (int)minY, size, size); if (pictureBoxSize.Width == 0) { pictureBoxSize = new Size(Math.Min(Math.Max(sourceRectangle.Width, minSize.Width), maxSize.Width), Math.Min(Math.Max(sourceRectangle.Height, minSize.Height), maxSize.Height)); pictureBox.Size = pictureBoxSize; } TibiaPath path = new TibiaPath(); path.begin = new Coordinate(begin); path.end = new Coordinate(end); path.path = result; pictureBox.paths.Add(path); pictureBox.map = m; pictureBox.sourceWidth = size; pictureBox.mapCoordinate = new Coordinate(sourceRectangle.X + sourceRectangle.Width / 2, sourceRectangle.Y + sourceRectangle.Height / 2, begin.z); pictureBox.zCoordinate = begin.z; pictureBox.UpdateMap(); return(pictureBox); }
public static DijkstraNode FindRoute(Node start, Node goal, Point3D exactGoal, DijkstraNode previousPath = null) { // Dijkstra algorithm // We use Dijkstra instead of A* because there are random teleports in Tibia (e.g. boats) // And it is difficult to create a good A* heuristic that takes random teleports into account // and Dijkstra has more consistently good performance than A* with a bad heuristic List<DijkstraNode> openSet = new List<DijkstraNode> { new DijkstraNode(null, new Connection(start), 0) }; HashSet<Node> closedSet = new HashSet<Node>(); double closestDistance = double.MaxValue; DijkstraNode closestNode = null; Dictionary<Node, DijkstraNode> nodes = new Dictionary<Node, DijkstraNode>(); if (previousPath != null) { DijkstraNode it = previousPath; DijkstraNode prevNode = null; while(it != null) { if (it.node == start) { it.previous = null; it.connection = new Connection(it.node); return previousPath; } if (prevNode != null && !nodes.ContainsKey(prevNode.node)) { nodes.Add(prevNode.node, it); } prevNode = it; it = it.previous; } } while (openSet.Count > 0) { // Extract path with current minimal cost DijkstraNode current = GetMinimum(openSet); if (current.node == goal) { return current; } if (nodes.ContainsKey(current.node)) { DijkstraNode node = nodes[current.node]; node.previous = current; return previousPath; } double distance = Distance(current.node, exactGoal); if (distance < closestDistance) { closestNode = current; closestDistance = distance; } // Add node to closed set openSet.Remove(current); closedSet.Add(current.node); // Iterate over all neighboring nodes foreach (Connection neighbor in current.node.neighbors) { if (closedSet.Contains(neighbor.node)) continue; // If node is not in closed set, create a new path, we use conn.settings.cost for 'special' connections (e.g. teleports, stairs) double newCost = current.cost + (neighbor.connection == null ? Distance(current.node, neighbor.node) : 100); DijkstraNode neighborAStar = openSet.Find(o => o.node == neighbor.node); if (neighborAStar == null) { // The node is not in the open set; add it openSet.Add(new DijkstraNode(current, neighbor, newCost)); } else if (neighborAStar.cost < newCost) { // If the node is already in the open set and with a lower cost than this path, this path cannot be optimal, so skip it continue; } else { // If the node is already in the open set but with a higher cost, remove it and add this node openSet.Remove(neighborAStar); openSet.Add(new DijkstraNode(current, neighbor, newCost)); } } } return closestNode; }
public static DijkstraNode FindRoute(Node start, Node goal, Point3D exactGoal, DijkstraNode previousPath = null) { // Dijkstra algorithm // We use Dijkstra instead of A* because there are random teleports in Tibia (e.g. boats) // And it is difficult to create a good A* heuristic that takes random teleports into account // and Dijkstra has more consistently good performance than A* with a bad heuristic List <DijkstraNode> openSet = new List <DijkstraNode> { new DijkstraNode(null, new Connection(start), 0) }; HashSet <Node> closedSet = new HashSet <Node>(); double closestDistance = double.MaxValue; DijkstraNode closestNode = null; Dictionary <Node, DijkstraNode> nodes = new Dictionary <Node, DijkstraNode>(); if (previousPath != null) { DijkstraNode it = previousPath; DijkstraNode prevNode = null; while (it != null) { if (it.node == start) { it.previous = null; it.connection = new Connection(it.node); return(previousPath); } if (prevNode != null && !nodes.ContainsKey(prevNode.node)) { nodes.Add(prevNode.node, it); } prevNode = it; it = it.previous; } } while (openSet.Count > 0) { // Extract path with current minimal cost DijkstraNode current = GetMinimum(openSet); if (current.node == goal) { return(current); } if (nodes.ContainsKey(current.node)) { DijkstraNode node = nodes[current.node]; node.previous = current; return(previousPath); } double distance = Distance(current.node, exactGoal); if (distance < closestDistance) { closestNode = current; closestDistance = distance; } // Add node to closed set openSet.Remove(current); closedSet.Add(current.node); // Iterate over all neighboring nodes foreach (Connection neighbor in current.node.neighbors) { if (closedSet.Contains(neighbor.node)) { continue; } // If node is not in closed set, create a new path, we use conn.settings.cost for 'special' connections (e.g. teleports, stairs) double newCost = current.cost + (neighbor.connection == null ? Distance(current.node, neighbor.node) : 100); DijkstraNode neighborAStar = openSet.Find(o => o.node == neighbor.node); if (neighborAStar == null) { // The node is not in the open set; add it openSet.Add(new DijkstraNode(current, neighbor, newCost)); } else if (neighborAStar.cost < newCost) { // If the node is already in the open set and with a lower cost than this path, this path cannot be optimal, so skip it continue; } else { // If the node is already in the open set but with a higher cost, remove it and add this node openSet.Remove(neighborAStar); openSet.Add(new DijkstraNode(current, neighbor, newCost)); } } } return(closestNode); }