diff --git a/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt index 9e64bff7dd..b110bc3060 100644 --- a/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt @@ -126,12 +126,12 @@ object SpecificUnitAutomation { .firstOrNull()?.action?.invoke() } - fun automateSettlerActions(unit: MapUnit) { + fun automateSettlerActions(unit: MapUnit, tilesWhereWeWillBeCaptured: Set) { if (unit.civ.gameInfo.turns == 0) { // Special case, we want AI to settle in place on turn 1. val foundCityAction = UnitActions.getFoundCityAction(unit, unit.getTile()) // Depending on era and difficulty we might start with more than one settler. In that case settle the one with the best location val otherSettlers = unit.civ.units.getCivUnits().filter { it.currentMovement > 0 && it.baseUnit == unit.baseUnit } - if(foundCityAction?.action != null && + if (foundCityAction?.action != null && otherSettlers.none { CityLocationTileRanker.rankTileAsCityCenter( it.getTile(), unit.civ @@ -145,14 +145,13 @@ object SpecificUnitAutomation { } } - if (unit.getTile().militaryUnit == null // Don't move until you're accompanied by a military unit - && !unit.civ.isCityState() // ..unless you're a city state that was unable to settle its city on turn 1 - && unit.getDamageFromTerrain() < unit.health) return // Also make sure we won't die waiting + if (unit.getDamageFromTerrain() < unit.health) return // Also make sure we won't die waiting // It's possible that we'll see a tile "over the sea" that's better than the tiles close by, but that's not a reason to abandon the close tiles! // Also this lead to some routing problems, see https://github.com/yairm210/Unciv/issues/3653 val bestCityLocation: Tile? = CityLocationTileRanker.getBestTilesToFoundCity(unit).firstOrNull { + if (it.first in tilesWhereWeWillBeCaptured) return@firstOrNull false val pathSize = unit.movement.getShortestPath(it.first).size return@firstOrNull pathSize in 1..3 }?.first @@ -169,7 +168,7 @@ object SpecificUnitAutomation { if (frontierCity != null && getFrontierScore(frontierCity) > 0 && unit.movement.canReach(frontierCity.getCenterTile())) unit.movement.headTowards(frontierCity.getCenterTile()) if (UnitAutomation.tryExplore(unit)) return // try to find new areas - UnitAutomation.wander(unit) // go around aimlessly + UnitAutomation.wander(unit, tilesToAvoid = tilesWhereWeWillBeCaptured) // go around aimlessly return } diff --git a/core/src/com/unciv/logic/automation/unit/UnitAutomation.kt b/core/src/com/unciv/logic/automation/unit/UnitAutomation.kt index 2af9d00089..a708c41b5f 100644 --- a/core/src/com/unciv/logic/automation/unit/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/UnitAutomation.kt @@ -110,10 +110,14 @@ object UnitAutomation { } @JvmStatic - fun wander(unit: MapUnit, stayInTerritory: Boolean = false) { + fun wander(unit: MapUnit, stayInTerritory: Boolean = false, tilesToAvoid:Set = setOf()) { val unitDistanceToTiles = unit.movement.getDistanceToTiles() val reachableTiles = unitDistanceToTiles - .filter { unit.movement.canMoveTo(it.key) && unit.movement.canReach(it.key) } + .filter { + it.key !in tilesToAvoid + && unit.movement.canMoveTo(it.key) + && unit.movement.canReach(it.key) + } val reachableTilesMaxWalkingDistance = reachableTiles .filter { it.value.totalDistance == unit.currentMovement @@ -234,11 +238,17 @@ object UnitAutomation { unit.movement.moveToTile(tilesCanMoveTo.minByOrNull { it.value.totalDistance }!!.key) } + val tilesWhereWeWillBeCaptured = unit.currentTile.getTilesInDistance(5) + .mapNotNull { it.militaryUnit } + .filter { it.civ.isAtWarWith(unit.civ) } + .flatMap { it.movement.getReachableTilesInCurrentTurn() } + .toSet() + if (unit.hasUnique(UniqueType.FoundCity)) - return SpecificUnitAutomation.automateSettlerActions(unit) + return SpecificUnitAutomation.automateSettlerActions(unit, tilesWhereWeWillBeCaptured) if (unit.cache.hasUniqueToBuildImprovements) - return WorkerAutomation.automateWorkerAction(unit) + return WorkerAutomation.automateWorkerAction(unit, tilesWhereWeWillBeCaptured) if (unit.hasUnique(UniqueType.MayFoundReligion) && unit.civ.religionManager.religionState < ReligionState.Religion @@ -762,14 +772,12 @@ object UnitAutomation { private fun tryRunAwayIfNeccessary(unit: MapUnit): Boolean { // This is a little 'Bugblatter Beast of Traal': Run if we can attack an enemy // Cheaper than determining which enemies could attack us next turn - //todo - stay when we're stacked with a good military unit??? val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys .filter { containsEnemyMilitaryUnit(unit, it) } - if (enemyUnitsInWalkingDistance.isNotEmpty() && !unit.baseUnit.isMilitary()) { - if (unit.getTile().militaryUnit == null && !unit.getTile().isCityCenter()) - runAway(unit) - + if (enemyUnitsInWalkingDistance.isNotEmpty() && !unit.baseUnit.isMilitary() + && unit.getTile().militaryUnit == null && !unit.getTile().isCityCenter()) { + runAway(unit) return true } diff --git a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt index 3f9b6b53c4..e7cdd30b6b 100644 --- a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt @@ -108,8 +108,8 @@ class WorkerAutomation( companion object { /** Maps to instance [WorkerAutomation.automateWorkerAction] knowing only the MapUnit */ - fun automateWorkerAction(unit: MapUnit) { - unit.civ.getWorkerAutomation().automateWorkerAction(unit) + fun automateWorkerAction(unit: MapUnit, tilesWhereWeWillBeCaptured: Set) { + unit.civ.getWorkerAutomation().automateWorkerAction(unit, tilesWhereWeWillBeCaptured) } /** Convenience shortcut supports old calling syntax for [WorkerAutomation.getPriority] */ @@ -131,9 +131,9 @@ class WorkerAutomation( /** * Automate one Worker - decide what to do and where, move, start or continue work. */ - fun automateWorkerAction(unit: MapUnit) { + fun automateWorkerAction(unit: MapUnit, tilesWhereWeWillBeCaptured: Set) { val currentTile = unit.getTile() - val tileToWork = findTileToWork(unit) + val tileToWork = findTileToWork(unit, tilesWhereWeWillBeCaptured) if (getPriority(tileToWork, civInfo) < 3) { // building roads is more important if (tryConnectingCities(unit)) return @@ -201,7 +201,7 @@ class WorkerAutomation( // Idle CS units should wander so they don't obstruct players so much if (unit.civ.isCityState()) - wander(unit, stayInTerritory = true) + wander(unit, stayInTerritory = true, tilesToAvoid = tilesWhereWeWillBeCaptured) } /** @@ -279,16 +279,17 @@ class WorkerAutomation( * Looks for a worthwhile tile to improve * @return The current tile if no tile to work was found */ - private fun findTileToWork(unit: MapUnit): Tile { + private fun findTileToWork(unit: MapUnit, tilesToAvoid: Set): Tile { val currentTile = unit.getTile() val workableTiles = currentTile.getTilesInDistance(4) .filter { - (it.civilianUnit == null || it == currentTile) - && (it.owningCity == null || it.getOwner()==civInfo) - && getPriority(it) > 1 - && it.getTilesInDistance(2) // don't work in range of enemy cities + it !in tilesToAvoid + && (it.civilianUnit == null || it == currentTile) + && (it.owningCity == null || it.getOwner()==civInfo) + && getPriority(it) > 1 + && it.getTilesInDistance(2) // don't work in range of enemy cities .none { tile -> tile.isCityCenter() && tile.getCity()!!.civ.isAtWarWith(civInfo) } - && it.getTilesInDistance(3) // don't work in range of enemy units + && it.getTilesInDistance(3) // don't work in range of enemy units .none { tile -> tile.militaryUnit != null && tile.militaryUnit!!.civ.isAtWarWith(civInfo)} } .sortedByDescending { getPriority(it) }