Cache civ uniques while refreshing world map tiles. (#9284)

* Cache civ uniques while refreshing world map tiles.

This should have a pretty significant performance improvement of about 10% next rount time.

* Forgot to add stateForConditionals to cache key.

* Make cache keys depend on function

* Restrict LocalUniqueCache API to avoid using it the wrong way.

* Use default values instead of two methods
This commit is contained in:
WhoIsJohannes
2023-04-26 09:17:58 +02:00
committed by GitHub
parent 0b0a400f65
commit cccaa88456
18 changed files with 137 additions and 76 deletions

View File

@ -46,13 +46,11 @@ object Automation {
if (specialist) { if (specialist) {
// If you have the Food Bonus, count as 1 extra food production (base is 2food) // If you have the Food Bonus, count as 1 extra food production (base is 2food)
for (unique in localUniqueCache.get(UniqueType.FoodConsumptionBySpecialists.name, for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.FoodConsumptionBySpecialists))
city.getMatchingUniques(UniqueType.FoodConsumptionBySpecialists)))
if (city.matchesFilter(unique.params[1])) if (city.matchesFilter(unique.params[1]))
yieldStats.food -= (unique.params[0].toFloat() / 100f) * 2f // base 2 food per Pop yieldStats.food -= (unique.params[0].toFloat() / 100f) * 2f // base 2 food per Pop
// Specialist Happiness Percentage Change 0f-1f // Specialist Happiness Percentage Change 0f-1f
for (unique in localUniqueCache.get(UniqueType.UnhappinessFromPopulationTypePercentageChange.name, for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.UnhappinessFromPopulationTypePercentageChange))
city.getMatchingUniques(UniqueType.UnhappinessFromPopulationTypePercentageChange)))
if (city.matchesFilter(unique.params[2]) && unique.params[1] == "Specialists") if (city.matchesFilter(unique.params[2]) && unique.params[1] == "Specialists")
yieldStats.happiness -= (unique.params[0].toFloat() / 100f) // relative val is negative, make positive yieldStats.happiness -= (unique.params[0].toFloat() / 100f) // relative val is negative, make positive
if (city.civ.getHappiness() < 0) yieldStats.happiness *= 2 // double weight for unhappy civilization if (city.civ.getHappiness() < 0) yieldStats.happiness *= 2 // double weight for unhappy civilization

View File

@ -182,10 +182,10 @@ class CityStats(val city: City) {
val specialist = city.getRuleset().specialists[specialistName] val specialist = city.getRuleset().specialists[specialistName]
?: return Stats() ?: return Stats()
val stats = specialist.cloneStats() val stats = specialist.cloneStats()
for (unique in localUniqueCache.get(UniqueType.StatsFromSpecialist.name, city.getMatchingUniques(UniqueType.StatsFromSpecialist))) for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromSpecialist))
if (city.matchesFilter(unique.params[1])) if (city.matchesFilter(unique.params[1]))
stats.add(unique.stats) stats.add(unique.stats)
for (unique in localUniqueCache.get(UniqueType.StatsFromObject.name, city.civ.getMatchingUniques(UniqueType.StatsFromObject))) for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromObject))
if (unique.params[1] == specialistName) if (unique.params[1] == specialistName)
stats.add(unique.stats) stats.add(unique.stats)
return stats return stats

View File

@ -13,7 +13,10 @@ import com.unciv.ui.components.extensions.toPercent
class TileStatFunctions(val tile: Tile) { class TileStatFunctions(val tile: Tile) {
fun getTileStats(observingCiv: Civilization?): Stats = getTileStats(tile.getCity(), observingCiv) fun getTileStats(
observingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
): Stats = getTileStats(tile.getCity(), observingCiv, localUniqueCache)
fun getTileStats(city: City?, observingCiv: Civilization?, fun getTileStats(city: City?, observingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false) localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
@ -24,19 +27,21 @@ class TileStatFunctions(val tile: Tile) {
val stateForConditionals = StateForConditionals(civInfo = observingCiv, city = city, tile = tile) val stateForConditionals = StateForConditionals(civInfo = observingCiv, city = city, tile = tile)
if (city != null) { if (city != null) {
var tileUniques = city.getMatchingUniques(UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals) var tileUniques =
.filter { city.matchesFilter(it.params[2]) } localUniqueCache.forCityGetMatchingUniques(
tileUniques += city.getMatchingUniques(UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals) city, UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals)
for (unique in localUniqueCache.get("StatsFromTilesAndObjects", tileUniques)) { .filter { city.matchesFilter(it.params[2]) }
tileUniques += localUniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals)
for (unique in tileUniques) {
if (!unique.conditionalsApply(stateForConditionals)) continue if (!unique.conditionalsApply(stateForConditionals)) continue
val tileType = unique.params[1] val tileType = unique.params[1]
if (!tile.matchesTerrainFilter(tileType, observingCiv)) continue if (!tile.matchesTerrainFilter(tileType, observingCiv)) continue
stats.add(unique.stats) stats.add(unique.stats)
} }
for (unique in localUniqueCache.get("StatsFromTilesWithout", for (unique in localUniqueCache.forCityGetMatchingUniques(
city.getMatchingUniques(UniqueType.StatsFromTilesWithout, StateForConditionals.IgnoreConditionals)) city, UniqueType.StatsFromTilesWithout, StateForConditionals.IgnoreConditionals)) {
) {
if ( if (
unique.conditionalsApply(stateForConditionals) && unique.conditionalsApply(stateForConditionals) &&
tile.matchesTerrainFilter(unique.params[1]) && tile.matchesTerrainFilter(unique.params[1]) &&
@ -109,19 +114,19 @@ class TileStatFunctions(val tile: Tile) {
if (city != null) { if (city != null) {
// Since the tile changes every time, we cache all uniques, and filter by conditional state only when iterating // Since the tile changes every time, we cache all uniques, and filter by conditional state only when iterating
val cachedStatPercentFromObjectUniques = uniqueCache.get(UniqueType.StatPercentFromObject.name, val cachedStatPercentFromObjectCityUniques = uniqueCache.forCityGetMatchingUniques(
city.getMatchingUniques(UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals)) city, UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals)
for (unique in cachedStatPercentFromObjectUniques) { for (unique in cachedStatPercentFromObjectCityUniques) {
if (!unique.conditionalsApply(stateForConditionals)) continue if (!unique.conditionalsApply(stateForConditionals)) continue
val tileFilter = unique.params[2] val tileFilter = unique.params[2]
if (tile.matchesTerrainFilter(tileFilter, observingCiv)) if (tile.matchesTerrainFilter(tileFilter, observingCiv))
stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat() stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat()
} }
val cachedAllStatPercentFromObjectUniques = uniqueCache.get(UniqueType.AllStatsPercentFromObject.name, val cachedAllStatPercentFromObjectCityUniques = uniqueCache.forCityGetMatchingUniques(
city.getMatchingUniques(UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals)) city, UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals)
for (unique in cachedAllStatPercentFromObjectUniques) { for (unique in cachedAllStatPercentFromObjectCityUniques) {
if (!unique.conditionalsApply(stateForConditionals)) continue if (!unique.conditionalsApply(stateForConditionals)) continue
val tileFilter = unique.params[1] val tileFilter = unique.params[1]
if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue
@ -131,13 +136,17 @@ class TileStatFunctions(val tile: Tile) {
} }
} else if (observingCiv != null) { } else if (observingCiv != null) {
for (unique in observingCiv.getMatchingUniques(UniqueType.StatPercentFromObject, stateForConditionals)) { val cachedStatPercentFromObjectCivUniques = uniqueCache.forCivGetMatchingUniques(
observingCiv, UniqueType.StatPercentFromObject, stateForConditionals)
for (unique in cachedStatPercentFromObjectCivUniques) {
val tileFilter = unique.params[2] val tileFilter = unique.params[2]
if (tile.matchesTerrainFilter(tileFilter, observingCiv)) if (tile.matchesTerrainFilter(tileFilter, observingCiv))
stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat() stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat()
} }
for (unique in observingCiv.getMatchingUniques(UniqueType.AllStatsPercentFromObject, stateForConditionals)) { val cachedAllStatPercentFromObjectCivUniques = uniqueCache.forCivGetMatchingUniques(
observingCiv, UniqueType.AllStatsPercentFromObject, stateForConditionals)
for (unique in cachedAllStatPercentFromObjectCivUniques) {
val tileFilter = unique.params[1] val tileFilter = unique.params[1]
if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue
val statPercentage = unique.params[0].toFloat() val statPercentage = unique.params[0].toFloat()
@ -222,7 +231,7 @@ class TileStatFunctions(val tile: Tile) {
improvement: TileImprovement, improvement: TileImprovement,
city: City, city: City,
conditionalState: StateForConditionals, conditionalState: StateForConditionals,
cityUniqueCache: LocalUniqueCache uniqueCache: LocalUniqueCache
): Stats { ): Stats {
val stats = Stats() val stats = Stats()
@ -231,9 +240,8 @@ class TileStatFunctions(val tile: Tile) {
// therefore if we want the cache to be useful it needs to hold the pre-filtered uniques, // therefore if we want the cache to be useful it needs to hold the pre-filtered uniques,
// and then for each improvement we'll filter the uniques locally. // and then for each improvement we'll filter the uniques locally.
// This is still a MASSIVE save of RAM! // This is still a MASSIVE save of RAM!
val tileUniques = cityUniqueCache.get(UniqueType.StatsFromTiles.name, val tileUniques = uniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals)
city.getMatchingUniques(UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals) .filter { city.matchesFilter(it.params[2]) } // These are the uniques for all improvements for this city,
.filter { city.matchesFilter(it.params[2]) }) // These are the uniques for all improvements for this city,
.filter { it.conditionalsApply(conditionalState) } // ...and this is those with applicable conditions .filter { it.conditionalsApply(conditionalState) } // ...and this is those with applicable conditions
val improvementUniques = val improvementUniques =
improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, conditionalState) improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, conditionalState)
@ -250,9 +258,11 @@ class TileStatFunctions(val tile: Tile) {
fun statsFromObject() { fun statsFromObject() {
// Same as above - cache holds unfiltered uniques for the city, while we use only the filtered ones // Same as above - cache holds unfiltered uniques for the city, while we use only the filtered ones
val uniques = cityUniqueCache.get(UniqueType.StatsFromObject.name, val uniques = uniqueCache.forCityGetMatchingUniques(
city.getMatchingUniques(UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals)) city,
.filter { it.conditionalsApply(conditionalState) } UniqueType.StatsFromObject,
StateForConditionals.IgnoreConditionals
).filter { it.conditionalsApply(conditionalState) }
for (unique in uniques) { for (unique in uniques) {
if (improvement.matchesFilter(unique.params[1])) { if (improvement.matchesFilter(unique.params[1])) {
stats.add(unique.stats) stats.add(unique.stats)
@ -280,9 +290,11 @@ class TileStatFunctions(val tile: Tile) {
if (city != null) { if (city != null) {
// As above, since the conditional is tile-dependant, // As above, since the conditional is tile-dependant,
// we save uniques in the cache without conditional filtering, and use only filtered ones // we save uniques in the cache without conditional filtering, and use only filtered ones
val allStatPercentUniques = cityUniqueCache.get(UniqueType.AllStatsPercentFromObject.name, val allStatPercentUniques = cityUniqueCache.forCityGetMatchingUniques(
city.getMatchingUniques(UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals)) city,
.filter { it.conditionalsApply(conditionalState) } UniqueType.AllStatsPercentFromObject,
StateForConditionals.IgnoreConditionals
).filter { it.conditionalsApply(conditionalState) }
for (unique in allStatPercentUniques) { for (unique in allStatPercentUniques) {
if (!improvement.matchesFilter(unique.params[1])) continue if (!improvement.matchesFilter(unique.params[1])) continue
for (stat in Stat.values()) { for (stat in Stat.values()) {
@ -291,9 +303,11 @@ class TileStatFunctions(val tile: Tile) {
} }
// Same trick different unique - not sure if worth generalizing this 'late apply' of conditions? // Same trick different unique - not sure if worth generalizing this 'late apply' of conditions?
val statPercentUniques = cityUniqueCache.get(UniqueType.StatPercentFromObject.name, val statPercentUniques = cityUniqueCache.forCityGetMatchingUniques(
city.getMatchingUniques(UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals)) city,
.filter { it.conditionalsApply(conditionalState) } UniqueType.StatPercentFromObject,
StateForConditionals.IgnoreConditionals
).filter { it.conditionalsApply(conditionalState) }
for (unique in statPercentUniques) { for (unique in statPercentUniques) {
if (!improvement.matchesFilter(unique.params[2])) continue if (!improvement.matchesFilter(unique.params[2])) continue

View File

@ -184,7 +184,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
// Calls the clone function of the NamedStats this class is derived from, not a clone function of this class // Calls the clone function of the NamedStats this class is derived from, not a clone function of this class
val stats = cloneStats() val stats = cloneStats()
for (unique in localUniqueCache.get("StatsFromObject", city.getMatchingUniques(UniqueType.StatsFromObject))) { for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromObject)) {
if (!matchesFilter(unique.params[1])) continue if (!matchesFilter(unique.params[1])) continue
stats.add(unique.stats) stats.add(unique.stats)
} }
@ -193,7 +193,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
stats.add(unique.stats) stats.add(unique.stats)
if (!isWonder) if (!isWonder)
for (unique in localUniqueCache.get("StatsFromBuildings", city.getMatchingUniques(UniqueType.StatsFromBuildings))) { for (unique in localUniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromBuildings)) {
if (matchesFilter(unique.params[1])) if (matchesFilter(unique.params[1]))
stats.add(unique.stats) stats.add(unique.stats)
} }
@ -204,12 +204,12 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
val stats = percentStatBonus?.clone() ?: Stats() val stats = percentStatBonus?.clone() ?: Stats()
val civInfo = city?.civ ?: return stats // initial stats val civInfo = city?.civ ?: return stats // initial stats
for (unique in localUniqueCache.get("StatPercentFromObject", civInfo.getMatchingUniques(UniqueType.StatPercentFromObject))) { for (unique in localUniqueCache.forCivGetMatchingUniques(civInfo, UniqueType.StatPercentFromObject)) {
if (matchesFilter(unique.params[2])) if (matchesFilter(unique.params[2]))
stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat()) stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
} }
for (unique in localUniqueCache.get("AllStatsPercentFromObject", civInfo.getMatchingUniques(UniqueType.AllStatsPercentFromObject))) { for (unique in localUniqueCache.forCivGetMatchingUniques(civInfo, UniqueType.AllStatsPercentFromObject)) {
if (!matchesFilter(unique.params[1])) continue if (!matchesFilter(unique.params[1])) continue
for (stat in Stat.values()) { for (stat in Stat.values()) {
stats.add(stat, unique.params[0].toFloat()) stats.add(stat, unique.params[0].toFloat())

View File

@ -301,8 +301,35 @@ class LocalUniqueCache(val cache:Boolean = true) {
// This stores sequences *that iterate directly on a list* - that is, pre-resolved // This stores sequences *that iterate directly on a list* - that is, pre-resolved
private val keyToUniques = HashMap<String, Sequence<Unique>>() private val keyToUniques = HashMap<String, Sequence<Unique>>()
fun forCityGetMatchingUniques(
city: City,
uniqueType: UniqueType,
stateForConditionals: StateForConditionals = StateForConditionals(
city.civ,
city
)
): Sequence<Unique> {
return get(
"city-${city.id}-${uniqueType.name}-${stateForConditionals}",
city.getMatchingUniques(uniqueType, stateForConditionals)
)
}
fun forCivGetMatchingUniques(
civ: Civilization,
uniqueType: UniqueType,
stateForConditionals: StateForConditionals = StateForConditionals(
civ
)
): Sequence<Unique> {
return get(
"civ-${civ.civName}-${uniqueType.name}-${stateForConditionals}",
civ.getMatchingUniques(uniqueType, stateForConditionals)
)
}
/** Get cached results as a sequence */ /** Get cached results as a sequence */
fun get(key: String, sequence: Sequence<Unique>): Sequence<Unique> { private fun get(key: String, sequence: Sequence<Unique>): Sequence<Unique> {
if (!cache) return sequence if (!cache) return sequence
if (keyToUniques.containsKey(key)) return keyToUniques[key]!! if (keyToUniques.containsKey(key)) return keyToUniques[key]!!
// Iterate the sequence, save actual results as a list, as return a sequence to that // Iterate the sequence, save actual results as a list, as return a sequence to that

View File

@ -8,6 +8,7 @@ import com.unciv.UncivGame
import com.unciv.logic.city.City import com.unciv.logic.city.City
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.extensions.addToCenter import com.unciv.ui.components.extensions.addToCenter
@ -31,8 +32,8 @@ class CityTileGroup(val city: City, tile: Tile, tileSetStrings: TileSetStrings)
layerMisc.touchable = Touchable.childrenOnly layerMisc.touchable = Touchable.childrenOnly
} }
override fun update(viewingCiv: Civilization?) { override fun update(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
super.update(city.civ) super.update(city.civ, localUniqueCache)
tileState = CityTileState.NONE tileState = CityTileState.NONE

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.scenes.scene2d.Group
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.components.tilegroups.layers.TileLayerBorders import com.unciv.ui.components.tilegroups.layers.TileLayerBorders
import com.unciv.ui.components.tilegroups.layers.TileLayerCityButton import com.unciv.ui.components.tilegroups.layers.TileLayerCityButton
import com.unciv.ui.components.tilegroups.layers.TileLayerFeatures import com.unciv.ui.components.tilegroups.layers.TileLayerFeatures
@ -74,10 +75,10 @@ open class TileGroup(
|| viewingCiv.viewableTiles.contains(tile) || viewingCiv.viewableTiles.contains(tile)
|| viewingCiv.isSpectator() || viewingCiv.isSpectator()
private fun reset() { private fun reset(localUniqueCache: LocalUniqueCache) {
layerTerrain.reset() layerTerrain.reset()
layerBorders.reset() layerBorders.reset()
layerMisc.reset() layerMisc.reset(localUniqueCache)
layerOverlay.reset() layerOverlay.reset()
layerUnitArt.reset() layerUnitArt.reset()
layerUnitFlag.reset() layerUnitFlag.reset()
@ -94,8 +95,9 @@ open class TileGroup(
layerCityButton.isVisible = isVisible layerCityButton.isVisible = isVisible
} }
open fun update(viewingCiv: Civilization? = null) { open fun update(
viewingCiv: Civilization? = null,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)) {
layerMisc.removeHexOutline() layerMisc.removeHexOutline()
layerMisc.hideTerrainOverlay() layerMisc.hideTerrainOverlay()
layerOverlay.hideHighlight() layerOverlay.hideHighlight()
@ -107,7 +109,7 @@ open class TileGroup(
// Do not update layers if tile is not explored by viewing player // Do not update layers if tile is not explored by viewing player
if (viewingCiv != null && !(isForceVisible || viewingCiv.hasExplored(tile))) { if (viewingCiv != null && !(isForceVisible || viewingCiv.hasExplored(tile))) {
reset() reset(localUniqueCache)
// If tile has explored neighbors - reveal layers partially // If tile has explored neighbors - reveal layers partially
if (tile.neighbors.none { viewingCiv.hasExplored(it) }) if (tile.neighbors.none { viewingCiv.hasExplored(it) })
// Else - hide all layers // Else - hide all layers
@ -117,14 +119,14 @@ open class TileGroup(
removeMissingModReferences() removeMissingModReferences()
layerTerrain.update(viewingCiv) layerTerrain.update(viewingCiv, localUniqueCache)
layerFeatures.update(viewingCiv) layerFeatures.update(viewingCiv, localUniqueCache)
layerBorders.update(viewingCiv) layerBorders.update(viewingCiv, localUniqueCache)
layerOverlay.update(viewingCiv) layerOverlay.update(viewingCiv, localUniqueCache)
layerMisc.update(viewingCiv) layerMisc.update(viewingCiv, localUniqueCache)
layerUnitArt.update(viewingCiv) layerUnitArt.update(viewingCiv, localUniqueCache)
layerUnitFlag.update(viewingCiv) layerUnitFlag.update(viewingCiv, localUniqueCache)
layerCityButton.update(viewingCiv) layerCityButton.update(viewingCiv, localUniqueCache)
} }
private fun removeMissingModReferences() { private fun removeMissingModReferences() {

View File

@ -5,10 +5,10 @@ import com.badlogic.gdx.scenes.scene2d.Touchable
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.extensions.center import com.unciv.ui.components.extensions.center
import com.unciv.ui.components.extensions.darken import com.unciv.ui.components.extensions.darken
import com.unciv.ui.screens.worldscreen.WorldScreen
class WorldTileGroup(tile: Tile, tileSetStrings: TileSetStrings) class WorldTileGroup(tile: Tile, tileSetStrings: TileSetStrings)
@ -18,8 +18,8 @@ class WorldTileGroup(tile: Tile, tileSetStrings: TileSetStrings)
layerMisc.touchable = Touchable.disabled layerMisc.touchable = Touchable.disabled
} }
override fun update(viewingCiv: Civilization?) { override fun update(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
super.update(viewingCiv) super.update(viewingCiv, localUniqueCache)
updateWorkedIcon(viewingCiv!!) updateWorkedIcon(viewingCiv!!)
} }

View File

@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Image import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.models.tilesets.TileSetCache import com.unciv.models.tilesets.TileSetCache
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
import com.unciv.ui.components.tilegroups.TileSetStrings import com.unciv.ui.components.tilegroups.TileSetStrings
@ -32,8 +33,10 @@ abstract class TileLayer(val tileGroup: TileGroup, size: Float) : Group() {
fun isViewable(viewingCiv: Civilization) = tileGroup.isViewable(viewingCiv) fun isViewable(viewingCiv: Civilization) = tileGroup.isViewable(viewingCiv)
fun update(viewingCiv: Civilization?) { fun update(
doUpdate(viewingCiv) viewingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)) {
doUpdate(viewingCiv, localUniqueCache)
determineVisibility() determineVisibility()
} }
@ -41,6 +44,8 @@ abstract class TileLayer(val tileGroup: TileGroup, size: Float) : Group() {
isVisible = hasChildren() isVisible = hasChildren()
} }
protected abstract fun doUpdate(viewingCiv: Civilization?) protected abstract fun doUpdate(
viewingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false))
} }

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Image import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
import kotlin.math.PI import kotlin.math.PI
@ -135,7 +136,7 @@ class TileLayerBorders(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup,
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
updateBorders() updateBorders()
} }

View File

@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.components.tilegroups.CityButton import com.unciv.ui.components.tilegroups.CityButton
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
import com.unciv.ui.components.tilegroups.WorldTileGroup import com.unciv.ui.components.tilegroups.WorldTileGroup
@ -43,7 +44,7 @@ class TileLayerCityButton(tileGroup: TileGroup, size: Float) : TileLayer(tileGro
cityButton?.moveButtonDown() cityButton?.moveButtonDown()
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
if (tileGroup !is WorldTileGroup) if (tileGroup !is WorldTileGroup)
return return

View File

@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.RoadStatus import com.unciv.logic.map.tile.RoadStatus
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
import kotlin.math.atan2 import kotlin.math.atan2
@ -69,7 +70,7 @@ class TileLayerFeatures(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
updateRoadImages() updateRoadImages()
} }

View File

@ -13,6 +13,7 @@ import com.unciv.models.helpers.MapArrowType
import com.unciv.models.helpers.MiscArrowTypes import com.unciv.models.helpers.MiscArrowTypes
import com.unciv.models.helpers.TintedMapArrow import com.unciv.models.helpers.TintedMapArrow
import com.unciv.models.helpers.UnitMovementMemoryType import com.unciv.models.helpers.UnitMovementMemoryType
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.components.extensions.center import com.unciv.ui.components.extensions.center
import com.unciv.ui.components.extensions.centerX import com.unciv.ui.components.extensions.centerX
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
@ -268,7 +269,11 @@ class TileLayerMisc(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup, si
} }
// JN updating display of tile yields // JN updating display of tile yields
private fun updateYieldIcon(viewingCiv: Civilization?, show: Boolean) { private fun updateYieldIcon(
viewingCiv: Civilization?,
show: Boolean,
localUniqueCache: LocalUniqueCache
) {
val effectiveVisible = show && val effectiveVisible = show &&
!tileGroup.isForMapEditorIcon && // don't have a map to calc yields !tileGroup.isForMapEditorIcon && // don't have a map to calc yields
!(viewingCiv == null && tileGroup.isForceVisible) // main menu background !(viewingCiv == null && tileGroup.isForceVisible) // main menu background
@ -278,9 +283,9 @@ class TileLayerMisc(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup, si
if (effectiveVisible) yields.run { if (effectiveVisible) yields.run {
// Update YieldGroup Icon // Update YieldGroup Icon
if (tileGroup is CityTileGroup) if (tileGroup is CityTileGroup)
setStats(tile().stats.getTileStats(tileGroup.city, viewingCiv)) setStats(tile().stats.getTileStats(tileGroup.city, viewingCiv, localUniqueCache))
else else
setStats(tile().stats.getTileStats(viewingCiv)) setStats(tile().stats.getTileStats(viewingCiv, localUniqueCache))
toFront() toFront()
centerX(tileGroup) centerX(tileGroup)
isVisible = true isVisible = true
@ -345,7 +350,7 @@ class TileLayerMisc(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup, si
determineVisibility() determineVisibility()
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
var showResourcesAndImprovements = true var showResourcesAndImprovements = true
var showTileYields = true var showTileYields = true
@ -356,7 +361,7 @@ class TileLayerMisc(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup, si
} }
updateImprovementIcon(viewingCiv, showResourcesAndImprovements) updateImprovementIcon(viewingCiv, showResourcesAndImprovements)
updateYieldIcon(viewingCiv, showTileYields) updateYieldIcon(viewingCiv, showTileYields, localUniqueCache)
updateResourceIcon(viewingCiv, showResourcesAndImprovements) updateResourceIcon(viewingCiv, showResourcesAndImprovements)
if (tileGroup !is WorldTileGroup || DebugUtils.SHOW_TILE_COORDS) if (tileGroup !is WorldTileGroup || DebugUtils.SHOW_TILE_COORDS)
updateStartingLocationIcon(true) updateStartingLocationIcon(true)
@ -374,9 +379,9 @@ class TileLayerMisc(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup, si
|| terrainOverlay.isVisible || terrainOverlay.isVisible
} }
fun reset() { fun reset(localUniqueCache: LocalUniqueCache) {
updateImprovementIcon(null, false) updateImprovementIcon(null, false)
updateYieldIcon(null, false) updateYieldIcon(null, false, localUniqueCache)
updateResourceIcon(null, false) updateResourceIcon(null, false)
updateStartingLocationIcon(false) updateStartingLocationIcon(false)
clearArrows() clearArrows()

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Actor
import com.unciv.Constants import com.unciv.Constants
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
@ -79,7 +80,7 @@ class TileLayerOverlay(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup,
determineVisibility() determineVisibility()
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
val isViewable = viewingCiv == null || isViewable(viewingCiv) val isViewable = viewingCiv == null || isViewable(viewingCiv)
fog.isVisible = !isViewable && !tileGroup.isForceVisible fog.isVisible = !isViewable && !tileGroup.isForceVisible

View File

@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.Tile import com.unciv.logic.map.tile.Tile
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
@ -194,7 +195,7 @@ class TileLayerTerrain(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup,
} }
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
updateTileImage(viewingCiv) updateTileImage(viewingCiv)
updateRivers(tileGroup.tile.hasBottomRightRiver, tileGroup.tile.hasBottomRiver, tileGroup.tile.hasBottomLeftRiver) updateRivers(tileGroup.tile.hasBottomRightRiver, tileGroup.tile.hasBottomRiver, tileGroup.tile.hasBottomLeftRiver)
updateTileColor(viewingCiv) updateTileColor(viewingCiv)

View File

@ -5,6 +5,7 @@ import com.badlogic.gdx.scenes.scene2d.Group
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.mapunit.MapUnit import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
@ -63,7 +64,7 @@ class TileLayerUnitArt(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup,
color.a = 0.5f color.a = 0.5f
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
val slot1Unit = tileGroup.tile.civilianUnit val slot1Unit = tileGroup.tile.civilianUnit
val slot2Unit = tileGroup.tile.militaryUnit val slot2Unit = tileGroup.tile.militaryUnit

View File

@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.mapunit.MapUnit import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.tilegroups.TileGroup import com.unciv.ui.components.tilegroups.TileGroup
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
@ -133,7 +134,7 @@ class TileLayerUnitFlag(tileGroup: TileGroup, size: Float) : TileLayer(tileGroup
} }
override fun doUpdate(viewingCiv: Civilization?) { override fun doUpdate(viewingCiv: Civilization?, localUniqueCache: LocalUniqueCache) {
clearSlots() clearSlots()
fillSlots(viewingCiv) fillSlots(viewingCiv)

View File

@ -32,6 +32,7 @@ import com.unciv.logic.map.tile.Tile
import com.unciv.models.UncivSound import com.unciv.models.UncivSound
import com.unciv.models.helpers.MapArrowType import com.unciv.models.helpers.MapArrowType
import com.unciv.models.helpers.MiscArrowTypes import com.unciv.models.helpers.MiscArrowTypes
import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.ui.audio.SoundPlayer import com.unciv.ui.audio.SoundPlayer
import com.unciv.ui.components.KeyCharAndCode import com.unciv.ui.components.KeyCharAndCode
@ -567,8 +568,9 @@ class WorldMapHolder(
} }
// General update of all tiles // General update of all tiles
val uniqueCache = LocalUniqueCache(true)
for (tileGroup in tileGroups.values) for (tileGroup in tileGroups.values)
tileGroup.update(viewingCiv) tileGroup.update(viewingCiv, uniqueCache)
// Update tiles according to selected unit/city // Update tiles according to selected unit/city
val unitTable = worldScreen.bottomUnitTable val unitTable = worldScreen.bottomUnitTable