mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 14:24:43 -04: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:
parent
fc81d7dda3
commit
edd09910a1
@ -126,12 +126,12 @@ object SpecificUnitAutomation {
|
|||||||
.firstOrNull()?.action?.invoke()
|
.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.
|
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())
|
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
|
// 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 }
|
val otherSettlers = unit.civ.units.getCivUnits().filter { it.currentMovement > 0 && it.baseUnit == unit.baseUnit }
|
||||||
if(foundCityAction?.action != null &&
|
if (foundCityAction?.action != null &&
|
||||||
otherSettlers.none {
|
otherSettlers.none {
|
||||||
CityLocationTileRanker.rankTileAsCityCenter(
|
CityLocationTileRanker.rankTileAsCityCenter(
|
||||||
it.getTile(), unit.civ
|
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
|
if (unit.getDamageFromTerrain() < unit.health) return // Also make sure we won't die waiting
|
||||||
&& !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
|
|
||||||
|
|
||||||
// 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!
|
// 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
|
// Also this lead to some routing problems, see https://github.com/yairm210/Unciv/issues/3653
|
||||||
val bestCityLocation: Tile? =
|
val bestCityLocation: Tile? =
|
||||||
CityLocationTileRanker.getBestTilesToFoundCity(unit).firstOrNull {
|
CityLocationTileRanker.getBestTilesToFoundCity(unit).firstOrNull {
|
||||||
|
if (it.first in tilesWhereWeWillBeCaptured) return@firstOrNull false
|
||||||
val pathSize = unit.movement.getShortestPath(it.first).size
|
val pathSize = unit.movement.getShortestPath(it.first).size
|
||||||
return@firstOrNull pathSize in 1..3
|
return@firstOrNull pathSize in 1..3
|
||||||
}?.first
|
}?.first
|
||||||
@ -169,7 +168,7 @@ object SpecificUnitAutomation {
|
|||||||
if (frontierCity != null && getFrontierScore(frontierCity) > 0 && unit.movement.canReach(frontierCity.getCenterTile()))
|
if (frontierCity != null && getFrontierScore(frontierCity) > 0 && unit.movement.canReach(frontierCity.getCenterTile()))
|
||||||
unit.movement.headTowards(frontierCity.getCenterTile())
|
unit.movement.headTowards(frontierCity.getCenterTile())
|
||||||
if (UnitAutomation.tryExplore(unit)) return // try to find new areas
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +110,14 @@ object UnitAutomation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@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 unitDistanceToTiles = unit.movement.getDistanceToTiles()
|
||||||
val reachableTiles = unitDistanceToTiles
|
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
|
val reachableTilesMaxWalkingDistance = reachableTiles
|
||||||
.filter { it.value.totalDistance == unit.currentMovement
|
.filter { it.value.totalDistance == unit.currentMovement
|
||||||
@ -234,11 +238,17 @@ object UnitAutomation {
|
|||||||
unit.movement.moveToTile(tilesCanMoveTo.minByOrNull { it.value.totalDistance }!!.key)
|
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))
|
if (unit.hasUnique(UniqueType.FoundCity))
|
||||||
return SpecificUnitAutomation.automateSettlerActions(unit)
|
return SpecificUnitAutomation.automateSettlerActions(unit, tilesWhereWeWillBeCaptured)
|
||||||
|
|
||||||
if (unit.cache.hasUniqueToBuildImprovements)
|
if (unit.cache.hasUniqueToBuildImprovements)
|
||||||
return WorkerAutomation.automateWorkerAction(unit)
|
return WorkerAutomation.automateWorkerAction(unit, tilesWhereWeWillBeCaptured)
|
||||||
|
|
||||||
if (unit.hasUnique(UniqueType.MayFoundReligion)
|
if (unit.hasUnique(UniqueType.MayFoundReligion)
|
||||||
&& unit.civ.religionManager.religionState < ReligionState.Religion
|
&& unit.civ.religionManager.religionState < ReligionState.Religion
|
||||||
@ -762,14 +772,12 @@ object UnitAutomation {
|
|||||||
private fun tryRunAwayIfNeccessary(unit: MapUnit): Boolean {
|
private fun tryRunAwayIfNeccessary(unit: MapUnit): Boolean {
|
||||||
// This is a little 'Bugblatter Beast of Traal': Run if we can attack an enemy
|
// 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
|
// 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
|
val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys
|
||||||
.filter { containsEnemyMilitaryUnit(unit, it) }
|
.filter { containsEnemyMilitaryUnit(unit, it) }
|
||||||
|
|
||||||
if (enemyUnitsInWalkingDistance.isNotEmpty() && !unit.baseUnit.isMilitary()) {
|
if (enemyUnitsInWalkingDistance.isNotEmpty() && !unit.baseUnit.isMilitary()
|
||||||
if (unit.getTile().militaryUnit == null && !unit.getTile().isCityCenter())
|
&& unit.getTile().militaryUnit == null && !unit.getTile().isCityCenter()) {
|
||||||
runAway(unit)
|
runAway(unit)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,8 +108,8 @@ class WorkerAutomation(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Maps to instance [WorkerAutomation.automateWorkerAction] knowing only the MapUnit */
|
/** Maps to instance [WorkerAutomation.automateWorkerAction] knowing only the MapUnit */
|
||||||
fun automateWorkerAction(unit: MapUnit) {
|
fun automateWorkerAction(unit: MapUnit, tilesWhereWeWillBeCaptured: Set<Tile>) {
|
||||||
unit.civ.getWorkerAutomation().automateWorkerAction(unit)
|
unit.civ.getWorkerAutomation().automateWorkerAction(unit, tilesWhereWeWillBeCaptured)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convenience shortcut supports old calling syntax for [WorkerAutomation.getPriority] */
|
/** 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.
|
* 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 currentTile = unit.getTile()
|
||||||
val tileToWork = findTileToWork(unit)
|
val tileToWork = findTileToWork(unit, tilesWhereWeWillBeCaptured)
|
||||||
|
|
||||||
if (getPriority(tileToWork, civInfo) < 3) { // building roads is more important
|
if (getPriority(tileToWork, civInfo) < 3) { // building roads is more important
|
||||||
if (tryConnectingCities(unit)) return
|
if (tryConnectingCities(unit)) return
|
||||||
@ -201,7 +201,7 @@ class WorkerAutomation(
|
|||||||
|
|
||||||
// Idle CS units should wander so they don't obstruct players so much
|
// Idle CS units should wander so they don't obstruct players so much
|
||||||
if (unit.civ.isCityState())
|
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
|
* Looks for a worthwhile tile to improve
|
||||||
* @return The current tile if no tile to work was found
|
* @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 currentTile = unit.getTile()
|
||||||
val workableTiles = currentTile.getTilesInDistance(4)
|
val workableTiles = currentTile.getTilesInDistance(4)
|
||||||
.filter {
|
.filter {
|
||||||
(it.civilianUnit == null || it == currentTile)
|
it !in tilesToAvoid
|
||||||
&& (it.owningCity == null || it.getOwner()==civInfo)
|
&& (it.civilianUnit == null || it == currentTile)
|
||||||
&& getPriority(it) > 1
|
&& (it.owningCity == null || it.getOwner()==civInfo)
|
||||||
&& it.getTilesInDistance(2) // don't work in range of enemy cities
|
&& getPriority(it) > 1
|
||||||
|
&& it.getTilesInDistance(2) // don't work in range of enemy cities
|
||||||
.none { tile -> tile.isCityCenter() && tile.getCity()!!.civ.isAtWarWith(civInfo) }
|
.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)}
|
.none { tile -> tile.militaryUnit != null && tile.militaryUnit!!.civ.isAtWarWith(civInfo)}
|
||||||
}
|
}
|
||||||
.sortedByDescending { getPriority(it) }
|
.sortedByDescending { getPriority(it) }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user