From 38f544a35db8fda07ddc3b7d941f9b682ab46edf Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Thu, 2 Feb 2023 20:22:13 +0200 Subject: [PATCH] chore: improvement stat functions are more related to 'stat' than to 'improvement' --- .../com/unciv/logic/automation/Automation.kt | 4 +- .../map/tile/TileInfoImprovementFunctions.kt | 136 ----------------- .../unciv/logic/map/tile/TileStatFunctions.kt | 140 +++++++++++++++++- .../src/com/unciv/ui/cityscreen/CityScreen.kt | 4 +- .../pickerscreens/ImprovementPickerScreen.kt | 4 +- 5 files changed, 145 insertions(+), 143 deletions(-) diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index dbf050a591..92cd3738fd 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -1,7 +1,7 @@ package com.unciv.logic.automation -import com.unciv.logic.city.CityFocus import com.unciv.logic.city.City +import com.unciv.logic.city.CityFocus import com.unciv.logic.city.INonPerpetualConstruction import com.unciv.logic.civilization.Civilization import com.unciv.logic.map.BFS @@ -389,7 +389,7 @@ object Automation { // Improvements are good: less points if (tile.improvement != null && - tile.improvementFunctions.getImprovementStats( + tile.stats.getImprovementStats( tile.getTileImprovement()!!, city.civ, city, diff --git a/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt b/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt index bd4387f70a..99e534da39 100644 --- a/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt +++ b/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt @@ -169,142 +169,6 @@ class TileInfoImprovementFunctions(val tile: Tile) { } - // Also multiplies the stats by the percentage bonus for improvements (but not for tiles) - fun getImprovementStats( - improvement: TileImprovement, - observingCiv: Civilization, - city: City?, - cityUniqueCache: LocalUniqueCache = LocalUniqueCache(false) - ): Stats { - val stats = improvement.cloneStats() - if (tile.hasViewableResource(observingCiv) && tile.tileResource.isImprovedBy(improvement.name) - && tile.tileResource.improvementStats != null - ) - stats.add(tile.tileResource.improvementStats!!.clone()) // resource-specific improvement - - val conditionalState = StateForConditionals(civInfo = observingCiv, city = city, tile = tile) - for (unique in improvement.getMatchingUniques(UniqueType.Stats, conditionalState)) { - stats.add(unique.stats) - } - - for (unique in improvement.getMatchingUniques(UniqueType.ImprovementStatsForAdjacencies, conditionalState)) { - val adjacent = unique.params[1] - val numberOfBonuses = tile.neighbors.count { - it.matchesFilter(adjacent, observingCiv) - || it.getUnpillagedRoad().name == adjacent - } - stats.add(unique.stats.times(numberOfBonuses.toFloat())) - } - - if (city != null) stats.add(getImprovementStatsForCity(improvement, city, conditionalState, cityUniqueCache)) - - for ((stat, value) in getImprovementPercentageStats(improvement, observingCiv, city, cityUniqueCache)) { - stats[stat] *= value.toPercent() - } - - return stats - } - - private fun getImprovementStatsForCity( - improvement: TileImprovement, - city: City, - conditionalState: StateForConditionals, - cityUniqueCache: LocalUniqueCache - ): Stats { - val stats = Stats() - - fun statsFromTiles(){ - // Since the conditionalState contains the current tile, it is different for each tile, - // 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. - // This is still a MASSIVE save of RAM! - val tileUniques = cityUniqueCache.get(UniqueType.StatsFromTiles.name, - city.getMatchingUniques(UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals) - .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 - val improvementUniques = - improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, conditionalState) - - for (unique in tileUniques + improvementUniques) { - if (improvement.matchesFilter(unique.params[1]) - || unique.params[1] == Constants.freshWater && tile.isAdjacentTo(Constants.freshWater) - || unique.params[1] == "non-fresh water" && !tile.isAdjacentTo(Constants.freshWater) - ) - stats.add(unique.stats) - } - } - statsFromTiles() - - fun statsFromObject() { - // Same as above - cache holds unfiltered uniques for the city, while we use only the filtered ones - val uniques = cityUniqueCache.get(UniqueType.StatsFromObject.name, - city.getMatchingUniques(UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals)) - .filter { it.conditionalsApply(conditionalState) } - for (unique in uniques) { - if (improvement.matchesFilter(unique.params[1])) { - stats.add(unique.stats) - } - } - } - statsFromObject() - return stats - } - - @Suppress("MemberVisibilityCanBePrivate") - fun getImprovementPercentageStats( - improvement: TileImprovement, - observingCiv: Civilization, - city: City?, - cityUniqueCache: LocalUniqueCache - ): Stats { - val stats = Stats() - val conditionalState = StateForConditionals(civInfo = observingCiv, city = city, tile = tile) - - // I would love to make an interface 'canCallMatchingUniques' - // from which both cityInfo and CivilizationInfo derive, so I don't have to duplicate all this code - // But something something too much for this PR. - - if (city != null) { - // As above, since the conditional is tile-dependant, - // we save uniques in the cache without conditional filtering, and use only filtered ones - val allStatPercentUniques = cityUniqueCache.get(UniqueType.AllStatsPercentFromObject.name, - city.getMatchingUniques(UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals)) - .filter { it.conditionalsApply(conditionalState) } - for (unique in allStatPercentUniques) { - if (!improvement.matchesFilter(unique.params[1])) continue - for (stat in Stat.values()) { - stats[stat] += unique.params[0].toFloat() - } - } - - // Same trick different unique - not sure if worth generalizing this 'late apply' of conditions? - val statPercentUniques = cityUniqueCache.get(UniqueType.StatPercentFromObject.name, - city.getMatchingUniques(UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals)) - .filter { it.conditionalsApply(conditionalState) } - - for (unique in statPercentUniques) { - if (!improvement.matchesFilter(unique.params[2])) continue - val stat = Stat.valueOf(unique.params[1]) - stats[stat] += unique.params[0].toFloat() - } - - } else { - for (unique in observingCiv.getMatchingUniques(UniqueType.AllStatsPercentFromObject, conditionalState)) { - if (!improvement.matchesFilter(unique.params[1])) continue - for (stat in Stat.values()) { - stats[stat] += unique.params[0].toFloat() - } - } - for (unique in observingCiv.getMatchingUniques(UniqueType.StatPercentFromObject, conditionalState)) { - if (!improvement.matchesFilter(unique.params[2])) continue - val stat = Stat.valueOf(unique.params[1]) - stats[stat] += unique.params[0].toFloat() - } - } - - return stats - } - /** Marks tile as target tile for a building with a [UniqueType.CreatesOneImprovement] unique */ fun markForCreatesOneImprovement(improvement: String) { diff --git a/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt b/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt index f825c805d8..6a619452c3 100644 --- a/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt +++ b/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt @@ -3,6 +3,7 @@ package com.unciv.logic.map.tile import com.unciv.Constants import com.unciv.logic.city.City import com.unciv.logic.civilization.Civilization +import com.unciv.models.ruleset.tile.TileImprovement import com.unciv.models.ruleset.unique.LocalUniqueCache import com.unciv.models.ruleset.unique.StateForConditionals import com.unciv.models.ruleset.unique.UniqueType @@ -71,7 +72,7 @@ class TileStatFunctions(val tile: Tile) { val improvement = tile.getUnpillagedTileImprovement() if (improvement != null) - stats.add(tile.improvementFunctions.getImprovementStats(improvement, observingCiv, city, localUniqueCache)) + stats.add(getImprovementStats(improvement, observingCiv, city, localUniqueCache)) if (stats.gold != 0f && observingCiv.goldenAges.isGoldenAge()) stats.gold++ @@ -174,4 +175,141 @@ class TileStatFunctions(val tile: Tile) { return stats.food + stats.production + stats.gold } + + // Also multiplies the stats by the percentage bonus for improvements (but not for tiles) + fun getImprovementStats( + improvement: TileImprovement, + observingCiv: Civilization, + city: City?, + cityUniqueCache: LocalUniqueCache = LocalUniqueCache(false) + ): Stats { + val stats = improvement.cloneStats() + if (tile.hasViewableResource(observingCiv) && tile.tileResource.isImprovedBy(improvement.name) + && tile.tileResource.improvementStats != null + ) + stats.add(tile.tileResource.improvementStats!!.clone()) // resource-specific improvement + + val conditionalState = StateForConditionals(civInfo = observingCiv, city = city, tile = tile) + for (unique in improvement.getMatchingUniques(UniqueType.Stats, conditionalState)) { + stats.add(unique.stats) + } + + for (unique in improvement.getMatchingUniques(UniqueType.ImprovementStatsForAdjacencies, conditionalState)) { + val adjacent = unique.params[1] + val numberOfBonuses = tile.neighbors.count { + it.matchesFilter(adjacent, observingCiv) + || it.getUnpillagedRoad().name == adjacent + } + stats.add(unique.stats.times(numberOfBonuses.toFloat())) + } + + if (city != null) stats.add(getImprovementStatsForCity(improvement, city, conditionalState, cityUniqueCache)) + + for ((stat, value) in getImprovementPercentageStats(improvement, observingCiv, city, cityUniqueCache)) { + stats[stat] *= value.toPercent() + } + + return stats + } + + private fun getImprovementStatsForCity( + improvement: TileImprovement, + city: City, + conditionalState: StateForConditionals, + cityUniqueCache: LocalUniqueCache + ): Stats { + val stats = Stats() + + fun statsFromTiles(){ + // Since the conditionalState contains the current tile, it is different for each tile, + // 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. + // This is still a MASSIVE save of RAM! + val tileUniques = cityUniqueCache.get(UniqueType.StatsFromTiles.name, + city.getMatchingUniques(UniqueType.StatsFromTiles, StateForConditionals.IgnoreConditionals) + .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 + val improvementUniques = + improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, conditionalState) + + for (unique in tileUniques + improvementUniques) { + if (improvement.matchesFilter(unique.params[1]) + || unique.params[1] == Constants.freshWater && tile.isAdjacentTo(Constants.freshWater) + || unique.params[1] == "non-fresh water" && !tile.isAdjacentTo(Constants.freshWater) + ) + stats.add(unique.stats) + } + } + statsFromTiles() + + fun statsFromObject() { + // Same as above - cache holds unfiltered uniques for the city, while we use only the filtered ones + val uniques = cityUniqueCache.get(UniqueType.StatsFromObject.name, + city.getMatchingUniques(UniqueType.StatsFromObject, StateForConditionals.IgnoreConditionals)) + .filter { it.conditionalsApply(conditionalState) } + for (unique in uniques) { + if (improvement.matchesFilter(unique.params[1])) { + stats.add(unique.stats) + } + } + } + statsFromObject() + return stats + } + + @Suppress("MemberVisibilityCanBePrivate") + fun getImprovementPercentageStats( + improvement: TileImprovement, + observingCiv: Civilization, + city: City?, + cityUniqueCache: LocalUniqueCache + ): Stats { + val stats = Stats() + val conditionalState = StateForConditionals(civInfo = observingCiv, city = city, tile = tile) + + // I would love to make an interface 'canCallMatchingUniques' + // from which both cityInfo and CivilizationInfo derive, so I don't have to duplicate all this code + // But something something too much for this PR. + + if (city != null) { + // As above, since the conditional is tile-dependant, + // we save uniques in the cache without conditional filtering, and use only filtered ones + val allStatPercentUniques = cityUniqueCache.get(UniqueType.AllStatsPercentFromObject.name, + city.getMatchingUniques(UniqueType.AllStatsPercentFromObject, StateForConditionals.IgnoreConditionals)) + .filter { it.conditionalsApply(conditionalState) } + for (unique in allStatPercentUniques) { + if (!improvement.matchesFilter(unique.params[1])) continue + for (stat in Stat.values()) { + stats[stat] += unique.params[0].toFloat() + } + } + + // Same trick different unique - not sure if worth generalizing this 'late apply' of conditions? + val statPercentUniques = cityUniqueCache.get(UniqueType.StatPercentFromObject.name, + city.getMatchingUniques(UniqueType.StatPercentFromObject, StateForConditionals.IgnoreConditionals)) + .filter { it.conditionalsApply(conditionalState) } + + for (unique in statPercentUniques) { + if (!improvement.matchesFilter(unique.params[2])) continue + val stat = Stat.valueOf(unique.params[1]) + stats[stat] += unique.params[0].toFloat() + } + + } else { + for (unique in observingCiv.getMatchingUniques(UniqueType.AllStatsPercentFromObject, conditionalState)) { + if (!improvement.matchesFilter(unique.params[1])) continue + for (stat in Stat.values()) { + stats[stat] += unique.params[0].toFloat() + } + } + for (unique in observingCiv.getMatchingUniques(UniqueType.StatPercentFromObject, conditionalState)) { + if (!improvement.matchesFilter(unique.params[2])) continue + val stat = Stat.valueOf(unique.params[1]) + stats[stat] += unique.params[0].toFloat() + } + } + + return stats + } + } diff --git a/core/src/com/unciv/ui/cityscreen/CityScreen.kt b/core/src/com/unciv/ui/cityscreen/CityScreen.kt index f6bc9066f1..d51443374f 100644 --- a/core/src/com/unciv/ui/cityscreen/CityScreen.kt +++ b/core/src/com/unciv/ui/cityscreen/CityScreen.kt @@ -197,13 +197,13 @@ class CityScreen( fun isExistingImprovementValuable(tile: Tile, improvementToPlace: TileImprovement): Boolean { if (tile.improvement == null) return false val civInfo = city.civ - val existingStats = tile.improvementFunctions.getImprovementStats( + val existingStats = tile.stats.getImprovementStats( tile.getTileImprovement()!!, civInfo, city, cityUniqueCache ) - val replacingStats = tile.improvementFunctions.getImprovementStats( + val replacingStats = tile.stats.getImprovementStats( improvementToPlace, civInfo, city, diff --git a/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt index 7ee7f0d7cc..5418eba307 100644 --- a/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt @@ -155,7 +155,7 @@ class ImprovementPickerScreen( val statIcons = getStatIconsTable(provideResource, removeImprovement) // get benefits of the new improvement - val stats = tile.improvementFunctions.getImprovementStats( + val stats = tile.stats.getImprovementStats( improvement, currentPlayerCiv, tile.getCity(), @@ -164,7 +164,7 @@ class ImprovementPickerScreen( // subtract the benefits of the replaced improvement, if any val existingImprovement = tile.getTileImprovement() if (existingImprovement != null && removeImprovement) { - val existingStats = tile.improvementFunctions.getImprovementStats( + val existingStats = tile.stats.getImprovementStats( existingImprovement, currentPlayerCiv, tile.getCity(),