public void removeUnit(BattleUnit unit) { units.Remove(unit); }
public static List <MoveNode> getMoveNodes(BattleTile[,] tiles, int width, int height, List <BattleUnit> units, BattleUnit unit, int maxSteps, bool filtered = false) { var binaryMap = createBinaryMap(width, height); binaryMap[unit.X, unit.Y] = true; //visit starting node int i = 0, steps = 0; int[] xList = new int[] { 0, -1, 0, 1 }; int[] yList = new int[] { -1, 0, 1, 0 }; int min = 0, max = 0; double initialSafetyScore = getTileSafetyScore(units, unit, unit.X, unit.Y); double minSafetyScore = initialSafetyScore; double maxSafetyScore = initialSafetyScore; List <MoveNode> nodeList = new List <MoveNode>(); nodeList.Add(new MoveNode(unit.X, unit.Y, 0, null, initialSafetyScore)); while (i < nodeList.Count) { for (int j = 0; j < 4; j++) { int x = nodeList[i].x + xList[j]; int y = nodeList[i].y + yList[j]; steps = nodeList[i].steps + 1; //if node is off the map if (x < 0 || y < 0 || x >= width || y >= height) { continue; //skip this node } //if node has already been visited if (binaryMap[x, y]) { continue; //skip this node } //if node is not grass if (tiles[x, y].type != "grass") { continue; //skip this node } var mapUnit = tiles[x, y].getFirstUnit(); //filter out enemy units if (filtered) { //if unit exists on node and is enemy if (mapUnit != null && mapUnit.team != unit.team) { //if enemy is not dead if (mapUnit.status != "dead") { continue; //skip this node } } } double safetyScore = getTileSafetyScore(units, unit, x, y); minSafetyScore = safetyScore < minSafetyScore ? safetyScore : minSafetyScore; maxSafetyScore = safetyScore > maxSafetyScore ? safetyScore : maxSafetyScore; //min = steps < min ? steps : min; //max = steps > max ? steps : max; nodeList.Add(new MoveNode(x, y, steps, nodeList[i], safetyScore)); binaryMap[x, y] = true; //visit node } i++; } //filter out occupied nodes if (filtered) { //remove occupied mapNodes UNLESS occupied by self nodeList = nodeList.Where(node => { if (tiles[node.x, node.y].units.Count > 0) //if there are units { if (tiles[node.x, node.y].units[0].Equals(unit)) //if unit is self { return(true); //node is valid } } else { //if there are no units return(true); //node is valid } return(false); }).ToList(); } //normalize safety scores nodeList.ForEach(node => { node.SafetyScore = (node.SafetyScore - minSafetyScore) / (maxSafetyScore - minSafetyScore); }); return(nodeList); }
public void addUnit(BattleUnit unit) { units.Add(unit); }
//generate action coverage for a particular unit public static List <BattleAction> generateCoverage(BattleMap map, List <BattleUnit> units, BattleUnit unit) { double min = 0, max = 1; List <BattleAction> coverage = new List <BattleAction>(); coverage.Add(getDefaultAction(unit)); //if currently charging action, add it to coverage as well to see if it is worth switching if (unit.action != null) { int damage = getDamage(unit, unit.action.target, unit.action.actiondef); unit.action.score = getScore(unit, unit.action.target, damage); coverage.Add(unit.action); } List <MoveNode> moveNodes = new List <MoveNode>(); //if already moved, generate move nodes for current position only if (unit.moved) { moveNodes.Add(new MoveNode(unit.X, unit.Y, 0, null)); } else //generate all move nodes using pathfinding algorithm { moveNodes = BattleMap.getMoveNodes(map.tiles, GameBattle.MAP_WIDTH, GameBattle.MAP_HEIGHT, units, unit, -999, true); //list of possible move nodes moveNodes = moveNodes.Where(node => node.steps <= unit.moveLimit).ToList(); } unit.jobclass.actions.ForEach(actiondef => { moveNodes.ForEach(node => { var diamond = createDiamond(node.x, node.y, actiondef.range, GameBattle.MAP_WIDTH, GameBattle.MAP_HEIGHT, false); //list of possible action nodes diamond.ForEach(d => { int totalDamage = 0; double totalScore = 0; int actionCTR = BattleQueue.calculateCTR(100, actiondef.speed); bool sticky = false; actiondef.spread.ForEach(s => { int x = s.X + d.X; int y = s.Y + d.Y; if (BattleMap.inRange(x, y)) //if target node is in range { BattleUnit target = null; if (x == node.x && y == node.y) //is this where the actor is moving to? { target = unit; //then he will hit himself } else { target = map.getFirstTileUnit(x, y); //get the unit at spread node //if target is actor but actor will move before it hits if (target != null && unit.Equals(target) && (node.x != unit.X || node.y != unit.Y)) { target = null; //ignore actor } //if action will be invoked after target's turn (target has opportunity to move) if (target != null && target.CTR < actionCTR) { if (actiondef.sticky) //action can stick { sticky = true; } else //action cannot stick { target = null; //ignore target } } //if action will invoke before target's turn if (target != null & target.CTR >= actionCTR) { //pass } //if target is to be killed before action is invoked if (target != null) { int futureDamage = BattleQueue.getTargetFutureDamage(unit, target, actionCTR); if (futureDamage > target.hp) { target = null; //ignore target } } } if (target != null) { int damage = getDamage(unit, target, actiondef); //get damage totalScore += getScore(unit, target, damage); //get score from damage totalDamage += damage; //add damage to running total for this action } } }); //if nothing was accomplished in the spread, ignore this action node if (totalScore <= 0) { return; } totalScore += node.SafetyScore; int dx = d.X - node.x; int dy = d.Y - node.y; double distance = Math.Sqrt(dx * dx + dy * dy); //distance from action node to move node totalScore += distance; //todo: for ranged attacks higher is better, for ranged buffs lower is better BattleAction battleAction = new BattleAction(unit, actiondef); battleAction.node = node; battleAction.distance = distance; battleAction.damage = totalDamage; battleAction.score = totalScore; battleAction.moveNodes = moveNodes; battleAction.diamond = diamond; if (sticky) { battleAction.target = GameBattle.getMap().getFirstTileUnit(d.X, d.Y); } if (battleAction.target == null) { battleAction.target = GameBattle.getMap().tiles[d.X, d.Y]; } coverage.Add(battleAction); min = totalScore < min ? totalScore : min; max = totalScore > max ? totalScore : max; }); //diamond }); //mapNodes }); //actions //normalization of scores coverage.ForEach(c => { c.score = (c.score - min) / (max - min); }); coverage.Sort(); return(coverage); }