diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 516fa852b6..95762bdb8f 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -5,16 +5,17 @@ import com.unciv.Constants import com.unciv.UncivGame import com.unciv.logic.automation.NextTurnAutomation import com.unciv.logic.city.CityConstructions -import com.unciv.logic.civilization.CivilizationInfo -import com.unciv.logic.civilization.LocationAction -import com.unciv.logic.civilization.PlayerType +import com.unciv.logic.civilization.* import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileMap +import com.unciv.logic.trade.TradeOffer +import com.unciv.logic.trade.TradeType import com.unciv.models.metadata.GameParameters import com.unciv.models.ruleset.Difficulty import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache import java.util.* +import kotlin.collections.ArrayList class GameInfo { @Transient lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn @@ -58,6 +59,7 @@ class GameInfo { fun getCurrentPlayerCivilization() = currentPlayerCiv fun getBarbarianCivilization() = getCivilization("Barbarians") fun getDifficulty() = difficultyObject + fun getCities() = civilizations.flatMap { it.cities } //endregion fun nextTurn() { @@ -265,11 +267,62 @@ class GameInfo { // This doesn't HAVE to go here, but why not. // As of version 3.1.3, trade offers of "Declare war on X" and "Introduction to X" were changed to X, // with the extra text being added only on UI display (solved a couple of problems). - for(trade in civInfo.tradeRequests.map { it.trade }) - for(offer in trade.theirOffers.union(trade.ourOffers)){ + for(trade in civInfo.tradeRequests.map { it.trade }) { + for (offer in trade.theirOffers.union(trade.ourOffers)) { offer.name = offer.name.removePrefix("Declare war on ") offer.name = offer.name.removePrefix("Introduction to ") } + } + + // As of 3.4.9 cities are referenced by id, not by name + // So try to update every tradeRequest (if there are no conflicting names) + for(tradeRequest in civInfo.tradeRequests) { + val trade = tradeRequest.trade + val toRemove = ArrayList() + for (offer in trade.ourOffers) { + if (offer.type == TradeType.City) { + val countNames = civInfo.cities.count { it.name == offer.name} + + if (countNames == 1) + offer.name = civInfo.cities.first { it.name == offer.name}.id + // There are conflicting names: we can't guess what city was being offered + else if (countNames > 1) + toRemove.add(offer) + } + } + + trade.ourOffers.removeAll(toRemove) + toRemove.clear() + + val themCivInfo = getCivilization(tradeRequest.requestingCiv) + for (offer in trade.theirOffers) { + if (offer.type == TradeType.City) { + val countNames = themCivInfo.cities.count { it.name == offer.name} + + if (countNames == 1) + offer.name = themCivInfo.cities.first { it.name == offer.name}.id + // There are conflicting names: we can't guess what city was being offered + else if (countNames > 1) + toRemove.add(offer) + } + } + + trade.theirOffers.removeAll(toRemove) + } + + // As of 3.4.9 cities are referenced by id, not by name + val toRemove = ArrayList() + for (popupAlert in civInfo.popupAlerts.filter { it.type == AlertType.CityConquered }) { + val countNames = getCities().count { it.name == popupAlert.value } + + if (countNames == 1) + popupAlert.value = getCities().first { it.name == popupAlert.value }.id + else if (countNames > 1) { + // Sorry again, conflicting names: who knows what city you conquered? + toRemove.add(popupAlert) + } + } + civInfo.popupAlerts.removeAll(toRemove) } for (civInfo in civilizations) civInfo.setNationTransient() diff --git a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt index 5803a89439..fea0420fc9 100644 --- a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt @@ -93,7 +93,7 @@ class SpecificUnitAutomation{ fun automateSettlerActions(unit: MapUnit) { if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit - val tilesNearCities = unit.civInfo.gameInfo.civilizations.flatMap { it.cities } + val tilesNearCities = unit.civInfo.gameInfo.getCities() .flatMap { val distanceAwayFromCity = if (unit.civInfo.knows(it.civInfo) diff --git a/core/src/com/unciv/logic/automation/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/WorkerAutomation.kt index 7fe584dac6..dfc1af1c52 100644 --- a/core/src/com/unciv/logic/automation/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/WorkerAutomation.kt @@ -45,14 +45,14 @@ class WorkerAutomation(val unit: MapUnit) { val citiesToNumberOfUnimprovedTiles = HashMap() for (city in unit.civInfo.cities) { - citiesToNumberOfUnimprovedTiles[city.name] = + citiesToNumberOfUnimprovedTiles[city.id] = city.getTiles().count { it.isLand && it.civilianUnit == null && tileCanBeImproved(it, unit.civInfo) } } val mostUndevelopedCity = unit.civInfo.cities - .filter { citiesToNumberOfUnimprovedTiles[it.name]!! > 0 } - .sortedByDescending { citiesToNumberOfUnimprovedTiles[it.name] } + .filter { citiesToNumberOfUnimprovedTiles[it.id]!! > 0 } + .sortedByDescending { citiesToNumberOfUnimprovedTiles[it.id] } .firstOrNull { unit.movement.canReach(it.getCenterTile()) } //goto most undeveloped city if (mostUndevelopedCity != null && mostUndevelopedCity != unit.currentTile.owningCity) { val reachedTile = unit.movement.headTowards(mostUndevelopedCity.getCenterTile()) diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 5182ad4d5c..a1ccabbc24 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -256,7 +256,7 @@ class Battle(val gameInfo:GameInfo) { } if (attackerCiv.isPlayerCivilization()) { - attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered, city.name)) + attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered, city.id)) UncivGame.Current.settings.addCompletedTutorialTask("Conquer a city") } else { diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 409b82480f..098a4c0ac7 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -19,8 +19,10 @@ import com.unciv.models.ruleset.tile.ResourceSupplyList import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.stats.Stats import com.unciv.ui.utils.withoutItem -import kotlin.math.* import java.util.* +import kotlin.collections.HashMap +import kotlin.collections.HashSet +import kotlin.math.* class CityInfo { @Transient lateinit var civInfo: CivilizationInfo @@ -31,6 +33,7 @@ class CityInfo { @Transient var hasJustBeenConquered = false // this is so that military units can enter the city, even before we decide what to do with it var location: Vector2 = Vector2.Zero + var id: String = UUID.randomUUID().toString() var name: String = "" var foundingCiv = "" var turnAcquired = 0 @@ -99,15 +102,16 @@ class CityInfo { //region pure functions fun clone(): CityInfo { val toReturn = CityInfo() - toReturn.location=location - toReturn.name=name - toReturn.health=health + toReturn.location = location + toReturn.id = id + toReturn.name = name + toReturn.health = health toReturn.population = population.clone() - toReturn.cityConstructions=cityConstructions.clone() + toReturn.cityConstructions = cityConstructions.clone() toReturn.expansion = expansion.clone() toReturn.tiles = tiles toReturn.workedTiles = workedTiles - toReturn.isBeingRazed=isBeingRazed + toReturn.isBeingRazed = isBeingRazed toReturn.attackedThisTurn = attackedThisTurn toReturn.resistanceCounter = resistanceCounter toReturn.foundingCiv = foundingCiv diff --git a/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt b/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt index 83706c20d0..e0e1257f23 100644 --- a/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt +++ b/core/src/com/unciv/logic/civilization/CivInfoTransientUpdater.kt @@ -128,7 +128,7 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo){ val citiesReachedToMediums = HashMap>() var citiesToCheck = mutableListOf(civInfo.getCapital()) citiesReachedToMediums[civInfo.getCapital()] = arrayListOf("Start") - val allCivCities = civInfo.gameInfo.civilizations.flatMap { it.cities } + val allCivCities = civInfo.gameInfo.getCities() val theWheelIsResearched = civInfo.tech.isResearched("The Wheel") diff --git a/core/src/com/unciv/logic/trade/TradeEvaluation.kt b/core/src/com/unciv/logic/trade/TradeEvaluation.kt index 5aea433144..94472e6f95 100644 --- a/core/src/com/unciv/logic/trade/TradeEvaluation.kt +++ b/core/src/com/unciv/logic/trade/TradeEvaluation.kt @@ -38,7 +38,7 @@ class TradeEvaluation{ TradeType.Technology -> return true TradeType.Introduction -> return true TradeType.WarDeclaration -> return true - TradeType.City -> return offerer.cities.any { it.name==tradeOffer.name } + TradeType.City -> return offerer.cities.any { it.id == tradeOffer.name } } } @@ -136,7 +136,7 @@ class TradeEvaluation{ else return 0 // why should we pay you to go fight someone...? } TradeType.City -> { - val city = tradePartner.cities.first { it.name==offer.name } + val city = tradePartner.cities.first { it.id==offer.name } val stats = city.cityStats.currentCityStats if(civInfo.getHappiness() + city.cityStats.happinessList.values.sum() < 0) return 0 // we can't really afford to go into negative happiness because of buying a city @@ -204,7 +204,7 @@ class TradeEvaluation{ } TradeType.City -> { - val city = civInfo.cities.first { it.name==offer.name } + val city = civInfo.cities.first { it.id == offer.name } val stats = city.cityStats.currentCityStats val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food return sumOfStats.toInt() * 100 diff --git a/core/src/com/unciv/logic/trade/TradeLogic.kt b/core/src/com/unciv/logic/trade/TradeLogic.kt index 41df65a28c..5a72523345 100644 --- a/core/src/com/unciv/logic/trade/TradeLogic.kt +++ b/core/src/com/unciv/logic/trade/TradeLogic.kt @@ -48,7 +48,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci if (!civInfo.isOneCityChallenger() && !otherCivilization.isOneCityChallenger() && !civInfo.isCityState() && !otherCivilization.isCityState()) { for (city in civInfo.cities.filterNot { it.isCapital() }) - offers.add(TradeOffer(city.name, TradeType.City, 0)) + offers.add(TradeOffer(city.id, TradeType.City, 0)) } val otherCivsWeKnow = civInfo.getKnownCivs() @@ -94,7 +94,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci to.tech.addTechnology(offer.name) } if (offer.type == TradeType.City) { - val city = from.cities.first { it.name == offer.name } + val city = from.cities.first { it.id == offer.name } city.moveToCiv(to) city.getCenterTile().getUnits().forEach { it.movement.teleportToClosestMoveableTile() } to.updateViewableTiles() diff --git a/core/src/com/unciv/logic/trade/TradeOffer.kt b/core/src/com/unciv/logic/trade/TradeOffer.kt index ed709d826b..4c5abbb993 100644 --- a/core/src/com/unciv/logic/trade/TradeOffer.kt +++ b/core/src/com/unciv/logic/trade/TradeOffer.kt @@ -1,5 +1,6 @@ package com.unciv.logic.trade +import com.unciv.UncivGame import com.unciv.models.translations.tr data class TradeOffer(var name:String, var type: TradeType, @@ -18,6 +19,7 @@ data class TradeOffer(var name:String, var type: TradeType, var offerText = when(type){ TradeType.WarDeclaration -> "Declare war on [$name]" TradeType.Introduction -> "Introduction to [$name]" + TradeType.City -> UncivGame.Current.gameInfo.getCities().first{ it.id == name }.name else -> name }.tr() if (type !in tradesToNotHaveNumbers) offerText += " ($amount)" diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index 528fc34b0f..f75f9abe87 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -256,7 +256,7 @@ class Building : NamedStats(), IConstruction{ // Regular wonders if (isWonder){ - if(civInfo.gameInfo.civilizations.flatMap { it.cities } + if(civInfo.gameInfo.getCities() .any {it.cityConstructions.isBuilt(name)}) return "Wonder is already built" diff --git a/core/src/com/unciv/ui/worldscreen/AlertPopup.kt b/core/src/com/unciv/ui/worldscreen/AlertPopup.kt index b3628f784f..a58db69bd6 100644 --- a/core/src/com/unciv/ui/worldscreen/AlertPopup.kt +++ b/core/src/com/unciv/ui/worldscreen/AlertPopup.kt @@ -58,7 +58,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu } } AlertType.CityConquered -> { - val city = worldScreen.gameInfo.civilizations.flatMap { it.cities }.first { it.name == popupAlert.value} + val city = worldScreen.gameInfo.getCities().first { it.id == popupAlert.value } addGoodSizedLabel("What would you like to do with the city?",24) .padBottom(20f).row() val conqueringCiv = worldScreen.gameInfo.currentPlayerCiv