AI considers liberating city-states from other civilizations (#7157)

* AI will consider liberating a city state on conquest

* Factor in current civ happiness for liberation consideration

* Refactor foundingCiv check into function
This commit is contained in:
OptimizedForDensity 2022-06-16 14:12:26 -04:00 committed by GitHub
parent 4fb2ad8fab
commit 623b84ea30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 19 deletions

View File

@ -299,8 +299,6 @@ object NextTurnAutomation {
private fun valueCityStateAlliance(civInfo: CivilizationInfo, cityState: CivilizationInfo): Int {
var value = 0
if (!cityState.isAlive() || cityState.cities.isEmpty() || civInfo.cities.isEmpty())
return value
if (civInfo.wantsToFocusOn(Victory.Focus.Culture) && cityState.canGiveStat(Stat.Culture)) {
value += 10
@ -310,24 +308,33 @@ object NextTurnAutomation {
value += 10
}
else if (civInfo.wantsToFocusOn(Victory.Focus.Military)) {
// Don't ally close city-states, conquer them instead
val distance = getMinDistanceBetweenCities(civInfo, cityState)
if (distance < 20)
value -= (20 - distance) / 4
if (!cityState.isAlive())
value -= 5
else {
// Don't ally close city-states, conquer them instead
val distance = getMinDistanceBetweenCities(civInfo, cityState)
if (distance < 20)
value -= (20 - distance) / 4
}
}
else if (civInfo.wantsToFocusOn(Victory.Focus.CityStates)) {
value += 5 // Generally be friendly
}
if (civInfo.gold < 100) {
// Consider bullying for cash
value -= 5
}
if (civInfo.getHappiness() < 5 && cityState.canGiveStat(Stat.Happiness)) {
value += 10 - civInfo.getHappiness()
}
if (civInfo.getHappiness() > 5 && cityState.canGiveStat(Stat.Food)) {
value += 5
}
if (!cityState.isAlive() || cityState.cities.isEmpty() || civInfo.cities.isEmpty())
return value
if (civInfo.gold < 100) {
// Consider bullying for cash
value -= 5
}
if (cityState.getAllyCiv() != null && cityState.getAllyCiv() != civInfo.civName) {
// easier not to compete if a third civ has this locked down
val thirdCivInfluence = cityState.getDiplomacyManager(cityState.getAllyCiv()!!).getInfluence().toInt()
@ -936,6 +943,31 @@ object NextTurnAutomation {
diplomacyManager.removeFlag(DiplomacyFlags.SettledCitiesNearUs)
}
/** Handle decision making after city conquest, namely whether the AI should liberate, puppet,
* or raze a city */
fun onConquerCity(civInfo: CivilizationInfo, city: CityInfo) {
if (!city.hasDiplomaticMarriage()) {
val foundingCiv = civInfo.gameInfo.getCivilization(city.foundingCiv)
var valueAlliance = valueCityStateAlliance(civInfo, foundingCiv)
if (civInfo.getHappiness() < 0)
valueAlliance -= civInfo.getHappiness() // put extra weight on liberating if unhappy
if (foundingCiv.isCityState() && city.civInfo != civInfo && foundingCiv != civInfo
&& !civInfo.isAtWarWith(foundingCiv)
&& valueAlliance > 0) {
city.liberateCity(civInfo)
return
}
}
city.puppetCity(civInfo)
if ((city.population.population < 4 || civInfo.isCityState())
&& city.foundingCiv != civInfo.civName && city.canBeDestroyed(justCaptured = true)) {
// raze if attacker is a city state
city.annexCity()
city.isBeingRazed = true
}
}
fun getMinDistanceBetweenCities(civ1: CivilizationInfo, civ2: CivilizationInfo): Int {
return getClosestCities(civ1, civ2).aerialDistance
}

View File

@ -3,6 +3,7 @@ package com.unciv.logic.battle
import com.badlogic.gdx.math.Vector2
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.automation.NextTurnAutomation
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.CivilizationInfo
@ -540,15 +541,7 @@ object Battle {
// we're not taking our former capital
attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered, city.id))
} else {
// ideally here we would do some AI thinking for liberation vs. razing
// e.g., valueCityStateAlliance() > 0 to determine if we should liberate a city-state
city.puppetCity(attackerCiv)
if ((city.population.population < 4 || attackerCiv.isCityState())
&& city.canBeDestroyed(justCaptured = true)) {
// raze if attacker is a city state
city.annexCity()
city.isBeingRazed = true
}
NextTurnAutomation.onConquerCity(attackerCiv, city)
}
if (attackerCiv.isPlayerCivilization())

View File

@ -145,6 +145,8 @@ class CityInfo {
private var flagsCountdown = HashMap<String, Int>()
fun hasDiplomaticMarriage(): Boolean = foundingCiv == ""
constructor() // for json parsing, we need to have a default constructor
constructor(civInfo: CivilizationInfo, cityLocation: Vector2) { // new city!
this.civInfo = civInfo