From 13859e78c1c98275ea91b37af48183863a7320b9 Mon Sep 17 00:00:00 2001 From: Xander Lenstra <71121390+xlenstra@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:57:55 +0200 Subject: [PATCH] Unified and enumified production percentage bonus uniques (#5521) --- .../assets/jsons/Civ V - Vanilla/Beliefs.json | 13 ++- .../jsons/Civ V - Vanilla/Buildings.json | 24 +++--- .../assets/jsons/Civ V - Vanilla/Nations.json | 2 +- .../jsons/Civ V - Vanilla/Policies.json | 20 ++--- .../jsons/Civ V - Vanilla/TileResources.json | 2 +- core/src/com/unciv/logic/city/CityStats.kt | 82 ++++++++++++------- .../ruleset/unique/UniqueParameterType.kt | 24 ++++++ .../unciv/models/ruleset/unique/UniqueType.kt | 36 ++++++-- 8 files changed, 135 insertions(+), 68 deletions(-) diff --git a/android/assets/jsons/Civ V - Vanilla/Beliefs.json b/android/assets/jsons/Civ V - Vanilla/Beliefs.json index 0004ebe153..3d6f95afc1 100644 --- a/android/assets/jsons/Civ V - Vanilla/Beliefs.json +++ b/android/assets/jsons/Civ V - Vanilla/Beliefs.json @@ -75,15 +75,14 @@ { "name": "Monument to the Gods", "type": "Pantheon", - "uniques": ["+[15]% Production when constructing [Wonders]"] + "uniques": ["[+15]% Production when constructing [All] wonders [in cities following this religion]"] // ToDo: Should only be ancient/classical era wonders, but implementing that is another can of worms - // For that we really should need an era.matchesFilter() AND support for multiple values in - // construction.matchesFilter(), so we could write something like: - //"uniques": ["[+15]% Production when constructing [{Ancient Era} {Wonders}]", - // "[+15]% Production when constructing [{Classical Era} {Wonders}]"] + // For that we really should need an era.matchesFilter(), so we could write something like: + //"uniques": ["[+15]% Production when constructing [Ancient era] wonders [in cities following this religion]", + // "[+15]% Production when constructing [Classical era] wonders [in cities following this religion]"] // For now this feels like overkill, but I'll leave this here for the future - - // Alternatively, we could approximate this with "[+15]% Production when constructing [Wonders] " + + // Alternatively, we could approximate this with "[+15]% Production when constructing [All] wonders [in all cities] " }, { "name": "One with Nature", diff --git a/android/assets/jsons/Civ V - Vanilla/Buildings.json b/android/assets/jsons/Civ V - Vanilla/Buildings.json index c15edbd34b..b49c1d1cd4 100644 --- a/android/assets/jsons/Civ V - Vanilla/Buildings.json +++ b/android/assets/jsons/Civ V - Vanilla/Buildings.json @@ -67,7 +67,7 @@ "culture": 1, "isWonder": true, "greatPersonPoints": {"Great Engineer": 1}, - "uniques": ["+[10]% [Food] [in all cities]", "+[15]% Production when constructing [Ranged] units [in all cities]"], + "uniques": ["[+10]% [Food] [in all cities]", "[+15]% Production when constructing [Ranged] units [in all cities]"], "requiredTech": "Archery", "quote": "'It is not so much for its beauty that the forest makes a claim upon men's hearts, as for that subtle something, that quality of air, that emanation from old trees, that so wonderfully changes and renews a weary spirit.' - Robert Louis Stevenson" }, @@ -239,7 +239,7 @@ "maintenance": 1, "requiredNearbyImprovedResources": ["Horses","Sheep","Cattle"], "hurryCostModifier": 25, - "uniques": ["+[15]% Production when constructing [Mounted] units [in this city]", + "uniques": ["[+15]% Production when constructing [Mounted] units [in this city]", "[+1 Production] from [Cattle] tiles [in this city]", "[+1 Production] from [Horses] tiles [in this city]", "[+1 Production] from [Sheep] tiles [in this city]"], @@ -528,9 +528,9 @@ "requiredNearbyImprovedResources": ["Iron"], "requiredTech": "Metal Casting", // If spaceship parts are changed into units, the spaceship part unique should be changed to - // "+[15]% Production when constructing [Spaceship part] units [in this city]" - "uniques": ["+[15]% Production when constructing [Spaceship part] [in this city]", - "+[15]% Production when constructing [Land] units [in this city]", + // "[+15]% Production when constructing [Spaceship part] units [in this city]" + "uniques": ["[+15]% Production when constructing [Spaceship part] buildings [in this city]", + "[+15]% Production when constructing [Land] units [in this city]", "[+1 Production] from [Iron] tiles [in this city]"] }, // Column 6 @@ -684,7 +684,7 @@ "culture": 1, "isWonder": true, "greatPersonPoints": {"Great Artist": 2}, - "uniques": ["+[25]% [Culture] [in all cities]"], + "uniques": ["[+25]% [Culture] [in all cities]"], "requiredTech": "Acoustics", "quote": "'I live and love in God's peculiar light.' - Michelangelo Buonarroti" }, @@ -765,7 +765,7 @@ "maintenance": 3, "requiredBuilding": "Harbor", "uniques": ["[+1 Production, +1 Gold] from [Water resource] tiles [in this city]", - "Must be next to [Coast]", "+[15]% Production when constructing [Water] units [in this city]"], + "Must be next to [Coast]", "[+15]% Production when constructing [Water] units [in this city]"], "requiredTech": "Navigation" }, { @@ -802,7 +802,7 @@ "specialistSlots": {"Engineer": 1}, "hurryCostModifier": 25, "maintenance": 2, - "uniques": ["Must not be on [Hill]", "+[10]% Production when constructing [Buildings] [in this city]"], + "uniques": ["Must not be on [Hill]", "[+10]% Production when constructing [All] buildings [in this city]"], "requiredTech": "Economics" }, { @@ -1107,9 +1107,9 @@ "isWonder": true, "greatPersonPoints": {"Great Scientist": 1}, // If spaceship parts are changed into units, the spaceship part unique should be changed to - // "+[25]% Production when constructing [Spaceship part] units [in this city]" + // "[+25]% Production when constructing [Spaceship part] units [in this city]" "uniques": ["[2] free [Great Scientist] units appear", - "+[25]% Production when constructing [Spaceship part] [in this city]", + "[+25]% Production when constructing [Spaceship part] buildings [in this city]", "Gain a free [Spaceship Factory] [in this city]"], "requiredTech": "Satellites", "quote": "'The wonder is, not that the field of stars is so vast, but that man has measured it.' - Anatole France" @@ -1127,8 +1127,8 @@ "cost": 360, "requiredBuilding": "Factory", // If spaceship parts are changed into units, this unique should be changed to - // "+[50]% Production when constructing [Spaceship part] units [in this city]" - "uniques": ["+[50]% Production when constructing [Spaceship part] [in this city]"], + // "[+50]% Production when constructing [Spaceship part] units [in this city]" + "uniques": ["[+50]% Production when constructing [Spaceship part] buildings [in this city]"], "requiredTech": "Robotics" }, // Column 16 diff --git a/android/assets/jsons/Civ V - Vanilla/Nations.json b/android/assets/jsons/Civ V - Vanilla/Nations.json index fd2f07a4fd..6ac6b148c7 100644 --- a/android/assets/jsons/Civ V - Vanilla/Nations.json +++ b/android/assets/jsons/Civ V - Vanilla/Nations.json @@ -117,7 +117,7 @@ "innerColor": [98,10,210], "favoredReligion": "Islam", "uniqueName": "Monument Builders", - "uniques": ["+[20]% Production when constructing [Wonders]"], + "uniques": ["[+20]% Production when constructing [All] wonders [in all cities]"], "cities": ["Thebes","Memphis","Heliopolis","Elephantine","Alexandria","Pi-Ramesses","Giza","Byblos","Akhetaten", "Hieraconpolis","Abydos","Asyut","Avaris","Lisht","Buto","Edfu","Pithom","Busiris","Kahun","Athribis", "Mendes","Elashmunein","Tanis","Bubastis","Oryx","Sebennytus","Akhmin","Karnak","Luxor","El Kab","Armant", diff --git a/android/assets/jsons/Civ V - Vanilla/Policies.json b/android/assets/jsons/Civ V - Vanilla/Policies.json index 910cfa8a74..9513dfd7d0 100644 --- a/android/assets/jsons/Civ V - Vanilla/Policies.json +++ b/android/assets/jsons/Civ V - Vanilla/Policies.json @@ -6,7 +6,7 @@ "policies": [ { "name": "Aristocracy", - "uniques": ["+[15]% Production when constructing [Wonders]", "[+1 Happiness] per [10] population [in all cities]"], + "uniques": ["[+15]% Production when constructing [All] wonders [in all cities]", "[+1 Happiness] per [10] population [in all cities]"], "row": 1, "column": 1 }, @@ -49,7 +49,7 @@ "policies": [ { "name": "Republic", - "uniques": ["[+1 Production] [in all cities]", "+[5]% Production when constructing [Buildings]"], + "uniques": ["[+1 Production] [in all cities]", "[+5]% Production when constructing [All] buildings [in all cities]"], "row": 1, "column": 1 }, @@ -61,7 +61,7 @@ }, { "name": "Collective Rule", - "uniques": ["+[50]% Production when constructing [Settler] units [in capital]", "Free [Settler] appears"], + "uniques": ["[+50]% Production when constructing [Settler] units [in capital]", "Free [Settler] appears"], "requires": ["Republic"], "row": 2, "column": 1 @@ -94,7 +94,7 @@ "policies": [ { "name": "Warrior Code", - "uniques":["+[15]% Production when constructing [Melee] units [in all cities]", "Free [Great General] appears"], + "uniques":["[+15]% Production when constructing [Melee] units [in all cities]", "Free [Great General] appears"], "row": 1, "column": 2 }, @@ -135,7 +135,7 @@ },{ "name": "Piety", "era": "Classical era", - "uniques": ["+[100]% Production when constructing a [Shrine]", "+[100]% Production when constructing a [Temple]", + "uniques": ["[+100]% Production when constructing [Shrine] buildings [in all cities]", "[+100]% Production when constructing [Temple] buildings [in all cities]", "Incompatible with [Rationalism]"], "policies": [ { @@ -159,7 +159,7 @@ }, { "name": "Reformation", - "uniques": ["+[33]% [Culture] [in all cities with a world wonder]", "Empire enters golden age"], + "uniques": ["[+33]% [Culture] [in all cities with a world wonder]", "Empire enters golden age"], "requires": ["Organized Religion"], "row": 2, "column": 3 @@ -225,7 +225,7 @@ }, { "name": "Commerce", - "uniques": ["+[25]% [Gold] [in capital]"], + "uniques": ["[+25]% [Gold] [in capital]"], "era": "Medieval era", "policies": [ { @@ -391,7 +391,7 @@ }, { "name": "Police State", - "uniques": ["[+3 Happiness] from every [Courthouse]", "+[100]% Production when constructing [Courthouse]"], + "uniques": ["[+3 Happiness] from every [Courthouse]", "[+100]% Production when constructing [Courthouse] buildings [in all cities]"], // There are also some uniques regarding espoinage, which as of this writing is not yet implemented "requires": ["Militarism"], "row": 2, @@ -399,7 +399,7 @@ }, { "name": "Total War", - "uniques": ["+[25]% Production when constructing [Military] units [in all cities]", "New [Military] units start with [15] Experience [in all cities]"], + "uniques": ["[+25]% Production when constructing [Military] units [in all cities]", "New [Military] units start with [15] Experience [in all cities]"], "requires": ["Police State","Fascism"], "row": 3, "column": 4 @@ -426,7 +426,7 @@ }, { "name": "Planned Economy", - "uniques": ["+[25]% [Science] from every [Factory]", "+[100]% Production when constructing a [Factory]"], + "uniques": ["+[25]% [Science] from every [Factory]", "[+100]% Production when constructing [Factory] buildings [in all cities]"], "row": 1, "column": 3 }, diff --git a/android/assets/jsons/Civ V - Vanilla/TileResources.json b/android/assets/jsons/Civ V - Vanilla/TileResources.json index 085286145a..be19954fb6 100644 --- a/android/assets/jsons/Civ V - Vanilla/TileResources.json +++ b/android/assets/jsons/Civ V - Vanilla/TileResources.json @@ -240,7 +240,7 @@ "gold": 2, "improvement": "Quarry", "improvementStats": {"production": 1}, - "uniques": ["+[15]% Production when constructing [Wonders]"] + "uniques": ["[+15]% Production when constructing [All] wonders [in all cities]"] }, { "name": "Whales", diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index edccc57b0f..b03827fb27 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -7,6 +7,7 @@ import com.unciv.logic.map.RoadStatus import com.unciv.models.Counter import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.ModOptionsConstants +import com.unciv.models.ruleset.unique.StateForConditionals import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unit.BaseUnit @@ -266,14 +267,7 @@ class CityStats(val cityInfo: CityInfo) { val uniques = uniqueSequence.toList().asSequence() // Since this is sometimes run from a different thread (getConstructionButtonDTOs), // this helps mitigate concurrency problems. - - if (currentConstruction is Building && !currentConstruction.isAnyWonder()) - for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing [] buildings" }) { - val stat = Stat.valueOf(unique.params[1]) - if (currentConstruction.isStatRelated(stat)) - stats.production += unique.params[0].toInt() - } - + for (unique in uniques.filter { it.isOfType(UniqueType.StatPercentBonus) }) { if (!unique.conditionalsApply(cityInfo.civInfo, cityInfo)) continue stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat()) @@ -285,33 +279,65 @@ class CityStats(val cityInfo: CityInfo) { stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat()) // - // Params: "+[amount]% [Stat] [cityFilter]", pretty crazy amirite - // For instance "+[50]% [Production] [in all cities] - for (unique in uniques.filter { it.placeholderText == "+[]% [] []"}) + // Deprecated since 3.17.10 + // Params: "+[amount]% [Stat] [cityFilter]", pretty crazy amirite + // For instance "+[50]% [Production] [in all cities] + for (unique in uniques.filter { it.isOfType(UniqueType.StatPercentBonusCitiesDeprecated) }) + if (cityInfo.matchesFilter(unique.params[2])) + stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat()) + // + + for (unique in uniques.filter { it.isOfType(UniqueType.StatPercentBonusCities) }) { + if (!unique.conditionalsApply(StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo))) continue if (cityInfo.matchesFilter(unique.params[2])) stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat()) - - for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing []" }) { - if (constructionMatchesFilter(currentConstruction, unique.params[1])) - stats.production += unique.params[0].toInt() } - // Used for specific buildings (e.g. +100% Production when constructing a Factory) - for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing a []" }) { - if (constructionMatchesFilter(currentConstruction, unique.params[1])) - stats.production += unique.params[0].toInt() - } - - // "+[amount]% Production when constructing [constructionFilter] [cityFilter]" - for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing [] []" }) { + + val uniquesToCheck = + if (currentConstruction is Building && !currentConstruction.isAnyWonder()) { + uniques.filter { it.isOfType(UniqueType.PercentProductionWonders) } + } else if (currentConstruction is Building && currentConstruction.isAnyWonder()) { + uniques.filter { it.isOfType(UniqueType.PercentProductionBuildings) } + } else if (currentConstruction is BaseUnit) { + uniques.filter { it.isOfType(UniqueType.PercentProductionUnits) } + } else { // Science/Gold production + sequenceOf() + } + for (unique in uniquesToCheck) { + if (!unique.conditionalsApply(StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo))) continue if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2])) stats.production += unique.params[0].toInt() } - // "+[amount]% Production when constructing [unitFilter] units [cityFilter]" - for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing [] units []" }) { - if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2])) - stats.production += unique.params[0].toInt() - } + // Deprecated since 3.17.10 + if (currentConstruction is Building && !currentConstruction.isAnyWonder()) + for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionStatBuildings) }) { + val stat = Stat.valueOf(unique.params[1]) + if (currentConstruction.isStatRelated(stat)) + stats.production += unique.params[0].toInt() + } + for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionConstructions) }) { + if (constructionMatchesFilter(currentConstruction, unique.params[1])) + stats.production += unique.params[0].toInt() + } + // Used for specific buildings (e.g. +100% Production when constructing a Factory) + for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionBuildingName) }) { + if (constructionMatchesFilter(currentConstruction, unique.params[1])) + stats.production += unique.params[0].toInt() + } + + // "+[amount]% Production when constructing [constructionFilter] [cityFilter]" + for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionConstructionsCities) }) { + if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2])) + stats.production += unique.params[0].toInt() + } + + // "+[amount]% Production when constructing [unitFilter] units [cityFilter]" + for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionUnitsDeprecated) }) { + if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2])) + stats.production += unique.params[0].toInt() + } + // // Deprecated since 3.17.1 if (cityInfo.civInfo.getHappiness() >= 0) { diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt index b521aa659b..df73fa3cd8 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt @@ -1,6 +1,7 @@ package com.unciv.models.ruleset.unique import com.unciv.models.ruleset.BeliefType +import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.TerrainType @@ -91,6 +92,29 @@ enum class UniqueParameterType(val parameterName:String) { return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific } }, + BuildingFilter("buildingFilter") { + override fun getErrorSeverity( + parameterText: String, + ruleset: Ruleset + ): UniqueType.UniqueComplianceErrorSeverity? { + if (parameterText == "All") return null + if (Stat.values().any { it.name == parameterText }) return null + if (BuildingName.getErrorSeverity(parameterText, ruleset) == null) return null + return UniqueType.UniqueComplianceErrorSeverity.WarningOnly + } + }, + // Only used in values deprecated in 3.17.10 + ConstructionFilter("constructionFilter") { + override fun getErrorSeverity( + parameterText: String, + ruleset: Ruleset + ): UniqueType.UniqueComplianceErrorSeverity? { + if (BuildingFilter.getErrorSeverity(parameterText, ruleset) == null) return null + if (BaseUnitFilter.getErrorSeverity(parameterText, ruleset) == null) return null + return UniqueType.UniqueComplianceErrorSeverity.WarningOnly + } + }, + // TerrainFilter("terrainFilter") { private val knownValues = setOf("All", "Coastal", "River", "Open terrain", "Rough terrain", "Water resource", diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 5f35d56724..1cd0b56635 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -67,8 +67,35 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) { @Deprecated("As of 3.16.16", ReplaceWith("[stats] "), DeprecationLevel.ERROR) StatBonusForNumberOfSpecialists("[stats] if this city has at least [amount] specialists", UniqueTarget.Global), + StatsFromSpecialist("[stats] from every specialist [cityFilter]", UniqueTarget.Global), + @Deprecated("As of 3.16.16", ReplaceWith("[stats] from every specialist [in all cities]"), DeprecationLevel.WARNING) + StatsFromSpecialistDeprecated("[stats] from every specialist", UniqueTarget.Global), + + StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global), + + StatsSpendingGreatPeople("[stats] whenever a Great Person is expended", UniqueTarget.Global), + + StatPercentBonus("[amount]% [stat]", UniqueTarget.Global), BonusStatsFromCityStates("[amount]% [stat] from City-States", UniqueTarget.Global), + StatPercentBonusCities("[amount]% [stat] [cityFilter]", UniqueTarget.Global), + @Deprecated("As of 3.17.10", ReplaceWith("[+amount]% [stat] [cityFilter]"), DeprecationLevel.WARNING) + StatPercentBonusCitiesDeprecated("+[amount]% [stat] [cityFilter]", UniqueTarget.Global), + + PercentProductionWonders("[amount]% Production when constructing [buildingFilter] wonders [cityFilter]", UniqueTarget.Global, UniqueTarget.Resource, UniqueTarget.FollowerBelief), + PercentProductionBuildings("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]", UniqueTarget.Global), + PercentProductionUnits("[amount]% Production when constructing [baseUnitFilter] units [cityFilter]", UniqueTarget.Global), + + @Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING) + PercentProductionStatBuildings("+[amount]% Production when constructing [stat] buildings", UniqueTarget.Global), + @Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING) + PercentProductionConstructions("+[amount]% Production when constructing [constructionFilter]", UniqueTarget.Global), + @Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING) + PercentProductionBuildingName("+[amount]% Production when constructing a [buildingName]", UniqueTarget.Global), + @Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING) + PercentProductionConstructionsCities("+[amount]% Production when constructing [constructionFilter] [cityFilter]", UniqueTarget.Global), + @Deprecated("As of 3.17.10", ReplaceWith("[+amount]% Production when constructing [baseUnitFilter] units [cityFilter]"), DeprecationLevel.WARNING) + PercentProductionUnitsDeprecated("+[amount]% Production when constructing [baseUnitFilter] units [cityFilter]", UniqueTarget.Global), RemoveAnnexUnhappiness("Remove extra unhappiness from annexed cities", UniqueTarget.Building), UnhappinessFromPopulationPercentageChange("[amount]% unhappiness from population [cityFilter]", UniqueTarget.Global), @@ -79,15 +106,6 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) { UnhappinessFromPopulationPercentageChangeOld2("Unhappiness from population decreased by [amount]% [cityFilter]", UniqueTarget.Global), - - StatsFromSpecialist("[stats] from every specialist [cityFilter]", UniqueTarget.Global), - @Deprecated("As of 3.16.16", ReplaceWith("[stats] from every specialist [in all cities]"), DeprecationLevel.WARNING) - StatsFromSpecialistDeprecated("[stats] from every specialist", UniqueTarget.Global), - - StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global), - - StatsSpendingGreatPeople("[stats] whenever a Great Person is expended", UniqueTarget.Global), - /////// City-State related uniques // I don't like the fact that currently "city state bonuses" are separate from the "global bonuses",