From e276a08d612db96de0f5d59d53f2ce8e00fc007d Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Sat, 8 Jun 2024 20:58:38 +0200 Subject: [PATCH] UniqueParameterType getErrorSeverity/isKnownValue architecture simplification (#11701) * UniqueParameterType getErrorSeverity/isKnownValue simplification * Minor changes to a few UniqueType documentations * Allow UnitsGainPromotion e.g. "[Melee] units gain the [Morale] promotion" --- .../ruleset/unique/UniqueParameterType.kt | 563 +++++++----------- .../ruleset/unique/UniqueTriggerActivation.kt | 15 +- .../unciv/models/ruleset/unique/UniqueType.kt | 11 +- docs/Modders/uniques.md | 21 +- 4 files changed, 244 insertions(+), 366 deletions(-) diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt index 2d4f0d59f2..c9eaec7e84 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt @@ -12,7 +12,7 @@ import com.unciv.models.ruleset.validation.Suppression import com.unciv.models.stats.Stat import com.unciv.models.translations.TranslationFileWriter -// 'region' names beginning with an underscore are used here for a prettier "Structure window" - they go in front ot the rest. +// 'region' names beginning with an underscore are used here for a prettier "Structure window" - they go in front of the rest. /** * These manage validation of parameters in [Unique]s and @@ -29,6 +29,7 @@ import com.unciv.models.translations.TranslationFileWriter * @param docExample used by UniqueDocsWriter to fill parameters with plausible values for the examples. * @param docDescription used by UniqueDocsWriter to generate the Abbreviations list at the end for types that can be explained in one long line. Should be omitted when the Wiki contains a paragraph for this type. * @param displayName used by [TranslationFileWriter] for section header comments - needed _only_ if [getTranslationWriterStringsForOutput] returns a non-empty list. + * @param severityDefault the default severity used by the [getErrorSeverityViaKnownValue] helper, but not the [getErrorSeverityForFilter] one. Change to [RulesetInvariant][UniqueType.UniqueParameterErrorSeverity.RulesetInvariant] if you override [isKnownValue] and the ruleset is unused. */ //region _Fields @Suppress("unused") // Some are used only via enumerating the enum matching on parameterName @@ -36,84 +37,60 @@ enum class UniqueParameterType( var parameterName: String, val docExample: String, val docDescription: String? = null, - val displayName: String = parameterName + val displayName: String = parameterName, + private val severityDefault: UniqueType.UniqueParameterErrorSeverity = UniqueType.UniqueParameterErrorSeverity.RulesetSpecific ) { //endregion - - Number("amount", "3", "This indicates a whole number, possibly with a + or - sign, such as `2`, `+13`, or `-3`") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - return if (parameterText.toIntOrNull() == null) UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - else null - } + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = + parameterText.getInvariantSeverityUnless { toIntOrNull() != null } }, PositiveNumber("positiveAmount", "3", "This indicates a positive whole number, larger than zero, a '+' sign is optional") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - val amount = parameterText.toIntOrNull() - ?: return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - if (amount <= 0) return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - return null - } + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = + parameterText.getInvariantSeverityUnless { toIntOrNull()?.let { it > 0 } == true } }, Fraction("fraction", docExample = "0.5", "Indicates a fractional number, which can be negative") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? { - return if (parameterText.toFloatOrNull () == null) UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - else null - } + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = + parameterText.getInvariantSeverityUnless { toFloatOrNull() != null } }, RelativeNumber("relativeAmount", "+20", "This indicates a number, usually with a + or - sign, such as `+25` (this kind of parameter is often followed by '%' which is nevertheless not part of the value)") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - return if (parameterText.toIntOrNull() == null) UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - else null - } + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = + parameterText.getInvariantSeverityUnless { toIntOrNull() != null } }, - Countable("countable", "1000", "This indicate a number or a numeric variable") { + Countable("countable", "1000", "This indicates a number or a numeric variable") { // todo add more countables private val knownValues = setOf( "year" ) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (parameterText.toIntOrNull() != null) return true - if (Stat.isStat(parameterText)) return true - if (parameterText in ruleset.tileResources) return true - if (parameterText in ruleset.units) return true - if (parameterText in ruleset.buildings) return true - return false - } - - override fun getErrorSeverity( - parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? { - return if (isKnownValue(parameterText, ruleset)) null - else UniqueType.UniqueParameterErrorSeverity.RulesetSpecific + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + parameterText.toIntOrNull() != null -> true + Stat.isStat(parameterText) -> true + parameterText in ruleset.tileResources -> true + parameterText in ruleset.units -> true + else -> parameterText in ruleset.buildings } }, // todo potentially remove if OneTimeRevealSpecificMapTiles changes - KeywordAll("'all'", "All") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = - if (parameterText in Constants.all) null else UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + KeywordAll("'all'", "All", severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant) { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in Constants.all }, /** Implemented by [ICombatant.matchesCategory][com.unciv.logic.battle.ICombatant.matchesFilter] */ CombatantFilter("combatantFilter", "City", "This indicates a combatant, which can either be a unit or a city (when bombarding). Must either be `City` or a `mapUnitFilter`") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText == "City") return true - if (MapUnitFilter.isKnownValue(parameterText, ruleset)) return true - if (CityFilter.isKnownValue(parameterText, ruleset)) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText == "City" -> true + MapUnitFilter.isKnownValue(parameterText, ruleset) -> true + else -> CityFilter.isKnownValue(parameterText, ruleset) } override fun getTranslationWriterStringsForOutput() = setOf("City") }, @@ -123,15 +100,13 @@ enum class UniqueParameterType( private val knownValues = setOf(Constants.wounded, Constants.barbarians, "Barbarian", "City-State", Constants.embarked, "Non-City") - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (ruleset.unitPromotions.values.any { it.hasUnique(parameterText) }) return true - if (CivFilter.isKnownValue(parameterText, ruleset)) return true - if (BaseUnitFilter.isKnownValue(parameterText, ruleset)) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + ruleset.unitPromotions.values.any { it.hasUnique(parameterText) } -> true + CivFilter.isKnownValue(parameterText, ruleset) -> true + else -> BaseUnitFilter.isKnownValue(parameterText, ruleset) } override fun getTranslationWriterStringsForOutput() = knownValues @@ -144,104 +119,83 @@ enum class UniqueParameterType( "Nuclear Weapon", "Great Person", "Religious", "relevant", // used for UniqueType.UnitStartingPromotions ) + Constants.all - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (UnitName.getErrorSeverity(parameterText, ruleset) == null) return true - if (ruleset.units.values.any { it.uniques.contains(parameterText) }) return true - if (UnitTypeFilter.isKnownValue(parameterText, ruleset)) return true - if (TechFilter.isKnownValue(parameterText, ruleset)) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + UnitName.getErrorSeverity(parameterText, ruleset) == null -> true + parameterText.isFilteringUniqueIn(ruleset.units) -> true + UnitTypeFilter.isKnownValue(parameterText, ruleset) -> true + TechFilter.isKnownValue(parameterText, ruleset) -> true + else -> false } override fun getTranslationWriterStringsForOutput() = knownValues }, /** Implemented by [UnitType.matchesFilter][com.unciv.models.ruleset.unit.UnitType.matchesFilter] */ - UnitTypeFilter("unitType", "Water", null, "Unit Type Filters") { + UnitTypeFilter("unitType", "Water", "Can be 'Land', 'Water', 'Air', any unit type, a filtering Unique on a unit type, or a multi-filter of these", "Unit Type Filters") { private val knownValues = setOf( "Land", "Water", "Air", ) override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (ruleset.unitTypes.containsKey(parameterText)) return true - if (ruleset.unitTypes.values.any { it.uniques.contains(parameterText) }) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + parameterText in ruleset.unitTypes -> true + else -> parameterText.isFilteringUniqueIn(ruleset.unitTypes) } override fun isTranslationWriterGuess(parameterText: String, ruleset: Ruleset) = - parameterText in ruleset.unitTypes.keys || parameterText in getTranslationWriterStringsForOutput() + parameterText in ruleset.unitTypes || parameterText in knownValues override fun getTranslationWriterStringsForOutput() = knownValues }, /** Used by [BaseUnitFilter] and e.g. [UniqueType.OneTimeFreeUnit] */ UnitName("unit", "Musketman") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (ruleset.units.containsKey(parameterText)) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific // OneTimeFreeUnitRuins crashes with a bad parameter - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.units }, /** Used by [UniqueType.GreatPersonEarnedFaster] */ GreatPerson("greatPerson", "Great General") { - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - if (ruleset.units[parameterText]?.hasUnique(UniqueType.GreatPerson) == true) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = + ruleset.units[parameterText]?.hasUnique(UniqueType.GreatPerson) == true }, /** Implemented in [Unique.stats][com.unciv.models.ruleset.unique.Unique.stats] */ - Stats("stats", "+1 Gold, +2 Production", "For example: `+2 Production, +3 Food`. Note that the stat names need to be capitalized!") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (com.unciv.models.stats.Stats.isStats(parameterText)) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + Stats("stats", "+1 Gold, +2 Production", "For example: `+2 Production, +3 Food`. Note that the stat names need to be capitalized!", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = + com.unciv.models.stats.Stats.isStats(parameterText) }, /** Many UniqueTypes like [UniqueType.StatPercentBonus] */ - StatName("stat", "Culture", "This is one of the 7 major stats in the game - `Gold`, `Science`, `Production`, `Food`, `Happiness`, `Culture` and `Faith`. Note that the stat names need to be capitalized!") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (Stat.isStat(parameterText)) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + StatName("stat", "Culture", "This is one of the 7 major stats in the game - `Gold`, `Science`, `Production`, `Food`, `Happiness`, `Culture` and `Faith`. Note that the stat names need to be capitalized!", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = Stat.isStat(parameterText) }, /** [UniqueType.DamageUnitsPlunder] and others near that one */ - CivWideStatName("civWideStat", "Gold", "All the following stats have civ-wide fields: `Gold`, `Science`, `Culture`, `Faith`") { + CivWideStatName("civWideStat", "Gold", "All the following stats have civ-wide fields: `Gold`, `Science`, `Culture`, `Faith`", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { private val knownValues = Stat.statsWithCivWideField.map { it.name }.toSet() - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - if (parameterText in knownValues) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in knownValues }, - /** Implemented by [Civ.matchesFilter][com.unciv.logic.civilization.Civilization.matchesFilter] */ CivFilter("civFilter", Constants.cityStates) { private val knownValues = setOf("AI player", "Human player") - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (NationFilter.isKnownValue(parameterText, ruleset)) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + else -> NationFilter.isKnownValue(parameterText, ruleset) } }, @@ -249,14 +203,12 @@ enum class UniqueParameterType( NationFilter("nationFilter", Constants.cityStates) { private val knownValues = setOf(Constants.cityStates, "Major") + Constants.all - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (ruleset.nations.containsKey(parameterText)) return true - if (ruleset.nations.values.any { it.hasUnique(parameterText) }) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + ruleset.nations.containsKey(parameterText) -> true + else -> ruleset.nations.values.any { it.hasUnique(parameterText) } } }, @@ -287,42 +239,31 @@ enum class UniqueParameterType( "in cities following our religion", ) + Constants.all - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - return false - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in knownValues override fun getTranslationWriterStringsForOutput() = knownValues }, /** Used by [BuildingFilter] and e.g. [UniqueType.ConditionalCityWithBuilding] */ BuildingName("buildingName", "Library", "The name of any building") { - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - if (parameterText in ruleset.buildings) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.buildings }, /** Implemented by [Building.matchesFilter][com.unciv.models.ruleset.Building.matchesFilter] */ BuildingFilter("buildingFilter", "Culture") { - private val knownValues = mutableSetOf("Building", "Buildings", "Wonder", "Wonders", "National Wonder", "National", "World Wonder", "World") - .apply { addAll(Stat.names()); addAll(Constants.all) } + private val knownValues = setOf("Building", "Buildings", "Wonder", "Wonders", "National Wonder", "National", "World Wonder", "World") + + Stat.names() + Constants.all - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (BuildingName.getErrorSeverity(parameterText, ruleset) == null) return true - if (ruleset.buildings.values.any { it.hasUnique(parameterText) }) return true - if (TechFilter.isKnownValue(parameterText, ruleset)) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + BuildingName.isKnownValue(parameterText, ruleset) -> true + ruleset.buildings.values.any { it.hasUnique(parameterText) } -> true + TechFilter.isKnownValue(parameterText, ruleset) -> true + else -> false } override fun isTranslationWriterGuess(parameterText: String, ruleset: Ruleset) = @@ -330,15 +271,11 @@ enum class UniqueParameterType( }, /** Implemented by [PopulationManager.getPopulationFilterAmount][com.unciv.logic.city.managers.CityPopulationManager.getPopulationFilterAmount] */ - PopulationFilter("populationFilter", "Followers of this Religion", null, "Population Filters") { + PopulationFilter("populationFilter", "Followers of this Religion", null, "Population Filters", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { private val knownValues = setOf("Population", "Specialists", "Unemployed", "Followers of the Majority Religion", "Followers of this Religion") - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - if (parameterText in knownValues) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in knownValues override fun getTranslationWriterStringsForOutput() = knownValues }, @@ -352,18 +289,15 @@ enum class UniqueParameterType( "Impassable", "Land", "Water" ) + ResourceType.values().map { it.name + " resource" } + Constants.all - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - return when (parameterText) { - in knownValues -> true - in ruleset.terrains -> true - in ruleset.tileResources -> true - in ruleset.terrains.values.asSequence().flatMap { it.uniques } -> true - in ruleset.tileResources.values.asSequence().flatMap { it.uniques } -> true - else -> false - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + parameterText in ruleset.terrains -> true + parameterText in ruleset.tileResources -> true + parameterText.isFilteringUniqueIn(ruleset.terrains) -> true + parameterText.isFilteringUniqueIn(ruleset.tileResources) -> true + else -> false } override fun isTranslationWriterGuess(parameterText: String, ruleset: Ruleset) = @@ -376,14 +310,13 @@ enum class UniqueParameterType( TileFilter("tileFilter", "Farm", "Anything that can be used either in an improvementFilter or in a terrainFilter can be used here, plus 'unimproved'", "Tile Filters") { private val knownValues = setOf("unimproved", "improved", "All Road", "Great Improvement") - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (ImprovementFilter.isKnownValue(parameterText, ruleset)) return true - if (TerrainFilter.isKnownValue(parameterText, ruleset)) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + ImprovementFilter.isKnownValue(parameterText, ruleset) -> true + TerrainFilter.isKnownValue(parameterText, ruleset) -> true + else -> false } override fun getTranslationWriterStringsForOutput() = knownValues @@ -392,31 +325,20 @@ enum class UniqueParameterType( /** Used by [NaturalWonderGenerator][com.unciv.logic.map.mapgenerator.NaturalWonderGenerator], only tests base terrain or a feature */ SimpleTerrain("simpleTerrain", "Elevated") { private val knownValues = setOf("Elevated", "Water", "Land") - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (parameterText in knownValues) return null - if (ruleset.terrains.containsKey(parameterText)) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = + parameterText in knownValues || parameterText in ruleset.terrains }, /** Used by [NaturalWonderGenerator.trySpawnOnSuitableLocation][com.unciv.logic.map.mapgenerator.NaturalWonderGenerator.trySpawnOnSuitableLocation], only tests base terrain */ BaseTerrain("baseTerrain", Constants.grassland, "The name of any terrain that is a base terrain according to the json file") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (ruleset.terrains[parameterText]?.type?.isBaseTerrain == true) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = + ruleset.terrains[parameterText]?.type?.isBaseTerrain == true }, /** Used by: [UniqueType.LandUnitsCrossTerrainAfterUnitGained] (CivilizationInfo.addUnit), * [UniqueType.ChangesTerrain] (MapGenerator.convertTerrains) */ TerrainName("terrainName", Constants.forest) { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (ruleset.terrains.containsKey(parameterText)) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.terrains }, /** Used for region definitions, can be a terrain type with region unique, or "Hybrid" @@ -424,282 +346,231 @@ enum class UniqueParameterType( * See also: [UniqueType.ConditionalInRegionOfType], [UniqueType.ConditionalInRegionExceptOfType], [MapRegions][com.unciv.logic.map.mapgenerator.mapregions.MapRegions] */ RegionType("regionType", "Hybrid", null, "Region Types") { private val knownValues = setOf("Hybrid") - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (parameterText in knownValues) return null - if (ruleset.terrains[parameterText]?.hasUnique(UniqueType.RegionRequirePercentSingleType) == true || - ruleset.terrains[parameterText]?.hasUnique(UniqueType.RegionRequirePercentTwoTypes) == true) - return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + else -> ruleset.terrains[parameterText]?.run { + hasUnique(UniqueType.RegionRequirePercentSingleType) || hasUnique(UniqueType.RegionRequirePercentTwoTypes) + } == true } + // Doubtful any Unique using this is not hidden, but translation files have had this for a while anyway: override fun getTranslationWriterStringsForOutput() = knownValues }, /** Used for start placements: [UniqueType.HasQuality], MapRegions.MapGenTileData.evaluate */ - TerrainQuality("terrainQuality", "Undesirable", null, "Terrain Quality") { + TerrainQuality("terrainQuality", "Undesirable", null, "Terrain Quality", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { private val knownValues = setOf("Undesirable", "Food", "Desirable", "Production") - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (parameterText in knownValues) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in knownValues + // Doubtful any Unique using this is not hidden, but translation files have had this for a while anyway: override fun getTranslationWriterStringsForOutput() = knownValues }, /** [UniqueType.UnitStartingPromotions], [UniqueType.TerrainGrantsPromotion], [UniqueType.ConditionalUnitWithPromotion] and others */ Promotion("promotion", "Shock I", "The name of any promotion") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.unitPromotions -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.unitPromotions }, /** [UniqueType.OneTimeFreeTechRuins], [UniqueType.ConditionalDuringEra] and similar */ Era("era", "Ancient era", "The name of any era") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.eras -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.eras }, Speed("speed", "Quick", "The name of any speed") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.speeds -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.speeds }, /** For [UniqueType.CreatesOneImprovement] */ - ImprovementName("improvementName", "Trading Post", "The name of any improvement") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? { - if (parameterText == Constants.cancelImprovementOrder) - return UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - if (ruleset.tileImprovements.containsKey(parameterText)) return null - return UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + ImprovementName("improvementName", "Trading Post", "The name of any improvement excluding 'Cancel improvement order'") { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = + parameterText in ruleset.tileImprovements && parameterText != Constants.cancelImprovementOrder + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = + if (parameterText == Constants.cancelImprovementOrder) UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + else getErrorSeverityViaKnownValue(parameterText, ruleset) }, /** Implemented by [TileImprovement.matchesFilter][com.unciv.models.ruleset.tile.TileImprovement.matchesFilter] */ ImprovementFilter("improvementFilter", "All Road", null, "Improvement Filters") { private val knownValues = setOf("Improvement", "All Road", "Great Improvement", "Great") + Constants.all - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (ImprovementName.getErrorSeverity(parameterText, ruleset) == null) return true - if (ruleset.tileImprovements.values.any { it.hasUnique(parameterText) }) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in knownValues -> true + ImprovementName.isKnownValue(parameterText, ruleset) -> true + ruleset.tileImprovements.values.any { it.hasUnique(parameterText) } -> true + else -> false } override fun isTranslationWriterGuess(parameterText: String, ruleset: Ruleset) = - parameterText !in Constants.all && getErrorSeverity(parameterText, ruleset) == null + parameterText !in Constants.all && getErrorSeverityForFilter(parameterText, ruleset) == null override fun getTranslationWriterStringsForOutput() = knownValues }, /** Used by [UniqueType.ConsumesResources] and others, implementation not centralized */ Resource("resource", "Iron", "The name of any resource") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.tileResources -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.tileResources }, - StockpiledResource("stockpiledResource", "StockpiledResource", "The name of any stockpiled") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = if (parameterText in ruleset.tileResources && ruleset.tileResources[parameterText]!!.isStockpiled()) null - else UniqueType.UniqueParameterErrorSeverity.RulesetSpecific + /** Used by [UniqueType.OneTimeConsumeResources], [UniqueType.OneTimeProvideResources], [UniqueType.CostsResources], [UniqueType.UnitActionStockpileCost], implementation not centralized */ + StockpiledResource("stockpiledResource", "Mana", "The name of any stockpiled resource") { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = ruleset.tileResources[parameterText]?.isStockpiled() == true }, - /** Used by [UniqueType.FreeExtraBeliefs], see ReligionManager.getBeliefsToChooseAt* functions */ - BeliefTypeName("beliefType", "Follower", "'Pantheon', 'Follower', 'Founder' or 'Enhancer'") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in BeliefType.values().map { it.name } -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + BeliefTypeName("beliefType", "Follower", "'Pantheon', 'Follower', 'Founder' or 'Enhancer'", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = BeliefType.values().any { it.name == parameterText } }, /** unused at the moment with vanilla rulesets */ Belief("belief", "God of War", "The name of any belief") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.beliefs -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.beliefs }, /** Used by [UniqueType.FreeExtraBeliefs] and its any variant, see ReligionManager.getBeliefsToChooseAt* functions */ - FoundingOrEnhancing("foundingOrEnhancing", "founding", "`founding` or `enhancing`", "Prophet Action Filters") { - // Used in FreeExtraBeliefs, FreeExtraAnyBeliefs + FoundingOrEnhancing("foundingOrEnhancing", "founding", "`founding` or `enhancing`", "Prophet Action Filters", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { private val knownValues = setOf("founding", "enhancing") - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in knownValues -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in knownValues override fun getTranslationWriterStringsForOutput() = knownValues }, /** [UniqueType.ConditionalTech] and others, no central implementation */ Event("event", "Inspiration", "The name of any event") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.events -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.events }, /** [UniqueType.ConditionalTech] and others, no central implementation */ Technology("tech", "Agriculture", "The name of any tech") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.technologies -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.technologies }, /** Implemented by [Technology.matchesFilter][com.unciv.models.ruleset.tech.Technology.matchesFilter] */ TechFilter("techFilter", "Agriculture") { - private val knownValues = setOf("All", "all") - - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = getErrorSeverityForFilter(parameterText, ruleset) - - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (parameterText in ruleset.technologies) return true - if (parameterText in ruleset.eras) return true - return false + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when (parameterText) { + in Constants.all -> true + in ruleset.technologies -> true + in ruleset.eras -> true + else -> false } - - override fun getTranslationWriterStringsForOutput() = knownValues + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) + override fun getTranslationWriterStringsForOutput() = Constants.all }, /** unused at the moment with vanilla rulesets */ Specialist("specialist", "Merchant", "The name of any specialist") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when (parameterText) { - in ruleset.specialists -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.specialists }, /** [UniqueType.ConditionalAfterPolicyOrBelief] and others, no central implementation */ Policy("policy", "Oligarchy", "The name of any policy") { - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - return when (parameterText) { - in ruleset.policies -> null - else -> UniqueType.UniqueParameterErrorSeverity.RulesetSpecific - } - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.policies }, - /** Implemtented by [com.unciv.models.ruleset.Policy.matchesFilter] */ + /** Implemented by [com.unciv.models.ruleset.Policy.matchesFilter] */ PolicyFilter("policyFilter", "Oligarchy", "The name of any policy") { - private val knownValues = Constants.all - - override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean { - if (parameterText in knownValues) return true - if (parameterText in ruleset.policies) return true - if (ruleset.policies.values.any { it.hasUnique(parameterText) }) return true - return false - } - - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - return getErrorSeverityForFilter(parameterText, ruleset) + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + parameterText in Constants.all -> true + parameterText in ruleset.policies -> true + ruleset.policies.values.any { it.hasUnique(parameterText) } -> true + else -> false } + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = getErrorSeverityForFilter(parameterText, ruleset) }, /** Used by [UniqueType.HiddenWithoutVictoryType], implementation in Civilopedia, OverviewScreen and to exclude e.g. from Quests */ - VictoryT("victoryType", "Domination", "The name of any victory type: 'Neutral', 'Cultural', 'Diplomatic', 'Domination', 'Scientific', 'Time'") { - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - return if (parameterText in ruleset.victories) null - else UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + VictoryT("victoryType", + "Domination", "The name of any victory type: 'Cultural', 'Diplomatic', 'Domination', 'Scientific', 'Time' or one of your mod's VictoryTypes.json names" + ) { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in ruleset.victories }, /** Used by [UniqueType.KillUnitPlunder] and [UniqueType.KillUnitPlunderNearCity], implementation in [Battle.tryEarnFromKilling][com.unciv.logic.battle.Battle.tryEarnFromKilling] */ - CostOrStrength("costOrStrength", "Cost", "`Cost` or `Strength`") { + CostOrStrength("costOrStrength", "Cost", "`Cost` or `Strength`", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { private val knownValues = setOf("Cost", "Strength") - override fun getErrorSeverity( - parameterText: String, - ruleset: Ruleset - ): UniqueType.UniqueParameterErrorSeverity? { - return if (parameterText in knownValues) null - else UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - } + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = parameterText in knownValues }, /** Mod declarative compatibility: Define Mod relations by their name. */ - ModName("modFilter", "DeCiv Redux", """A Mod name, case-sensitive _or_ a simple wildcard filter beginning and ending in an Asterisk, case-insensitive""", "Mod name filter") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = when { - BaseRuleset.values().any { it.fullName == parameterText } -> null // Only Builtin ruleset names can contain '-' - parameterText == "*Civ V -*" || parameterText == "*Civ V - *" -> null // Wildcard filter for builtin - '-' in parameterText -> UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - parameterText.matches(Regex("""^\*[^*]+\*$""")) -> null - parameterText.startsWith('*') || parameterText.endsWith('*') -> UniqueType.UniqueParameterErrorSeverity.RulesetInvariant - else -> null + ModName("modFilter", + "DeCiv Redux", """A Mod name, case-sensitive _or_ a simple wildcard filter beginning and ending in an Asterisk, case-insensitive""", "Mod name filter", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when { + BaseRuleset.values().any { it.fullName == parameterText } -> true // Verbatim builtin + parameterText == "*Civ V -*" || parameterText == "*Civ V - *" -> true // Wildcard filter for builtin + '-' in parameterText -> false // Only Builtin ruleset names can contain '-' + parameterText.matches(Regex("""^\*[^*]+\*$""")) -> true // Wildcard on both ends and no wildcard in between + parameterText.startsWith('*') || parameterText.endsWith('*') -> true // one-sided wildcards aren't implemented (feel free to...) + else -> true } - override fun getTranslationWriterStringsForOutput() = scanExistingValues(this) }, /** Suppress RulesetValidator warnings: Parameter check delegated to RulesetValidator, and auto-translation off. */ - ValidationWarning("validationWarning", Suppression.parameterDocExample, Suppression.parameterDocDescription, "Mod-check warning") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? = - if (Suppression.isValidFilter(parameterText)) null - else UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ValidationWarning("validationWarning", + Suppression.parameterDocExample, Suppression.parameterDocDescription, "Mod-check warning", + severityDefault = UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + ) { + override fun isKnownValue(parameterText: String, ruleset: Ruleset) = Suppression.isValidFilter(parameterText) }, /** Behaves like [Unknown], but states explicitly the parameter is OK and its contents are ignored */ Comment("comment", "comment", null, "Unique Specials") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = null - + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = null override fun getTranslationWriterStringsForOutput() = scanExistingValues(this) }, /** We don't know anything about this parameter - this needs to return * [isTranslationWriterGuess]() == `true` for all inputs or TranslationFileWriter will have a problem! */ Unknown("param", "Unknown") { - override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): - UniqueType.UniqueParameterErrorSeverity? = null + override fun getErrorSeverity(parameterText: String, ruleset: Ruleset) = null }; //region _Internals - /** Validate a [Unique] parameter */ - abstract fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? + /** Validate a [Unique] parameter. + * - The default implementation flags [severityDefault] when [isKnownValue] returns `false`. + * - This means [getErrorSeverity] or [isKnownValue] ***must*** be overridden or else the UniqueParameterType is never valid. + * - Can be delegated to helper [getErrorSeverityForFilter] for multiFilters (uses [isKnownValue]). + * - Can be delegated to helper [getErrorSeverityViaKnownValue] for simple filters (uses [isKnownValue]). + */ + open fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? = + getErrorSeverityViaKnownValue(parameterText, ruleset) + /** Checks if [parameterText] is a valid value. + * - The default implementation returns `false`, and controls the default implementation of [getErrorSeverity]. + * - If this is overridden and [getErrorSeverity] is not, the "bad" outcome is [severityDefault]. + * - [getErrorSeverity] takes precedence and chooses whether to call this or not. + * - This means [getErrorSeverity] or [isKnownValue] ***must*** be overridden or else the UniqueParameterType is never valid. + */ open fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean = false - fun getErrorSeverityForFilter(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? { + protected fun getErrorSeverityForFilter(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? { val isKnown = MultiFilter.multiFilter(parameterText, { isKnownValue(it, ruleset) }, true) if (isKnown) return null return UniqueType.UniqueParameterErrorSeverity.PossibleFilteringUnique } + protected fun getErrorSeverityViaKnownValue( + parameterText: String, ruleset: Ruleset, + errorSeverity: UniqueType.UniqueParameterErrorSeverity = severityDefault + ) = if (isKnownValue(parameterText, ruleset)) null else errorSeverity + + protected fun String.isFilteringUniqueIn(map: Map) + = map.values.any { this in it.uniques } + + protected fun String.getInvariantSeverityUnless(predicate: String.() -> Boolean) = + if (predicate()) null else UniqueType.UniqueParameterErrorSeverity.RulesetInvariant + /** Pick this type when [TranslationFileWriter] tries to guess for an untyped [Unique] */ open fun isTranslationWriterGuess(parameterText: String, ruleset: Ruleset): Boolean = getErrorSeverity(parameterText, ruleset) == null diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt b/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt index 15388202ba..bb78575698 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt @@ -540,20 +540,19 @@ object UniqueTriggerActivation { UniqueType.UnitsGainPromotion -> { val filter = unique.params[0] - val promotion = unique.params[1] + val promotionName = unique.params[1] + val promotion = ruleset.unitPromotions[promotionName] ?: return null - val unitsToPromote = civInfo.units.getCivUnits().filter { it.matchesFilter(filter) } - .filter { unitToPromote -> - ruleset.unitPromotions.values.any { - it.name == promotion && unitToPromote.type.name in it.unitTypes - } + val unitsToPromote = civInfo.units.getCivUnits() + .filter { + it.matchesFilter(filter) && (it.type.name in promotion.unitTypes || promotion.unitTypes.isEmpty()) }.toList() if (unitsToPromote.isEmpty()) return null return { val promotedUnitLocations: MutableList = mutableListOf() for (civUnit in unitsToPromote) { - civUnit.promotions.addPromotion(promotion, isFree = true) + civUnit.promotions.addPromotion(promotionName, isFree = true) promotedUnitLocations.add(civUnit.getTile().position) } @@ -562,7 +561,7 @@ object UniqueTriggerActivation { notification, MapUnitAction(promotedUnitLocations), NotificationCategory.Units, - "unitPromotionIcons/${unique.params[1]}" + "unitPromotionIcons/$promotionName" ) } true diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 627533a08d..916bbbd27e 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -189,7 +189,10 @@ enum class UniqueType( /// Resource production & consumption ConsumesResources("Consumes [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit), ProvidesResources("Provides [amount] [resource]", UniqueTarget.Global, UniqueTarget.Improvement, UniqueTarget.FollowerBelief), - CostsResources("Costs [amount] [stockpiledResource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit), + //todo should these two be merged to avoid the confusion? + /** @see UnitActionStockpileCost */ + CostsResources("Costs [amount] [stockpiledResource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit, + docDescription = "Do not confuse with \"costs [amount] [stockpiledResource]\" (lowercase 'c'), the Unit Action Modifier."), // Todo: Get rid of forced sign (+[relativeAmount]) and unify these two, e.g.: "[relativeAmount]% [resource/resourceType] production" // Note that the parameter type 'resourceType' (strategic, luxury, bonus) currently doesn't exist and should then be added as well StrategicResourcesIncrease("Quantity of strategic resources produced by the empire +[relativeAmount]%", UniqueTarget.Global), // used by Policies @@ -510,8 +513,9 @@ enum class UniqueType( docDescription = "Requires [amount] of Movement to execute. Unit's Movement is rounded up"), UnitActionStatsCost("costs [stats] stats", UniqueTarget.UnitActionModifier, docDescription = "A positive Integer value will be subtracted from your stock. Food and Production will be removed from Closest City's current stock"), + /** @see CostsResources */ UnitActionStockpileCost("costs [amount] [stockpiledResource]", UniqueTarget.UnitActionModifier, - docDescription = "A positive Integer value will be subtracted from your stock."), + docDescription = "A positive Integer value will be subtracted from your stock. Do not confuse with \"Costs [amount] [stockpiledResource]\" (uppercase 'C') for Improvements, Buildings, and Units."), UnitActionOnce("once", UniqueTarget.UnitActionModifier), UnitActionLimitedTimes("[amount] times", UniqueTarget.UnitActionModifier), UnitActionExtraLimitedTimes("[amount] additional time(s)", UniqueTarget.UnitActionModifier), @@ -802,7 +806,8 @@ enum class UniqueType( OneTimeChangeTerrain("Turn this tile into a [terrainName] tile", UniqueTarget.Triggerable), - UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Triggerable), // Not used in Vanilla + UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Triggerable, + docDescription = "Works only with promotions that are valid for the unit's type - or for promotions that do not specify any."), // Not used in Vanilla FreeStatBuildings("Provides the cheapest [stat] building in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy FreeSpecificBuildings("Provides a [buildingName] in your first [positiveAmount] cities for free", UniqueTarget.Triggerable), // used in Policy TriggerEvent("Triggers a [event] event", UniqueTarget.Triggerable), diff --git a/docs/Modders/uniques.md b/docs/Modders/uniques.md index c9f806789a..073e175961 100644 --- a/docs/Modders/uniques.md +++ b/docs/Modders/uniques.md @@ -107,12 +107,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Triggerable ??? example "Instantly consumes [positiveAmount] [stockpiledResource]" - Example: "Instantly consumes [3] [StockpiledResource]" + Example: "Instantly consumes [3] [Mana]" Applicable to: Triggerable ??? example "Instantly provides [positiveAmount] [stockpiledResource]" - Example: "Instantly provides [3] [StockpiledResource]" + Example: "Instantly provides [3] [Mana]" Applicable to: Triggerable @@ -163,6 +163,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Triggerable ??? example "[mapUnitFilter] units gain the [promotion] promotion" + Works only with promotions that are valid for the unit's type - or for promotions that do not specify any. Example: "[Wounded] units gain the [Shock I] promotion" Applicable to: Triggerable @@ -1068,7 +1069,8 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Building, Unit, Improvement ??? example "Costs [amount] [stockpiledResource]" - Example: "Costs [3] [StockpiledResource]" + Do not confuse with "costs [amount] [stockpiledResource]" (lowercase 'c'), the Unit Action Modifier. + Example: "Costs [3] [Mana]" Applicable to: Building, Unit, Improvement @@ -2459,8 +2461,8 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: UnitActionModifier ??? example "<costs [amount] [stockpiledResource]>" - A positive Integer value will be subtracted from your stock. - Example: "<costs [3] [StockpiledResource]>" + A positive Integer value will be subtracted from your stock. Do not confuse with "Costs [amount] [stockpiledResource]" (uppercase 'C') for Improvements, Buildings, and Units. + Example: "<costs [3] [Mana]>" Applicable to: UnitActionModifier @@ -2507,12 +2509,12 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl *[civWideStat]: All the following stats have civ-wide fields: `Gold`, `Science`, `Culture`, `Faith`. *[combatantFilter]: This indicates a combatant, which can either be a unit or a city (when bombarding). Must either be `City` or a `mapUnitFilter`. *[costOrStrength]: `Cost` or `Strength`. -*[countable]: This indicate a number or a numeric variable. +*[countable]: This indicates a number or a numeric variable. *[era]: The name of any era. *[event]: The name of any event. *[foundingOrEnhancing]: `founding` or `enhancing`. *[fraction]: Indicates a fractional number, which can be negative. -*[improvementName]: The name of any improvement. +*[improvementName]: The name of any improvement excluding 'Cancel improvement order' *[modFilter]: A Mod name, case-sensitive _or_ a simple wildcard filter beginning and ending in an Asterisk, case-insensitive. *[policy]: The name of any policy. *[policyFilter]: The name of any policy. @@ -2524,8 +2526,9 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl *[speed]: The name of any speed. *[stat]: This is one of the 7 major stats in the game - `Gold`, `Science`, `Production`, `Food`, `Happiness`, `Culture` and `Faith`. Note that the stat names need to be capitalized! *[stats]: For example: `+2 Production, +3 Food`. Note that the stat names need to be capitalized! -*[stockpiledResource]: The name of any stockpiled. +*[stockpiledResource]: The name of any stockpiled resource. *[tech]: The name of any tech. *[tileFilter]: Anything that can be used either in an improvementFilter or in a terrainFilter can be used here, plus 'unimproved' +*[unitType]: Can be 'Land', 'Water', 'Air', any unit type, a filtering Unique on a unit type, or a multi-filter of these. *[validationWarning]: Suppresses one specific Ruleset validation warning. This can specify the full text verbatim including correct upper/lower case, or it can be a wildcard case-insensitive simple pattern starting and ending in an asterisk ('*'). If the suppression unique is used within an object or as modifier (not ModOptions), the wildcard symbols can be omitted, as selectivity is better due to the limited scope. -*[victoryType]: The name of any victory type: 'Neutral', 'Cultural', 'Diplomatic', 'Domination', 'Scientific', 'Time' \ No newline at end of file +*[victoryType]: The name of any victory type: 'Cultural', 'Diplomatic', 'Domination', 'Scientific', 'Time' or one of your mod's VictoryTypes.json names.