mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-11 11:28:03 +07:00
chore: Split civilian unit automation into separate file
Also limited missile automation to air units only, doesn't make sense for land-based self-destructing units to follow missile automation
This commit is contained in:
parent
f4b7822728
commit
7503493d7d
@ -164,11 +164,9 @@ object AirUnitAutomation {
|
||||
return
|
||||
}
|
||||
|
||||
if (unit.baseUnit.isAirUnit()) {
|
||||
val pathsToCities = unit.movement.getAerialPathsToCities()
|
||||
if (pathsToCities.isEmpty()) return // can't actually move anywhere else
|
||||
tryMoveToCitiesToAerialAttackFrom(pathsToCities, unit)
|
||||
} else UnitAutomation.tryHeadTowardsEnemyCity(unit)
|
||||
val pathsToCities = unit.movement.getAerialPathsToCities()
|
||||
if (pathsToCities.isEmpty()) return // can't actually move anywhere else
|
||||
tryMoveToCitiesToAerialAttackFrom(pathsToCities, unit)
|
||||
}
|
||||
|
||||
private fun tryRelocateToCitiesWithEnemyNearBy(unit: MapUnit): Boolean {
|
||||
|
@ -0,0 +1,181 @@
|
||||
package com.unciv.logic.automation.unit
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.managers.ReligionState
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.UnitActionType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
||||
|
||||
object CivilianUnitAutomation {
|
||||
|
||||
fun automateCivilianUnit(unit: MapUnit) {
|
||||
if (tryRunAwayIfNeccessary(unit)) return
|
||||
|
||||
if (unit.currentTile.isCityCenter() && unit.currentTile.getCity()!!.isCapital()
|
||||
&& !unit.hasUnique(UniqueType.AddInCapital)
|
||||
&& unit.civ.units.getCivUnits().any { unit.hasUnique(UniqueType.AddInCapital) }){
|
||||
// First off get out of the way, then decide if you actually want to do something else
|
||||
val tilesCanMoveTo = unit.movement.getDistanceToTiles()
|
||||
.filter { unit.movement.canMoveTo(it.key) }
|
||||
if (tilesCanMoveTo.isNotEmpty())
|
||||
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() }
|
||||
.filter { it.militaryUnit?.civ != unit.civ }
|
||||
.toSet()
|
||||
|
||||
if (unit.hasUnique(UniqueType.FoundCity))
|
||||
return SpecificUnitAutomation.automateSettlerActions(unit, tilesWhereWeWillBeCaptured)
|
||||
|
||||
if (unit.cache.hasUniqueToBuildImprovements)
|
||||
return unit.civ.getWorkerAutomation().automateWorkerAction(unit, tilesWhereWeWillBeCaptured)
|
||||
|
||||
if (unit.cache.hasUniqueToCreateWaterImprovements){
|
||||
if (!unit.civ.getWorkerAutomation().automateWorkBoats(unit))
|
||||
UnitAutomation.tryExplore(unit)
|
||||
return
|
||||
}
|
||||
|
||||
if (unit.hasUnique(UniqueType.MayFoundReligion)
|
||||
&& unit.civ.religionManager.religionState < ReligionState.Religion
|
||||
&& unit.civ.religionManager.mayFoundReligionAtAll()
|
||||
)
|
||||
return ReligiousUnitAutomation.foundReligion(unit)
|
||||
|
||||
if (unit.hasUnique(UniqueType.MayEnhanceReligion)
|
||||
&& unit.civ.religionManager.religionState < ReligionState.EnhancedReligion
|
||||
&& unit.civ.religionManager.mayEnhanceReligionAtAll(unit)
|
||||
)
|
||||
return ReligiousUnitAutomation.enhanceReligion(unit)
|
||||
|
||||
// We try to add any unit in the capital we can, though that might not always be desirable
|
||||
// For now its a simple option to allow AI to win a science victory again
|
||||
if (unit.hasUnique(UniqueType.AddInCapital))
|
||||
return SpecificUnitAutomation.automateAddInCapital(unit)
|
||||
|
||||
//todo this now supports "Great General"-like mod units not combining 'aura' and citadel
|
||||
// abilities, but not additional capabilities if automation finds no use for those two
|
||||
if (unit.cache.hasStrengthBonusInRadiusUnique
|
||||
&& SpecificUnitAutomation.automateGreatGeneral(unit))
|
||||
return
|
||||
if (unit.cache.hasCitadelPlacementUnique && SpecificUnitAutomation.automateCitadelPlacer(unit))
|
||||
return
|
||||
if (unit.cache.hasCitadelPlacementUnique || unit.cache.hasStrengthBonusInRadiusUnique)
|
||||
return SpecificUnitAutomation.automateGreatGeneralFallback(unit)
|
||||
|
||||
if (unit.civ.religionManager.maySpreadReligionAtAll(unit))
|
||||
return ReligiousUnitAutomation.automateMissionary(unit)
|
||||
|
||||
if (unit.hasUnique(UniqueType.PreventSpreadingReligion) || unit.canDoLimitedAction(Constants.removeHeresy))
|
||||
return ReligiousUnitAutomation.automateInquisitor(unit)
|
||||
|
||||
val isLateGame = isLateGame(unit.civ)
|
||||
// Great scientist -> Hurry research if late game
|
||||
if (isLateGame) {
|
||||
val hurriedResearch = UnitActions.invokeUnitAction(unit, UnitActionType.HurryResearch)
|
||||
if (hurriedResearch) return
|
||||
}
|
||||
|
||||
// Great merchant -> Conduct trade mission if late game and if not at war.
|
||||
// TODO: This could be more complex to walk to the city state that is most beneficial to
|
||||
// also have more influence.
|
||||
if (unit.hasUnique(UniqueType.CanTradeWithCityStateForGoldAndInfluence)
|
||||
// Don't wander around with the great merchant when at war. Barbs might also be a
|
||||
// problem, but hopefully by the time we have a great merchant, they're under control.
|
||||
&& !unit.civ.isAtWar()
|
||||
&& isLateGame
|
||||
) {
|
||||
val tradeMissionCanBeConductedEventually =
|
||||
SpecificUnitAutomation.conductTradeMission(unit)
|
||||
if (tradeMissionCanBeConductedEventually)
|
||||
return
|
||||
}
|
||||
|
||||
// Great engineer -> Try to speed up wonder construction if late game
|
||||
if (isLateGame &&
|
||||
(unit.hasUnique(UniqueType.CanSpeedupConstruction)
|
||||
|| unit.hasUnique(UniqueType.CanSpeedupWonderConstruction))) {
|
||||
val wonderCanBeSpedUpEventually = SpecificUnitAutomation.speedupWonderConstruction(unit)
|
||||
if (wonderCanBeSpedUpEventually)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// This has to come after the individual abilities for the great people that can also place
|
||||
// instant improvements (e.g. great scientist).
|
||||
if (unit.hasUnique(UniqueType.ConstructImprovementInstantly)) {
|
||||
// catch great prophet for civs who can't found/enhance/spread religion
|
||||
// includes great people plus moddable units
|
||||
val improvementCanBePlacedEventually =
|
||||
SpecificUnitAutomation.automateImprovementPlacer(unit)
|
||||
if (!improvementCanBePlacedEventually)
|
||||
UnitActions.invokeUnitAction(unit, UnitActionType.StartGoldenAge)
|
||||
}
|
||||
|
||||
// TODO: The AI tends to have a lot of great generals. Maybe there should be a cutoff
|
||||
// (depending on number of cities) and after that they should just be used to start golden
|
||||
// ages?
|
||||
|
||||
return // The AI doesn't know how to handle unknown civilian units
|
||||
}
|
||||
|
||||
private fun isLateGame(civ: Civilization): Boolean {
|
||||
val researchCompletePercent =
|
||||
(civ.tech.researchedTechnologies.size * 1.0f) / civ.gameInfo.ruleset.technologies.size
|
||||
return researchCompletePercent >= 0.8f
|
||||
}
|
||||
|
||||
/** Returns whether the civilian spends its turn hiding and not moving */
|
||||
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
|
||||
val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys
|
||||
.filter { UnitAutomation.containsEnemyMilitaryUnit(unit, it) }
|
||||
|
||||
if (enemyUnitsInWalkingDistance.isNotEmpty() && !unit.baseUnit.isMilitary()
|
||||
&& unit.getTile().militaryUnit == null && !unit.getTile().isCityCenter()) {
|
||||
runAway(unit)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun runAway(unit: MapUnit) {
|
||||
val reachableTiles = unit.movement.getDistanceToTiles()
|
||||
val enterableCity = reachableTiles.keys
|
||||
.firstOrNull { it.isCityCenter() && unit.movement.canMoveTo(it) }
|
||||
if (enterableCity != null) {
|
||||
unit.movement.moveToTile(enterableCity)
|
||||
return
|
||||
}
|
||||
val defensiveUnit = reachableTiles.keys
|
||||
.firstOrNull {
|
||||
it.militaryUnit != null && it.militaryUnit!!.civ == unit.civ && it.civilianUnit == null
|
||||
}
|
||||
if (defensiveUnit != null) {
|
||||
unit.movement.moveToTile(defensiveUnit)
|
||||
return
|
||||
}
|
||||
val tileFurthestFromEnemy = reachableTiles.keys
|
||||
.filter { unit.movement.canMoveTo(it) && unit.getDamageFromTerrain(it) < unit.health }
|
||||
.maxByOrNull { countDistanceToClosestEnemy(unit, it) }
|
||||
?: return // can't move anywhere!
|
||||
unit.movement.moveToTile(tileFurthestFromEnemy)
|
||||
}
|
||||
|
||||
|
||||
private fun countDistanceToClosestEnemy(unit: MapUnit, tile: Tile): Int {
|
||||
for (i in 1..3)
|
||||
if (tile.getTilesAtDistance(i).any { UnitAutomation.containsEnemyMilitaryUnit(unit, it) })
|
||||
return i
|
||||
return 4
|
||||
}
|
||||
}
|
@ -11,15 +11,11 @@ import com.unciv.logic.battle.ICombatant
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.battle.TargetHelper
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.NotificationCategory
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.logic.civilization.managers.ReligionState
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.UnitActionType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsPillage
|
||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade
|
||||
|
||||
@ -155,7 +151,7 @@ object UnitAutomation {
|
||||
|
||||
|
||||
if (unit.isCivilian()) {
|
||||
automateCivilianUnit(unit)
|
||||
CivilianUnitAutomation.automateCivilianUnit(unit)
|
||||
return
|
||||
}
|
||||
|
||||
@ -174,7 +170,7 @@ object UnitAutomation {
|
||||
if ((unit.hasUnique(UniqueType.BuildImprovements) || unit.hasUnique(UniqueType.FoundCity) ||
|
||||
unit.hasUnique(UniqueType.ReligiousUnit) || unit.hasUnique(UniqueType.CreateWaterImprovements))
|
||||
&& !unit.civ.isAtWar()){
|
||||
automateCivilianUnit(unit)
|
||||
CivilianUnitAutomation.automateCivilianUnit(unit)
|
||||
return
|
||||
}
|
||||
|
||||
@ -187,7 +183,7 @@ object UnitAutomation {
|
||||
if (unit.baseUnit.isNuclearWeapon())
|
||||
return AirUnitAutomation.automateNukes(unit)
|
||||
|
||||
if (unit.hasUnique(UniqueType.SelfDestructs))
|
||||
if (unit.baseUnit.isAirUnit() && unit.hasUnique(UniqueType.SelfDestructs))
|
||||
return AirUnitAutomation.automateMissile(unit)
|
||||
|
||||
if (tryGoToRuinAndEncampment(unit) && unit.currentMovement == 0f) return
|
||||
@ -235,127 +231,6 @@ object UnitAutomation {
|
||||
wander(unit, stayInTerritory = true)
|
||||
}
|
||||
|
||||
private fun automateCivilianUnit(unit: MapUnit) {
|
||||
if (tryRunAwayIfNeccessary(unit)) return
|
||||
|
||||
if (unit.currentTile.isCityCenter() && unit.currentTile.getCity()!!.isCapital()
|
||||
&& !unit.hasUnique(UniqueType.AddInCapital)
|
||||
&& unit.civ.units.getCivUnits().any { unit.hasUnique(UniqueType.AddInCapital) }){
|
||||
// First off get out of the way, then decide if you actually want to do something else
|
||||
val tilesCanMoveTo = unit.movement.getDistanceToTiles()
|
||||
.filter { unit.movement.canMoveTo(it.key) }
|
||||
if (tilesCanMoveTo.isNotEmpty())
|
||||
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() }
|
||||
.filter { it.militaryUnit?.civ != unit.civ }
|
||||
.toSet()
|
||||
|
||||
if (unit.hasUnique(UniqueType.FoundCity))
|
||||
return SpecificUnitAutomation.automateSettlerActions(unit, tilesWhereWeWillBeCaptured)
|
||||
|
||||
if (unit.cache.hasUniqueToBuildImprovements)
|
||||
return unit.civ.getWorkerAutomation().automateWorkerAction(unit, tilesWhereWeWillBeCaptured)
|
||||
|
||||
if (unit.cache.hasUniqueToCreateWaterImprovements){
|
||||
if (!unit.civ.getWorkerAutomation().automateWorkBoats(unit))
|
||||
tryExplore(unit)
|
||||
return
|
||||
}
|
||||
|
||||
if (unit.hasUnique(UniqueType.MayFoundReligion)
|
||||
&& unit.civ.religionManager.religionState < ReligionState.Religion
|
||||
&& unit.civ.religionManager.mayFoundReligionAtAll()
|
||||
)
|
||||
return ReligiousUnitAutomation.foundReligion(unit)
|
||||
|
||||
if (unit.hasUnique(UniqueType.MayEnhanceReligion)
|
||||
&& unit.civ.religionManager.religionState < ReligionState.EnhancedReligion
|
||||
&& unit.civ.religionManager.mayEnhanceReligionAtAll(unit)
|
||||
)
|
||||
return ReligiousUnitAutomation.enhanceReligion(unit)
|
||||
|
||||
// We try to add any unit in the capital we can, though that might not always be desirable
|
||||
// For now its a simple option to allow AI to win a science victory again
|
||||
if (unit.hasUnique(UniqueType.AddInCapital))
|
||||
return SpecificUnitAutomation.automateAddInCapital(unit)
|
||||
|
||||
//todo this now supports "Great General"-like mod units not combining 'aura' and citadel
|
||||
// abilities, but not additional capabilities if automation finds no use for those two
|
||||
if (unit.cache.hasStrengthBonusInRadiusUnique
|
||||
&& SpecificUnitAutomation.automateGreatGeneral(unit))
|
||||
return
|
||||
if (unit.cache.hasCitadelPlacementUnique && SpecificUnitAutomation.automateCitadelPlacer(unit))
|
||||
return
|
||||
if (unit.cache.hasCitadelPlacementUnique || unit.cache.hasStrengthBonusInRadiusUnique)
|
||||
return SpecificUnitAutomation.automateGreatGeneralFallback(unit)
|
||||
|
||||
if (unit.civ.religionManager.maySpreadReligionAtAll(unit))
|
||||
return ReligiousUnitAutomation.automateMissionary(unit)
|
||||
|
||||
if (unit.hasUnique(UniqueType.PreventSpreadingReligion) || unit.canDoLimitedAction(Constants.removeHeresy))
|
||||
return ReligiousUnitAutomation.automateInquisitor(unit)
|
||||
|
||||
val isLateGame = isLateGame(unit.civ)
|
||||
// Great scientist -> Hurry research if late game
|
||||
if (isLateGame) {
|
||||
val hurriedResearch = UnitActions.invokeUnitAction(unit, UnitActionType.HurryResearch)
|
||||
if (hurriedResearch) return
|
||||
}
|
||||
|
||||
// Great merchant -> Conduct trade mission if late game and if not at war.
|
||||
// TODO: This could be more complex to walk to the city state that is most beneficial to
|
||||
// also have more influence.
|
||||
if (unit.hasUnique(UniqueType.CanTradeWithCityStateForGoldAndInfluence)
|
||||
// Don't wander around with the great merchant when at war. Barbs might also be a
|
||||
// problem, but hopefully by the time we have a great merchant, they're under control.
|
||||
&& !unit.civ.isAtWar()
|
||||
&& isLateGame
|
||||
) {
|
||||
val tradeMissionCanBeConductedEventually =
|
||||
SpecificUnitAutomation.conductTradeMission(unit)
|
||||
if (tradeMissionCanBeConductedEventually)
|
||||
return
|
||||
}
|
||||
|
||||
// Great engineer -> Try to speed up wonder construction if late game
|
||||
if (isLateGame &&
|
||||
(unit.hasUnique(UniqueType.CanSpeedupConstruction)
|
||||
|| unit.hasUnique(UniqueType.CanSpeedupWonderConstruction))) {
|
||||
val wonderCanBeSpedUpEventually = SpecificUnitAutomation.speedupWonderConstruction(unit)
|
||||
if (wonderCanBeSpedUpEventually)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// This has to come after the individual abilities for the great people that can also place
|
||||
// instant improvements (e.g. great scientist).
|
||||
if (unit.hasUnique(UniqueType.ConstructImprovementInstantly)
|
||||
) {
|
||||
// catch great prophet for civs who can't found/enhance/spread religion
|
||||
// includes great people plus moddable units
|
||||
val improvementCanBePlacedEventually =
|
||||
SpecificUnitAutomation.automateImprovementPlacer(unit)
|
||||
if (!improvementCanBePlacedEventually)
|
||||
UnitActions.invokeUnitAction(unit, UnitActionType.StartGoldenAge)
|
||||
}
|
||||
|
||||
// TODO: The AI tends to have a lot of great generals. Maybe there should be a cutoff
|
||||
// (depending on number of cities) and after that they should just be used to start golden
|
||||
// ages?
|
||||
|
||||
return // The AI doesn't know how to handle unknown civilian units
|
||||
}
|
||||
|
||||
private fun isLateGame(civ: Civilization): Boolean {
|
||||
val researchCompletePercent =
|
||||
(civ.tech.researchedTechnologies.size * 1.0f) / civ.gameInfo.ruleset.technologies.size
|
||||
return researchCompletePercent >= 0.8f
|
||||
}
|
||||
|
||||
/** @return true only if the unit has 0 movement left */
|
||||
private fun tryAttacking(unit: MapUnit): Boolean {
|
||||
@ -389,21 +264,7 @@ object UnitAutomation {
|
||||
|
||||
val currentUnitTile = unit.getTile()
|
||||
|
||||
val nearbyRangedEnemyUnits = unit.currentTile.getTilesInDistance(3)
|
||||
.flatMap { tile -> tile.getUnits().filter { unit.civ.isAtWarWith(it.civ) } }
|
||||
|
||||
val tilesInRangeOfAttack = nearbyRangedEnemyUnits
|
||||
.flatMap { it.getTile().getTilesInDistance(it.getRange()) }
|
||||
|
||||
val tilesWithinBombardmentRange = unit.currentTile.getTilesInDistance(3)
|
||||
.filter { it.isCityCenter() && it.getCity()!!.civ.isAtWarWith(unit.civ) }
|
||||
.flatMap { it.getTilesInDistance(it.getCity()!!.range) }
|
||||
|
||||
val tilesWithTerrainDamage = unit.currentTile.getTilesInDistance(3)
|
||||
.filter { unit.getDamageFromTerrain(it) > 0 }
|
||||
|
||||
val dangerousTiles = (tilesInRangeOfAttack + tilesWithinBombardmentRange + tilesWithTerrainDamage).toHashSet()
|
||||
|
||||
val dangerousTiles = getDangerousTiles(unit)
|
||||
|
||||
val viableTilesForHealing = unitDistanceToTiles.keys
|
||||
.filter { it !in dangerousTiles && unit.movement.canMoveTo(it) }
|
||||
@ -449,6 +310,23 @@ object UnitAutomation {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getDangerousTiles(unit: MapUnit): HashSet<Tile> {
|
||||
val nearbyRangedEnemyUnits = unit.currentTile.getTilesInDistance(3)
|
||||
.flatMap { tile -> tile.getUnits().filter { unit.civ.isAtWarWith(it.civ) } }
|
||||
|
||||
val tilesInRangeOfAttack = nearbyRangedEnemyUnits
|
||||
.flatMap { it.getTile().getTilesInDistance(it.getRange()) }
|
||||
|
||||
val tilesWithinBombardmentRange = unit.currentTile.getTilesInDistance(3)
|
||||
.filter { it.isCityCenter() && it.getCity()!!.civ.isAtWarWith(unit.civ) }
|
||||
.flatMap { it.getTilesInDistance(it.getCity()!!.range) }
|
||||
|
||||
val tilesWithTerrainDamage = unit.currentTile.getTilesInDistance(3)
|
||||
.filter { unit.getDamageFromTerrain(it) > 0 }
|
||||
|
||||
return (tilesInRangeOfAttack + tilesWithinBombardmentRange + tilesWithTerrainDamage).toHashSet()
|
||||
}
|
||||
|
||||
fun tryPillageImprovement(unit: MapUnit): Boolean {
|
||||
if (unit.isCivilian()) return false
|
||||
val unitDistanceToTiles = unit.movement.getDistanceToTiles()
|
||||
@ -720,7 +598,6 @@ object UnitAutomation {
|
||||
)
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
private fun tryGarrisoningRangedLandUnit(unit: MapUnit): Boolean {
|
||||
@ -731,14 +608,6 @@ object UnitAutomation {
|
||||
&& unit.movement.canMoveTo(centerTile)
|
||||
}
|
||||
|
||||
fun isCityThatNeedsDefendingInWartime(city: City): Boolean {
|
||||
if (city.health < city.getMaxHealth()) return true // this city is under attack!
|
||||
for (enemyCivCity in unit.civ.diplomacy.values
|
||||
.filter { it.diplomaticStatus == DiplomaticStatus.War }
|
||||
.map { it.otherCiv() }.flatMap { it.cities })
|
||||
if (city.getCenterTile().aerialDistanceTo(enemyCivCity.getCenterTile()) <= 5) return true // this is an edge city that needs defending
|
||||
return false
|
||||
}
|
||||
|
||||
val citiesToTry = if (!unit.civ.isAtWar()) {
|
||||
if (unit.getTile().isCityCenter()) return true // It's always good to have a unit in the city center, so if you haven't found anyone around to attack, forget it.
|
||||
@ -760,6 +629,15 @@ object UnitAutomation {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isCityThatNeedsDefendingInWartime(city: City): Boolean {
|
||||
if (city.health < city.getMaxHealth()) return true // this city is under attack!
|
||||
for (enemyCivCity in city.civ.diplomacy.values
|
||||
.filter { it.diplomaticStatus == DiplomaticStatus.War }
|
||||
.map { it.otherCiv() }.flatMap { it.cities })
|
||||
if (city.getCenterTile().aerialDistanceTo(enemyCivCity.getCenterTile()) <= 5) return true // this is an edge city that needs defending
|
||||
return false
|
||||
}
|
||||
|
||||
private fun tryStationingMeleeNavalUnit(unit: MapUnit): Boolean {
|
||||
fun isMeleeNaval(mapUnit: MapUnit) = mapUnit.baseUnit.isMelee() && mapUnit.type.isWaterUnit()
|
||||
|
||||
@ -799,54 +677,8 @@ object UnitAutomation {
|
||||
unit.action = null
|
||||
}
|
||||
|
||||
/** Returns whether the civilian spends its turn hiding and not moving */
|
||||
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
|
||||
val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys
|
||||
.filter { containsEnemyMilitaryUnit(unit, it) }
|
||||
|
||||
if (enemyUnitsInWalkingDistance.isNotEmpty() && !unit.baseUnit.isMilitary()
|
||||
&& unit.getTile().militaryUnit == null && !unit.getTile().isCityCenter()) {
|
||||
runAway(unit)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun runAway(unit: MapUnit) {
|
||||
val reachableTiles = unit.movement.getDistanceToTiles()
|
||||
val enterableCity = reachableTiles.keys
|
||||
.firstOrNull { it.isCityCenter() && unit.movement.canMoveTo(it) }
|
||||
if (enterableCity != null) {
|
||||
unit.movement.moveToTile(enterableCity)
|
||||
return
|
||||
}
|
||||
val defensiveUnit = reachableTiles.keys
|
||||
.firstOrNull {
|
||||
it.militaryUnit != null && it.militaryUnit!!.civ == unit.civ && it.civilianUnit == null
|
||||
}
|
||||
if (defensiveUnit != null) {
|
||||
unit.movement.moveToTile(defensiveUnit)
|
||||
return
|
||||
}
|
||||
val tileFurthestFromEnemy = reachableTiles.keys
|
||||
.filter { unit.movement.canMoveTo(it) && unit.getDamageFromTerrain(it) < unit.health }
|
||||
.maxByOrNull { countDistanceToClosestEnemy(unit, it) }
|
||||
?: return // can't move anywhere!
|
||||
unit.movement.moveToTile(tileFurthestFromEnemy)
|
||||
}
|
||||
|
||||
|
||||
private fun countDistanceToClosestEnemy(unit: MapUnit, tile: Tile): Int {
|
||||
for (i in 1..3)
|
||||
if (tile.getTilesAtDistance(i).any { containsEnemyMilitaryUnit(unit, it) })
|
||||
return i
|
||||
return 4
|
||||
}
|
||||
|
||||
private fun containsEnemyMilitaryUnit(unit: MapUnit, tile: Tile) =
|
||||
internal fun containsEnemyMilitaryUnit(unit: MapUnit, tile: Tile) =
|
||||
tile.militaryUnit != null
|
||||
&& tile.militaryUnit!!.civ.isAtWarWith(unit.civ)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user