diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 5fc0c12229..d9f439fe72 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -33,6 +33,7 @@ import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.Speed import com.unciv.models.ruleset.nation.Difficulty +import com.unciv.models.ruleset.unique.LocalUniqueCache import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.translations.tr import com.unciv.ui.audio.MusicMood @@ -669,10 +670,11 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion civInfo.cache.updateCitiesConnectedToCapital(true) // We need to determine the GLOBAL happiness state in order to determine the city stats + val localUniqueCache = LocalUniqueCache() for (city in civInfo.cities) { - city.cityStats.updateTileStats() // Some nat wonders can give happiness! + city.cityStats.updateTileStats(localUniqueCache) // Some nat wonders can give happiness! city.cityStats.updateCityHappiness( - city.cityConstructions.getStats() + city.cityConstructions.getStats(localUniqueCache) ) } @@ -689,7 +691,8 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion if (!ruleset.tileResources.containsKey(city.demandedResource)) city.demandedResource = "" - city.cityStats.update() + // No uniques have changed since the cache was created, so we can still use it + city.cityStats.update(localUniqueCache=localUniqueCache) } } } diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index f2a10c1c43..97bcbb1292 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -345,8 +345,8 @@ object Automation { return when { powerLevelComparison > 2 -> ThreatLevel.VeryHigh powerLevelComparison > 1.5f -> ThreatLevel.High - powerLevelComparison < (1 / 1.5f) -> ThreatLevel.Low powerLevelComparison < 0.5f -> ThreatLevel.VeryLow + powerLevelComparison < (1 / 1.5f) -> ThreatLevel.Low else -> ThreatLevel.Medium } } diff --git a/core/src/com/unciv/logic/city/City.kt b/core/src/com/unciv/logic/city/City.kt index c2978dcff0..e6d1aa18eb 100644 --- a/core/src/com/unciv/logic/city/City.kt +++ b/core/src/com/unciv/logic/city/City.kt @@ -622,7 +622,7 @@ class City : IsPartOfGameInfoSerialization { } // Uniques special to this city - private fun getLocalMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(civ, this)): Sequence { + fun getLocalMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(civ, this)): Sequence { return ( cityConstructions.builtBuildingUniqueMap.getUniques(uniqueType).filter { it.isLocalEffect } + religion.getUniques().filter { it.isOfType(uniqueType) } diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index cb601fda26..ffa5bfcdae 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -110,9 +110,8 @@ class CityConstructions : IsPartOfGameInfoSerialization { /** * @return [Stats] provided by all built buildings in city plus the bonus from Library */ - fun getStats(): StatTreeNode { + fun getStats(localUniqueCache: LocalUniqueCache): StatTreeNode { val stats = StatTreeNode() - val localUniqueCache = LocalUniqueCache() for (building in getBuiltBuildings()) stats.addStats(building.getStats(city, localUniqueCache), building.name) return stats diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index 3cd0b29d36..50701ad29c 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -354,9 +354,8 @@ class CityStats(val city: City) { //endregion //region State-Changing Methods - fun updateTileStats() { + fun updateTileStats(localUniqueCache:LocalUniqueCache = LocalUniqueCache()) { val stats = Stats() - val localUniqueCache = LocalUniqueCache() val workedTiles = city.tilesInRange.asSequence() .filter { city.location == it.position @@ -490,12 +489,14 @@ class CityStats(val city: City) { fun update(currentConstruction: IConstruction = city.cityConstructions.getCurrentConstruction(), updateTileStats:Boolean = true, - updateCivStats:Boolean = true) { - if (updateTileStats) updateTileStats() + updateCivStats:Boolean = true, + localUniqueCache:LocalUniqueCache = LocalUniqueCache()) { + + if (updateTileStats) updateTileStats(localUniqueCache) // We need to compute Tile yields before happiness - val statsFromBuildings = city.cityConstructions.getStats() // this is performance heavy, so calculate once + val statsFromBuildings = city.cityConstructions.getStats(localUniqueCache) // this is performance heavy, so calculate once updateBaseStatList(statsFromBuildings) updateCityHappiness(statsFromBuildings) updateStatPercentBonusList(currentConstruction) diff --git a/core/src/com/unciv/models/ruleset/unique/Unique.kt b/core/src/com/unciv/models/ruleset/unique/Unique.kt index 055e1e9f39..19f6dbb572 100644 --- a/core/src/com/unciv/models/ruleset/unique/Unique.kt +++ b/core/src/com/unciv/models/ruleset/unique/Unique.kt @@ -325,11 +325,18 @@ class LocalUniqueCache(val cache:Boolean = true) { uniqueType: UniqueType, ignoreConditionals: Boolean = false ): Sequence { - val stateForConditionals = if (ignoreConditionals) StateForConditionals.IgnoreConditionals - else StateForConditionals(city.civ, city) + // City uniques are a combination of *global civ* uniques plus *city relevant* uniques (see City.getMatchingUniques()) + // We can cache the civ uniques separately, so if we have several cities using the same cache, + // we can cache the list of *civ uniques* to reuse between cities. + // This is assuming that we're ignoring conditionals, because otherwise - + // the conditionals will render the the *filtered uniques* different anyway, so there's no reason to cache... + val uniques = if (!ignoreConditionals) city.getMatchingUniques(uniqueType, StateForConditionals(city.civ, city)) + else forCivGetMatchingUniques(city.civ, uniqueType, StateForConditionals.IgnoreConditionals) + + city.getLocalMatchingUniques(uniqueType, StateForConditionals.IgnoreConditionals) + return get( "city-${city.id}-${uniqueType.name}-${ignoreConditionals}", - city.getMatchingUniques(uniqueType, stateForConditionals) + uniques ) } @@ -340,12 +347,16 @@ class LocalUniqueCache(val cache:Boolean = true) { civ ) ): Sequence { - val sequence = civ.getMatchingUniques(uniqueType, stateForConditionals) - if (!cache) return sequence // So we don't need to toString the stateForConditionals + val sequence = civ.getMatchingUniques(uniqueType, StateForConditionals.IgnoreConditionals) + // The uniques CACHED are ALL civ uniques, regardless of conditional matching. + // The uniques RETURNED are uniques AFTER conditional matching. + // This allows reuse of the cached values, between runs with different conditionals - + // for example, iterate on all tiles and get StatPercentForObject uniques relevant for each tile, + // each tile will have different conditional state, but they will all reuse the same list of uniques for the civ return get( - "civ-${civ.civName}-${uniqueType.name}-${stateForConditionals}", + "civ-${civ.civName}-${uniqueType.name}", sequence - ) + ).filter { it.conditionalsApply(stateForConditionals) } } /** Get cached results as a sequence */