diff --git a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt index 67a53974cc..8de8f8202f 100644 --- a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt @@ -253,9 +253,7 @@ object SpecificUnitAutomation { val relatedStat = improvement.maxByOrNull { it.value }?.key ?: Stat.Culture val citiesByStatBoost = unit.civInfo.cities.sortedByDescending { - val stats = Stats() - for (bonus in it.cityStats.statPercentBonusList.values) stats.add(bonus) - stats[relatedStat] + it.cityStats.statPercentBonusTree.totalStats[relatedStat] } diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 49a9890213..9caf89ad4e 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -107,16 +107,6 @@ class CityConstructions { return maintenanceCost } - /** - * @return Bonus (%) [Stats] provided by all built buildings in city - */ - fun getStatPercentBonuses(): Stats { - val stats = Stats() - for (building in getBuiltBuildings()) - stats.add(building.getStatPercentageBonuses(cityInfo)) - return stats - } - fun getCityProductionTextForCityButton(): String { val currentConstructionSnapshot = currentConstructionFromQueue // See below var result = currentConstructionSnapshot.tr() diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index ba679aa141..52ca50fd34 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -45,12 +45,13 @@ class StatTreeNode { } } - val totalStats: Stats by lazy { - val toReturn = Stats() - if (innerStats != null) toReturn.add(innerStats!!) - for (child in children.values) toReturn.add(child.totalStats) - toReturn - } + val totalStats: Stats + get() { + val toReturn = Stats() + if (innerStats != null) toReturn.add(innerStats!!) + for (child in children.values) toReturn.add(child.totalStats) + return toReturn + } } /** Holds and calculates [Stats] for a city. @@ -63,10 +64,10 @@ class CityStats(val cityInfo: CityInfo) { var baseStatTree = StatTreeNode() - var baseStatList = LinkedHashMap() - var statPercentBonusList = LinkedHashMap() + var statPercentBonusTree = StatTreeNode() + // Computed from baseStatList and statPercentBonusList - this is so the players can see a breakdown var finalStatList = LinkedHashMap() @@ -124,6 +125,12 @@ class CityStats(val cityInfo: CityInfo) { return stats } + private fun addStatPercentBonusesFromBuildings(statPercentBonusTree: StatTreeNode) { + for (building in cityInfo.cityConstructions.getBuiltBuildings()) + statPercentBonusTree.addStats(building.getStatPercentageBonuses(cityInfo), "Buildings", building.name) + } + + private fun getStatsFromCityStates(): Stats { val stats = Stats() @@ -265,10 +272,11 @@ class CityStats(val cityInfo: CityInfo) { return stats } - private fun getStatsPercentBonusesFromUniquesBySource(currentConstruction: IConstruction):StatMap { - val sourceToStats = StatMap() - fun addUniqueStats(unique: Unique, stat:Stat, amount:Float) { - sourceToStats.add(getSourceNameForUnique(unique), Stats().add(stat, amount)) + private fun getStatsPercentBonusesFromUniquesBySource(currentConstruction: IConstruction): StatTreeNode { + val sourceToStats = StatTreeNode() + + fun addUniqueStats(unique:Unique, stat:Stat, amount:Float) { + sourceToStats.addStats(Stats().add(stat, amount), getSourceNameForUnique(unique), unique.sourceObjectName ?: "") } for (unique in cityInfo.getMatchingUniques(UniqueType.StatPercentBonus)) { @@ -472,25 +480,24 @@ class CityStats(val cityInfo: CityInfo) { } - private fun updateStatPercentBonusList(currentConstruction: IConstruction, localBuildingUniques: Sequence) { - val newStatPercentBonusList = StatMap() + private fun updateStatPercentBonusList(currentConstruction: IConstruction) { + val newStatsBonusTree = StatTreeNode() - newStatPercentBonusList["Golden Age"] = getStatPercentBonusesFromGoldenAge(cityInfo.civInfo.goldenAges.isGoldenAge()) - .plus(cityInfo.cityConstructions.getStatPercentBonuses()) // This function is to be deprecated but it'll take a while. - newStatPercentBonusList["Railroads"] = getStatPercentBonusesFromRailroad() // Name chosen same as tech, for translation, but theoretically independent - newStatPercentBonusList["Puppet City"] = getStatPercentBonusesFromPuppetCity() - newStatPercentBonusList["Unit Supply"] = getStatPercentBonusesFromUnitSupply() + newStatsBonusTree.addStats(getStatPercentBonusesFromGoldenAge(cityInfo.civInfo.goldenAges.isGoldenAge()),"Golden Age") + addStatPercentBonusesFromBuildings(newStatsBonusTree) + newStatsBonusTree.addStats(getStatPercentBonusesFromRailroad(), "Railroad") + newStatsBonusTree.addStats(getStatPercentBonusesFromPuppetCity(), "Puppet City") + newStatsBonusTree.addStats(getStatPercentBonusesFromUnitSupply(), "Unit Supply") - for ((source, stats) in getStatsPercentBonusesFromUniquesBySource(currentConstruction)) - newStatPercentBonusList.add(source, stats) + newStatsBonusTree.add(getStatsPercentBonusesFromUniquesBySource(currentConstruction)) if (UncivGame.Current.superchargedForDebug) { val stats = Stats() for (stat in Stat.values()) stats[stat] = 10000f - newStatPercentBonusList["Supercharged"] = stats + newStatsBonusTree.addStats(stats, "Supercharged") } - statPercentBonusList = newStatPercentBonusList + statPercentBonusTree = newStatsBonusTree } /** Does not update tile stats - instead, updating tile stats updates this */ @@ -498,17 +505,12 @@ class CityStats(val cityInfo: CityInfo) { updateTileStats:Boolean = true) { if (updateTileStats) updateTileStats() - // We calculate this here for concurrency reasons - // If something needs this, we pass this through as a parameter - val localBuildingUniques = cityInfo.cityConstructions.builtBuildingUniqueMap.getAllUniques() - - // We need to compute Tile yields before happiness val statsFromBuildings = cityInfo.cityConstructions.getStats() // this is performance heavy, so calculate once updateBaseStatList(statsFromBuildings) updateCityHappiness(statsFromBuildings) - updateStatPercentBonusList(currentConstruction, localBuildingUniques) + updateStatPercentBonusList(currentConstruction) updateFinalStatList(currentConstruction) // again, we don't edit the existing currentCityStats directly, in order to avoid concurrency exceptions @@ -525,8 +527,7 @@ class CityStats(val cityInfo: CityInfo) { for ((key, value) in baseStatTree.children) newFinalStatList[key] = value.totalStats.clone() - val statPercentBonusesSum = Stats() - for (bonus in statPercentBonusList.values) statPercentBonusesSum.add(bonus) + val statPercentBonusesSum = statPercentBonusTree.totalStats for (entry in newFinalStatList.values) entry.production *= statPercentBonusesSum.production.toPercent() diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 631d96defc..7ed8374951 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -168,7 +168,8 @@ class TechManager { var allCitiesScience = 0f civInfo.cities.forEach { it -> val totalBaseScience = it.cityStats.baseStatTree.totalStats.science - val totalBonusPercents = it.cityStats.statPercentBonusList.filter { it.key != "Policies" }.values.map { it.science }.sum() + val totalBonusPercents = it.cityStats.statPercentBonusTree.children.asSequence() + .filter { it.key != "Policies" }.map { it.value.totalStats.science }.sum() allCitiesScience += totalBaseScience * totalBonusPercents.toPercent() } scienceOfLast8Turns[civInfo.gameInfo.turns % 8] = allCitiesScience.toInt() diff --git a/core/src/com/unciv/models/ruleset/IHasUniques.kt b/core/src/com/unciv/models/ruleset/IHasUniques.kt index 68249a4b17..ed895c7ca5 100644 --- a/core/src/com/unciv/models/ruleset/IHasUniques.kt +++ b/core/src/com/unciv/models/ruleset/IHasUniques.kt @@ -19,7 +19,7 @@ interface IHasUniques { * But making this a function is relevant for future "unify Unciv object" plans ;) * */ fun getUniqueTarget(): UniqueTarget - + fun getMatchingUniques(uniqueTemplate: String, stateForConditionals: StateForConditionals? = null): Sequence { val matchingUniques = uniqueMap[uniqueTemplate] ?: return sequenceOf() return matchingUniques.asSequence().filter { it.conditionalsApply(stateForConditionals) } diff --git a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt index 779ed3e4fc..db0393a194 100644 --- a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt @@ -149,7 +149,9 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(BaseScreen.skin) private fun addStatsToHashmap(statTreeNode: StatTreeNode, hashMap: HashMap, stat:Stat, showDetails:Boolean, indentation:Int=0) { for ((name, child) in statTreeNode.children) { - hashMap["- ".repeat(indentation) + name] = child.totalStats[stat] + val statAmount = child.totalStats[stat] + if (statAmount == 0f) continue + hashMap["- ".repeat(indentation) + name] = statAmount if (showDetails) addStatsToHashmap(child, hashMap, stat, showDetails, indentation + 1) } } @@ -207,23 +209,22 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(BaseScreen.skin) statValuesTable.add("Total".toLabel()) statValuesTable.add(sumOfAllBaseValues.toOneDecimalLabel()).row() - val relevantBonuses = cityStats.statPercentBonusList.filter { it.value[stat] != 0f } - if (relevantBonuses.isNotEmpty()) { + val relevantBonuses = LinkedHashMap() + addStatsToHashmap(cityStats.statPercentBonusTree, relevantBonuses, stat, showDetails) + + val totalBonusStats = cityStats.statPercentBonusTree.totalStats + if (totalBonusStats[stat] != 0f) { statValuesTable.add("Bonuses".toLabel(fontSize = FONT_SIZE_STAT_INFO_HEADER)).colspan(2) .padTop(20f).row() - var sumOfBonuses = 0f - for (entry in relevantBonuses) { - val specificStatValue = entry.value[stat] - sumOfBonuses += specificStatValue - statValuesTable.add(entry.key.toLabel()) - statValuesTable.add(specificStatValue.toPercentLabel()).row() // negative bonus + for ((source, bonusAmount) in relevantBonuses) { + statValuesTable.add(source.toLabel()).left() + statValuesTable.add(bonusAmount.toPercentLabel()).row() // negative bonus } statValuesTable.addSeparator() statValuesTable.add("Total".toLabel()) - statValuesTable.add(sumOfBonuses.toPercentLabel()).row() // negative bonus - } + statValuesTable.add(totalBonusStats[stat].toPercentLabel()).row() // negative bonus + - if (stat != Stat.Happiness) { statValuesTable.add("Final".toLabel(fontSize = FONT_SIZE_STAT_INFO_HEADER)).colspan(2) .padTop(20f).row() var finalTotal = 0f