diff --git a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt index 17a0130b56..4ac422ddbe 100644 --- a/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/SpecificUnitAutomation.kt @@ -195,8 +195,8 @@ object SpecificUnitAutomation { fun automateImprovementPlacer(unit: MapUnit) { if (unit.getTile().militaryUnit == null) return // Don't move until you're accompanied by a military unit - val improvementName = unit.getUniques().first { it.equalsPlaceholderText("Can construct []") } - .getPlaceholderParameters()[0] + val improvementName = unit.getUniques().first { it.placeholderText == "Can construct []" } + .params[0] val improvement = unit.civInfo.gameInfo.ruleSet.tileImprovements[improvementName]!! val relatedStat = improvement.toHashMap().maxBy { it.value }!!.key diff --git a/core/src/com/unciv/logic/automation/UnitAutomation.kt b/core/src/com/unciv/logic/automation/UnitAutomation.kt index b3b459f012..2259e356dd 100644 --- a/core/src/com/unciv/logic/automation/UnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/UnitAutomation.kt @@ -103,7 +103,7 @@ object UnitAutomation { if (unit.name == Constants.greatGeneral || unit.baseUnit.replaces == Constants.greatGeneral) return SpecificUnitAutomation.automateGreatGeneral(unit) - if (unit.getUniques().any { it.equalsPlaceholderText("Can construct []") }) + if (unit.getUniques().any { it.placeholderText == "Can construct []" }) return SpecificUnitAutomation.automateImprovementPlacer(unit) // includes great people plus moddable units return // The AI doesn't know how to handle unknown civilian units diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index a1c44c87d4..9a53bba483 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -12,8 +12,6 @@ import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.TileInfo import com.unciv.models.AttackableTile import com.unciv.models.ruleset.unit.UnitType -import com.unciv.models.translations.equalsPlaceholderText -import com.unciv.models.translations.getPlaceholderParameters import java.util.* import kotlin.math.max @@ -51,8 +49,8 @@ object Battle { // Withdraw from melee ability if (attacker is MapUnitCombatant && attacker.isMelee() && defender is MapUnitCombatant ) { - val withdraw = defender.unit.getUniques().firstOrNull { it.startsWith("May withdraw before melee")} - if (withdraw != null && doWithdrawFromMeleeAbility(attacker, defender, withdraw)) return + val withdraw = defender.unit.getUniques().firstOrNull { it.text.startsWith("May withdraw before melee")} + if (withdraw != null && doWithdrawFromMeleeAbility(attacker, defender, withdraw.text)) return } val isAlreadyDefeatedCity = defender is CityCombatant && defender.isDefeated() @@ -149,8 +147,8 @@ object Battle { && defender is MapUnitCombatant && attacker is MapUnitCombatant) { for (unique in attacker.unit.getUniques()) { - if(unique.equalsPlaceholderText("Heals [] damage if it kills a unit")){ - val amountToHeal = unique.getPlaceholderParameters()[0].toInt() + if(unique.placeholderText == "Heals [] damage if it kills a unit"){ + val amountToHeal = unique.params[0].toInt() attacker.unit.healBy(amountToHeal) } } diff --git a/core/src/com/unciv/logic/battle/BattleDamage.kt b/core/src/com/unciv/logic/battle/BattleDamage.kt index f2312a5de2..35b3337ca5 100644 --- a/core/src/com/unciv/logic/battle/BattleDamage.kt +++ b/core/src/com/unciv/logic/battle/BattleDamage.kt @@ -25,7 +25,7 @@ object BattleDamage { val modifiers = mutableListOf() for (ability in unit.getUniques()) { // This beut allows us to have generic unit uniques: "Bonus vs City 75%", "Penatly vs Mounted 25%" etc. - val regexResult = Regex(BONUS_VS_UNIT_TYPE).matchEntire(ability) + val regexResult = Regex(BONUS_VS_UNIT_TYPE).matchEntire(ability.text) if (regexResult == null) continue val vs = regexResult.groups[2]!!.value val modificationAmount = regexResult.groups[3]!!.value.toFloat() / 100 // if it says 15%, that's 0.15f in modification @@ -114,8 +114,8 @@ object BattleDamage { modifiers.putAll(getTileSpecificModifiers(attacker, defender.getTile())) for (ability in attacker.unit.getUniques()) { - if(ability.equalsPlaceholderText("Bonus as Attacker []%")) { - val bonus = ability.getPlaceholderParameters()[0].toFloat() / 100 + if(ability.placeholderText == "Bonus as Attacker []%") { + val bonus = ability.params[0].toFloat() / 100 if (modifiers.containsKey("Attacker Bonus")) modifiers["Attacker Bonus"] = modifiers["Attacker Bonus"]!! + bonus else modifiers["Attacker Bonus"] = bonus @@ -182,11 +182,11 @@ object BattleDamage { } if(attacker.isRanged()) { - val defenceVsRanged = 0.25f * defender.unit.getUniques().count { it == "+25% Defence against ranged attacks" } + val defenceVsRanged = 0.25f * defender.unit.getUniques().count { it.text == "+25% Defence against ranged attacks" } if (defenceVsRanged > 0) modifiers["defence vs ranged"] = defenceVsRanged } - val carrierDefenceBonus = 0.25f * defender.unit.getUniques().count { it == "+25% Combat Bonus when defending" } + val carrierDefenceBonus = 0.25f * defender.unit.getUniques().count { it.text == "+25% Combat Bonus when defending" } if (carrierDefenceBonus > 0) modifiers["Armor Plating"] = carrierDefenceBonus diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 0d96d512ec..d5fa648d10 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -10,6 +10,7 @@ import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.map.action.MapUnitAction import com.unciv.logic.map.action.StringAction import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.Unique import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.UnitType import java.text.DecimalFormat @@ -99,7 +100,7 @@ class MapUnit { if (isEmbarked()) return getEmbarkedMovement() var movement = baseUnit.movement - movement += getUniques().count { it == "+1 Movement" } + movement += getUniques().count { it.text == "+1 Movement" } if (type.isWaterUnit() && !type.isCivilian() && civInfo.hasUnique("All military naval units receive +1 movement and +1 sight")) @@ -121,31 +122,31 @@ class MapUnit { // This SHOULD NOT be a hashset, because if it is, then promotions with the same text (e.g. barrage I, barrage II) // will not get counted twice! - @Transient var tempUniques= ArrayList() + @Transient var tempUniques= ArrayList() - fun getUniques(): ArrayList { + fun getUniques(): ArrayList { return tempUniques } fun updateUniques(){ - val uniques = ArrayList() + val uniques = ArrayList() val baseUnit = baseUnit() - uniques.addAll(baseUnit.uniques) - uniques.addAll(promotions.promotions.map { currentTile.tileMap.gameInfo.ruleSet.unitPromotions[it]!!.effect }) + uniques.addAll(baseUnit.uniqueObjects) + uniques.addAll(promotions.promotions.map { currentTile.tileMap.gameInfo.ruleSet.unitPromotions[it]!!.unique }) tempUniques = uniques - ignoresTerrainCost = ("Ignores terrain cost" in uniques) - roughTerrainPenalty = ("Rough terrain penalty" in uniques) - doubleMovementInCoast = ("Double movement in coast" in uniques) - doubleMovementInForestAndJungle = ("Double movement rate through Forest and Jungle" in uniques) - doubleMovementInSnowTundraAndHills = ("Double movement in Snow, Tundra and Hills" in uniques) - canEnterIceTiles = ("Can enter ice tiles" in uniques) - cannotEnterOceanTiles = ("Cannot enter ocean tiles" in uniques) - cannotEnterOceanTilesUntilAstronomy = ("Cannot enter ocean tiles until Astronomy" in uniques) + ignoresTerrainCost = hasUnique("Ignores terrain cost") + roughTerrainPenalty = hasUnique("Rough terrain penalty") + doubleMovementInCoast = hasUnique("Double movement in coast") + doubleMovementInForestAndJungle = hasUnique("Double movement rate through Forest and Jungle") + doubleMovementInSnowTundraAndHills = hasUnique("Double movement in Snow, Tundra and Hills") + canEnterIceTiles = hasUnique("Can enter ice tiles") + cannotEnterOceanTiles = hasUnique("Cannot enter ocean tiles") + cannotEnterOceanTilesUntilAstronomy = hasUnique("Cannot enter ocean tiles until Astronomy") } fun hasUnique(unique:String): Boolean { - return getUniques().contains(unique) + return getUniques().any { it.text == unique } } fun updateVisibleTiles() { @@ -156,7 +157,7 @@ class MapUnit { } else { var visibilityRange = 2 - visibilityRange += getUniques().count { it == "+1 Visibility Range" } + visibilityRange += getUniques().count { it.text == "+1 Visibility Range" } if (hasUnique("+2 Visibility Range")) visibilityRange += 2 // This shouldn't be stackable if (hasUnique("Limited Visibility")) visibilityRange -= 1 if (civInfo.hasUnique("+1 Sight for all land military units")) @@ -439,7 +440,7 @@ class MapUnit { fun endTurn() { doPostTurnAction() if (currentMovement == getMaxMovement().toFloat() // didn't move this turn - || getUniques().contains("Unit will heal every turn, even if it performs an action")){ + || hasUnique("Unit will heal every turn, even if it performs an action")){ heal() } if(action != null && health > 99) @@ -631,15 +632,15 @@ class MapUnit { fun interceptChance():Int{ val interceptUnique = getUniques() - .firstOrNull { it.endsWith(CHANCE_TO_INTERCEPT_AIR_ATTACKS) } + .firstOrNull { it.text.endsWith(CHANCE_TO_INTERCEPT_AIR_ATTACKS) } if(interceptUnique==null) return 0 - val percent = Regex("\\d+").find(interceptUnique)!!.value.toInt() + val percent = Regex("\\d+").find(interceptUnique.text)!!.value.toInt() return percent } fun isTransportTypeOf(mapUnit: MapUnit): Boolean { - val isAircraftCarrier = getUniques().contains("Can carry 2 aircraft") - val isMissileCarrier = getUniques().contains("Can carry 2 missiles") + val isAircraftCarrier = hasUnique("Can carry 2 aircraft") + val isMissileCarrier = hasUnique("Can carry 2 missiles") if(!isMissileCarrier && !isAircraftCarrier) return false if(!mapUnit.type.isAirUnit()) return false @@ -655,7 +656,7 @@ class MapUnit { if (owner != mapUnit.owner) return false var unitCapacity = 2 - unitCapacity += getUniques().count { it == "Can carry 1 extra air unit" } + unitCapacity += getUniques().count { it.text == "Can carry 1 extra air unit" } if (currentTile.airUnits.filter { it.isTransported }.size >= unitCapacity) return false @@ -664,8 +665,8 @@ class MapUnit { fun interceptDamagePercentBonus():Int{ var sum=0 - for(unique in getUniques().filter { it.startsWith(BONUS_WHEN_INTERCEPTING) }){ - val percent = Regex("\\d+").find(unique)!!.value.toInt() + for(unique in getUniques().filter { it.text.startsWith(BONUS_WHEN_INTERCEPTING) }){ + val percent = Regex("\\d+").find(unique.text)!!.value.toInt() sum += percent } return sum diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index a30f3990b5..548c376860 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -6,6 +6,7 @@ import com.unciv.logic.city.IConstruction import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.map.MapUnit import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.Unique import com.unciv.models.translations.Translations import com.unciv.models.translations.tr import com.unciv.models.stats.INamed @@ -29,6 +30,7 @@ class BaseUnit : INamed, IConstruction { var requiredTech:String? = null var requiredResource:String? = null var uniques =HashSet() + val uniqueObjects: List by lazy { uniques.map { Unique(it) } } var promotions =HashSet() var obsoleteTech:String?=null var upgradesTo:String? = null diff --git a/core/src/com/unciv/models/ruleset/unit/Promotion.kt b/core/src/com/unciv/models/ruleset/unit/Promotion.kt index 9662c76cc8..e5e67df2ab 100644 --- a/core/src/com/unciv/models/ruleset/unit/Promotion.kt +++ b/core/src/com/unciv/models/ruleset/unit/Promotion.kt @@ -1,6 +1,7 @@ package com.unciv.models.ruleset.unit import com.unciv.models.ruleset.Ruleset +import com.unciv.models.ruleset.Unique import com.unciv.models.stats.INamed import com.unciv.models.translations.Translations import com.unciv.models.translations.tr @@ -9,6 +10,7 @@ class Promotion : INamed{ override lateinit var name: String var prerequisites = listOf() lateinit var effect:String + val unique:Unique by lazy { Unique(effect) } var unitTypes = listOf() // The json parser wouldn't agree to deserialize this as a list of UnitTypes. =( fun getDescription(promotionsForUnitType: Collection, forCivilopedia:Boolean=false, ruleSet:Ruleset? = null):String { diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index 23f746eb30..b28152a21f 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -351,8 +351,8 @@ object UnitActions { if (unit.civInfo.hasUnique("Double gold from Great Merchant trade missions")) goldEarned *= 2 unit.civInfo.gold += goldEarned.toInt() - val relevantUnique = unit.getUniques().first { it.startsWith(CAN_UNDERTAKE) } - val influenceEarned = Regex("\\d+").find(relevantUnique)!!.value.toInt() + val relevantUnique = unit.getUniques().first { it.text.startsWith(CAN_UNDERTAKE) } + val influenceEarned = Regex("\\d+").find(relevantUnique.text)!!.value.toInt() tile.owningCity!!.civInfo.getDiplomacyManager(unit.civInfo).influence += influenceEarned unit.civInfo.addNotification("Your trade mission to [${tile.owningCity!!.civInfo}] has earned you [${goldEarned.toInt()}] gold and [$influenceEarned] influence!", null, Color.GOLD) addGoldPerGreatPersonUsage(unit.civInfo) @@ -364,8 +364,8 @@ object UnitActions { fun getImprovementConstructionActions(unit: MapUnit, tile: TileInfo): ArrayList { val finalActions = ArrayList() - for (unique in unit.getUniques().filter { it.equalsPlaceholderText("Can construct []") }) { - val improvementName = unique.getPlaceholderParameters()[0] + for (unique in unit.getUniques().filter { it.placeholderText == "Can construct []" }) { + val improvementName = unique.params[0] finalActions += UnitAction( type = UnitActionType.Create, title = "Create [$improvementName]",