/// <summary> /// Gets all the moves available for an entity on the map. /// </summary> /// <param name="entity"></param> /// <param name="map"></param> /// <returns></returns> public static List<Move> GetMoves(Entity entity, TileMap map) { List<Move> moves = new List<Move>(); if (entity == null || map == null) { return moves; } // Get the origin of the entity on the map var origin = map.ObjectsInMap.Find(m => m.Entity == entity).Location; foreach (var relativeDestination in entity.MoveBehavior.GetPossibleMovements()) { // All possible movements are in relative to the origin, so add the origin // to make the destination be within the map var destination = relativeDestination + origin; if (entity.MoveBehavior.Capabilities.HasFlag(MoveBehavior.MoveCapabilities.Dig) || entity.MoveBehavior.Capabilities.HasFlag(MoveBehavior.MoveCapabilities.Jump) || entity.MoveBehavior.Capabilities.HasFlag(MoveBehavior.MoveCapabilities.Fly)) { Vector2 distanceVector = destination - origin; if (((uint)Math.Floor(distanceVector.Length())) <= entity.MoveDistance) { List<Vector2> path = new List<Vector2>(); path.Add(origin); path.Add(destination); Move m = new Move(path); moves.Add(m); } } else { Move m = FindMove(entity, map, origin, destination); if (m != null) { moves.Add(m); } } } return moves; }
/// <summary> /// /// </summary> /// <param name="origin">Point to start the path search</param> /// <param name="destination">Point to end the path search</param> /// <returns></returns> private static Move FindMove(Entity entity, TileMap map, Vector2 origin, Vector2 destination) { List<Vector2> path = new List<Vector2>(); // There's always a path if the origin is the destination if (origin == destination) { path.Add(origin); return new Move(path); } // Use A* to try and make a path to the destination. Return null // if no path is found, or a list of moves (in order) if found. // Make sure to cache the destination for future use. // If the unit can fly, dig, or jump, just make sure the destination is within reach // and currently doesn't contain anything // For other cases, try and use A* to find a path Dictionary<Vector2, AStarValues> openPoints = new Dictionary<Vector2, AStarValues>(); List<KeyValuePair<Vector2, AStarValues>> closedPoints = new List<KeyValuePair<Vector2, AStarValues>>(); AStarValues originValues = new AStarValues(); originValues.G = 0; originValues.Parent = origin; openPoints.Add(origin, originValues); KeyValuePair<Vector2, AStarValues> current = new KeyValuePair<Vector2, AStarValues>(origin, originValues); var relativePositions = GetRelativePositions(); while (openPoints.Count > 0) { current = openPoints.OrderBy(p => p.Value.F).First<KeyValuePair<Vector2, AStarValues>>(); // Drop the nextPoint from openPoints and add it to the closedPoints openPoints.Remove(current.Key); closedPoints.Add(current); if (current.Key == destination) { break; } // Add to our open list the new set of squares to use. foreach (Vector2 position in relativePositions) { Vector2 next = current.Key + position; if (closedPoints.Any(p => p.Key == next) || !map.HasTile(next) || map.ObjectsInMap.Any<RenderObject>(p => p.Location == next)) { continue; } AStarValues values = new AStarValues(); values.Parent = current.Key; // Use the current G value plus the G value to move to the next point to find the actual G value values.G = (int)GetMoveCost(position) + current.Value.G; if (values.G > entity.MoveDistance) { // The distance is too great. don't add to the open points continue; } if (next == destination) { openPoints[next] = values; break; } // H is calculated using the Manhatten method. Essentially, calculate the move cost from the proposed square // to the destination square using only horizontal and vertical movements. values.H = (int)GetMoveCost(destination - next); values.F = values.G + values.H; if (openPoints.ContainsKey(next)) { // Check the G score of the current path to this square. If this path is faster, then update the // value to include the current values for going this direction if (openPoints[next].G > values.G) { openPoints[next] = values; } } else { openPoints[next] = values; } } } // The origin always gets put into the closedList, so check for anything greater than 1 var lastPoint = closedPoints.Last(); if (lastPoint.Key != destination) { // Didn't find a path to the destination return null; } var currentPoint = lastPoint; while (currentPoint.Key != origin) { path.Add(currentPoint.Key); currentPoint = closedPoints.First(v => v.Key == currentPoint.Value.Parent); } // Cache this path return new Move(path); }