/// <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> /// Adds a UnitEntity to the map at the given position, /// and returns a reference to its corresponding MapUnit /// </summary> /// <param name="unitEntity"></param> /// <param name="position"></param> /// <returns></returns> public MapUnit AddUnit(UnitEntity unitEntity, Position position) { MapUnit mu; try { mu = unitMap[position.xPos, position.yPos]; } catch (System.IndexOutOfRangeException) { //unit not added throw new MapSizeException("Selection outside of bounds"); } if (mu != null) { throw new MapUnitOverlapException("Added unit on top of another unit"); } else { mu = new MapUnit(unitEntity); mu.Position = new Position(position.xPos, position.yPos); unitMap[position.xPos, position.yPos] = mu; return(mu); } }
//public MapUnit GetUnitByPosition(int x, int y) //{ // if (x >= terrainMap.GetLength(0) || x < 0) // { // throw new MapSizeException("Selection outside of X bounds"); // } // if (y >= terrainMap.GetLength(1) || y < 0) // { // throw new MapSizeException("Selection outside of Y bounds"); // } // MapUnit mu = unitMap[x, y]; // if (mu == null) // { // throw new UnitNotFoundException("No unit present at x,y"); // } // return mu; //} /// <summary> /// Gets the movable squares for the unit, based on their speed, etc. /// </summary> /// <param name="value"></param> /// <returns></returns> internal UnitRange GenerateMovesquares(UnitEntity value) { MapUnit mu = GetUnityByEntity(value); List <Position> blockedSpaces = GetUnitAllies(value.TeamID); return(GetPathsForUnit(mu.Stats, mu.Position, blockedSpaces)); }
private int CalculateEngagementRange(Position engageSquare, UnitEntity target) { Position targPos = SessionMap.GetUnityByEntity(target).Position; Position diff = engageSquare - targPos; return(Math.Abs(diff.xPos) + Math.Abs(diff.yPos)); }
/// <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 enemy unit already being there)</param> /// <returns></returns> /// //TODO: Add this to a different class public UnitRange GetPathsForUnit(UnitEntity unit, 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.speed, unit.xPos, unit.yPos, unit.moveCosts); ////Step 2 ////run Dijkstra's algorithm //accessible = DijkstraPathfinder.CalculatePathDistance(unvisited, unit.xPos, unit.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.speed)) //{ // validPositions.Add(new Position(node.pos.xPos, node.pos.yPos)); //} ////blocked positions can be pathed through, but not ended in //validPositions.RemoveWhere(s => blockedSpaces.Contains(s)); ////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> /// dds the given unit to this Roster /// </summary> /// <param name="unit"></param> /// <returns>True if unit was added, false otherwise</returns> public bool AddUnit(UnitEntity unit) { if (unit.TeamID != TeamID) { unit.TeamID = this.TeamID; return(this.units.Add(unit)); } return(false); }
/// <summary> /// Depending on the state, returns objects to render: /// - if no unit selected -> GetActions(x,y), OpenMenu(); /// - if new unit selected -> SelectUnit(x,y); /// - if unit already selected -> GetActions(x,y [selectedUnit]), OpenMenu(); /// - if In Menu -> SelectFromMenu(); /// </summary> /// <param name="x"></param> /// <param name="y"></param> public List <GameAction> OnConfirmationInputReceived(int x, int y) { List <GameAction> retList = new List <GameAction>(); switch (state) { case SelectionState.NoSelection: //attempt to select a unit if (SessionMap.QueryTileContents(x, y) == MapSelectionResponse.unitTile) { UnitEntity target = SessionMap.GetUnitInfoByPosition(x, y); //only select units who haven't already acted are valid if (target.HasActed == false) { SelectUnit(x, y); retList.Add(new UnitSelectedAction(selectedUnit)); return(retList); } } //open the map menu (e.g. end turn, etc.) openMenus.Push(GetActions()); retList.Add(new MenuOpenedAction(openMenus.Peek())); return(retList); case SelectionState.UnitSelected: //if the current position is in the valid move-space of the unit, //generate the actions from there if (moveSquares.moveRange.Contains(new Position(x, y))) { //open the unit menu (attack, wait, etc.) openMenus.Push(GetActions(x, y)); retList.Add(new MenuOpenedAction(openMenus.Peek())); return(retList); } //the current position is past the valid move-space of the unit, //generate the actions from the last valid position else if (lastPath != null) { openMenus.Push(GetActions(lastPath.Last().xPos, lastPath.Last().yPos)); retList.Add(new MenuOpenedAction(openMenus.Peek())); return(retList); } else { throw new SessionStateInvalidException("Could not determine where to calculate actions from"); } case SelectionState.MenuOpen: throw new NotImplementedException(); default: throw new SessionStateInvalidException("Could not determine a course of action"); } }
/// <summary> /// Moves the unit along the path /// </summary> /// <param name="path"></param> /// <param name="position"></param> private List <GameAction> MoveUnit(UnitEntity unit, List <Position> path) { List <GameAction> retList = new List <GameAction>(); MapUnit mu = SessionMap.GetUnityByEntity(unit); foreach (Position pnode in path) { bool isFinal = path.Last().Equals(pnode); if (!mu.Position.Equals(pnode)) { SessionMap.MoveUnit(mu, pnode, isFinal); retList.Add(new UnitMoveAction(mu, pnode, isFinal)); //Unit Moved event } } return(retList); }
/// <summary> /// Finds a unit on the map, based on their UnitEntity /// </summary> /// <param name="value"></param> /// <returns></returns> public MapUnit GetUnityByEntity(UnitEntity value) { int w = unitMap.GetLength(0); // width int h = unitMap.GetLength(1); // height for (int x = 0; x < w; ++x) { for (int y = 0; y < h; ++y) { if (unitMap[x, y] != null && unitMap[x, y].Stats.Equals(value)) { return(unitMap[x, y]); } } } return(null); }
/// <summary> /// Gets actions for the currently selected unit at the given position /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private MenuData GetActions(int x, int y) { //UnitActionCommand enum List <string> unitActions = new List <string> { //always add wait UnitActionCommand.Wait.ToString() }; //attack foreach (Position pos in selectedUnit.AllAttackRanges()) { UnitEntity unitEntity = SessionMap.QueryUnitPosition(x + pos.xPos, y + pos.yPos); if (unitEntity == null) { continue; } if (unitEntity.TeamID != selectedUnit.TeamID) { unitActions.Add(UnitActionCommand.Attack.ToString()); break; } } //Staff foreach (Position pos in selectedUnit.AllUtilityRanges()) { UnitEntity unitEntity = SessionMap.QueryUnitPosition(x + pos.xPos, y + pos.yPos); if (unitEntity == null) { continue; } if (unitEntity.TeamID == selectedUnit.TeamID) { unitActions.Add(UnitActionCommand.Staff.ToString()); break; } } return(new MenuData(unitActions) { MenuType = MenuTypes.Unit }); }
/// <summary> /// Navigate backwards through a given menu, returning the previous menu, or unselecting the unit if /// selected and no open menu /// </summary> /// <returns></returns> public List <GameAction> OnNegationInputReceived() { List <GameAction> retList = new List <GameAction>(); if (MenuOpen) { retList.Add(new MenuClosedAction(openMenus.Pop())); return(retList); } if (selectedUnit != null) { UnitEntity ue = selectedUnit; selectedUnit = null; retList.Add(new UnitDeselectedAction(ue)); return(retList); } //nothing to back out of. return(retList); }
private List <UnitEntity> GetAttackTargets() { Position attackPosition = lastPath.Last(); List <UnitEntity> retList = new List <UnitEntity>(); foreach (Position pos in selectedUnit.AllAttackRanges()) { UnitEntity unitEntity = SessionMap.QueryUnitPosition(attackPosition.xPos + pos.xPos, attackPosition.yPos + pos.yPos); if (unitEntity == null) { continue; } if (unitEntity.TeamID != selectedUnit.TeamID) { retList.Add(unitEntity); } } return(retList); }
public MapUnit(UnitEntity ue) { this.Stats = ue; }
public CombatDrawAction(UnitEntity attacker, UnitEntity defender) : base(GameActionType.CombatDraw) { this.Attacker = attacker; this.Defender = defender; }
public CombatHitAction(UnitEntity attacker, int attackerDamage, List <int> randNumbersUsed) : base(GameActionType.CombatHit) { this.AttackingUnit = attacker; this.AttackerDamage = attackerDamage; this.RandNumbersUsed = randNumbersUsed; }
public UnitDeselectedAction(UnitEntity ue) : base(GameActionType.UnitDeselected) { this.DeselectedUnit = ue; }
public UnitSelectedAction(UnitEntity selectedUnit) : base(GameActionType.UnitSelected) { this.SelectedUnit = selectedUnit; }
/// <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"); } }
public CombatMissAction(UnitEntity attackingUnit, List <int> randNumbersUsed) : base(GameActionType.CombatMiss) { this.RandNumbersUsed = randNumbersUsed; this.AttackingUnit = attackingUnit; }
public List <GameAction> InitiateCombat(UnitEntity attacker, UnitEntity defender, int range) { return(InitiateCombat((FE7UnitEntity)attacker, (FE7UnitEntity)defender, range)); }
public UnitWaitAction(UnitEntity selectedUnit) : base(GameActionType.UnitWait) { this.WaitingUnit = selectedUnit; }