Cities referenced by UUID (#1638)

* Cities referenced by UUID

* UUID defaulted in CityInfo

Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
r3versi 2020-01-09 18:41:55 +01:00 committed by Yair Morgenstern
parent 065e944896
commit 036d4058f1
11 changed files with 83 additions and 24 deletions

View File

@ -5,16 +5,17 @@ import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.automation.NextTurnAutomation import com.unciv.logic.automation.NextTurnAutomation
import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.CityConstructions
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.*
import com.unciv.logic.civilization.LocationAction
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap 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.metadata.GameParameters
import com.unciv.models.ruleset.Difficulty import com.unciv.models.ruleset.Difficulty
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.RulesetCache
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class GameInfo { class GameInfo {
@Transient lateinit var difficultyObject: Difficulty // Since this is static game-wide, and was taking a large part of nextTurn @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 getCurrentPlayerCivilization() = currentPlayerCiv
fun getBarbarianCivilization() = getCivilization("Barbarians") fun getBarbarianCivilization() = getCivilization("Barbarians")
fun getDifficulty() = difficultyObject fun getDifficulty() = difficultyObject
fun getCities() = civilizations.flatMap { it.cities }
//endregion //endregion
fun nextTurn() { fun nextTurn() {
@ -265,11 +267,62 @@ class GameInfo {
// This doesn't HAVE to go here, but why not. // 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, // 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). // with the extra text being added only on UI display (solved a couple of problems).
for(trade in civInfo.tradeRequests.map { it.trade }) for(trade in civInfo.tradeRequests.map { it.trade }) {
for(offer in trade.theirOffers.union(trade.ourOffers)){ for (offer in trade.theirOffers.union(trade.ourOffers)) {
offer.name = offer.name.removePrefix("Declare war on ") offer.name = offer.name.removePrefix("Declare war on ")
offer.name = offer.name.removePrefix("Introduction to ") 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<TradeOffer>()
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<PopupAlert>()
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() for (civInfo in civilizations) civInfo.setNationTransient()

View File

@ -93,7 +93,7 @@ class SpecificUnitAutomation{
fun automateSettlerActions(unit: MapUnit) { fun automateSettlerActions(unit: MapUnit) {
if(unit.getTile().militaryUnit==null) return // Don't move until you're accompanied by a military unit 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 { .flatMap {
val distanceAwayFromCity = val distanceAwayFromCity =
if (unit.civInfo.knows(it.civInfo) if (unit.civInfo.knows(it.civInfo)

View File

@ -45,14 +45,14 @@ class WorkerAutomation(val unit: MapUnit) {
val citiesToNumberOfUnimprovedTiles = HashMap<String, Int>() val citiesToNumberOfUnimprovedTiles = HashMap<String, Int>()
for (city in unit.civInfo.cities) { for (city in unit.civInfo.cities) {
citiesToNumberOfUnimprovedTiles[city.name] = citiesToNumberOfUnimprovedTiles[city.id] =
city.getTiles().count { it.isLand && it.civilianUnit == null city.getTiles().count { it.isLand && it.civilianUnit == null
&& tileCanBeImproved(it, unit.civInfo) } && tileCanBeImproved(it, unit.civInfo) }
} }
val mostUndevelopedCity = unit.civInfo.cities val mostUndevelopedCity = unit.civInfo.cities
.filter { citiesToNumberOfUnimprovedTiles[it.name]!! > 0 } .filter { citiesToNumberOfUnimprovedTiles[it.id]!! > 0 }
.sortedByDescending { citiesToNumberOfUnimprovedTiles[it.name] } .sortedByDescending { citiesToNumberOfUnimprovedTiles[it.id] }
.firstOrNull { unit.movement.canReach(it.getCenterTile()) } //goto most undeveloped city .firstOrNull { unit.movement.canReach(it.getCenterTile()) } //goto most undeveloped city
if (mostUndevelopedCity != null && mostUndevelopedCity != unit.currentTile.owningCity) { if (mostUndevelopedCity != null && mostUndevelopedCity != unit.currentTile.owningCity) {
val reachedTile = unit.movement.headTowards(mostUndevelopedCity.getCenterTile()) val reachedTile = unit.movement.headTowards(mostUndevelopedCity.getCenterTile())

View File

@ -256,7 +256,7 @@ class Battle(val gameInfo:GameInfo) {
} }
if (attackerCiv.isPlayerCivilization()) { 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") UncivGame.Current.settings.addCompletedTutorialTask("Conquer a city")
} }
else { else {

View File

@ -19,8 +19,10 @@ import com.unciv.models.ruleset.tile.ResourceSupplyList
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.ui.utils.withoutItem import com.unciv.ui.utils.withoutItem
import kotlin.math.*
import java.util.* import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.HashSet
import kotlin.math.*
class CityInfo { class CityInfo {
@Transient lateinit var civInfo: CivilizationInfo @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 @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 location: Vector2 = Vector2.Zero
var id: String = UUID.randomUUID().toString()
var name: String = "" var name: String = ""
var foundingCiv = "" var foundingCiv = ""
var turnAcquired = 0 var turnAcquired = 0
@ -99,15 +102,16 @@ class CityInfo {
//region pure functions //region pure functions
fun clone(): CityInfo { fun clone(): CityInfo {
val toReturn = CityInfo() val toReturn = CityInfo()
toReturn.location=location toReturn.location = location
toReturn.name=name toReturn.id = id
toReturn.health=health toReturn.name = name
toReturn.health = health
toReturn.population = population.clone() toReturn.population = population.clone()
toReturn.cityConstructions=cityConstructions.clone() toReturn.cityConstructions = cityConstructions.clone()
toReturn.expansion = expansion.clone() toReturn.expansion = expansion.clone()
toReturn.tiles = tiles toReturn.tiles = tiles
toReturn.workedTiles = workedTiles toReturn.workedTiles = workedTiles
toReturn.isBeingRazed=isBeingRazed toReturn.isBeingRazed = isBeingRazed
toReturn.attackedThisTurn = attackedThisTurn toReturn.attackedThisTurn = attackedThisTurn
toReturn.resistanceCounter = resistanceCounter toReturn.resistanceCounter = resistanceCounter
toReturn.foundingCiv = foundingCiv toReturn.foundingCiv = foundingCiv

View File

@ -128,7 +128,7 @@ class CivInfoTransientUpdater(val civInfo: CivilizationInfo){
val citiesReachedToMediums = HashMap<CityInfo, ArrayList<String>>() val citiesReachedToMediums = HashMap<CityInfo, ArrayList<String>>()
var citiesToCheck = mutableListOf(civInfo.getCapital()) var citiesToCheck = mutableListOf(civInfo.getCapital())
citiesReachedToMediums[civInfo.getCapital()] = arrayListOf("Start") 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") val theWheelIsResearched = civInfo.tech.isResearched("The Wheel")

View File

@ -38,7 +38,7 @@ class TradeEvaluation{
TradeType.Technology -> return true TradeType.Technology -> return true
TradeType.Introduction -> return true TradeType.Introduction -> return true
TradeType.WarDeclaration -> 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...? else return 0 // why should we pay you to go fight someone...?
} }
TradeType.City -> { 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 val stats = city.cityStats.currentCityStats
if(civInfo.getHappiness() + city.cityStats.happinessList.values.sum() < 0) 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 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 -> { 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 stats = city.cityStats.currentCityStats
val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food val sumOfStats = stats.culture+stats.gold+stats.science+stats.production+stats.happiness+stats.food
return sumOfStats.toInt() * 100 return sumOfStats.toInt() * 100

View File

@ -48,7 +48,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
if (!civInfo.isOneCityChallenger() && !otherCivilization.isOneCityChallenger() if (!civInfo.isOneCityChallenger() && !otherCivilization.isOneCityChallenger()
&& !civInfo.isCityState() && !otherCivilization.isCityState()) { && !civInfo.isCityState() && !otherCivilization.isCityState()) {
for (city in civInfo.cities.filterNot { it.isCapital() }) 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() val otherCivsWeKnow = civInfo.getKnownCivs()
@ -94,7 +94,7 @@ class TradeLogic(val ourCivilization:CivilizationInfo, val otherCivilization: Ci
to.tech.addTechnology(offer.name) to.tech.addTechnology(offer.name)
} }
if (offer.type == TradeType.City) { 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.moveToCiv(to)
city.getCenterTile().getUnits().forEach { it.movement.teleportToClosestMoveableTile() } city.getCenterTile().getUnits().forEach { it.movement.teleportToClosestMoveableTile() }
to.updateViewableTiles() to.updateViewableTiles()

View File

@ -1,5 +1,6 @@
package com.unciv.logic.trade package com.unciv.logic.trade
import com.unciv.UncivGame
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
data class TradeOffer(var name:String, var type: TradeType, 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){ var offerText = when(type){
TradeType.WarDeclaration -> "Declare war on [$name]" TradeType.WarDeclaration -> "Declare war on [$name]"
TradeType.Introduction -> "Introduction to [$name]" TradeType.Introduction -> "Introduction to [$name]"
TradeType.City -> UncivGame.Current.gameInfo.getCities().first{ it.id == name }.name
else -> name else -> name
}.tr() }.tr()
if (type !in tradesToNotHaveNumbers) offerText += " ($amount)" if (type !in tradesToNotHaveNumbers) offerText += " ($amount)"

View File

@ -256,7 +256,7 @@ class Building : NamedStats(), IConstruction{
// Regular wonders // Regular wonders
if (isWonder){ if (isWonder){
if(civInfo.gameInfo.civilizations.flatMap { it.cities } if(civInfo.gameInfo.getCities()
.any {it.cityConstructions.isBuilt(name)}) .any {it.cityConstructions.isBuilt(name)})
return "Wonder is already built" return "Wonder is already built"

View File

@ -58,7 +58,7 @@ class AlertPopup(val worldScreen: WorldScreen, val popupAlert: PopupAlert): Popu
} }
} }
AlertType.CityConquered -> { 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) addGoodSizedLabel("What would you like to do with the city?",24)
.padBottom(20f).row() .padBottom(20f).row()
val conqueringCiv = worldScreen.gameInfo.currentPlayerCiv val conqueringCiv = worldScreen.gameInfo.currentPlayerCiv