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 "