/// <summary> /// Tries to start moving the selected character to the node that was just selected /// or tries to go to hit the enemy at the selected node /// </summary> /// <param name="selNode">The node that was just selected</param> /// <returns>Returns true if the character will do an action, false if they just got deselected</returns> private bool AttemptMoveOrAttackOrInteract(Node selNode) { MoveAttack charAtNode = _mAContRef.GetCharacterMAByNode(selNode); // If the current character can move there if (_charSelected.MoveTiles.Contains(selNode) && selNode.Occupying == CharacterType.None) { // We want the user to be able to select after moving, so // when the ally finishes moving, return control MoveAttack.OnCharacterFinishedMoving += ReturnControlAfterMove; // Start moving the ally Node startNode = _mAContRef.GetNodeByWorldPosition(_charSelected.transform.position); DoMove(startNode, selNode); return(true); } // If the current character can attack there (and wants to attack), and there is an (active) enemy there. // Then we want the current character to walk to the closest node to there and attack else if (_charSelected.AttackTiles.Contains(selNode) && !_charSelected.TargetFriendly && charAtNode != null && charAtNode.WhatAmI == CharacterType.Enemy && charAtNode.gameObject.activeInHierarchy) { AttemptMoveAndAttack(selNode); return(true); } // If the current character can heal/buff there (and wants to), and there is an ally there. // Then we want the current character to walk to the closest node to there and heal/buff else if (_charSelected.AttackTiles.Contains(selNode) && _charSelected.TargetFriendly && charAtNode != null && charAtNode.WhatAmI == CharacterType.Ally && charAtNode != _charSelected) { AttemptMoveAndAttack(selNode); return(true); } // If the current character can interact there, and there is an interactable thing there else if (_charSelected.InteractTiles.Contains(selNode) && selNode.Occupying == CharacterType.Interactable) { AttemptMoveAndInteract(selNode); return(true); } // If none of the above, just deselect them else { Deselect(); return(false); } }
/// <summary> /// Has the current enemy move and then increments it so that the next time this is called, the next enemy will move /// </summary> private IEnumerator NextEnemy() { // So that the this enemy can't start until the previous one is done while (_duringEnemyTurn) { yield return(null); } _duringEnemyTurn = true; if (_enemyIndex < _enemiesMA.Count) { // Try to get the current enemy we should move _currentEnemy = _enemiesMA[_enemyIndex]; // If the enemy does not exist, do not try to move it if (_currentEnemy != null) { // Call the begin single enemy event if (OnBeginSingleEnemy != null) { OnBeginSingleEnemy(_currentEnemy); } //Debug.Log("Begin " + _currentEnemy.name + "'s turn"); // See if the current enemy will be active Node enemyNode = _mAContRef.GetNodeByWorldPosition(_currentEnemy.transform.position); for (int i = 0; i < _alliesMA.Count; ++i) { MoveAttack ally = _alliesMA[i]; // Make sure the enemy exists if (ally == null) { _alliesMA.RemoveAt(i); --i; continue; } Node allyNode = _mAContRef.GetNodeByWorldPosition(ally.transform.position); if (Mathf.Abs(enemyNode.Position.x - allyNode.Position.x) + Mathf.Abs(enemyNode.Position.y - allyNode.Position.y) <= _aggroRange) { break; } } } // Have the current enemy take their turn // Now called from the CamFollow OnFinishEnemyPan event //TakeSingleTurn(); } else { //Debug.Log("All enemies done"); if (OnEnemyTurnEnd != null) { OnEnemyTurnEnd(); } } _duringEnemyTurn = false; yield return(null); }
/// <summary> /// Takes the turn of the enemy /// </summary> public void TakeTurn() { // Reset my turn _mARef.ResetMyTurn(); // Get the node this enemy is standing on _standingNode = _mAContRef.GetNodeByWorldPosition(this.transform.position); // We are going to use the enemy's move tiles, so we need to recalculate those, // since other characters have probably moved on their turn _mARef.CalculateAllTiles(); // Find the tile the enemy should move to Node nodeToMoveTo = FindTileToMoveTo(); // Make sure we have a place to move if (nodeToMoveTo != null) { // After the enemy finishes moving, it will call the CharacterFinishedMovingEvent. // So we add the BeginAttemptAction function to be called when that event is called. // It will remove itself from the event once it is called. // The idea is that only one function will be attached to that event at a time (will belong to current character) MoveAttack.OnCharacterFinishedMoving += BeginAttemptAction; // Start moving the enemy MoveToTile(nodeToMoveTo); /// We want the enemy's turn to look like Move, Action, End. /// But we only call MoveToTile above. /// This is because we need the enemy to finish moving before it does its action. /// Action is called when the character finishes moving. /// Similarly, End is called when the character finishes its action. } // If we have no where to move, just finish this enemy's turn else { // Call the finish enemy turn event if (OnSingleEnemyFinish != null) { OnSingleEnemyFinish(); } } }
/// <summary> /// Actually destroys the object. Should only be called by an animation event /// </summary> protected virtual void Ascend() { // Since this is done, we need to let other character move to where this just was Node myNode = _mAContRef.GetNodeByWorldPosition(this.transform.position); myNode.Occupying = CharacterType.None; // We need to move this character out from the character's parent before we recalculate the visuals, or this will be included in those calculaiton GameObject graveyard = new GameObject("Graveyard"); this.transform.parent = graveyard.transform; // We then need to recreate all the visuals, so that the user can see they can move over the dead body //NEEDTOFIXmAContRef.CreateAllVisualTiles(); // Call the OnCharacterDeath event OnCharacterDeath?.Invoke(); // Remove the ongoing action to signal we are done MoveAttack.RemoveOngoingAction(); //Debug.Log(this.gameObject.name + " has died"); Destroy(graveyard); Destroy(this.gameObject); }
/// <summary> /// Finds and saves the nodes that can be moved to, attacked, and interacted with in the /// appropriate lists. /// Called from MoveAttackGUIController when this character is selected /// </summary> public void CalculateAllTiles() { // Initialize the three lists _moveTiles = new List <Node>(); _attackTiles = new List <Node>(); _interactTiles = new List <Node>(); // Get the node this character is currently standing at Node startNode = _mAContRef.GetNodeByWorldPosition(this.transform.position); // This list is what has already been tested List <Node> testedNodes = new List <Node>(); // This list holds the nodes that have yet to be tested for validity List <Node> currentNodes = new List <Node>(); currentNodes.Add(startNode); // This list holds the move nodes that are "edge" tiles and have something we can interact/attack/buff near them List <Node> edgeMoveNodes = new List <Node>(); edgeMoveNodes.Add(startNode); // This is how many iterations of checks we have gone over. Aka, how many tiles have been traversed in one path int depth = 0; // 1. First iterate only over the range of tiles we can move while (depth < _moveRange) { int amountNodes = currentNodes.Count; for (int i = 0; i < amountNodes; ++i) { // If the current node is null, end this iteration and start the next one if (currentNodes[i] != null) { Vector2Int curNodePos = currentNodes[i].Position; // Check above node Vector2Int testPos = curNodePos + Vector2Int.up; bool isAboveValid = MoveTestNode(testPos, testedNodes, currentNodes); // Check left node testPos = curNodePos + Vector2Int.left; bool isLeftValid = MoveTestNode(testPos, testedNodes, currentNodes); // Check right node testPos = curNodePos + Vector2Int.right; bool isRightValid = MoveTestNode(testPos, testedNodes, currentNodes); // Check down node testPos = curNodePos + Vector2Int.down; bool isDownValid = MoveTestNode(testPos, testedNodes, currentNodes); // If they were not all valid, then there is something adjacent to the cur tile where // we should put an attack/interactable tile. So add it to edge tiles if (!(isAboveValid && isLeftValid && isRightValid && isDownValid) && currentNodes[i].Occupying == CharacterType.None) { edgeMoveNodes.Add(currentNodes[i]); } } } // Removes the nodes that have already been iterated over for (int i = 0; i < amountNodes; ++i) { currentNodes.RemoveAt(0); } ++depth; } // All the remaining currentNodes are edge tiles (as long as no one is there) foreach (Node n in currentNodes) { if (n.Occupying == CharacterType.None) { edgeMoveNodes.Add(n); } } // Reset the tested nodes, since we do not care if the tiles were already tested for movement, since they could have failed // those tested due to them being an interactable/attack tile testedNodes = new List <Node>(); // 2. Now iterate 4-way over the edge tiles to check for interactables (There is no need for a while statement, // since we are only iterating one more tile, but it keeps it looking similar) while (depth < _moveRange + 1) { int amountNodes = edgeMoveNodes.Count; for (int i = 0; i < amountNodes; ++i) { // If the current node is null, end this iteration and start the next one if (edgeMoveNodes[i] != null) { Vector2Int curNodePos = edgeMoveNodes[i].Position; // Check above node Vector2Int testPos = curNodePos + Vector2Int.up; InteractTestNode(testPos, testedNodes, edgeMoveNodes); // Check left node testPos = curNodePos + Vector2Int.left; InteractTestNode(testPos, testedNodes, edgeMoveNodes); // Check right node testPos = curNodePos + Vector2Int.right; InteractTestNode(testPos, testedNodes, edgeMoveNodes); // Check down node testPos = curNodePos + Vector2Int.down; InteractTestNode(testPos, testedNodes, edgeMoveNodes); } } // Removes the nodes that have already been iterated over for (int i = 0; i < amountNodes; ++i) { edgeMoveNodes.RemoveAt(0); } ++depth; } // 3. Finally, iterate over the remaining depth to check for attacking while (depth < _moveRange + _attackRange) { int amountNodes = edgeMoveNodes.Count; for (int i = 0; i < amountNodes; ++i) { // If the current node is null, end this iteration and start the next one if (edgeMoveNodes[i] != null) { Vector2Int curNodePos = edgeMoveNodes[i].Position; // Check above node Vector2Int testPos = curNodePos + Vector2Int.up; AttackTestNode(testPos, testedNodes, edgeMoveNodes); // Check left node testPos = curNodePos + Vector2Int.left; AttackTestNode(testPos, testedNodes, edgeMoveNodes); // Check right node testPos = curNodePos + Vector2Int.right; AttackTestNode(testPos, testedNodes, edgeMoveNodes); // Check down node testPos = curNodePos + Vector2Int.down; AttackTestNode(testPos, testedNodes, edgeMoveNodes); } } // Removes the nodes that have already been iterated over for (int i = 0; i < amountNodes; ++i) { edgeMoveNodes.RemoveAt(0); } ++depth; } }