mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-12 08:49:22 +07:00
AI civilian improvement: Don't freeze when enemy is near, keep working in tiles where he can't reach
This commit is contained in:
@ -126,12 +126,12 @@ object SpecificUnitAutomation {
|
||||
.firstOrNull()?.action?.invoke()
|
||||
}
|
||||
|
||||
fun automateSettlerActions(unit: MapUnit) {
|
||||
fun automateSettlerActions(unit: MapUnit, tilesWhereWeWillBeCaptured: Set<Tile>) {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -110,10 +110,14 @@ object UnitAutomation {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun wander(unit: MapUnit, stayInTerritory: Boolean = false) {
|
||||
fun wander(unit: MapUnit, stayInTerritory: Boolean = false, tilesToAvoid:Set<Tile> = 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
|
||||
}
|
||||
|
||||
|
@ -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<Tile>) {
|
||||
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<Tile>) {
|
||||
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>): 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) }
|
||||
|
Reference in New Issue
Block a user