From 3661ad8d5b6b749511ad7e52b992d73a648a4c74 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Thu, 3 Feb 2022 14:58:17 +0200 Subject: [PATCH] Find all replaceable uniques, generate replacement text, check for compatibility, and replace! (#6105) * Find all replaceable uniques, generate replacement text, check for compatibility, and replace! All of the work up until now has lead up to this point - since we already know what's out and what to replace it with, we can do 90%+ of the work ourselves and spare the sanity of our modders For an example run: - Download your favorite mod - Check its deprecated uniques in the options menu - Run the autoupdater from the same menu - See how they disappear - Bask in the glory * A few unreplaced uniques raised problems in some of the replacement texts * Old unusable deprecated uniques are suddenly important again, because they can help autoupdate mods! I'm wondering if to remove outdated mods entirely from the uniques.md, since they're going to keep accumulating * Padding for the 'autoupdate' button * Only display autoupdate button if there are unique to update Also, fixed some of the revived deprecated unique's replacement text thanks to fancy new tests :) Tech uniques autoupdate! * Toast now pops correctly, added translation entries --- .../jsons/translations/template.properties | 3 + core/src/com/unciv/models/ruleset/Ruleset.kt | 9 +- .../unciv/models/ruleset/unique/UniqueType.kt | 57 +++++++++- .../ui/worldscreen/mainmenu/OptionsPopup.kt | 104 +++++++++++++++++- docs/uniques.md | 30 ++++- 5 files changed, 185 insertions(+), 18 deletions(-) diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 146a5231e5..a541f5a800 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -552,6 +552,9 @@ Order trade offers by amount = Check extension mods based on vanilla = Checking mods for errors... = No problems found. = +Autoupdate mod uniques = +Uniques updated! = + Show experimental world wrap for maps = HIGHLY EXPERIMENTAL - YOU HAVE BEEN WARNED! = Enable portrait orientation = diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index 40e68786da..262e160697 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -322,18 +322,18 @@ class Ruleset { forOptionsPopup, name, severityToReport, - uniqueContainer + uniqueContainer.getUniqueTarget() ) lines.addAll(errors) } } - private fun checkUnique( + fun checkUnique( unique: Unique, forOptionsPopup: Boolean, name: String, severityToReport: UniqueType.UniqueComplianceErrorSeverity, - uniqueContainer: IHasUniques + uniqueTarget: UniqueTarget ): List { if (unique.type == null) { if (!forOptionsPopup) return emptyList() @@ -417,8 +417,7 @@ class Ruleset { rulesetErrors.add(deprecationText, severity) } - val acceptableUniqueType = uniqueContainer.getUniqueTarget() - if (unique.type.targetTypes.none { acceptableUniqueType.canAcceptUniqueTarget(it) }) + if (unique.type.targetTypes.none { uniqueTarget.canAcceptUniqueTarget(it) }) rulesetErrors.add( "$name's unique \"${unique.text}\" cannot be put on this type of object!", RulesetErrorSeverity.Warning diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index cb6faa1dd9..9e515b90f0 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -157,7 +157,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: GrowthPercentBonus("[amount]% growth [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), CarryOverFood("[amount]% Food is carried over after population increases [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), - @Deprecated("as of 3.19.2", ReplaceWith("[amount]% Food is carried over after population increases [cityFilter]")) + @Deprecated("as of 3.19.2", ReplaceWith("[amount]% Food is carried over after population increases [in this city]")) CarryOverFoodDeprecated("[amount]% of food is carried over after population increases", UniqueTarget.Global, UniqueTarget.FollowerBelief), @Deprecated("as of 3.19.2", ReplaceWith("[amount]% Food is carried over after population increases [cityFilter]")) CarryOverFoodAlsoDeprecated("[amount]% of food is carried over [cityFilter] after population increases", UniqueTarget.Global, UniqueTarget.FollowerBelief), @@ -180,7 +180,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: BorderGrowthPercentageWithoutPercentageSign("[amount]% Culture cost of natural border growth [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), @Deprecated("as of 3.19.1", ReplaceWith("[-amount]% Culture cost of natural border growth [cityFilter]")) DecreasedAcquiringTilesCost("-[amount]% Culture cost of acquiring tiles [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), - @Deprecated("as of 3.19.1", ReplaceWith("[amount]% Culture cost of natural border growth [cityFilter]")) + @Deprecated("as of 3.19.1", ReplaceWith("[amount]% Culture cost of natural border growth [in all cities]")) CostOfNaturalBorderGrowth("[amount]% cost of natural border growth", UniqueTarget.Global, UniqueTarget.FollowerBelief), TileCostPercentage("[amount]% Gold cost of acquiring tiles [cityFilter]", UniqueTarget.FollowerBelief, UniqueTarget.Global), @Deprecated("as of 3.19.1", ReplaceWith("[-amount]% Gold cost of acquiring tiles [cityFilter]")) @@ -716,15 +716,60 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[+amount]% Production when constructing [baseUnitFilter] units [cityFilter]"), DeprecationLevel.ERROR) PercentProductionUnitsDeprecated("+[amount]% Production when constructing [baseUnitFilter] units [cityFilter]", UniqueTarget.Global), - @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[+amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.ERROR) + @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[+amount]% Production when constructing [stat] buildings [in all cities]"), DeprecationLevel.ERROR) PercentProductionStatBuildings("+[amount]% Production when constructing [stat] buildings", UniqueTarget.Global), - @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[+amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.ERROR) + @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[+amount]% Production when constructing [constructionFilter] buildings [in all cities]"), DeprecationLevel.ERROR) PercentProductionConstructions("+[amount]% Production when constructing [constructionFilter]", UniqueTarget.Global), - @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.ERROR) + @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[amount]% Production when constructing [buildingName] buildings [in all cities]"), DeprecationLevel.ERROR) PercentProductionBuildingName("+[amount]% Production when constructing a [buildingName]", UniqueTarget.Global), - @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.ERROR) + @Deprecated("as of 3.17.10 - removed 3.18.5", ReplaceWith("[amount]% Production when constructing [constructionFilter] buildings [cityFilter]"), DeprecationLevel.ERROR) PercentProductionConstructionsCities("+[amount]% Production when constructing [constructionFilter] [cityFilter]", UniqueTarget.Global), + + @Deprecated("As of 3.17.1 - removed 3.17.13", ReplaceWith("Double movement in [terrainFilter]"), DeprecationLevel.ERROR) + DoubleMovementCoast("Double movement in coast", UniqueTarget.Unit), + @Deprecated("As of 3.17.1 - removed 3.17.13", ReplaceWith("Double movement in [terrainFilter]"), DeprecationLevel.ERROR) + DoubleMovementForestJungle("Double movement rate through Forest and Jungle", UniqueTarget.Unit), + @Deprecated("As of 3.17.1 - removed 3.17.13", ReplaceWith("Double movement in [terrainFilter]"), DeprecationLevel.ERROR) + DoubleMovementSnowTundraHill("Double movement in Snow, Tundra and Hills", UniqueTarget.Unit), + + + @Deprecated("As of 3.17.3 - removed 3.17.13", ReplaceWith("[amount]% Strength"), DeprecationLevel.ERROR) + StrengthPlus("+[amount]% Strength", UniqueTarget.Unit), + @Deprecated("As of 3.17.3 - removed 3.17.13", ReplaceWith("[amount]% Strength"), DeprecationLevel.ERROR) + StrengthMin("-[amount]% Strength", UniqueTarget.Unit), + @Deprecated("As of 3.17.3 - removed 3.17.13", ReplaceWith("[amount]% Strength OR "), DeprecationLevel.ERROR) + StrengthPlusVs("+[amount]% Strength vs [combatantFilter]", UniqueTarget.Unit), + @Deprecated("As of 3.17.3 - removed 3.17.13", ReplaceWith("[amount]% Strength OR "), DeprecationLevel.ERROR) + StrengthMinVs("-[amount]% Strength vs [combatantFilter]", UniqueTarget.Unit), + @Deprecated("As of 3.17.3 - removed 3.17.13", ReplaceWith("[amount]% Strength"), DeprecationLevel.ERROR) + CombatBonus("+[amount]% Combat Strength", UniqueTarget.Unit), + + @Deprecated("As of 3.16.11 - removed 3.17.11", ReplaceWith("[+1] Movement "), DeprecationLevel.ERROR) + EmbarkedUnitMovement1("Increases embarked movement +1", UniqueTarget.Global), + @Deprecated("As of 3.16.11 - removed 3.17.11", ReplaceWith("[+1] Movement "), DeprecationLevel.ERROR) + EmbarkedUnitMovement2("+1 Movement for all embarked units", UniqueTarget.Global), + + @Deprecated("As of 3.16.11 - removed 3.17.11", ReplaceWith("[amount]% unhappiness from population [in all cities]"), DeprecationLevel.ERROR) + UnhappinessFromPopulationPercentageChangeOld1("Unhappiness from population decreased by [amount]%", UniqueTarget.Global), + @Deprecated("As of 3.16.11 - removed 3.17.11", ReplaceWith("[amount]% unhappiness from population [cityFilter]"), DeprecationLevel.ERROR) + UnhappinessFromPopulationPercentageChangeOld2("Unhappiness from population decreased by [amount]% [cityFilter]", UniqueTarget.Global), + + @Deprecated("As of 3.16.14 - removed 3.17.11", ReplaceWith("[amount]% growth [cityFilter]"), DeprecationLevel.ERROR) + GrowthPercentBonusPositive("+[amount]% growth [cityFilter]", UniqueTarget.Global), + @Deprecated("As of 3.16.14 - removed 3.17.11", ReplaceWith("[amount]% growth [cityFilter] "), DeprecationLevel.ERROR) + GrowthPercentBonusWhenNotAtWar("+[amount]% growth [cityFilter] when not at war", UniqueTarget.Global), + @Deprecated("As of 3.16.16 - removed as of 3.17.11", ReplaceWith("[amount]% maintenance costs "), DeprecationLevel.ERROR) + DecreasedUnitMaintenanceCostsByFilter("-[amount]% [mapUnitFilter] unit maintenance costs", UniqueTarget.Global), + @Deprecated("As of 3.16.16 - removed 3.17.11", ReplaceWith("[amount]% maintenance costs "), DeprecationLevel.ERROR) + DecreasedUnitMaintenanceCostsGlobally("-[amount]% unit upkeep costs", UniqueTarget.Global), + + @Deprecated("As of 3.16.16 - removed 3.17.11", ReplaceWith("[stats] from every specialist [in all cities]"), DeprecationLevel.ERROR) + StatsFromSpecialistDeprecated("[stats] from every specialist", UniqueTarget.Global), + @Deprecated("As of 3.16.16 - removed 3.17.11", ReplaceWith("[stats] "), DeprecationLevel.ERROR) + StatBonusForNumberOfSpecialists("[stats] if this city has at least [amount] specialists", UniqueTarget.Global), + + // endregion ; diff --git a/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt b/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt index f410799cf3..0b23e98f9f 100644 --- a/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt +++ b/core/src/com/unciv/ui/worldscreen/mainmenu/OptionsPopup.kt @@ -15,6 +15,8 @@ import com.unciv.models.UncivSound import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.tile.ResourceType +import com.unciv.models.ruleset.unique.Unique +import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.tilesets.TileSetCache import com.unciv.models.translations.TranslationFileWriter import com.unciv.models.translations.tr @@ -26,6 +28,8 @@ import com.unciv.ui.utils.LanguageTable.Companion.addLanguageTables import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip import com.unciv.ui.worldscreen.WorldScreen import java.util.* +import kotlin.collections.HashMap +import kotlin.collections.HashSet import kotlin.math.floor import com.badlogic.gdx.utils.Array as GdxArray @@ -264,13 +268,15 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) { } add(modCheckCheckBox).row() - modCheckResultTable.add("Checking mods for errors...".toLabel()).row() add(modCheckResultTable) } private fun runModChecker(complex: Boolean = false) { modCheckFirstRun = false if (modCheckCheckBox == null) return + + modCheckResultTable.clear() + modCheckResultTable.add("Checking mods for errors...".toLabel()).row() modCheckCheckBox!!.disable() crashHandlingThread(name="ModChecker") { @@ -304,6 +310,12 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) { val expanderTab = ExpanderTab(mod.name, startsOutOpened = false){ it.defaults().align(Align.left) + if (!noProblem && mod.folderLocation != null) { + val replaceableUniques = getDeprecatedReplaceableUniques(mod) + if (replaceableUniques.isNotEmpty()) + it.add("Autoupdate mod uniques".toTextButton() + .onClick { autoUpdateUniques(mod, replaceableUniques) }).pad(10f).row() + } for (line in lines) { val label = if (line.starred) Label(line.text + "\n", BaseScreen.skin) .apply { setFontScale(22 / Fonts.ORIGINAL_FONT_SIZE) } @@ -329,6 +341,96 @@ class OptionsPopup(val previousScreen: BaseScreen) : Popup(previousScreen) { } } + private fun getDeprecatedReplaceableUniques(mod:Ruleset): HashMap { + + val objectsToCheck = sequenceOf( + mod.units, + mod.tileImprovements, + mod.unitPromotions, + mod.buildings, + mod.policies, + mod.nations, + mod.beliefs, + mod.technologies, + ) + val allDeprecatedUniques = HashSet() + val deprecatedUniquesToReplacementText = HashMap() + + val deprecatedUniques = objectsToCheck + .flatMap { it.values } + .flatMap { it.uniqueObjects } + .filter { it.getDeprecationAnnotation() != null } + + + for (deprecatedUnique in deprecatedUniques) { + if (allDeprecatedUniques.contains(deprecatedUnique.text)) continue + allDeprecatedUniques.add(deprecatedUnique.text) + + // note that this replacement does not contain conditionals attached to the original! + var uniqueReplacementText = deprecatedUnique.getReplacementText() + for (conditional in deprecatedUnique.conditionals) + uniqueReplacementText += " <${conditional.text}>" + val replacementUnique = Unique(uniqueReplacementText) + + val modInvariantErrors = mod.checkUnique( + replacementUnique, + false, + "", + UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant, + deprecatedUnique.sourceObjectType!! + ) + for (error in modInvariantErrors) + println(error.text + " - " + error.errorSeverityToReport) + if (modInvariantErrors.isNotEmpty()) continue // errors means no autoreplace + + if (mod.modOptions.isBaseRuleset) { + val modSpecificErrors = mod.checkUnique( + replacementUnique, + false, + "", + UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant, + deprecatedUnique.sourceObjectType + ) + for (error in modSpecificErrors) + println(error.text + " - " + error.errorSeverityToReport) + if (modSpecificErrors.isNotEmpty()) continue + } + + deprecatedUniquesToReplacementText[deprecatedUnique.text] = uniqueReplacementText + println("Replace \"${deprecatedUnique.text}\" with \"$uniqueReplacementText\"") + } + return deprecatedUniquesToReplacementText + } + + private fun autoUpdateUniques(mod: Ruleset, replaceableUniques: HashMap, ) { + + val filesToReplace = listOf( + "Units.json", + "TileImprovements.json", + "UnitPromotions.json", + "Buildings.json", + "Policies.json", + "Nations.json", + "Beliefs.json", + "Techs.json", + ) + + val jsonFolder = mod.folderLocation!!.child("jsons") + for (fileName in filesToReplace) { + val file = jsonFolder.child(fileName) + if (!file.exists() || file.isDirectory) continue + var newFileText = file.readString() + for ((original, replacement) in replaceableUniques) { + newFileText = newFileText.replace("\"$original\"", "\"$replacement\"") + } + file.writeString(newFileText, false) + } + val toastText = "Uniques updated!" + ToastPopup(toastText, screen).open(true) + RulesetCache.loadRulesets() + runModChecker() + } + private fun getDebugTab() = Table(BaseScreen.skin).apply { pad(10f) defaults().pad(5f) diff --git a/docs/uniques.md b/docs/uniques.md index e59f3f500f..acdaa49e10 100644 --- a/docs/uniques.md +++ b/docs/uniques.md @@ -1444,11 +1444,11 @@ Applicable to: Conditional - "City-State Influence degrades [amount]% slower" - Deprecated as of 3.18.17, replace with "[-amount]% City-State Influence degradation" - "Quantity of Resources gifted by City-States increased by [amount]%" - Deprecated as of 3.18.17, replace with "[+amount]% resources gifted by City-States" - "Happiness from Luxury Resources gifted by City-States increased by [amount]%" - Deprecated as of 3.18.17, replace with "[+amount]% Happiness from luxury resources gifted by City-States" - - "[amount]% of food is carried over after population increases" - Deprecated as of 3.19.2, replace with "[amount]% Food is carried over after population increases [cityFilter]" + - "[amount]% of food is carried over after population increases" - Deprecated as of 3.19.2, replace with "[amount]% Food is carried over after population increases [in this city]" - "[amount]% of food is carried over [cityFilter] after population increases" - Deprecated as of 3.19.2, replace with "[amount]% Food is carried over after population increases [cityFilter]" - "[amount]% Culture cost of natural border growth [cityFilter]" - Deprecated as of 3.19.2, replace with "[amount]% Culture cost of natural border growth [cityFilter]" - "-[amount]% Culture cost of acquiring tiles [cityFilter]" - Deprecated as of 3.19.1, replace with "[-amount]% Culture cost of natural border growth [cityFilter]" - - "[amount]% cost of natural border growth" - Deprecated as of 3.19.1, replace with "[amount]% Culture cost of natural border growth [cityFilter]" + - "[amount]% cost of natural border growth" - Deprecated as of 3.19.1, replace with "[amount]% Culture cost of natural border growth [in all cities]" - "-[amount]% Gold cost of acquiring tiles [cityFilter]" - Deprecated as of 3.19.1, replace with "[-amount]% Gold cost of acquiring tiles [cityFilter]" - "[stat] cost of purchasing [baseUnitFilter] units in cities [amount]%" - Deprecated as of 3.19.3, replace with "[stat] cost of purchasing [baseUnitFilter] units [amount]%" - "Maintenance on roads & railroads reduced by [amount]%" - Deprecated as of 3.18.17, replace with "[-amount]% maintenance on road & railroads" @@ -1489,10 +1489,20 @@ Applicable to: Conditional - "[amount] Sight for all [mapUnitFilter] units" - Deprecated as of 3.17.5 - removed 3.18.5, replace with "[amount] Sight " - "[amount]% Spread Religion Strength for [mapUnitFilter] units" - Deprecated as of 3.17.5 - removed 3.18.5, replace with "[amount]% Spread Religion Strength " - "+[amount]% Production when constructing [baseUnitFilter] units [cityFilter]" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[+amount]% Production when constructing [baseUnitFilter] units [cityFilter]" - - "+[amount]% Production when constructing [stat] buildings" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[+amount]% Production when constructing [buildingFilter] buildings [cityFilter]" - - "+[amount]% Production when constructing [constructionFilter]" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[+amount]% Production when constructing [buildingFilter] buildings [cityFilter]" - - "+[amount]% Production when constructing a [buildingName]" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[amount]% Production when constructing [buildingFilter] buildings [cityFilter]" - - "+[amount]% Production when constructing [constructionFilter] [cityFilter]" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[amount]% Production when constructing [buildingFilter] buildings [cityFilter]" + - "+[amount]% Production when constructing [stat] buildings" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[+amount]% Production when constructing [stat] buildings [in all cities]" + - "+[amount]% Production when constructing [constructionFilter]" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[+amount]% Production when constructing [constructionFilter] buildings [in all cities]" + - "+[amount]% Production when constructing a [buildingName]" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[amount]% Production when constructing [buildingName] buildings [in all cities]" + - "+[amount]% Production when constructing [constructionFilter] [cityFilter]" - Deprecated as of 3.17.10 - removed 3.18.5, replace with "[amount]% Production when constructing [constructionFilter] buildings [cityFilter]" + - "Increases embarked movement +1" - Deprecated As of 3.16.11 - removed 3.17.11, replace with "[+1] Movement " + - "+1 Movement for all embarked units" - Deprecated As of 3.16.11 - removed 3.17.11, replace with "[+1] Movement " + - "Unhappiness from population decreased by [amount]%" - Deprecated As of 3.16.11 - removed 3.17.11, replace with "[amount]% unhappiness from population [in all cities]" + - "Unhappiness from population decreased by [amount]% [cityFilter]" - Deprecated As of 3.16.11 - removed 3.17.11, replace with "[amount]% unhappiness from population [cityFilter]" + - "+[amount]% growth [cityFilter]" - Deprecated As of 3.16.14 - removed 3.17.11, replace with "[amount]% growth [cityFilter]" + - "+[amount]% growth [cityFilter] when not at war" - Deprecated As of 3.16.14 - removed 3.17.11, replace with "[amount]% growth [cityFilter] " + - "-[amount]% [mapUnitFilter] unit maintenance costs" - Deprecated As of 3.16.16 - removed as of 3.17.11, replace with "[amount]% maintenance costs " + - "-[amount]% unit upkeep costs" - Deprecated As of 3.16.16 - removed 3.17.11, replace with "[amount]% maintenance costs " + - "[stats] from every specialist" - Deprecated As of 3.16.16 - removed 3.17.11, replace with "[stats] from every specialist [in all cities]" + - "[stats] if this city has at least [amount] specialists" - Deprecated As of 3.16.16 - removed 3.17.11, replace with "[stats] " - "Not displayed as an available construction unless [buildingName] is built" - Deprecated as of 3.16.11, replace with "Not displayed as an available construction without [buildingName]" - "[stats] once [tech] is discovered" - Deprecated as of 3.17.10 - removed 3.18.19, replace with "[stats] " - "[amount]% Bonus XP gain" - Deprecated as of 3.18.12, replace with "[amount]% XP gained from combat" @@ -1504,6 +1514,14 @@ Applicable to: Conditional - "+[amount]% Strength in [tileFilter]" - Deprecated as of 3.17.5 - removed 3.18.5, replace with "[amount]% Strength " - "[amount] Visibility Range" - Deprecated as of 3.17.5 - removed 3.18.5, replace with "[amount] Sight" - "Limited Visibility" - Deprecated as of 3.17.5 - removed 3.18.5, replace with "[-1] Sight" + - "Double movement in coast" - Deprecated As of 3.17.1 - removed 3.17.13, replace with "Double movement in [terrainFilter]" + - "Double movement rate through Forest and Jungle" - Deprecated As of 3.17.1 - removed 3.17.13, replace with "Double movement in [terrainFilter]" + - "Double movement in Snow, Tundra and Hills" - Deprecated As of 3.17.1 - removed 3.17.13, replace with "Double movement in [terrainFilter]" + - "+[amount]% Strength" - Deprecated As of 3.17.3 - removed 3.17.13, replace with "[amount]% Strength" + - "-[amount]% Strength" - Deprecated As of 3.17.3 - removed 3.17.13, replace with "[amount]% Strength" + - "+[amount]% Strength vs [combatantFilter]" - Deprecated As of 3.17.3 - removed 3.17.13, replace with "[amount]% Strength OR " + - "-[amount]% Strength vs [combatantFilter]" - Deprecated As of 3.17.3 - removed 3.17.13, replace with "[amount]% Strength OR " + - "+[amount]% Combat Strength" - Deprecated As of 3.17.3 - removed 3.17.13, replace with "[amount]% Strength" - "Deal [amount] damage to adjacent enemy units" - Deprecated as of 3.18.17, replace with "Adjacent enemy units ending their turn take [amount] damage" - "Cannot be built on [tileFilter] tiles until [tech] is discovered" - Deprecated as of 3.18.5, replace with "Cannot be built on [tileFilter] tiles " - "[stats] on [tileFilter] tiles once [tech] is discovered" - Deprecated as of 3.17.10 - removed 3.18.19, replace with "[stats] from [tileFilter] tiles "