From 0233aa1bc5be4a06147f30d21e2048721ad3113f Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Sun, 4 Apr 2021 19:31:06 +0300 Subject: [PATCH] Code reorg - moved all 'city moves between civs' functions to a separate class --- core/src/com/unciv/logic/city/CityInfo.kt | 215 +---------------- .../logic/city/CityInfoConquestFunctions.kt | 220 ++++++++++++++++++ 2 files changed, 229 insertions(+), 206 deletions(-) create mode 100644 core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 7aa0d8e0f1..841c64fad2 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -1,21 +1,11 @@ package com.unciv.logic.city -import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.math.Vector2 -import com.unciv.Constants -import com.unciv.UncivGame -import com.unciv.logic.battle.Battle import com.unciv.logic.civilization.CivilizationInfo -import com.unciv.logic.civilization.NotificationIcon import com.unciv.logic.civilization.diplomacy.DiplomacyFlags -import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers -import com.unciv.logic.civilization.diplomacy.DiplomaticStatus import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileMap -import com.unciv.logic.trade.TradeLogic -import com.unciv.logic.trade.TradeOffer -import com.unciv.logic.trade.TradeType import com.unciv.models.ruleset.tile.ResourceSupplyList import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.unit.BaseUnit @@ -24,11 +14,8 @@ import com.unciv.models.stats.StatMap import com.unciv.models.stats.Stats import com.unciv.models.translations.equalsPlaceholderText import com.unciv.models.translations.getPlaceholderParameters -import com.unciv.ui.utils.withoutItem import java.util.* import kotlin.collections.HashSet -import kotlin.math.ceil -import kotlin.math.max import kotlin.math.min import kotlin.math.roundToInt @@ -313,13 +300,9 @@ class CityInfo { return stats } - internal fun getMaxHealth(): Int { - return 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth } - } + internal fun getMaxHealth() = 200 + cityConstructions.getBuiltBuildings().sumBy { it.cityHealth } - override fun toString(): String { - return name - } // for debug + override fun toString() = name // for debug //endregion //region state-changing functions @@ -411,188 +394,17 @@ class CityInfo { } } - fun annexCity() { - isPuppet = false - cityConstructions.inProgressConstructions.clear() // undo all progress of the previous civ on units etc. - cityStats.update() - if (!UncivGame.Current.consoleMode) - UncivGame.Current.worldScreen.shouldUpdate = true - } + fun annexCity() = CityInfoConquestFunctions(this).annexCity() /** This happens when we either puppet OR annex, basically whenever we conquer a city and don't liberate it */ - fun puppetCity(conqueringCiv: CivilizationInfo) { - - // Gain gold for plundering city - val goldPlundered = getGoldForCapturingCity(conqueringCiv) - conqueringCiv.gold += goldPlundered - conqueringCiv.addNotification("Received [$goldPlundered] Gold for capturing [$name]", centerTileInfo.position, NotificationIcon.Gold) - - val oldCiv = civInfo - val reconqueredOurCity = previousOwner == conqueringCiv.civName - - 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.population -= 1 + population.population / 4 // so from 2-4 population, remove 1, from 5-8, remove 2, etc. - reassignPopulation() - - if (reconqueredOurCity && resistanceCounter != 0) // we reconquered our city while it was still in resistance - we get it back with no resistance - resistanceCounter = 0 - else resistanceCounter = population.population // I checked, and even if you puppet there's resistance for conquering - 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 - cityConstructions.constructionQueue.clear() - cityConstructions.chooseNextConstruction() - } - - private fun diplomaticRepercussionsForConqueringCity(oldCiv: CivilizationInfo, conqueringCiv: CivilizationInfo) { - val currentPopulation = population.population - val percentageOfCivPopulationInThatCity = currentPopulation * 100f / - oldCiv.cities.sumBy { it.population.population } - val aggroGenerated = 10f + percentageOfCivPopulationInThatCity.roundToInt() - - // How can you conquer a city but not know the civ you conquered it from?! - // I don't know either, but some of our players have managed this, and crashed their game! - if (!conqueringCiv.knows(oldCiv)) - conqueringCiv.meetCivilization(oldCiv) - - oldCiv.getDiplomacyManager(conqueringCiv) - .addModifier(DiplomaticModifiers.CapturedOurCities, -aggroGenerated) - - for (thirdPartyCiv in conqueringCiv.getKnownCivs().filter { it.isMajorCiv() }) { - val aggroGeneratedForOtherCivs = (aggroGenerated / 10).roundToInt().toFloat() - if (thirdPartyCiv.isAtWarWith(oldCiv)) // You annoyed our enemy? - thirdPartyCiv.getDiplomacyManager(conqueringCiv) - .addModifier(DiplomaticModifiers.SharedEnemy, aggroGeneratedForOtherCivs) // Cool, keep at at! =D - else thirdPartyCiv.getDiplomacyManager(conqueringCiv) - .addModifier(DiplomaticModifiers.WarMongerer, -aggroGeneratedForOtherCivs) // Uncool bro. - } - } + fun puppetCity(conqueringCiv: CivilizationInfo) = CityInfoConquestFunctions(this).puppetCity(conqueringCiv) /* Liberating is returning a city to its founder - makes you LOSE warmongering points **/ - fun liberateCity(conqueringCiv: CivilizationInfo) { - if (foundingCiv == "") { // this should never happen but just in case... - puppetCity(conqueringCiv) - annexCity() - return - } + fun liberateCity(conqueringCiv: CivilizationInfo) = CityInfoConquestFunctions(this).liberateCity(conqueringCiv) - val oldCiv = civInfo + fun moveToCiv(newCivInfo: CivilizationInfo) = CityInfoConquestFunctions(this).moveToCiv(newCivInfo) - 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? - reassignPopulation() - - if (foundingCiv.cities.size == 1) cityConstructions.addBuilding(capitalCityIndicator()) // Resurrection! - isPuppet = false - cityStats.update() - - // Move units out of the city when liberated - for (unit in getTiles().flatMap { it.getUnits() }.toList()) - if (!unit.movement.canPassThrough(unit.currentTile)) - unit.movement.teleportToClosestMoveableTile() - - UncivGame.Current.worldScreen.shouldUpdate = true - } - - private fun diplomaticRepercussionsForLiberatingCity(conqueringCiv: CivilizationInfo) { - val oldOwningCiv = civInfo - val foundingCiv = civInfo.gameInfo.civilizations.first { it.civName == foundingCiv } - val percentageOfCivPopulationInThatCity = population.population * - 100f / (foundingCiv.cities.sumBy { it.population.population } + population.population) - val respecForLiberatingOurCity = 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)) - conqueringCiv.meetCivilization(foundingCiv) - - if (foundingCiv.isMajorCiv()) { - foundingCiv.getDiplomacyManager(conqueringCiv) - .addModifier(DiplomaticModifiers.CapturedOurCities, respecForLiberatingOurCity) - } else { - //Liberating a city state gives a large amount of influence, and peace - foundingCiv.getDiplomacyManager(conqueringCiv).influence = 90f - if (foundingCiv.isAtWarWith(conqueringCiv)) { - val tradeLogic = TradeLogic(foundingCiv, conqueringCiv) - tradeLogic.currentTrade.ourOffers.add(TradeOffer(Constants.peaceTreaty, TradeType.Treaty)) - tradeLogic.currentTrade.theirOffers.add(TradeOffer(Constants.peaceTreaty, TradeType.Treaty)) - tradeLogic.acceptTrade() - } - } - - val otherCivsRespecForLiberating = (respecForLiberatingOurCity / 10).roundToInt().toFloat() - for (thirdPartyCiv in conqueringCiv.getKnownCivs().filter { it.isMajorCiv() && it != oldOwningCiv }) { - thirdPartyCiv.getDiplomacyManager(conqueringCiv) - .addModifier(DiplomaticModifiers.LiberatedCity, otherCivsRespecForLiberating) // Cool, keep at at! =D - } - } - - fun moveToCiv(newCivInfo: CivilizationInfo) { - val oldCiv = civInfo - civInfo.cities = civInfo.cities.toMutableList().apply { remove(this@CityInfo) } - newCivInfo.cities = newCivInfo.cities.toMutableList().apply { add(this@CityInfo) } - civInfo = newCivInfo - hasJustBeenConquered = false - turnAcquired = civInfo.gameInfo.turns - - // now that the tiles have changed, we need to reassign population - for (it in workedTiles.filterNot { tiles.contains(it) }) { - workedTiles = workedTiles.withoutItem(it) - population.autoAssignPopulation() - } - - // Remove/relocate palace for old Civ - val capitalCityIndicator = capitalCityIndicator() - if (cityConstructions.isBuilt(capitalCityIndicator)) { - cityConstructions.removeBuilding(capitalCityIndicator) - if (oldCiv.cities.isNotEmpty()) { - oldCiv.cities.first().cityConstructions.addBuilding(capitalCityIndicator) // relocate palace - } - } - - - // 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 - if (newCivInfo.cities.count() == 1) { - cityConstructions.addBuilding(capitalCityIndicator) - } - - isBeingRazed = false - - // Transfer unique buildings - for (building in cityConstructions.getBuiltBuildings()) { - val civEquivalentBuilding = newCivInfo.getEquivalentBuilding(building.name) - if (building != civEquivalentBuilding) { - cityConstructions.removeBuilding(building.name) - cityConstructions.addBuilding(civEquivalentBuilding.name) - } - } - - tryUpdateRoadStatus() - } - - private fun tryUpdateRoadStatus() { + internal fun tryUpdateRoadStatus() { if (getCenterTile().roadStatus == RoadStatus.None) { val roadImprovement = getRuleset().tileImprovements["Road"] if (roadImprovement != null && roadImprovement.techRequired in civInfo.tech.techsResearched) @@ -617,16 +429,6 @@ class CityInfo { civInfo.updateDetailedCivResources() // this building could be a resource-requiring one } - fun getGoldForCapturingCity(conqueringCiv: CivilizationInfo): Int { - val baseGold = 20 + 10 * population.population + Random().nextInt(40) - val turnModifier = max(0, min(50, civInfo.gameInfo.turns - turnAcquired)) / 50f - val cityModifier = if (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() - } - /* When someone settles a city within 6 tiles of another civ, this makes the AI unhappy and it starts a rolling event. @@ -673,4 +475,5 @@ class CityInfo { } //endregion -} \ No newline at end of file +} + diff --git a/core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt b/core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt new file mode 100644 index 0000000000..a6284d0664 --- /dev/null +++ b/core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt @@ -0,0 +1,220 @@ +package com.unciv.logic.city + +import com.unciv.Constants +import com.unciv.UncivGame +import com.unciv.logic.battle.Battle +import com.unciv.logic.civilization.CivilizationInfo +import com.unciv.logic.civilization.NotificationIcon +import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers +import com.unciv.logic.civilization.diplomacy.DiplomaticStatus +import com.unciv.logic.trade.TradeLogic +import com.unciv.logic.trade.TradeOffer +import com.unciv.logic.trade.TradeType +import com.unciv.ui.utils.withoutItem +import java.util.* +import kotlin.math.max +import kotlin.math.min +import kotlin.math.roundToInt + +/** 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 + } + + + /** This happens when we either puppet OR annex, basically whenever we conquer a city and don't liberate it */ + fun puppetCity(conqueringCiv: CivilizationInfo) { + // Gain gold for plundering city + val goldPlundered = getGoldForCapturingCity(conqueringCiv) + city.apply { + conqueringCiv.gold += goldPlundered + conqueringCiv.addNotification("Received [$goldPlundered] Gold for capturing [$name]", getCenterTile().position, NotificationIcon.Gold) + + val oldCiv = civInfo + val reconqueredOurCity = previousOwner == conqueringCiv.civName + + 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.population -= 1 + population.population / 4 // so from 2-4 population, remove 1, from 5-8, remove 2, etc. + reassignPopulation() + + if (reconqueredOurCity && resistanceCounter != 0) // we reconquered our city while it was still in resistance - we get it back with no resistance + resistanceCounter = 0 + else resistanceCounter = population.population // I checked, and even if you puppet there's resistance for conquering + 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 + cityConstructions.constructionQueue.clear() + cityConstructions.chooseNextConstruction() + } + } + + + 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() + } + + + private fun diplomaticRepercussionsForConqueringCity(oldCiv: CivilizationInfo, conqueringCiv: CivilizationInfo) { + val currentPopulation = city.population.population + val percentageOfCivPopulationInThatCity = currentPopulation * 100f / + oldCiv.cities.sumBy { it.population.population } + val aggroGenerated = 10f + percentageOfCivPopulationInThatCity.roundToInt() + + // How can you conquer a city but not know the civ you conquered it from?! + // I don't know either, but some of our players have managed this, and crashed their game! + if (!conqueringCiv.knows(oldCiv)) + conqueringCiv.meetCivilization(oldCiv) + + oldCiv.getDiplomacyManager(conqueringCiv) + .addModifier(DiplomaticModifiers.CapturedOurCities, -aggroGenerated) + + for (thirdPartyCiv in conqueringCiv.getKnownCivs().filter { it.isMajorCiv() }) { + val aggroGeneratedForOtherCivs = (aggroGenerated / 10).roundToInt().toFloat() + if (thirdPartyCiv.isAtWarWith(oldCiv)) // You annoyed our enemy? + thirdPartyCiv.getDiplomacyManager(conqueringCiv) + .addModifier(DiplomaticModifiers.SharedEnemy, aggroGeneratedForOtherCivs) // Cool, keep at at! =D + else thirdPartyCiv.getDiplomacyManager(conqueringCiv) + .addModifier(DiplomaticModifiers.WarMongerer, -aggroGeneratedForOtherCivs) // Uncool bro. + } + } + + fun liberateCity(conqueringCiv: CivilizationInfo) { + city.apply { + if (foundingCiv == "") { // this should never happen but just in case... + puppetCity(conqueringCiv) + annexCity() + return + } + + 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? + reassignPopulation() + + if (foundingCiv.cities.size == 1) cityConstructions.addBuilding(capitalCityIndicator()) // Resurrection! + isPuppet = false + cityStats.update() + + // Move units out of the city when liberated + for (unit in getTiles().flatMap { it.getUnits() }.toList()) + if (!unit.movement.canPassThrough(unit.currentTile)) + unit.movement.teleportToClosestMoveableTile() + } + } + + + private fun diplomaticRepercussionsForLiberatingCity(conqueringCiv: CivilizationInfo) { + val oldOwningCiv = city.civInfo + val foundingCiv = oldOwningCiv.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() + + // In order to get "plus points" in Diplomacy, you have to establish diplomatic relations if you haven't yet + if (!conqueringCiv.knows(foundingCiv)) + conqueringCiv.meetCivilization(foundingCiv) + + if (foundingCiv.isMajorCiv()) { + foundingCiv.getDiplomacyManager(conqueringCiv) + .addModifier(DiplomaticModifiers.CapturedOurCities, respecForLiberatingOurCity) + } else { + //Liberating a city state gives a large amount of influence, and peace + foundingCiv.getDiplomacyManager(conqueringCiv).influence = 90f + if (foundingCiv.isAtWarWith(conqueringCiv)) { + val tradeLogic = TradeLogic(foundingCiv, conqueringCiv) + tradeLogic.currentTrade.ourOffers.add(TradeOffer(Constants.peaceTreaty, TradeType.Treaty)) + tradeLogic.currentTrade.theirOffers.add(TradeOffer(Constants.peaceTreaty, TradeType.Treaty)) + tradeLogic.acceptTrade() + } + } + + val otherCivsRespecForLiberating = (respecForLiberatingOurCity / 10).roundToInt().toFloat() + for (thirdPartyCiv in conqueringCiv.getKnownCivs().filter { it.isMajorCiv() && it != oldOwningCiv }) { + thirdPartyCiv.getDiplomacyManager(conqueringCiv) + .addModifier(DiplomaticModifiers.LiberatedCity, otherCivsRespecForLiberating) // Cool, keep at at! =D + } + } + + + fun moveToCiv(newCivInfo: CivilizationInfo) { + city.apply { + val oldCiv = civInfo + civInfo.cities = civInfo.cities.toMutableList().apply { remove(city) } + newCivInfo.cities = newCivInfo.cities.toMutableList().apply { add(city) } + civInfo = newCivInfo + hasJustBeenConquered = false + turnAcquired = civInfo.gameInfo.turns + + // now that the tiles have changed, we need to reassign population + for (it in workedTiles.filterNot { tiles.contains(it) }) { + workedTiles = workedTiles.withoutItem(it) + population.autoAssignPopulation() + } + + // Remove/relocate palace for old Civ + val capitalCityIndicator = capitalCityIndicator() + if (cityConstructions.isBuilt(capitalCityIndicator)) { + cityConstructions.removeBuilding(capitalCityIndicator) + if (oldCiv.cities.isNotEmpty()) { + oldCiv.cities.first().cityConstructions.addBuilding(capitalCityIndicator) // relocate palace + } + } + + + // 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 + if (newCivInfo.cities.count() == 1) { + cityConstructions.addBuilding(capitalCityIndicator) + } + + isBeingRazed = false + + // Transfer unique buildings + for (building in cityConstructions.getBuiltBuildings()) { + val civEquivalentBuilding = newCivInfo.getEquivalentBuilding(building.name) + if (building != civEquivalentBuilding) { + cityConstructions.removeBuilding(building.name) + cityConstructions.addBuilding(civEquivalentBuilding.name) + } + } + + tryUpdateRoadStatus() + } + } + + +} \ No newline at end of file