From ab82328211c4b9adf13b2c1d2bbbb0b8b93e88d8 Mon Sep 17 00:00:00 2001 From: Jack Rainy Date: Fri, 13 May 2022 12:34:10 +0300 Subject: [PATCH] Missing typed uniques for the units - part 2 (#6749) * Replacements for existing UniqueTypes * More typed uniques are added * Migration to the typed uniques * Minor optimization * More general approach on UniqueType usage * Better wording for the UniqueType --- .../unciv/logic/automation/UnitAutomation.kt | 4 +- core/src/com/unciv/logic/battle/Battle.kt | 15 +++---- core/src/com/unciv/logic/city/CityReligion.kt | 2 +- .../logic/civilization/CivilizationInfo.kt | 2 +- .../logic/civilization/ReligionManager.kt | 2 +- core/src/com/unciv/logic/map/MapUnit.kt | 26 +++++------ .../src/com/unciv/logic/map/UnitPromotions.kt | 2 +- .../ruleset/unique/UniqueParameterType.kt | 2 +- .../unciv/models/ruleset/unique/UniqueType.kt | 6 +++ .../com/unciv/models/ruleset/unit/BaseUnit.kt | 45 ++++++++++--------- .../unciv/ui/worldscreen/unit/UnitActions.kt | 12 ++--- .../com/unciv/app/desktop/UniqueDocsWriter.kt | 2 +- docs/Modders/uniques.md | 21 +++++++++ tests/src/com/unciv/testing/BasicTests.kt | 15 +++++++ 14 files changed, 99 insertions(+), 57 deletions(-) diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index 304a845128..9c4967d3e1 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -164,10 +164,10 @@ object UnitAutomation { if (unit.hasUnique(UniqueType.ConstructImprovementConsumingUnit)) return SpecificUnitAutomation.automateImprovementPlacer(unit) // includes great people plus moddable units - if (unit.getMatchingUniques("Can [] [] times").any{ it.params[0] == "Spread Religion" }) + if (unit.getMatchingUniques(UniqueType.CanActionSeveralTimes).any{ it.params[0] == "Spread Religion" }) return SpecificUnitAutomation.automateMissionary(unit) - if (unit.hasUnique("Prevents spreading of religion to the city it is next to")) + if (unit.hasUnique(UniqueType.PreventSpreadingReligion)) return SpecificUnitAutomation.automateInquisitor(unit) diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 518af30f8a..0465e5238f 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -635,16 +635,11 @@ object Battle { } } - val blastRadius = - if (!attacker.hasUnique(UniqueType.BlastRadius)) 2 - // Don't check conditionals as there are not supported - else attacker.unit.getMatchingUniques(UniqueType.BlastRadius).first().params[0].toInt() + val strength = attacker.unit.getMatchingUniques(UniqueType.NuclearWeapon) + .firstOrNull()?.params?.get(0)?.toInt() ?: return - val strength = when { - (attacker.unit.hasUnique("Nuclear weapon of Strength []")) -> - attacker.unit.getMatchingUniques("Nuclear weapon of Strength []").first().params[0].toInt() - else -> return - } + val blastRadius = attacker.unit.getMatchingUniques(UniqueType.BlastRadius) + .firstOrNull()?.params?.get(0)?.toInt() ?: 2 // Calculate the tiles that are hit val hitTiles = targetTile.getTilesInDistance(blastRadius) @@ -792,7 +787,7 @@ object Battle { } private fun tryInterceptAirAttack(attacker: MapUnitCombatant, attackedTile: TileInfo, interceptingCiv: CivilizationInfo, defender: ICombatant?) { - if (attacker.unit.hasUnique("Cannot be intercepted")) return + if (attacker.unit.hasUnique(UniqueType.CannotBeIntercepted)) return // Pick highest chance interceptor for (interceptor in interceptingCiv.getCivUnits() .filter { it.canIntercept(attackedTile) } diff --git a/core/src/com/unciv/logic/city/CityReligion.kt b/core/src/com/unciv/logic/city/CityReligion.kt index 71d89227b4..1c26ccdf24 100644 --- a/core/src/com/unciv/logic/city/CityReligion.kt +++ b/core/src/com/unciv/logic/city/CityReligion.kt @@ -292,7 +292,7 @@ class CityInfoReligionManager { fun isProtectedByInquisitor(): Boolean { for (tile in cityInfo.getCenterTile().neighbors) - if (tile.civilianUnit?.hasUnique("Prevents spreading of religion to the city it is next to") == true) + if (tile.civilianUnit?.hasUnique(UniqueType.PreventSpreadingReligion) == true) return true if (cityInfo.getCenterTile().civilianUnit?.name == "Inquisitor") return true return false diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index d6bfb52343..42d194f46a 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -1173,7 +1173,7 @@ class CivilizationInfo { if (placedUnit.hasUnique(UniqueType.ReligiousUnit) && gameInfo.isReligionEnabled()) { placedUnit.religion = when { - placedUnit.hasUnique("Takes your religion over the one in their birth city") + placedUnit.hasUnique(UniqueType.TakeReligionOverBirthCity) && religionManager.religion?.isMajorReligion() == true -> religionManager.religion!!.name city != null -> city.cityConstructions.cityInfo.religion.getMajorityReligionName() diff --git a/core/src/com/unciv/logic/civilization/ReligionManager.kt b/core/src/com/unciv/logic/civilization/ReligionManager.kt index 060af685f7..bca48505d9 100644 --- a/core/src/com/unciv/logic/civilization/ReligionManager.kt +++ b/core/src/com/unciv/logic/civilization/ReligionManager.kt @@ -257,7 +257,7 @@ class ReligionManager { shouldChoosePantheonBelief = false for (unit in civInfo.getCivUnits()) - if (unit.hasUnique(UniqueType.ReligiousUnit) && unit.hasUnique("Takes your religion over the one in their birth city")) + if (unit.hasUnique(UniqueType.ReligiousUnit) && unit.hasUnique(UniqueType.TakeReligionOverBirthCity)) unit.religion = newReligion.name } diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 2bde3362f1..d13693e9f1 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -259,10 +259,6 @@ class MapUnit { fun getUniques(): ArrayList = tempUniques - // TODO typify usages and remove this function - fun getMatchingUniques(placeholderText: String): Sequence = - tempUniquesMap.getUniques(placeholderText) - fun getMatchingUniques( uniqueType: UniqueType, stateForConditionals: StateForConditionals = StateForConditionals(civInfo, unit=this), @@ -275,8 +271,10 @@ class MapUnit { yieldAll(civInfo.getMatchingUniques(uniqueType, stateForConditionals)) } + // TODO typify usages and remove this function + @Deprecated("as of 4.0.15", ReplaceWith("hasUnique(uniqueType: UniqueType, ...)")) fun hasUnique(unique: String): Boolean { - return getMatchingUniques(unique).any() + return tempUniquesMap.getUniques(unique).any() } fun hasUnique( @@ -810,7 +808,8 @@ class MapUnit { if (unit == this) continue - if (unit.getMatchingUniques("Transfer Movement to []").any { matchesFilter(it.params[0]) } ) + if (unit.getMatchingUniques(UniqueType.TransferMovement) + .any { matchesFilter(it.params[0]) } ) currentMovement = maxOf(getMaxMovement().toFloat(), unit.getMaxMovement().toFloat()) } } @@ -1013,13 +1012,14 @@ class MapUnit { // Air Units can only Intercept if they didn't move this turn if (baseUnit.isAirUnit() && currentMovement == 0f) return false val maxAttacksPerTurn = 1 + - getMatchingUniques("[] extra interceptions may be made per turn").sumOf { it.params[0].toInt() } + getMatchingUniques(UniqueType.ExtraInterceptionsPerTurn) + .sumOf { it.params[0].toInt() } if (attacksThisTurn >= maxAttacksPerTurn) return false return true } fun interceptChance(): Int { - return getMatchingUniques("[]% chance to intercept air attacks").sumOf { it.params[0].toInt() } + return getMatchingUniques(UniqueType.ChanceInterceptAirAttacks).sumOf { it.params[0].toInt() } } fun isTransportTypeOf(mapUnit: MapUnit): Boolean { @@ -1044,13 +1044,13 @@ class MapUnit { } fun interceptDamagePercentBonus(): Int { - return getMatchingUniques("[]% Damage when intercepting") + return getMatchingUniques(UniqueType.DamageWhenIntercepting) .sumOf { it.params[0].toInt() } } fun receivedInterceptDamageFactor(): Float { var damageFactor = 1f - for (unique in getMatchingUniques("Damage taken from interception reduced by []%")) + for (unique in getMatchingUniques(UniqueType.DamageFromInterceptionReduced)) damageFactor *= 1f - unique.params[0].toFloat() / 100f return damageFactor } @@ -1158,18 +1158,18 @@ class MapUnit { } fun religiousActionsUnitCanDo(): Sequence { - return getMatchingUniques("Can [] [] times") + return getMatchingUniques(UniqueType.CanActionSeveralTimes) .map { it.params[0] } } fun canDoReligiousAction(action: String): Boolean { - return getMatchingUniques("Can [] [] times").any { it.params[0] == action } + return getMatchingUniques(UniqueType.CanActionSeveralTimes).any { it.params[0] == action } } /** For the actual value, check the member variable [maxAbilityUses] */ fun getBaseMaxActionUses(action: String): Int { - return getMatchingUniques("Can [] [] times") + return getMatchingUniques(UniqueType.CanActionSeveralTimes) .filter { it.params[0] == action } .sumOf { it.params[1].toInt() } } diff --git a/core/src/com/unciv/logic/map/UnitPromotions.kt b/core/src/com/unciv/logic/map/UnitPromotions.kt index eaced7fc16..6edbb0d736 100644 --- a/core/src/com/unciv/logic/map/UnitPromotions.kt +++ b/core/src/com/unciv/logic/map/UnitPromotions.kt @@ -67,7 +67,7 @@ class UnitPromotions { val ruleset = unit.civInfo.gameInfo.ruleSet val promotion = ruleset.unitPromotions[promotionName]!! - if (!promotion.hasUnique("Doing so will consume this opportunity to choose a Promotion")) + if (!promotion.hasUnique(UniqueType.SkipPromotion)) promotions.add(promotionName) // If we upgrade this unit to its new version, we already need to have this promotion added, diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt index 5fe6f1c805..063a717939 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt @@ -137,7 +137,7 @@ enum class UniqueParameterType( parameterText: String, ruleset: Ruleset ): UniqueType.UniqueComplianceErrorSeverity? { - if (ruleset.units[parameterText]?.hasUnique("Great Person - []") == true) return null + if (ruleset.units[parameterText]?.hasUnique(UniqueType.GreatPerson) == true) return null return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific } }, diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index b49cc2c2ed..4fe571ab46 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -443,6 +443,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: NoMovementToPillage("No movement cost to pillage", UniqueTarget.Unit, UniqueTarget.Global), CanMoveAfterAttacking("Can move after attacking", UniqueTarget.Unit), + TransferMovement("Transfer Movement to [unit]", UniqueTarget.Unit), MoveImmediatelyOnceBought("Can move immediately once bought", UniqueTarget.Unit), MayParadrop("May Paradrop up to [amount] tiles from inside friendly territory", UniqueTarget.Unit), @@ -471,8 +472,12 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: CarryAirUnits("Can carry [amount] [mapUnitFilter] units", UniqueTarget.Unit), CarryExtraAirUnits("Can carry [amount] extra [mapUnitFilter] units", UniqueTarget.Unit), CannotBeCarriedBy("Cannot be carried by [mapUnitFilter] units", UniqueTarget.Unit), + ChanceInterceptAirAttacks("[relativeAmount]% chance to intercept air attacks", UniqueTarget.Unit), DamageFromInterceptionReduced("Damage taken from interception reduced by [relativeAmount]%", UniqueTarget.Unit), + DamageWhenIntercepting("[relativeAmount]% Damage when intercepting", UniqueTarget.Unit), + ExtraInterceptionsPerTurn("[amount] extra interceptions may be made per turn", UniqueTarget.Unit), + CannotBeIntercepted("Cannot be intercepted", UniqueTarget.Unit), UnitMaintenanceDiscount("[relativeAmount]% maintenance costs", UniqueTarget.Unit, UniqueTarget.Global), UnitUpgradeCost("[relativeAmount]% Gold cost of upgrading", UniqueTarget.Unit, UniqueTarget.Global), @@ -730,6 +735,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: OneTimeUnitUpgrade("This Unit upgrades for free", UniqueTarget.Global), // Not used in Vanilla OneTimeUnitSpecialUpgrade("This Unit upgrades for free including special upgrades", UniqueTarget.Ruins), OneTimeUnitGainPromotion("This Unit gains the [promotion] promotion", UniqueTarget.Triggerable), // Not used in Vanilla + SkipPromotion("Doing so will consume this opportunity to choose a Promotion", UniqueTarget.Promotion), UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Triggerable), // Not used in Vanilla @Deprecated("as of 3.19.8", ReplaceWith("[+amount]% Strength ")) diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index 6d6197bb94..2404b68855 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -480,7 +480,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { // If this unit has special abilities that need to be kept track of, start doing so here if (unit.hasUnique(UniqueType.ReligiousUnit) && civInfo.gameInfo.isReligionEnabled()) { unit.religion = - if (unit.hasUnique("Takes your religion over the one in their birth city")) + if (unit.hasUnique(UniqueType.TakeReligionOverBirthCity)) civInfo.religionManager.religion?.name else cityConstructions.cityInfo.religion.getMajorityReligionName() @@ -575,9 +575,9 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { } } - fun isGreatPerson() = hasUnique("Great Person - []") + fun isGreatPerson() = hasUnique(UniqueType.GreatPerson) - fun isNuclearWeapon() = hasUnique("Nuclear weapon of Strength []") + fun isNuclearWeapon() = hasUnique(UniqueType.NuclearWeapon) fun movesLikeAirUnits() = getType().getMovementType() == UnitMovementType.Air @@ -651,29 +651,34 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { .flatMap { it.uniqueObjects } for (unique in allUniques) { - when { - unique.isOfType(UniqueType.Strength) && unique.params[0].toInt() > 0 -> { - if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsUnits) } ) { // Bonus vs some units - a quarter of the bonus - power *= (unique.params[0].toInt() / 4f).toPercent() - } else if ( - unique.conditionals.any { - it.isOfType(UniqueType.ConditionalVsCity) // City Attack - half the bonus - || it.isOfType(UniqueType.ConditionalAttacking) // Attack - half the bonus - || it.isOfType(UniqueType.ConditionalDefending) // Defense - half the bonus - || it.isOfType(UniqueType.ConditionalFightingInTiles) } // Bonus in terrain or feature - half the bonus - ) { - power *= (unique.params[0].toInt() / 2f).toPercent() + when (unique.type) { + UniqueType.Strength -> { + if (unique.params[0].toInt() > 0) { + if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsUnits) }) { // Bonus vs some units - a quarter of the bonus + power *= (unique.params[0].toInt() / 4f).toPercent() + } else if ( + unique.conditionals.any { + it.isOfType(UniqueType.ConditionalVsCity) // City Attack - half the bonus + || it.isOfType(UniqueType.ConditionalAttacking) // Attack - half the bonus + || it.isOfType(UniqueType.ConditionalDefending) // Defense - half the bonus + || it.isOfType(UniqueType.ConditionalFightingInTiles) + } // Bonus in terrain or feature - half the bonus + ) { + power *= (unique.params[0].toInt() / 2f).toPercent() + } } } - unique.isOfType(UniqueType.StrengthNearCapital) && unique.params[0].toInt() > 0 -> - power *= (unique.params[0].toInt() / 4f).toPercent() // Bonus decreasing with distance from capital - not worth much most of the map??? + UniqueType.StrengthNearCapital -> + if (unique.params[0].toInt() > 0) + power *= (unique.params[0].toInt() / 4f).toPercent() // Bonus decreasing with distance from capital - not worth much most of the map??? - unique.placeholderText == "May Paradrop up to [] tiles from inside friendly territory" // Paradrop - 25% bonus + UniqueType.MayParadrop // Paradrop - 25% bonus -> power += power / 4 - unique.isOfType(UniqueType.MustSetUp) // Must set up - 20 % penalty + UniqueType.MustSetUp // Must set up - 20 % penalty -> power -= power / 5 - unique.isOfType(UniqueType.AdditionalAttacks) // Extra attacks - 20% bonus per extra attack + UniqueType.AdditionalAttacks // Extra attacks - 20% bonus per extra attack -> power += (power * unique.params[0].toInt()) / 5 + else -> {} } } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 084836cd21..9e9a41094f 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -101,7 +101,7 @@ object UnitActions { // have the visual bug that the tile overlays for the eligible swap locations are drawn for // /all/ selected units instead of only the first one. This could be fixed, but again, // swapping makes little sense for multiselect anyway. - if (worldScreen.bottomUnitTable.selectedUnits.count() > 1) return + if (worldScreen.bottomUnitTable.selectedUnits.size > 1) return // Only show the swap action if there is at least one possible swap movement if (unit.movement.getUnitSwappableTiles().none()) return actionList += UnitAction( @@ -245,7 +245,7 @@ object UnitActions { private fun addParadropAction(unit: MapUnit, actionList: ArrayList) { val paradropUniques = - unit.getMatchingUniques("May Paradrop up to [] tiles from inside friendly territory") + unit.getMatchingUniques(UniqueType.MayParadrop) if (!paradropUniques.any() || unit.isEmbarked()) return unit.paradropRange = paradropUniques.maxOfOrNull { it.params[0] }!!.toInt() actionList += UnitAction(UnitActionType.Paradrop, @@ -588,7 +588,7 @@ object UnitActions { .getTilesInDistance(1) .flatMap { it.getUnits() } .any { - it.hasUnique("Prevents spreading of religion to the city it is next to") + it.hasUnique(UniqueType.PreventSpreadingReligion) && it.religion != unit.religion } actionList += UnitAction(UnitActionType.SpreadReligion, @@ -599,7 +599,7 @@ object UnitActions { unit.civInfo.addStat(Stat.valueOf(unique.params[1]), followersOfOtherReligions * unique.params[0].toInt()) } city.religion.addPressure(unit.religion!!, unit.getPressureAddedFromSpread()) - if (unit.hasUnique("Removes other religions when spreading religion")) + if (unit.hasUnique(UniqueType.RemoveOtherReligions)) city.religion.removeAllPressuresExceptFor(unit.religion!!) unit.currentMovement = 0f useActionWithLimitedUses(unit, Constants.spreadReligionAbilityCount) @@ -627,7 +627,7 @@ object UnitActions { val finalActions = ArrayList() var uniquesToCheck = unit.getMatchingUniques(UniqueType.ConstructImprovementConsumingUnit) if (unit.religiousActionsUnitCanDo().all { unit.abilityUsesLeft[it] == unit.maxAbilityUses[it] }) - uniquesToCheck += unit.getMatchingUniques("Can construct [] if it hasn't used other actions yet") + uniquesToCheck += unit.getMatchingUniques(UniqueType.CanConstructIfNoOtherActions) val civResources = unit.civInfo.getCivResourcesByName() for (unique in uniquesToCheck) { @@ -637,7 +637,7 @@ object UnitActions { val resourcesAvailable = improvement.uniqueObjects.none { it.isOfType(UniqueType.ConsumesResources) && - civResources[unique.params[1]] ?: 0 < unique.params[0].toInt() + (civResources[unique.params[1]] ?: 0) < unique.params[0].toInt() } finalActions += UnitAction(UnitActionType.Create, diff --git a/desktop/src/com/unciv/app/desktop/UniqueDocsWriter.kt b/desktop/src/com/unciv/app/desktop/UniqueDocsWriter.kt index 183d2b5214..609769ae5b 100644 --- a/desktop/src/com/unciv/app/desktop/UniqueDocsWriter.kt +++ b/desktop/src/com/unciv/app/desktop/UniqueDocsWriter.kt @@ -71,7 +71,7 @@ class UniqueDocsWriter { // `val paramExamples = uniqueType.parameterTypeMap.map { it.joinToString("/") { pt -> pt.docExample } }.toTypedArray()` // Might confuse modders to think "/" can go into the _actual_ unique and mean "or", so better show just one ("Farm" in the example above): val paramExamples = uniqueType.parameterTypeMap.map { it.first().docExample }.toTypedArray() - lines += "\tExample: \"${uniqueText.fillPlaceholders(*paramExamples)}\"\n" + lines += "\tExample: \"${uniqueText.fillPlaceholders(*paramExamples)}\"\r\n" } lines += "\tApplicable to: " + uniqueType.allTargets().sorted().joinToString() lines += "" diff --git a/docs/Modders/uniques.md b/docs/Modders/uniques.md index 5713cff185..fc4da8c147 100644 --- a/docs/Modders/uniques.md +++ b/docs/Modders/uniques.md @@ -1050,6 +1050,11 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl ??? example "Can move after attacking" Applicable to: Unit +??? example "Transfer Movement to [unit]" + Example: "Transfer Movement to [Musketman]" + + Applicable to: Unit + ??? example "Can move immediately once bought" Applicable to: Unit @@ -1066,6 +1071,9 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Unit +??? example "Doing so will consume this opportunity to choose a Promotion" + Applicable to: Unit + ??? example "Eliminates combat penalty for attacking across a coast" Applicable to: Unit @@ -1106,6 +1114,19 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Unit +??? example "[relativeAmount]% Damage when intercepting" + Example: "[+20]% Damage when intercepting" + + Applicable to: Unit + +??? example "[amount] extra interceptions may be made per turn" + Example: "[3] extra interceptions may be made per turn" + + Applicable to: Unit + +??? example "Cannot be intercepted" + Applicable to: Unit + ??? example "May capture killed [mapUnitFilter] units" Example: "May capture killed [Wounded] units" diff --git a/tests/src/com/unciv/testing/BasicTests.kt b/tests/src/com/unciv/testing/BasicTests.kt index 629ab61d57..b8aabb7c2b 100644 --- a/tests/src/com/unciv/testing/BasicTests.kt +++ b/tests/src/com/unciv/testing/BasicTests.kt @@ -157,6 +157,21 @@ class BasicTests { Assert.assertTrue("This test succeeds only if all uniques of buildings are presented in UniqueType.values()", allOK) } + @Test + fun allPromotionsUniquesHaveTheirUniqueTypes() { + val promotions = ruleset.unitPromotions.values + var allOK = true + for (promotion in promotions) { + for (unique in promotion.uniques) { + if (!UniqueType.values().any { it.placeholderText == unique.getPlaceholderText() }) { + println("${promotion.name}: $unique") + allOK = false + } + } + } + Assert.assertTrue("This test succeeds only if all uniques of promotions are presented in UniqueType.values()", allOK) + } + @Test fun allTerrainRelatedUniquesHaveTheirUniqueTypes() { val objects : MutableCollection = mutableListOf()