//move a single unit only IEnumerator SingleUnitRoutine(Unit unit) { GameControl.DisplayMessage("AI's Turn"); yield return(new WaitForSeconds(0.1f)); if (!unit.IsStunned()) { Faction faction = FactionManager.GetFaction(unit.factionID); _AIMode activeAIMode = !faction.useDefaultAIMode ? faction.aiMode : mode; while (unit.moveRemain > 0 || unit.CanAttack()) { StartCoroutine(MoveUnitRoutine(unit, activeAIMode)); while (movingUnit) { yield return(null); } if (unit == null) { break; } if (unit.attackRemain > 0 || unit.CanAttack()) { yield return(new WaitForSeconds(.1f)); } } //~ StartCoroutine(MoveUnitRoutine(unit, activeAIMode)); //~ while(movingUnit) yield return null; //~ yield return new WaitForSeconds(0.25f); } GameControl.EndTurn(); }
//move the whole faction, unit by unit IEnumerator FactionRoutine(Faction faction) { GameControl.DisplayMessage("AI's Turn"); yield return(new WaitForSeconds(0.3f)); //create a new list so no unit will be skipped is one of them is somehow destroyed (by counter attack); List <Unit> unitList = new List <Unit>(faction.allUnitList); for (int i = 0; i < unitList.Count; i++) { AIDebug("Start moving unit " + unitList[i].gameObject.name); yield return(new WaitForSeconds(unitInterval)); if (unitList[i].IsStunned()) { continue; } _AIMode activeAIMode = !faction.useDefaultAIMode ? faction.aiMode : mode; while (unitList[i].moveRemain > 0 || unitList[i].CanAttack()) { AIDebug("Moving unit " + unitList[i].gameObject.name + " (move remain:" + (unitList[i].moveRemain > 0) + ", can attack:" + unitList[i].CanAttack() + ")"); StartCoroutine(MoveUnitRoutine(unitList[i], activeAIMode)); while (movingUnit) { yield return(null); } if (unitList[i] == null) { break; } if (unitList[i].moveRemain > 0 || unitList[i].CanAttack()) { yield return(new WaitForSeconds(.1f)); } } //~ StartCoroutine(MoveUnitRoutine(unitList[i], activeAIMode)); //~ while(movingUnit) yield return null; //~ yield return new WaitForSeconds(0.25f); if (GameControl.GetGamePhase() == _GamePhase.Over) { yield break; } } GameControl.EndTurn(); }
//analyse the grid to know where the unit should move to private Tile Analyse(Unit unit, _AIMode activeMode) { //get all wakable tiles in range first List <Tile> walkableTilesInRange = GridManager.GetTilesWithinDistance(unit.tile, unit.GetEffectiveMoveRange(), true); walkableTilesInRange.Add(unit.tile); //get all visible hostile List <Unit> allHostileInSight = FactionManager.GetAllHostileUnit(unit.factionID); if (GameControl.EnableFogOfWar()) { for (int i = 0; i < allHostileInSight.Count; i++) { if (!FogOfWar.IsTileVisibleToFaction(allHostileInSight[i].tile, unit.factionID)) { allHostileInSight.RemoveAt(i); i -= 1; } } } //if cover system is in used if (GameControl.EnableCover()) { Tile tile = AnalyseCoverSystem(unit, walkableTilesInRange, allHostileInSight); if (tile != null) { return(tile); } } //if there are hostile if (allHostileInSight.Count > 0) { //fill up the walkableTilesInRange hostile list //then filter thru walkableTilesInRange, those that have a hostile in range will be add to a tilesWithHostileInRange List <Tile> tilesWithHostileInRange = new List <Tile>(); GridManager.SetupHostileInRangeforTile(unit, walkableTilesInRange); for (int i = 0; i < walkableTilesInRange.Count; i++) { if (walkableTilesInRange[i].GetHostileInRange().Count > 0) { tilesWithHostileInRange.Add(walkableTilesInRange[i]); } } //if the tilesWithHostileInRange is not empty after the process, means there's tiles which the unit can move into and attack //return one of those in the tilesWithHostileInRange so the unit can attack if (tilesWithHostileInRange.Count > 0) { //if the unit current tile is one of those tiles with hostile, just stay put and attack if (tilesWithHostileInRange.Contains(unit.tile)) { //randomize it a bit so the unit do move around but not stay in place all the time if (Random.Range(0f, 1f) > 0.25f) { return(unit.tile); } } return(tilesWithHostileInRange[Random.Range(0, tilesWithHostileInRange.Count)]); } } //if there's not potential target at all, check if the unit has any previous attacker //if there are, go after the last attacker if (unit.lastAttacker != null) { return(unit.lastAttacker.tile); } //for aggresive mode with FogOfWar disabled, try move towards the nearest unit if (activeMode == _AIMode.Aggressive && Random.Range(0f, 1f) > 0.25f) { List <Unit> allHostile = FactionManager.GetAllHostileUnit(unit.factionID); float nearest = Mathf.Infinity; int nearestIndex = 0; for (int i = 0; i < allHostile.Count; i++) { float dist = GridManager.GetDistance(allHostile[i].tile, unit.tile); if (dist < nearest) { nearest = dist; nearestIndex = i; } } return(allHostile[nearestIndex].tile); } //if there's really no hostile to go after, then just move randomly in one of the walkable int rand = Random.Range(0, walkableTilesInRange.Count); //clear in hostileInRange list for all moveable tile so, just in case the list is not empty (hostileInRange dont clear after each move) //so the unit dont try to attack anything when it moves into the targetTile walkableTilesInRange[rand].SetHostileInRange(new List <Tile>()); return(walkableTilesInRange[rand]); }
private bool movingUnit = false; //set to true when a unit is being moved IEnumerator MoveUnitRoutine(Unit unit, _AIMode activeMode) { TBTK.OnUnitSelected(unit); movingUnit = true; //Debug.Log("moving unit"); if (activeMode != _AIMode.Aggressive && !unit.trigger) { AIDebug("unit " + unit.gameObject.name + " is not triggered"); if (!untriggeredUnitMove) { StartCoroutine(EndMoveUnitRoutine()); unit.moveRemain = 0; unit.attackRemain = 0; } else { if (Random.value < 0.5f) { StartCoroutine(EndMoveUnitRoutine()); unit.moveRemain = 0; unit.attackRemain = 0; } else { AIDebug("Randomly move unit " + unit.gameObject.name + " anyway"); List <Tile> walkableTilesInRange = GridManager.GetTilesWithinDistance(unit.tile, Mathf.Min(1, unit.GetEffectiveMoveRange() / 2), true); if (walkableTilesInRange.Count > 0) { unit.Move(walkableTilesInRange[Random.Range(0, walkableTilesInRange.Count)]); } } } AIDebug("End unit " + unit.gameObject.name + " turn"); StartCoroutine(EndMoveUnitRoutine()); yield break; } Tile targetTile = Analyse(unit, activeMode); if (CameraControl.CenterOnSelectedUnit()) { bool visible = false; if (unit.tile.IsVisible()) { visible = true; } else if (targetTile != unit.tile) { List <Tile> path = unit.GetPathForAI(targetTile); for (int i = 0; i < path.Count; i++) { if (path[i].IsVisible()) { visible = true; break; } } targetTile = path[path.Count - 1]; Debug.DrawLine(unit.tile.GetPos(), targetTile.GetPos(), Color.red, 2); } if (visible) { CameraControl.OnUnitSelected(unit, false); while (CameraControl.IsLerping()) { yield return(null); } } } //first move to the targetTile if (targetTile != unit.tile) { unit.Move(targetTile); yield return(new WaitForSeconds(.1f)); //wait until the unit has moved into the targetTile while (!TurnControl.ClearToProceed()) { //AIDebug("waiting, unit "+unit.gameObject.name+" is moving"); AIDebug("waiting while unit is moving"); yield return(null); } } if (unit == null || unit.HP <= 0) //in case unit is destroyed by overwatch { StartCoroutine(EndMoveUnitRoutine()); yield break; } for (int i = 0; i < targetTile.hostileInRangeList.Count; i++) { if (targetTile.hostileInRangeList[i].unit == null || targetTile.hostileInRangeList[i].unit.factionID == unit.factionID) { targetTile.hostileInRangeList.RemoveAt(i); i -= 1; } } //if there's hostile within range, attack it if (targetTile.hostileInRangeList.Count > 0) { if (unit.CanAttack()) { AIDebug("waiting, unit " + unit.gameObject.name + " is attacking"); //~ if(targetTile!=unit.tile){ //wait until the unit has moved into the targetTile //~ yield return new WaitForSeconds(.25f); //~ while(!TurnControl.ClearToProceed()) yield return null; //~ } int rand = Random.Range(0, targetTile.hostileInRangeList.Count); unit.Attack(targetTile.hostileInRangeList[rand].unit); } else { if (unit.moveRemain > 0) { unit.moveRemain -= 1; } } } else { if (unit.moveRemain <= 0) { unit.attackRemain = 0; } } AIDebug("End unit " + unit.gameObject.name + " turn"); StartCoroutine(EndMoveUnitRoutine()); yield return(null); }