/// <summary> /// When the user has a ally selected and tries to select an enemy in range /// </summary> /// <param name="selNode">The node that the ally is trying to attack</param> private void AttemptMoveAndAttack(Node selNode) { // Set the node to attack _nodeToAttack = selNode; // Find out the node attackRange away from selNode that is closest to the charSelected // Get the viable nodes List <Node> potNodes = _mAContRef.GetNodesDistFromNode(selNode, _charSelected.AttackRange); Node nodeToMoveTo = null; // The node that will be moved to int distToMoveNode = int.MaxValue; // The distance to the closest node Node charSelectedNode = _mAContRef.GetNodeByWorldPosition(_charSelected.transform.position); // Node the ally is on // Cross reference them against the nodes this character can move to until a match is found foreach (Node testNode in potNodes) { // We haven't done pathing, so we can't compare Fs, so we will just calculate it by actual distance int testNodeDist = Mathf.Abs(testNode.Position.x - charSelectedNode.Position.x) + Mathf.Abs(testNode.Position.y - charSelectedNode.Position.y); //if (nodeToMoveTo != null) // Debug.Log("Seeing if node at " + testNode.Position + " is closer than " + nodeToMoveTo.Position + "from " + charSelectedNode.Position); //else // Debug.Log("Seeing if node at " + testNode.Position + " is closer than null from " + charSelectedNode.Position); // If the ally can move there or is already there and that node is closer than the closer than the current nodeToMoveTo if ((_charSelected.MoveTiles.Contains(testNode) || testNode == charSelectedNode) && testNodeDist < distToMoveNode) { //if (nodeToMoveTo != null) // Debug.Log("It was! Node at " + testNode.Position + " was closer than " + nodeToMoveTo.Position + " from " + charSelectedNode.Position + " by " + // testNodeDist + " compared to " + distToMoveNode); //else // Debug.Log("It was! Node at " + testNode.Position + " was closer than null from " + charSelectedNode.Position + " by " + // testNodeDist + " compared to " + distToMoveNode); nodeToMoveTo = testNode; distToMoveNode = testNodeDist; } } // Just make sure that the node exists if (nodeToMoveTo == null) { Debug.Log("Big trouble in MoveAttackGUIController. There was an enemy in range, but no valid tiles to attack them from"); Deselect(); return; } // When the ally finished moving, attack MoveAttack.OnCharacterFinishedMoving += BeginAttackAfterMove; // Move the character DoMove(charSelectedNode, nodeToMoveTo); }
/// <summary> /// Finds the node that the enemy "wants" to move to /// </summary> /// <returns>A Node that is the Node the enemy has chosen to move to</returns> private Node FindDesiredMovementNode() { // Determine if we found a node with an ally on it, by just checking if the position is on the grid Node nodeToAttack = _mAContRef.GetNodeAtPosition(_curAttackNodePos); Node currentEnemyNode = _mAContRef.GetNodeByWorldPosition(_currentEnemy.transform.position); if (currentEnemyNode == null) { Debug.Log("WARNING - BUG DETECTED: It seems there is a problem in the FindDesiredMovementNode function of EnemyMoveAttackAI " + "attached to " + this.name + "" + ". Double click this message for more information."); // It seems there is no node where this character is standing. That doesn't make a lot of sense to me } // If we found an ally in range to attack if (nodeToAttack != null && nodeToAttack.Occupying == CharacterType.Ally) { Debug.Log(_currentEnemy.name + " is close enough to strike"); // Check to make sure the enemy has a place to stand to attack and that the enemy can reach that node // // Get the nodes the enemy can attack the ally from List <Node> allyAttackNodes = _mAContRef.GetNodesDistFromNode(nodeToAttack, _currentEnemy.AttackRange); // Debug.Log(currentEnemy.name + " ally attack nodes in FindDesiredMovementode: " + allyAttackNodes); // Test to make sure allyAttackNodes contains something if (allyAttackNodes.Count <= 0) { return(null); } // The closest node to the current enemy that they can hit the enemy from. Assume its none of them Node closestAttackFromNode = null; // Assign this to basically infinity int closestAttackFromNodeDist = Mathf.Abs(_mAContRef.GridBotRight.x - _mAContRef.GridTopLeft.x) + Mathf.Abs(_mAContRef.GridTopLeft.y - _mAContRef.GridBotRight.y) + 2; // Iterate over each of the nodes the enemy could potentially stand at to attack for (int j = 0; j < allyAttackNodes.Count; ++j) { // Debug.Log(currentEnemy.name + " single node at j in FindDesiredMovementode: " + allyAttackNodes[j]); // See if the node exists and there is no character there (besides potentially this character if (allyAttackNodes[j] != null && (allyAttackNodes[j].Occupying == CharacterType.None || allyAttackNodes[j] == currentEnemyNode)) { // Test if it is closer than the currently closest node Node currentTestNode = allyAttackNodes[j]; int currentTestNodeDist = Mathf.Abs(currentTestNode.Position.x - currentEnemyNode.Position.x) + Mathf.Abs(currentTestNode.Position.y - currentEnemyNode.Position.y); //Debug.Log("Testing node at " + currentTestNode.position + ", which is " + currentTestNodeDist + " away from me at " + currentEnemyNode.position); if (currentTestNodeDist < closestAttackFromNodeDist) { // Debug.Log(currentEnemy.name + " single node occupying at j in FindDesiredMovementode: " + allyAttackNodes[j].occupying); // See if there is a path to there for the current enemy if (_mAContRef.Pathing(currentEnemyNode, allyAttackNodes[j], CharacterType.Enemy, false)) { // Set the new closest node to attack closestAttackFromNode = currentTestNode; closestAttackFromNodeDist = currentTestNodeDist; //Debug.Log("Found closer node to move to " + closestAttackFromNode.position + " in order to attack " + nodeToAttack.position); } } } } //Debug.Log("Found final node to move to " + closestAttackFromNode.position + " in order to attack " + nodeToAttack.position); return(closestAttackFromNode); } // If there is no ally in range to attack else { Debug.Log(_currentEnemy.name + " can't attack an ally this turn"); // If we have no ally to attack, we need to find the closest ally to me and move as close to them as possible Node[] closeNodes = FindAllyOutOfRange(); Node closestAllyNode = closeNodes[0]; Node closestAttackFromNode = closeNodes[1]; // If there are no closest allies, we just should move in place if (closestAllyNode == null) { return(currentEnemyNode); } // Test if we got a node or not // If we did not get a node, we have a problem. FindAllyOutOfRange should have only given us an ally with an opening to attack, // you will need to check that debug why it gave an ally this ally could not attack if (closestAttackFromNode == null) { Debug.Log("WARNING - BUG DETECTED: It seems there is a problem in the FindDesiredMovementNode function of EnemyMoveAttackAI " + "attached to " + this.name + "" + ". Double click this message for more information."); // See statement above "if" for more information return(currentEnemyNode); } // If we found a node to attack the ally from, we now need to start determining the path to that node. First, we try pathing // towards the node, not caring if we can actually make it the full there. Then we figure out which node along that path // this enemy would stop at. If that is a free spot, we just follow that path and done. // // Find the path to that found node, we don't care about if we can actually make it the full way there Node startNode = currentEnemyNode; int moveRangeDecrement = 0; Node lastResortNode = null; // If we are unable to fully move anywhere, this node will be returned. Will be updated in the do-while // The distance lastResortNode is from our target node. Start it out as infinite int lastResortNodeDist = int.MaxValue; // This list will hold the nodes we have already tested pathing from // and we have tested pathing from their adjacent nodes List <Node> finishedNodes = new List <Node>(); // This list will hold the nodes that we have tested pathing from, but have not yet // tested pathing from their adjacent nodes List <Node> touchOffNodes = new List <Node>(); // This list will hold the nodes we have not tested pathing from yet (obviously we have not // tested pathing from their adjacent nodes) List <Node> nodesToTest = new List <Node>(); nodesToTest.Add(startNode); Debug.Log("Depth Test: 0 for " + _currentEnemy.name); do { // Update the start node to be the first node to test startNode = nodesToTest[0]; // We let FindGoalNode determine the path and the node the current enemy would stop at along that path // Pathing gets called in FindGoalNode Node goalNode = FindGoalNode(startNode, closestAttackFromNode, moveRangeDecrement); // Test if the newly found goalNode is already occupied // If its not, we're done! We just need to return this goal node if (goalNode.Occupying == CharacterType.None) { Debug.Log("Congratulations " + _currentEnemy.name + "!!! Even without being in striking distance of an ally, you managed to find" + " a good place to move: " + goalNode.Position + ". Good for you."); return(goalNode); } // For if we can't get to the node, we need to update lastResort if this startNode is closer int currentStartNodeDist = startNode.F; if (startNode.Occupying == CharacterType.None && currentStartNodeDist < lastResortNodeDist) { lastResortNode = startNode; lastResortNodeDist = currentStartNodeDist; } // If it is occupied, we need to devise a new solution. Pathing straight from this enemy to that closestAttackFromNode will not get this // enemy to move as we wish. We will now try pathing from the nodes adjacent to this enemy and just decrement the amount it is able to move // by pretending the enemy has moved to them as one of its steps. Then we will preform the same test we just did above from the initialization // of the startNode variable nodesToTest.Remove(startNode); // Remove the startNode from the nodesToTest, since we just finished testing it touchOffNodes.Add(startNode); // Add the startNode to the touchOffNodes, so that we may later add the adjacent nodes to nodesToTest // Make sure there are nodes in touchOffNodes before we attempt to get the 0th index if (touchOffNodes.Count <= 0) { Debug.Log("WARNING - BUG DETECTED: It seems there is a problem in the FindDesiredMovementNode function of EnemyMoveAttackAI " + "attached to " + this.name + "" + ". Double click this message for more information."); // We encountered a situation where touchOffNodes was empty, this means that we couldn't reach the enemy and it was not properly tested break; } // Test if nodes to test is empty, if it is, we increment moveRangeDecrement and the 4 adjacent nodes // of the touchOffNode at index 0 to the nodesToTest and add the touchOffNode to finishedNodes // This is a while, but will often only happen once while (nodesToTest.Count == 0) { // We want to add all the adjacent nodes of the touchOffNodes to the current nodes so that we test all nodes // n spaces out at the same time while (touchOffNodes.Count > 0) { // Find the adjacent nodes from the first node in touchOffNodes List <Node> potNodesToAdd = _mAContRef.GetNodesDistFromNode(touchOffNodes[0], 1); // Add the first touchOffNode to finished nodes since it has now been searched and we have gotten its adjacent nodes // We can also now remove it from touchOffNodes finishedNodes.Add(touchOffNodes[0]); touchOffNodes.RemoveAt(0); // Iterate over these potNodesToAdd and add any node that has not already been tested to the nodes to test foreach (Node potNode in potNodesToAdd) { // See if it has already been tested or that it is impassable if (finishedNodes.Contains(potNode) || touchOffNodes.Contains(potNode) || potNode.Occupying == CharacterType.Wall || potNode.Occupying == CharacterType.Ally) { continue; } // Otherwise add it to the nodesToTest nodesToTest.Add(potNode); } } // The next nodes are 1 further than we have moved before probably ++moveRangeDecrement; Debug.Log("Depth Test: " + moveRangeDecrement + " for " + _currentEnemy.name); // We want to sort the nodes in the list based on their F value nodesToTest.Sort(new NodeComp()); Debug.Log("Start printing sorted Nodes To Test List. Start node is at " + startNode.Position); foreach (Node singleNode in nodesToTest) { Debug.Log("Node " + singleNode.Position + " F = " + singleNode.F); } Debug.Log("Stop printing sorted Nodes To Test List"); } } while (moveRangeDecrement < _currentEnemy.MoveRange); // If we made it here, we just give the lastResortNode return(lastResortNode); } }