Conquering a city destroys buildings inside the city (#4995)

* When conquering a city, some buildings are now destroyed

* Added missing translation for uniques
This commit is contained in:
Xander Lenstra
2021-08-26 16:09:55 +02:00
committed by GitHub
parent 59f8a0eebf
commit 86d1f143d9
4 changed files with 112 additions and 76 deletions

View File

@ -18,7 +18,8 @@
"culture": 2,
"cost": 40,
"hurryCostModifier": 40,
"maintenance": 1
"maintenance": 1,
"uniques": ["Destroyed when the city is captured"]
},
// Column 1
{
@ -128,7 +129,8 @@
"cityStrength": 5,
"cityHealth": 50,
"hurryCostModifier": 25,
"requiredTech": "Masonry"
"requiredTech": "Masonry",
"uniques": ["Destroyed when the city is captured"]
},
{
"name": "Walls of Babylon",
@ -138,7 +140,8 @@
"cityStrength": 6,
"cityHealth": 100,
"hurryCostModifier": 25,
"requiredTech": "Masonry"
"requiredTech": "Masonry",
"uniques": ["Destroyed when the city is captured"]
},
{
"name": "The Pyramids",
@ -163,7 +166,7 @@
"name": "Barracks",
"hurryCostModifier": 25,
"maintenance": 1,
"uniques": ["New [Military] units start with [15] Experience [in this city]"]
"uniques": ["New [Military] units start with [15] Experience [in this city]", "Destroyed when the city is captured"],
"requiredTech": "Bronze Working"
},
{
@ -173,7 +176,7 @@
"hurryCostModifier": 25,
"maintenance": 1,
"uniques": ["-[25]% Culture cost of acquiring tiles [in this city]","-[25]% Gold cost of acquiring tiles [in this city]",
"New [Military] units start with [15] Experience [in this city]"],
"New [Military] units start with [15] Experience [in this city]", "Destroyed when the city is captured"],
"requiredTech": "Bronze Working"
},
{
@ -270,7 +273,7 @@
"maintenance": 2,
"hurryCostModifier": 25,
"requiredTech": "Philosophy",
"uniques": ["Hidden when religion is disabled"]
"uniques": ["Hidden when religion is disabled", "Destroyed when the city is captured"]
},
{
"name": "National College",
@ -570,8 +573,8 @@
"cityHealth": 25,
"hurryCostModifier": 25,
"requiredBuilding": "Walls",
"requiredTech": "Chivalry"
// 4 cityStrength in vanilla, no ExtraCityHitPoints in vanilla
"requiredTech": "Chivalry",
"uniques": ["Destroyed when the city is captured"]
},
{
"name": "Mughal Fort",
@ -583,7 +586,7 @@
"culture": 2,
"hurryCostModifier": 25,
"requiredBuilding": "Walls",
"uniques": ["[+1 Gold] once [Flight] is discovered"],
"uniques": ["[+1 Gold] once [Flight] is discovered", "Destroyed when the city is captured"],
"requiredTech": "Chivalry"
},
{
@ -620,7 +623,7 @@
"hurryCostModifier": 25,
"maintenance": 1,
"requiredBuilding": "Barracks",
"uniques": ["New [Military] units start with [15] Experience [in this city]"]
"uniques": ["New [Military] units start with [15] Experience [in this city]", "Destroyed when the city is captured"],
"requiredTech": "Steel"
},
@ -643,7 +646,8 @@
"hurryCostModifier": 10,
"requiredBuilding": "Amphitheater",
"maintenance": 2,
"requiredTech": "Acoustics"
"requiredTech": "Acoustics",
"uniques": ["Destroyed when the city is captured"]
},
{
"name": "Sistine Chapel",
@ -800,7 +804,8 @@
"requiredBuilding": "Opera House",
"maintenance": 3,
"hurryCostModifier": 0,
"requiredTech": "Archaeology"
"requiredTech": "Archaeology",
"uniques": ["Destroyed when the city is captured"]
},
{
"name": "The Louvre",
@ -848,7 +853,7 @@
"hurryCostModifier": 0,
"maintenance": 1,
"requiredBuilding": "Armory",
"uniques": ["New [Military] units start with [15] Experience [in this city]"],
"uniques": ["New [Military] units start with [15] Experience [in this city]", "Destroyed when the city is captured"],
"requiredTech": "Military Science"
},
{
@ -905,7 +910,8 @@
"percentStatBonus": {"culture": 33},
"requiredBuilding": "Museum",
"maintenance": 3,
"requiredTech": "Radio"
"requiredTech": "Radio",
"uniques": ["Destroyed when the city is captured"]
},
{
"name": "Eiffel Tower",
@ -923,7 +929,8 @@
"cityHealth": 25,
"hurryCostModifier": 25,
"requiredBuilding": "Arsenal",
"requiredTech": "Replaceable Parts"
"requiredTech": "Replaceable Parts",
"uniques": ["Destroyed when the city is captured"]
},
{
"name": "Statue of Liberty",
@ -1134,7 +1141,7 @@
"faith": 2,
"uniques": ["[+1 Culture, +1 Faith] from [Wine] tiles [in this city]",
"[+1 Culture, +1 Faith] from [Incense] tiles [in this city]",
"Unbuildable", "Hidden when religion is disabled"]
"Unbuildable", "Hidden when religion is disabled", "Destroyed when the city is captured"]
},
{
"name": "Mosque",

View File

@ -1166,3 +1166,4 @@ in all cities with a garrison =
Only available after [] turns =
This Unit upgrades for free =
[stats] when a city adopts this religion for the first time =
Never destroyed when the city is captured =

View File

@ -15,15 +15,66 @@ import java.util.*
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.random.Random
/** Helper class for containing 200 lines of "how to move cities between civs" */
class CityInfoConquestFunctions(val city: CityInfo){
fun annexCity() {
city.isPuppet = false
city.cityConstructions.inProgressConstructions.clear() // undo all progress of the previous civ on units etc.
city.cityStats.update()
if (!UncivGame.Current.consoleMode)
UncivGame.Current.worldScreen.shouldUpdate = true
private val tileBasedRandom = Random(city.getCenterTile().position.toString().hashCode())
private fun getGoldForCapturingCity(conqueringCiv: CivilizationInfo): Int {
val baseGold = 20 + 10 * city.population.population + tileBasedRandom.nextInt(40)
val turnModifier = max(0, min(50, city.civInfo.gameInfo.turns - city.turnAcquired)) / 50f
val cityModifier = if (city.containsBuildingUnique("Doubles Gold given to enemy if city is captured")) 2f else 1f
val conqueringCivModifier = if (conqueringCiv.hasUnique("Receive triple Gold from Barbarian encampments and pillaging Cities")) 3f else 1f
val goldPlundered = baseGold * turnModifier * cityModifier * conqueringCivModifier
return goldPlundered.toInt()
}
private fun destroyBuildingsOnCapture() {
city.apply {
// Remove all national wonders (must come after the palace relocation because that's a national wonder too!)
for (building in cityConstructions.getBuiltBuildings()) {
when {
building.hasUnique("Never destroyed when the city is captured") -> continue
building.hasUnique("Destroyed when the city is captured") ->
cityConstructions.removeBuilding(building.name)
else -> {
if (tileBasedRandom.nextInt(100) < 34) {
cityConstructions.removeBuilding(building.name)
}
}
}
}
}
}
/** Function for stuff that should happen on any capture, be it puppet, annex or liberate.
* Stuff that should happen any time a city is moved between civs, so also when trading,
* should go in `this.moveToCiv()`, which this function also calls.
*/
private fun conquerCity(conqueringCiv: CivilizationInfo, conqueredCiv: CivilizationInfo, receivingCiv: CivilizationInfo) {
val goldPlundered = getGoldForCapturingCity(conqueringCiv)
city.apply {
conqueringCiv.addGold(goldPlundered)
conqueringCiv.addNotification("Received [$goldPlundered] Gold for capturing [$name]", getCenterTile().position, NotificationIcon.Gold)
val reconqueredCityWhileStillInResistance = previousOwner == conqueringCiv.civName && resistanceCounter != 0
this@CityInfoConquestFunctions.moveToCiv(receivingCiv)
destroyBuildingsOnCapture()
Battle.destroyIfDefeated(conqueredCiv, conqueringCiv)
health = getMaxHealth() / 2 // I think that cities recover to half health when conquered?
if (population.population > 1) population.addPopulation(-1 - population.population / 4) // so from 2-4 population, remove 1, from 5-8, remove 2, etc.
reassignPopulation()
resistanceCounter =
if (reconqueredCityWhileStillInResistance || foundingCiv == receivingCiv.civName) 0
else population.population // I checked, and even if you puppet there's resistance for conquering
}
}
@ -32,29 +83,16 @@ class CityInfoConquestFunctions(val city: CityInfo){
// Gain gold for plundering city
val goldPlundered = getGoldForCapturingCity(conqueringCiv)
city.apply {
conqueringCiv.addGold(goldPlundered)
conqueringCiv.addNotification("Received [$goldPlundered] Gold for capturing [$name]", getCenterTile().position, NotificationIcon.Gold)
val oldCiv = civInfo
val reconqueredCityWhileStillInResistance = previousOwner == conqueringCiv.civName && resistanceCounter != 0
previousOwner = oldCiv.civName
// must be before moving the city to the conquering civ,
// so the repercussions are properly checked
diplomaticRepercussionsForConqueringCity(oldCiv, conqueringCiv)
moveToCiv(conqueringCiv)
Battle.destroyIfDefeated(oldCiv, conqueringCiv)
if (population.population > 1) population.addPopulation(-1 - population.population / 4) // so from 2-4 population, remove 1, from 5-8, remove 2, etc.
reassignPopulation()
if (reconqueredCityWhileStillInResistance || foundingCiv == conqueringCiv.civName)
resistanceCounter = 0
else resistanceCounter = population.population // I checked, and even if you puppet there's resistance for conquering
conquerCity(conqueringCiv, oldCiv, conqueringCiv)
isPuppet = true
health = getMaxHealth() / 2 // I think that cities recover to half health when conquered?
cityStats.update()
// The city could be producing something that puppets shouldn't, like units
cityConstructions.currentConstructionIsUserSet = false
@ -63,18 +101,14 @@ class CityInfoConquestFunctions(val city: CityInfo){
}
}
fun getGoldForCapturingCity(conqueringCiv: CivilizationInfo): Int {
val baseGold = 20 + 10 * city.population.population + Random().nextInt(40)
val turnModifier = max(0, min(50, city.civInfo.gameInfo.turns - city.turnAcquired)) / 50f
val cityModifier = if (city.containsBuildingUnique("Doubles Gold given to enemy if city is captured")) 2f else 1f
val conqueringCivModifier = if (conqueringCiv.hasUnique("Receive triple Gold from Barbarian encampments and pillaging Cities")) 3f else 1f
val goldPlundered = baseGold * turnModifier * cityModifier * conqueringCivModifier
return goldPlundered.toInt()
fun annexCity() {
city.isPuppet = false
city.cityConstructions.inProgressConstructions.clear() // undo all progress of the previous civ on units etc.
city.cityStats.update()
if (!UncivGame.Current.consoleMode)
UncivGame.Current.worldScreen.shouldUpdate = true
}
private fun diplomaticRepercussionsForConqueringCity(oldCiv: CivilizationInfo, conqueringCiv: CivilizationInfo) {
val currentPopulation = city.population.population
val percentageOfCivPopulationInThatCity = currentPopulation * 100f /
@ -100,32 +134,24 @@ class CityInfoConquestFunctions(val city: CityInfo){
}
fun liberateCity(conqueringCiv: CivilizationInfo) {
val goldPlundered = getGoldForCapturingCity(conqueringCiv)
city.apply {
if (foundingCiv == "") { // this should never happen but just in case...
puppetCity(conqueringCiv)
annexCity()
this@CityInfoConquestFunctions.puppetCity(conqueringCiv)
this@CityInfoConquestFunctions.annexCity()
return
}
conqueringCiv.addGold(goldPlundered)
conqueringCiv.addNotification("Received [$goldPlundered] Gold for capturing [$name]", getCenterTile().position, NotificationIcon.Gold)
val oldCiv = civInfo
val foundingCiv = civInfo.gameInfo.civilizations.first { it.civName == foundingCiv }
if (foundingCiv.isDefeated()) // resurrected civ
for (diploManager in foundingCiv.diplomacy.values)
if (diploManager.diplomaticStatus == DiplomaticStatus.War)
diploManager.makePeace()
diplomaticRepercussionsForLiberatingCity(conqueringCiv)
moveToCiv(foundingCiv)
Battle.destroyIfDefeated(oldCiv, conqueringCiv)
health = getMaxHealth() / 2 // I think that cities recover to half health when conquered?
if (population.population > 1) population.addPopulation(-1 - population.population / 4) // so from 2-4 population, remove 1, from 5-8, remove 2, etc.
reassignPopulation()
val oldCiv = civInfo
diplomaticRepercussionsForLiberatingCity(conqueringCiv, oldCiv)
conquerCity(conqueringCiv, oldCiv, foundingCiv)
if (foundingCiv.cities.size == 1) cityConstructions.addBuilding(capitalCityIndicator()) // Resurrection!
isPuppet = false
@ -141,12 +167,11 @@ class CityInfoConquestFunctions(val city: CityInfo){
}
private fun diplomaticRepercussionsForLiberatingCity(conqueringCiv: CivilizationInfo) {
val oldOwningCiv = city.civInfo
val foundingCiv = oldOwningCiv.gameInfo.civilizations.first { it.civName == city.foundingCiv }
private fun diplomaticRepercussionsForLiberatingCity(conqueringCiv: CivilizationInfo, conqueredCiv: CivilizationInfo) {
val foundingCiv = conqueredCiv.gameInfo.civilizations.first { it.civName == city.foundingCiv }
val percentageOfCivPopulationInThatCity = city.population.population *
100f / (foundingCiv.cities.sumBy { it.population.population } + city.population.population)
val respecForLiberatingOurCity = 10f + percentageOfCivPopulationInThatCity.roundToInt()
val respectForLiberatingOurCity = 10f + percentageOfCivPopulationInThatCity.roundToInt()
// In order to get "plus points" in Diplomacy, you have to establish diplomatic relations if you haven't yet
if (!conqueringCiv.knows(foundingCiv))
@ -154,7 +179,7 @@ class CityInfoConquestFunctions(val city: CityInfo){
if (foundingCiv.isMajorCiv()) {
foundingCiv.getDiplomacyManager(conqueringCiv)
.addModifier(DiplomaticModifiers.CapturedOurCities, respecForLiberatingOurCity)
.addModifier(DiplomaticModifiers.CapturedOurCities, respectForLiberatingOurCity)
} else {
//Liberating a city state gives a large amount of influence, and peace
foundingCiv.getDiplomacyManager(conqueringCiv).influence = 90f
@ -166,8 +191,8 @@ class CityInfoConquestFunctions(val city: CityInfo){
}
}
val otherCivsRespecForLiberating = (respecForLiberatingOurCity / 10).roundToInt().toFloat()
for (thirdPartyCiv in conqueringCiv.getKnownCivs().filter { it.isMajorCiv() && it != oldOwningCiv }) {
val otherCivsRespecForLiberating = (respectForLiberatingOurCity / 10).roundToInt().toFloat()
for (thirdPartyCiv in conqueringCiv.getKnownCivs().filter { it.isMajorCiv() && it != conqueredCiv }) {
thirdPartyCiv.getDiplomacyManager(conqueringCiv)
.addModifier(DiplomaticModifiers.LiberatedCity, otherCivsRespecForLiberating) // Cool, keep at at! =D
}
@ -183,6 +208,7 @@ class CityInfoConquestFunctions(val city: CityInfo){
if (isOriginalCapital) civInfo.hasEverOwnedOriginalCapital = true
hasJustBeenConquered = false
turnAcquired = civInfo.gameInfo.turns
previousOwner = oldCiv.civName
// now that the tiles have changed, we need to reassign population
for (it in workedTiles.filterNot { tiles.contains(it) }) {
@ -199,13 +225,12 @@ class CityInfoConquestFunctions(val city: CityInfo){
}
}
for (building in cityConstructions.getBuiltBuildings()) {
if (building.isNationalWonder && !building.hasUnique("Never destroyed when the city is captured"))
cityConstructions.removeBuilding(building.name)
}
// Remove all national wonders (must come after the palace relocation because that's a national wonder too!)
for (building in cityConstructions.getBuiltBuildings().filter { it.isNationalWonder })
cityConstructions.removeBuilding(building.name)
// Locate palace for newCiv if this is the only city they have
// Place palace for newCiv if this is the only city they have
if (newCivInfo.cities.count() == 1) {
cityConstructions.addBuilding(capitalCityIndicator)
}

View File

@ -27,6 +27,9 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
fun getMatchingUniques(uniqueTemplate: String): Sequence<Unique> {
return uniqueObjects.asSequence().filter { it.placeholderText == uniqueTemplate }
}
fun hasUnique(uniqueTemplate: String): Boolean {
return uniqueObjects.any { it.placeholderText == uniqueTemplate }
}
fun canBePurchasedWithStat(cityInfo: CityInfo, stat: Stat, ignoreCityRequirements: Boolean = false): Boolean {
if (stat in listOf(Stat.Production, Stat.Happiness)) return false