diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 1c01fe6935..06b70306cb 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -14,6 +14,9 @@ import com.unciv.models.ruleset.unit.UnitType import com.unciv.ui.worldscreen.unit.UnitAction import com.unciv.ui.worldscreen.unit.UnitActions +const val CLOSE_TURNS_AWAY_LIMIT = 3f +const val CLOSE_TILES_AWAY_LIMIT = 5 + class UnitAutomation{ fun automateUnitMoves(unit: MapUnit) { @@ -80,7 +83,7 @@ class UnitAutomation{ if (unit.health < 80 && tryHealUnit(unit, unitDistanceToTiles)) return - // find the closest enemy unit that we know of within 5 spaces and advance towards it + // move towards the closest reasonably attackable enemy unit within 3 turns of movement (and 5 tiles range) if (tryAdvanceTowardsCloseEnemy(unit)) return if (unit.health < 100 && tryHealUnit(unit, unitDistanceToTiles)) return @@ -195,9 +198,13 @@ class UnitAutomation{ class AttackableTile(val tileToAttackFrom:TileInfo, val tileToAttack:TileInfo) - fun getAttackableEnemies(unit: MapUnit, unitDistanceToTiles: PathsToTilesWithinTurn): ArrayList { - val tilesWithEnemies = unit.civInfo.viewableTiles - .filter { containsAttackableEnemy(it, MapUnitCombatant(unit)) } + fun getAttackableEnemies( + unit: MapUnit, + unitDistanceToTiles: PathsToTilesWithinTurn, + tilesToCheck: List? = null + ): ArrayList { + val tilesWithEnemies = (tilesToCheck ?: unit.civInfo.viewableTiles) + .filter { containsAttackableEnemy(it, MapUnitCombatant(unit)) } val rangeOfAttack = unit.getRange() @@ -238,17 +245,32 @@ class UnitAutomation{ .filter { containsAttackableEnemy(it, CityCombatant(city)) } } + /** Move towards the closest attackable enemy of the [unit]. + * + * Limited by [CLOSE_TURNS_AWAY_LIMIT] and [CLOSE_TILES_AWAY_LIMIT]. + * Tiles attack from which would result in instant death of the [unit] are ignored. */ private fun tryAdvanceTowardsCloseEnemy(unit: MapUnit): Boolean { // this can be sped up if we check each layer separately - var closeEnemies = unit.getTile().getTilesInDistance(5) - .filter{ containsAttackableEnemy(it, MapUnitCombatant(unit)) && unit.movement.canReach(it)} - if(unit.type.isRanged()) - closeEnemies = closeEnemies.filterNot { it.isCityCenter() && it.getCity()!!.health==1 } + val unitDistanceToTiles = unit.movement.getDistanceToTilesWithinTurn( + unit.getTile().position, + unit.getMaxMovement() * CLOSE_TURNS_AWAY_LIMIT + ) + var closeEnemies = getAttackableEnemies( + unit, + unitDistanceToTiles, + tilesToCheck = unit.getTile().getTilesInDistance(CLOSE_TILES_AWAY_LIMIT) + ).filter { + BattleDamage().calculateDamageToAttacker(MapUnitCombatant(unit), + Battle(unit.civInfo.gameInfo).getMapCombatantOfTile(it.tileToAttack)!!) < unit.health + } - val closestEnemy = closeEnemies.minBy { it.arialDistanceTo(unit.getTile()) } + if(unit.type.isRanged()) + closeEnemies = closeEnemies.filterNot { it.tileToAttack.isCityCenter() && it.tileToAttack.getCity()!!.health==1 } + + val closestEnemy = closeEnemies.minBy { it.tileToAttack.arialDistanceTo(unit.getTile()) } if (closestEnemy != null) { - unit.movement.headTowards(closestEnemy) + unit.movement.headTowards(closestEnemy.tileToAttack) return true } return false