예제 #1
0
    /// <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);
        }
    }