Add May not annex cities unique (#9314)

* Add `May not annex cities` unique, following same behaviour as Civ5 Venice. Hopefully this time I won't need to re-install windows.

* Add `May not annex cities` unique, following same behaviour as Civ5 Venice. Hopefully this time I won't need to re-install windows.

* Fix raze button being unavailable in city screen

* Fixed raze button being available in city screen, as per civ5

* Corrected indentation

* mayAnnex instead of canAnnex, hasUnique rather than getMatchingUniques

* AI will follow rules

* Replaced getMatchingUniques with hasUnique, for real this time
This commit is contained in:
Skekdog
2023-05-09 21:02:19 +01:00
committed by GitHub
parent 203477eca1
commit a3ef6fa314
8 changed files with 53 additions and 21 deletions

View File

@ -1411,6 +1411,7 @@ What would you like to do with the city of [cityName]? =
Annex = Annex =
Annexed cities become part of your regular empire. = Annexed cities become part of your regular empire. =
Their citizens generate 2x the unhappiness, unless you build a courthouse. = Their citizens generate 2x the unhappiness, unless you build a courthouse. =
Your civilization may not annex this city. =
Puppet = Puppet =
Puppeted cities do not increase your tech or policy cost. = Puppeted cities do not increase your tech or policy cost. =
You have no control over the the production of puppeted cities. = You have no control over the the production of puppeted cities. =
@ -1420,6 +1421,7 @@ Liberate (city returns to [originalOwner]) =
Liberating a city returns it to its original owner, giving you a massive relationship boost with them! = Liberating a city returns it to its original owner, giving you a massive relationship boost with them! =
Raze = Raze =
Razing the city annexes it, and starts burning the city to the ground. = Razing the city annexes it, and starts burning the city to the ground. =
Razing the city puppets it, and starts burning the city to the ground. =
The population will gradually dwindle until the city is destroyed. = The population will gradually dwindle until the city is destroyed. =
Original capitals and holy cities cannot be razed. = Original capitals and holy cities cannot be razed. =
Destroy = Destroy =

View File

@ -1016,7 +1016,7 @@ object NextTurnAutomation {
ownMilitaryStrength < sumOfEnemiesMilitaryStrength * 0.66f ownMilitaryStrength < sumOfEnemiesMilitaryStrength * 0.66f
for (city in civInfo.cities) { for (city in civInfo.cities) {
if (city.isPuppet && city.population.population > 9 if (city.isPuppet && city.population.population > 9
&& !city.isInResistance() && !city.isInResistance() && !civInfo.hasUnique(UniqueType.MayNotAnnexCities)
) { ) {
city.annexCity() city.annexCity()
} }
@ -1124,7 +1124,7 @@ object NextTurnAutomation {
if ((city.population.population < 4 || civInfo.isCityState()) if ((city.population.population < 4 || civInfo.isCityState())
&& city.foundingCiv != civInfo.civName && city.canBeDestroyed(justCaptured = true)) { && city.foundingCiv != civInfo.civName && city.canBeDestroyed(justCaptured = true)) {
// raze if attacker is a city state // raze if attacker is a city state
city.annexCity() if (!civInfo.hasUnique(UniqueType.MayNotAnnexCities)) { city.annexCity() }
city.isBeingRazed = true city.isBeingRazed = true
} }
} }

View File

@ -595,6 +595,7 @@ object Battle {
if (city.isOriginalCapital && city.foundingCiv == attackerCiv.civName) { if (city.isOriginalCapital && city.foundingCiv == attackerCiv.civName) {
// retaking old capital // retaking old capital
city.puppetCity(attackerCiv) city.puppetCity(attackerCiv)
//Although in Civ5 Venice is unable to re-annex their capital, that seems a bit silly. No check for May not annex cities here.
city.annexCity() city.annexCity()
} else if (attackerCiv.isHuman()) { } else if (attackerCiv.isHuman()) {
// we're not taking our former capital // we're not taking our former capital

View File

@ -315,6 +315,13 @@ class CityInfoConquestFunctions(val city: City){
if (civ.gameInfo.isReligionEnabled()) religion.removeUnknownPantheons() if (civ.gameInfo.isReligionEnabled()) religion.removeUnknownPantheons()
if (newCiv.hasUnique(UniqueType.MayNotAnnexCities)) {
isPuppet = true
cityConstructions.currentConstructionIsUserSet = false
cityConstructions.constructionQueue.clear()
cityConstructions.chooseNextConstruction()
}
tryUpdateRoadStatus() tryUpdateRoadStatus()
cityStats.update() cityStats.update()

View File

@ -239,6 +239,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
GainInfluenceWithUnitGiftToCityState("Gain [amount] Influence with a [baseUnitFilter] gift to a City-State", UniqueTarget.Global), GainInfluenceWithUnitGiftToCityState("Gain [amount] Influence with a [baseUnitFilter] gift to a City-State", UniqueTarget.Global),
FaithCostOfGreatProphetChange("[relativeAmount]% Faith cost of generating Great Prophet equivalents", UniqueTarget.Global), FaithCostOfGreatProphetChange("[relativeAmount]% Faith cost of generating Great Prophet equivalents", UniqueTarget.Global),
RestingPointOfCityStatesFollowingReligionChange("Resting point for Influence with City-States following this religion [amount]", UniqueTarget.Global), RestingPointOfCityStatesFollowingReligionChange("Resting point for Influence with City-States following this religion [amount]", UniqueTarget.Global),
MayNotAnnexCities("May not annex cities", UniqueTarget.Nation),
ProvidesGoldWheneverGreatPersonExpended("Provides a sum of gold each time you spend a Great Person", UniqueTarget.Global), ProvidesGoldWheneverGreatPersonExpended("Provides a sum of gold each time you spend a Great Person", UniqueTarget.Global),
ProvidesStatsWheneverGreatPersonExpended("[stats] whenever a Great Person is expended", UniqueTarget.Global), ProvidesStatsWheneverGreatPersonExpended("[stats] whenever a Great Person is expended", UniqueTarget.Global),

View File

@ -259,7 +259,8 @@ class CityScreen(
addWltkIcon("OtherIcons/WLTK 1") { color = Color.FIREBRICK }.padRight(10f) addWltkIcon("OtherIcons/WLTK 1") { color = Color.FIREBRICK }.padRight(10f)
} }
if (city.isPuppet) { val canAnnex = !city.civ.hasUnique(UniqueType.MayNotAnnexCities)
if (city.isPuppet && canAnnex) {
val annexCityButton = "Annex city".toTextButton() val annexCityButton = "Annex city".toTextButton()
annexCityButton.labelCell.pad(10f) annexCityButton.labelCell.pad(10f)
annexCityButton.onClick { annexCityButton.onClick {
@ -272,8 +273,9 @@ class CityScreen(
val razeCityButton = "Raze city".toTextButton() val razeCityButton = "Raze city".toTextButton()
razeCityButton.labelCell.pad(10f) razeCityButton.labelCell.pad(10f)
razeCityButton.onClick { city.isBeingRazed = true; update() } razeCityButton.onClick { city.isBeingRazed = true; update() }
if (!canChangeState || !city.canBeDestroyed()) if (!canChangeState || !city.canBeDestroyed() || !canAnnex) {
razeCityButton.disable() razeCityButton.disable()
}
razeCityButtonHolder.add(razeCityButton) //.colspan(cityPickerTable.columns) razeCityButtonHolder.add(razeCityButton) //.colspan(cityPickerTable.columns)
} else { } else {

View File

@ -14,6 +14,7 @@ import com.unciv.logic.civilization.NotificationCategory
import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.PopupAlert
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.translations.fillPlaceholders import com.unciv.models.translations.fillPlaceholders
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.audio.MusicMood import com.unciv.ui.audio.MusicMood
@ -146,7 +147,8 @@ class AlertPopup(
close() close()
} }
} else { } else {
addAnnexOption { val mayAnnex = !conqueringCiv.hasUnique(UniqueType.MayNotAnnexCities)
addAnnexOption(mayAnnex = mayAnnex) {
city.puppetCity(conqueringCiv) city.puppetCity(conqueringCiv)
city.annexCity() city.annexCity()
worldScreen.shouldUpdate = true worldScreen.shouldUpdate = true
@ -154,16 +156,16 @@ class AlertPopup(
} }
addSeparator() addSeparator()
addPuppetOption { addPuppetOption(mayAnnex = mayAnnex) {
city.puppetCity(conqueringCiv) city.puppetCity(conqueringCiv)
worldScreen.shouldUpdate = true worldScreen.shouldUpdate = true
close() close()
} }
addSeparator() addSeparator()
addRazeOption(canRaze = { city.canBeDestroyed(justCaptured = true) } ) { addRazeOption(canRaze = city.canBeDestroyed(justCaptured = true), mayAnnex = mayAnnex) {
city.puppetCity(conqueringCiv) city.puppetCity(conqueringCiv)
city.annexCity() if (mayAnnex) { city.annexCity() }
city.isBeingRazed = true city.isBeingRazed = true
worldScreen.shouldUpdate = true worldScreen.shouldUpdate = true
close() close()
@ -239,13 +241,14 @@ class AlertPopup(
close() close()
} }
} else { } else {
addAnnexOption { val mayAnnex = !marryingCiv.hasUnique(UniqueType.MayNotAnnexCities)
addAnnexOption(mayAnnex) {
city.annexCity() city.annexCity()
close() close()
} }
addSeparator() addSeparator()
addPuppetOption { addPuppetOption(mayAnnex) {
city.isPuppet = true city.isPuppet = true
city.cityStats.update() city.cityStats.update()
worldScreen.shouldUpdate = true worldScreen.shouldUpdate = true
@ -428,16 +431,25 @@ class AlertPopup(
addGoodSizedLabel("Destroying the city instantly razes the city to the ground.").row() addGoodSizedLabel("Destroying the city instantly razes the city to the ground.").row()
} }
private fun addAnnexOption(annexAction: () -> Unit) { private fun addAnnexOption(mayAnnex: Boolean, annexAction: () -> Unit) {
val button = "Annex".toTextButton() val button = "Annex".toTextButton()
button.onActivation { annexAction() } button.apply {
button.keyShortcuts.add('a') if (!mayAnnex) disable() else {
button.onActivation { annexAction() }
button.keyShortcuts.add('a')
}
}
add(button).row() add(button).row()
addGoodSizedLabel("Annexed cities become part of your regular empire.").row() if (mayAnnex) {
addGoodSizedLabel("Their citizens generate 2x the unhappiness, unless you build a courthouse.").row() addGoodSizedLabel("Annexed cities become part of your regular empire.").row()
addGoodSizedLabel("Their citizens generate 2x the unhappiness, unless you build a courthouse.").row()
} else {
addGoodSizedLabel("Your civilization may not annex this city.").row()
}
} }
private fun addPuppetOption(puppetAction: () -> Unit) { private fun addPuppetOption(mayAnnex: Boolean, puppetAction: () -> Unit) {
val button = "Puppet".toTextButton() val button = "Puppet".toTextButton()
button.onActivation { puppetAction() } button.onActivation { puppetAction() }
button.keyShortcuts.add('p') button.keyShortcuts.add('p')
@ -445,7 +457,7 @@ class AlertPopup(
addGoodSizedLabel("Puppeted cities do not increase your tech or policy cost.").row() addGoodSizedLabel("Puppeted cities do not increase your tech or policy cost.").row()
addGoodSizedLabel("You have no control over the the production of puppeted cities.").row() addGoodSizedLabel("You have no control over the the production of puppeted cities.").row()
addGoodSizedLabel("Puppeted cities also generate 25% less Gold and Science.").row() addGoodSizedLabel("Puppeted cities also generate 25% less Gold and Science.").row()
addGoodSizedLabel("A puppeted city can be annexed at any time.").row() if (mayAnnex) addGoodSizedLabel("A puppeted city can be annexed at any time.").row()
} }
private fun addLiberateOption(foundingCiv: String, liberateAction: () -> Unit) { private fun addLiberateOption(foundingCiv: String, liberateAction: () -> Unit) {
@ -456,18 +468,22 @@ class AlertPopup(
addGoodSizedLabel("Liberating a city returns it to its original owner, giving you a massive relationship boost with them!") addGoodSizedLabel("Liberating a city returns it to its original owner, giving you a massive relationship boost with them!")
} }
private fun addRazeOption(canRaze: () -> Boolean, razeAction: () -> Unit) { private fun addRazeOption(canRaze: Boolean, mayAnnex: Boolean, razeAction: () -> Unit) {
val button = "Raze".toTextButton() val button = "Raze".toTextButton()
button.apply { button.apply {
if (!canRaze()) disable() if (!canRaze) disable()
else { else {
onActivation { razeAction() } onActivation { razeAction() }
keyShortcuts.add('r') keyShortcuts.add('r')
} }
} }
add(button).row() add(button).row()
if (canRaze()) { if (canRaze) {
addGoodSizedLabel("Razing the city annexes it, and starts burning the city to the ground.").row() if (mayAnnex) {
addGoodSizedLabel("Razing the city annexes it, and starts burning the city to the ground.").row()
} else {
addGoodSizedLabel("Razing the city puppets it, and starts burning the city to the ground.").row()
}
addGoodSizedLabel("The population will gradually dwindle until the city is destroyed.").row() addGoodSizedLabel("The population will gradually dwindle until the city is destroyed.").row()
} else { } else {
addGoodSizedLabel("Original capitals and holy cities cannot be razed.").row() addGoodSizedLabel("Original capitals and holy cities cannot be razed.").row()

View File

@ -863,6 +863,9 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
??? example "Will not be chosen for new games" ??? example "Will not be chosen for new games"
Applicable to: Nation Applicable to: Nation
??? example "May not annex cities"
Applicable to: Nation
??? example "Starts with [tech]" ??? example "Starts with [tech]"
Example: "Starts with [Agriculture]" Example: "Starts with [Agriculture]"