/// <summary> /// Returns a UnitRange containing up to 3 lists of points: which squares the unit can move to, /// which squares the unit can attack, and which squares the unit can utility-ize. /// </summary> /// <param name="unit">The unit we are querying for</param> /// <param name="blockedSpaces">spaces the unit is not allowed to occupy (e.g. due to ally unit already being there)</param> /// <returns></returns> /// //TODO: Add this to a different class public UnitRange GetPathsForUnit(UnitEntity unit, Position unitPosition, List <Position> blockedSpaces) { UnitRange retRange = new UnitRange(); List <PathNode> unvisited = new List <PathNode>(); List <PathNode> accessible = new List <PathNode>(); //Step 1 //make a node for each tile within raw movement range. unvisited = GetUnvisitedTiles(unit.Move, unitPosition, unit.MoveCosts, unit.TeamID); //Step 2 //run Dijkstra's algorithm accessible = DijkstraPathfinder.CalculatePathDistance(unvisited, unitPosition.xPos, unitPosition.yPos); //Accessible now has only the squares which the unit can actually reach. //break them back into Tuples for the UnitRange HashSet <Position> validPositions = new HashSet <Position>(); foreach (PathNode node in accessible.Where(node => node.distance <= unit.Move)) { validPositions.Add(new Position(node.pos.xPos, node.pos.yPos)); } //blocked positions can be pathed through, but not ended in. //you can end in your own space though validPositions.RemoveWhere(s => blockedSpaces.Contains(s) && !(s.Equals(unitPosition))); //Item ranges HashSet <Position> attackRanges = unit.AllAttackRanges(); HashSet <Position> utilityRanges = unit.AllUtilityRanges(); HashSet <Position> attackablePositions = new HashSet <Position>(); HashSet <Position> utilityPositions = new HashSet <Position>(); foreach (Position pos in validPositions) { foreach (Position offset in attackRanges) { Position targetTile = new Position(pos.xPos + offset.xPos, pos.yPos + offset.yPos); if (targetTile.xPos >= 0 && targetTile.xPos < Width && targetTile.yPos >= 0 && targetTile.yPos < Height) { attackablePositions.Add(targetTile); } } foreach (Position offset in utilityRanges) { Position targetTile = new Position(pos.xPos + offset.xPos, pos.yPos + offset.yPos); if (targetTile.xPos >= 0 && targetTile.xPos < Width && targetTile.yPos >= 0 && targetTile.yPos < Height) { utilityPositions.Add(targetTile); } } } retRange.moveRange = validPositions; retRange.attackRange = attackablePositions; retRange.utilityRange = utilityPositions; return(retRange); }
/// <summary> /// Finds a path to the given space, using current path as a starting point /// </summary> /// <param name="selectedUnit"></param> /// <param name="targ"></param> /// <param name="currentPath"></param> /// <returns></returns> internal List <Position> FindPath(UnitEntity unit, Position targ, List <Position> currentPath) { List <PathNode> unvisited = new List <PathNode>(); List <PathNode> accessible = new List <PathNode>(); //Part 1 - make sure the target is not already in the path if (currentPath.Contains(targ)) { int i = currentPath.IndexOf(targ); return(currentPath.GetRange(0, i + 1)); } //Part 2 - find path from existing position if (unit.Move - currentPath.Count > 0) //fail fast for finding path from scratch { Position previousPosition = currentPath.Last(); //Step 1 //make a node for each tile within modified movement range. unvisited = GetUnvisitedTiles(unit.Move - currentPath.Count, previousPosition, unit.MoveCosts, unit.TeamID); //Step 2 //run Dijkstra's algorithm accessible = DijkstraPathfinder.CalculatePathDistance(unvisited, previousPosition.xPos, previousPosition.yPos); if (accessible.Find(s => s.pos.Equals(targ)) != null) { //we can reach the new square from our existing path. return the new path as an append to the existing path List <Position> appendList = new List <Position>(); PathNode markerNode = accessible.Find(s => s.pos.Equals(targ)); while (markerNode != null && !markerNode.pos.Equals(previousPosition)) { appendList.Add(markerNode.pos); markerNode = markerNode.previousNode; } appendList.Reverse(); currentPath.AddRange(appendList); return(currentPath); } //else - we need to find the path from scratch } //Part 3 - calculate path from scratch //Step 1 //make a node for each tile within modified movement range. unvisited = GetUnvisitedTiles(unit.Move, currentPath[0], unit.MoveCosts, unit.TeamID); //Step 2 //run Dijkstra's algorithm accessible = DijkstraPathfinder.CalculatePathDistance(unvisited, currentPath[0].xPos, currentPath[0].yPos); if (accessible.Find(s => s.pos.Equals(targ)) != null) { //we can reach the new square from our existing path. return the new path as an append to the existing path List <Position> retList = new List <Position>(); PathNode markerNode = accessible.Find(s => s.pos.Equals(targ)); while (markerNode != null) { retList.Add(markerNode.pos); markerNode = markerNode.previousNode; } retList.Reverse(); return(retList); } else { throw new PathFindingException("Could not find path to a navigable tile"); } }