Added in [tileFilter] tiles conditional to combat uniques (#5393)

* Some code cleaning

* Added `in [tileFilter] tiles` conditional

* Limited Visibility -> [-1] Sight

* Fix tests and rename conditional

* Actually made the conditional functional

* Renamed conditional once again
This commit is contained in:
Xander Lenstra 2021-10-04 21:30:09 +02:00 committed by GitHub
parent 2884cbb469
commit 82e72ddcfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 127 additions and 86 deletions

View File

@ -248,7 +248,7 @@
{ {
"name": "Defender of the Faith", "name": "Defender of the Faith",
"type": "Enhancer", "type": "Enhancer",
"uniques": ["[+20]% Strength for [All] units in [Friendly Land]"], "uniques": ["[+20]% Strength <for [All] units> <when fighting in [Friendly Land] tiles>"],
// ToDo: Should only be friendly territory of cities that follow this religion // ToDo: Should only be friendly territory of cities that follow this religion
}, },
{ {
@ -264,7 +264,7 @@
{ {
"name": "Just War", "name": "Just War",
"type": "Enhancer", "type": "Enhancer",
"uniques": ["[+20]% Strength for [Non-City] units in [Enemy Land]"], "uniques": ["[+20]% Strength <for [All] units> <when fighting in [Enemy Land] tiles>"],
// ToDo: Should only be enemy territory of cities that follow this religion // ToDo: Should only be enemy territory of cities that follow this religion
}, },
{ {

View File

@ -194,7 +194,7 @@
"name": "Statue of Zeus", "name": "Statue of Zeus",
"culture": 1, "culture": 1,
"isWonder": true, "isWonder": true,
"uniques": ["+15% Combat Strength for all units when attacking Cities"], "uniques": ["[+15]% Strength <for [All] units> <vs Cities> <when attacking>"],
"requiredTech": "Bronze Working", "requiredTech": "Bronze Working",
"quote": "'He spoke, the son of Kronos, and nodded his head with the dark brows, and the immortally anointed hair of the great god swept from his divine head, and all Olympos was shaken' - The Iliad" "quote": "'He spoke, the son of Kronos, and nodded his head with the dark brows, and the immortally anointed hair of the great god swept from his divine head, and all Olympos was shaken' - The Iliad"
// "Requires [Honor]" in BNW // "Requires [Honor]" in BNW
@ -767,7 +767,7 @@
"culture": 3, "culture": 3,
"isWonder": true, "isWonder": true,
"greatPersonPoints": {"Great Engineer": 2}, "greatPersonPoints": {"Great Engineer": 2},
"uniques": ["+[15]% Strength for [Non-City] units fighting in [Friendly Land]", "uniques": ["[+15]% Strength <for [All] units> <when fighting in [Friendly Land] tiles>",
"Provides a free [Castle] [in this city]"], "Provides a free [Castle] [in this city]"],
"requiredTech": "Gunpowder", "requiredTech": "Gunpowder",
"quote": "'Bushido is realized in the presence of death. This means choosing death whenever there is a choice between life and death. There is no other reasoning.' - Yamamoto Tsunetomo" "quote": "'Bushido is realized in the presence of death. This means choosing death whenever there is a choice between life and death. There is no other reasoning.' - Yamamoto Tsunetomo"

View File

@ -432,7 +432,7 @@
}, },
{ {
"name": "Nationalism", "name": "Nationalism",
"uniques": ["+[15]% Strength for [Non-City] units fighting in [Friendly Land]"], "uniques": ["[+15]% Strength <for [All] units> <when fighting in [Friendly Land] tiles>"],
"row": 1, "row": 1,
"column": 5 "column": 5
}, },

View File

@ -9,37 +9,37 @@
// Ranged+Siege // Ranged+Siege
{ {
"name": "Accuracy I", "name": "Accuracy I",
"uniques": ["+[15]% Strength in [Open terrain]"], "uniques": ["[+15]% Strength <when fighting in [Open terrain] tiles> <when attacking>"],
"unitTypes": ["Siege","Archery","Ranged Gunpowder"] "unitTypes": ["Siege","Archery","Ranged Gunpowder"]
}, },
{ {
"name": "Accuracy II", "name": "Accuracy II",
"prerequisites": ["Accuracy I"], "prerequisites": ["Accuracy I"],
"uniques": ["+[15]% Strength in [Open terrain]"], "uniques": ["[+15]% Strength <when fighting in [Open terrain] tiles> <when attacking>"],
"unitTypes": ["Siege","Archery","Ranged Gunpowder"] "unitTypes": ["Siege","Archery","Ranged Gunpowder"]
}, },
{ {
"name": "Accuracy III", "name": "Accuracy III",
"prerequisites": ["Accuracy II"], "prerequisites": ["Accuracy II"],
"uniques": ["+[15]% Strength in [Open terrain]"], "uniques": ["[+15]% Strength <when fighting in [Open terrain] tiles> <when attacking>"],
"unitTypes": ["Siege","Archery","Ranged Gunpowder"] "unitTypes": ["Siege","Archery","Ranged Gunpowder"]
}, },
{ {
"name": "Barrage I", "name": "Barrage I",
"uniques": ["+[15]% Strength in [Rough terrain]"], "uniques": ["[+15]% Strength <when fighting in [Rough terrain] tiles> <when attacking>"],
"unitTypes": ["Siege","Archery","Ranged Gunpowder"] "unitTypes": ["Siege","Archery","Ranged Gunpowder"]
}, },
{ {
"name": "Barrage II", "name": "Barrage II",
"prerequisites": ["Barrage I"], "prerequisites": ["Barrage I"],
"uniques": ["+[15]% Strength in [Rough terrain]"], "uniques": ["[+15]% Strength <when fighting in [Rough terrain] tiles> <when attacking>"],
"unitTypes": ["Siege","Archery","Ranged Gunpowder"] "unitTypes": ["Siege","Archery","Ranged Gunpowder"]
}, },
{ {
"name": "Barrage III", "name": "Barrage III",
"prerequisites": ["Barrage II"], "prerequisites": ["Barrage II"],
"uniques": ["+[15]% Strength in [Rough terrain]"], "uniques": ["[+15]% Strength <when fighting in [Rough terrain] tiles> <when attacking>"],
"unitTypes": ["Siege","Archery","Ranged Gunpowder"] "unitTypes": ["Siege","Archery","Ranged Gunpowder"]
}, },
@ -65,37 +65,37 @@
// Melee, Mounted+Armor // Melee, Mounted+Armor
{ {
"name": "Shock I", "name": "Shock I",
"uniques": ["+[15]% Strength in [Open terrain]"], "uniques": ["[+15]% Strength <when fighting in [Open terrain] tiles>"],
"unitTypes": ["Sword","Gunpowder","Mounted","Armored"] "unitTypes": ["Sword","Gunpowder","Mounted","Armored"]
}, },
{ {
"name": "Shock II", "name": "Shock II",
"prerequisites": ["Shock I"], "prerequisites": ["Shock I"],
"uniques": ["+[15]% Strength in [Open terrain]"], "uniques": ["[+15]% Strength <when fighting in [Open terrain] tiles>"],
"unitTypes": ["Sword","Gunpowder","Mounted","Armored"] "unitTypes": ["Sword","Gunpowder","Mounted","Armored"]
}, },
{ {
"name": "Shock III", "name": "Shock III",
"prerequisites": ["Shock II"], "prerequisites": ["Shock II"],
"uniques": ["+[15]% Strength in [Open terrain]"], "uniques": ["[+15]% Strength <when fighting in [Open terrain] tiles>"],
"unitTypes": ["Sword","Gunpowder","Mounted","Armored"] "unitTypes": ["Sword","Gunpowder","Mounted","Armored"]
}, },
{ {
"name": "Drill I", "name": "Drill I",
"uniques": ["+[15]% Strength in [Rough terrain]"], "uniques": ["[+15]% Strength <when fighting in [Rough terrain] tiles>"],
"unitTypes": ["Sword","Gunpowder","Mounted","Armored"] "unitTypes": ["Sword","Gunpowder","Mounted","Armored"]
}, },
{ {
"name": "Drill II", "name": "Drill II",
"prerequisites": ["Drill I"], "prerequisites": ["Drill I"],
"uniques": ["+[15]% Strength in [Rough terrain]"], "uniques": ["[+15]% Strength <when fighting in [Rough terrain] tiles>"],
"unitTypes": ["Sword","Gunpowder","Mounted","Armored"] "unitTypes": ["Sword","Gunpowder","Mounted","Armored"]
}, },
{ {
"name": "Drill III", "name": "Drill III",
"prerequisites": ["Drill II"], "prerequisites": ["Drill II"],
"uniques": ["+[15]% Strength in [Rough terrain]"], "uniques": ["[+15]% Strength <when fighting in [Rough terrain] tiles>"],
"unitTypes": ["Sword","Gunpowder","Mounted","Armored"] "unitTypes": ["Sword","Gunpowder","Mounted","Armored"]
}, },
{ {
@ -537,7 +537,7 @@
}, },
{ {
"name": "Pictish Courage", // only for Pictish Warrior and subsequent upgrades "name": "Pictish Courage", // only for Pictish Warrior and subsequent upgrades
"uniques": ["No movement cost to pillage", "+[20]% Strength in [Foreign Land]"] "uniques": ["No movement cost to pillage", "[+20]% Strength <when fighting in [Foreign Land] tiles>"]
}, },
{ {
"name": "Home Sweet Home", // only for Mehal Sefari and subsequent upgrades "name": "Home Sweet Home", // only for Mehal Sefari and subsequent upgrades

View File

@ -66,7 +66,7 @@
"strength": 8, "strength": 8,
"cost": 40, "cost": 40,
"obsoleteTech": "Metal Casting", "obsoleteTech": "Metal Casting",
"uniques": ["+[33]% Strength in [Forest]", "+[33]% Strength in [Jungle]", "Heals [25] damage if it kills a unit"], "uniques": ["[+33]% Strength <when fighting in [Forest] tiles>", "[+33]% Strength <when fighting in [Jungle] tiles>", "Heals [25] damage if it kills a unit"],
"promotions": ["Woodsman"], "promotions": ["Woodsman"],
"upgradesTo": "Swordsman", "upgradesTo": "Swordsman",
"attackSound": "nonmetalhit" "attackSound": "nonmetalhit"
@ -315,7 +315,7 @@
"requiredTech": "Bronze Working", "requiredTech": "Bronze Working",
"obsoleteTech": "Physics", "obsoleteTech": "Physics",
"upgradesTo": "Trebuchet", "upgradesTo": "Trebuchet",
"uniques": ["[+300]% Strength <vs cities>", "No defensive terrain bonus", "[-33]% Strength <when defending>", "uniques": ["[+300]% Strength <vs cities> <when attacking>", "No defensive terrain bonus", "[-33]% Strength <when defending>",
"[-1] Sight", "Can only attack [City] units"], "[-1] Sight", "Can only attack [City] units"],
"promotions": ["Cover I"], "promotions": ["Cover I"],
"attackSound": "throw" "attackSound": "throw"
@ -425,8 +425,8 @@
"requiredTech": "Mathematics", "requiredTech": "Mathematics",
"obsoleteTech": "Physics", "obsoleteTech": "Physics",
"upgradesTo": "Trebuchet", "upgradesTo": "Trebuchet",
"uniques": ["[+200]% Strength <vs cities>", "No defensive terrain bonus", "uniques": ["[+200]% Strength <vs cities> <when attacking>", "No defensive terrain bonus",
"Must set up to ranged attack", "Limited Visibility"], "Must set up to ranged attack", "[-1] Sight"],
"hurryCostModifier": 20, "hurryCostModifier": 20,
"attackSound": "throw" "attackSound": "throw"
}, },
@ -442,8 +442,8 @@
"requiredTech": "Mathematics", "requiredTech": "Mathematics",
"obsoleteTech": "Physics", "obsoleteTech": "Physics",
"upgradesTo": "Trebuchet", "upgradesTo": "Trebuchet",
"uniques": ["[+200]% Strength <vs cities>", "No defensive terrain bonus", "uniques": ["[+200]% Strength <vs cities> <when attacking>", "No defensive terrain bonus",
"Must set up to ranged attack", "Limited Visibility"], "Must set up to ranged attack", "[-1] Sight"],
"hurryCostModifier": 20, "hurryCostModifier": 20,
"attackSound": "throw" "attackSound": "throw"
}, },
@ -500,7 +500,7 @@
"upgradesTo": "Longswordsman", "upgradesTo": "Longswordsman",
"obsoleteTech": "Gunpowder", "obsoleteTech": "Gunpowder",
"hurryCostModifier": 20, "hurryCostModifier": 20,
"uniques": ["+[33]% Strength in [Forest]", "+[33]% Strength in [Jungle]"], "uniques": ["[+33]% Strength <when fighting in [Forest] tiles>", "[+33]% Strength <when fighting in [Jungle] tiles>"],
"attackSound": "metalhit" "attackSound": "metalhit"
}, },
/* /*
@ -709,7 +709,7 @@
"requiredTech": "Physics", "requiredTech": "Physics",
"obsoleteTech": "Chemistry", "obsoleteTech": "Chemistry",
"upgradesTo": "Cannon", "upgradesTo": "Cannon",
"uniques": ["[+200]% Strength <vs cities>","No defensive terrain bonus","Must set up to ranged attack","Limited Visibility"], "uniques": ["[+200]% Strength <vs cities> <when attacking>","No defensive terrain bonus","Must set up to ranged attack","[-1] Sight"],
"attackSound": "throw" "attackSound": "throw"
}, },
{ {
@ -985,7 +985,7 @@
"requiredTech": "Chemistry", "requiredTech": "Chemistry",
"upgradesTo": "Artillery", "upgradesTo": "Artillery",
"obsoleteTech": "Dynamite", "obsoleteTech": "Dynamite",
"uniques": ["[+200]% Strength <vs cities>","No defensive terrain bonus","Must set up to ranged attack","Limited Visibility"], "uniques": ["[+200]% Strength <vs cities> <when attacking>","No defensive terrain bonus","Must set up to ranged attack","[-1] Sight"],
"attackSound": "cannon" "attackSound": "cannon"
}, },
@ -1026,9 +1026,9 @@
"requiredTech": "Rifling", "requiredTech": "Rifling",
"obsoleteTech": "Replaceable Parts", "obsoleteTech": "Replaceable Parts",
"upgradesTo": "Great War Infantry", "upgradesTo": "Great War Infantry",
"uniques": ["+[25]% Strength in [Snow]", "uniques": ["[+25]% Strength <when fighting in [Snow] tiles>",
"+[25]% Strength in [Tundra]", "[+25]% Strength <when fighting in [Tundra] tiles>",
"+[25]% Strength in [Hill]", "[+25]% Strength <when fighting in [Hill] tiles>",
"Double movement in [Snow]", "Double movement in [Snow]",
"Double movement in [Tundra]", "Double movement in [Tundra]",
"Double movement in [Hill]"], "Double movement in [Hill]"],
@ -1130,8 +1130,8 @@
"cost": 320, "cost": 320,
"requiredTech": "Dynamite", "requiredTech": "Dynamite",
"upgradesTo": "Rocket Artillery", "upgradesTo": "Rocket Artillery",
"uniques": ["[+200]% Strength <vs cities>","No defensive terrain bonus", "uniques": ["[+200]% Strength <vs cities> <when attacking>","No defensive terrain bonus",
"Must set up to ranged attack","Limited Visibility","Ranged attacks may be performed over obstacles"], "Must set up to ranged attack","[-1] Sight","Ranged attacks may be performed over obstacles"],
"attackSound": "artillery" "attackSound": "artillery"
}, },
@ -1171,7 +1171,7 @@
"requiredTech": "Replaceable Parts", "requiredTech": "Replaceable Parts",
"upgradesTo": "Infantry", "upgradesTo": "Infantry",
"obsoleteTech": "Plastics", "obsoleteTech": "Plastics",
"uniques": ["+[20]% Strength in [Foreign Land]"], "uniques": ["[+20]% Strength <when fighting in [Foreign Land] tiles>"],
"attackSound": "shot" "attackSound": "shot"
}, },
{ {
@ -1440,8 +1440,8 @@
"cost": 425, "cost": 425,
"requiredTech": "Rocketry", "requiredTech": "Rocketry",
"requiredResource": "Aluminum", "requiredResource": "Aluminum",
"uniques": ["[+200]% Strength <vs cities>","No defensive terrain bonus", "uniques": ["[+200]% Strength <vs cities> <when attacking>","No defensive terrain bonus",
"Limited Visibility","Ranged attacks may be performed over obstacles"], "[-1] Sight","Ranged attacks may be performed over obstacles"],
"attackSound": "artillery" "attackSound": "artillery"
}, },
{ {

View File

@ -1304,12 +1304,19 @@ Invisible to others =
# In this case "<when at war>" is a conditional. # In this case "<when at war>" is a conditional.
when not at war = when not at war =
when at war = when at war =
while the empire is happy =
during a Golden Age =
if this city has at least [amount] specialists = if this city has at least [amount] specialists =
for [mapUnitFilter] units =
vs cities = vs cities =
vs [mapUnitFilter] units = vs [mapUnitFilter] units =
for combat in [tileFilter] tiles =
when attacking = when attacking =
when defending = when defending =
# In English we just paste all these conditionals at the end of each unique, but in your language that # In English we just paste all these conditionals at the end of each unique, but in your language that
# may not turn into valid sentences. Therefore we have the following two translations to determine # may not turn into valid sentences. Therefore we have the following two translations to determine
# where they should go. # where they should go.

View File

@ -33,9 +33,14 @@ object BattleDamage {
val civInfo = combatant.getCivInfo() val civInfo = combatant.getCivInfo()
if (combatant is MapUnitCombatant) { if (combatant is MapUnitCombatant) {
val attackedTile =
if (combatAction == CombatAction.Attack) enemy.getTile()
else combatant.getTile()
for (unique in combatant.unit.getMatchingUniques( for (unique in combatant.unit.getMatchingUniques(
UniqueType.Strength, UniqueType.Strength,
StateForConditionals(civInfo, defender = enemy, attacker = combatant, combatAction = combatAction)) StateForConditionals(
civInfo, theirCombatant = enemy, ourCombatant = combatant, combatAction = combatAction, attackedTile = attackedTile)
)
) { ) {
modifiers.add(getModifierStringFromUnique(unique), unique.params[0].toInt()) modifiers.add(getModifierStringFromUnique(unique), unique.params[0].toInt())
} }
@ -132,6 +137,7 @@ object BattleDamage {
.isCityState() && civInfo.hasUnique("+30% Strength when fighting City-State units and cities") .isCityState() && civInfo.hasUnique("+30% Strength when fighting City-State units and cities")
) )
modifiers["vs [City-States]"] = 30 modifiers["vs [City-States]"] = 30
} }
if (enemy.getCivInfo().isBarbarian()) { if (enemy.getCivInfo().isBarbarian()) {
@ -195,11 +201,12 @@ object BattleDamage {
} }
} }
if (defender is CityCombatant && // Deprecated since 3.17.5
attacker.getCivInfo() if (defender is CityCombatant &&
.hasUnique("+15% Combat Strength for all units when attacking Cities") attacker.getCivInfo().hasUnique(UniqueType.StrengthVsCities)
) )
modifiers["Statue of Zeus"] = 15 modifiers["Statue of Zeus"] = 15
//
} else if (attacker is CityCombatant) { } else if (attacker is CityCombatant) {
if (attacker.city.getCenterTile().militaryUnit != null) { if (attacker.city.getCenterTile().militaryUnit != null) {
val garrisonBonus = attacker.city.getMatchingUniques("+[]% attacking strength for cities with garrisoned units") val garrisonBonus = attacker.city.getMatchingUniques("+[]% attacking strength for cities with garrisoned units")
@ -250,10 +257,12 @@ object BattleDamage {
} }
// //
for (unique in defender.unit.getMatchingUniques("+[]% defence in [] tiles")) { // Deprecated since 3.17.5
if (tile.matchesFilter(unique.params[1])) for (unique in defender.unit.getMatchingUniques(UniqueType.StrengthDefenseTiles)) {
modifiers.add("[${unique.params[1]}] defence", unique.params[0].toInt()) if (tile.matchesFilter(unique.params[1]))
} modifiers.add("[${unique.params[1]}] defence", unique.params[0].toInt())
}
//
if (defender.unit.isFortified()) if (defender.unit.isFortified())
modifiers["Fortification"] = 20 * defender.unit.getFortificationTurns() modifiers["Fortification"] = 20 * defender.unit.getFortificationTurns()
@ -270,11 +279,13 @@ object BattleDamage {
private fun getTileSpecificModifiers(unit: MapUnitCombatant, tile: TileInfo): Counter<String> { private fun getTileSpecificModifiers(unit: MapUnitCombatant, tile: TileInfo): Counter<String> {
val modifiers = Counter<String>() val modifiers = Counter<String>()
for (unique in unit.unit.getMatchingUniques("+[]% Strength in []")) { // Deprecated since 3.17.5
val filter = unique.params[1] for (unique in unit.unit.getMatchingUniques(UniqueType.StrengthIn)) {
if (tile.matchesFilter(filter, unit.getCivInfo())) val filter = unique.params[1]
modifiers.add(filter, unique.params[0].toInt()) if (tile.matchesFilter(filter, unit.getCivInfo()))
} modifiers.add(filter, unique.params[0].toInt())
}
//
for (unique in unit.getCivInfo().getMatchingUniques("+[]% Strength if within [] tiles of a []")) { for (unique in unit.getCivInfo().getMatchingUniques("+[]% Strength if within [] tiles of a []")) {
if (tile.getTilesInDistance(unique.params[1].toInt()) if (tile.getTilesInDistance(unique.params[1].toInt())
@ -282,11 +293,14 @@ object BattleDamage {
) )
modifiers[unique.params[2]] = unique.params[0].toInt() modifiers[unique.params[2]] = unique.params[0].toInt()
} }
for (unique in unit.getCivInfo().getMatchingUniques("[]% Strength for [] units in []")) {
if (unit.matchesCategory(unique.params[1]) && tile.matchesFilter(unique.params[2], unit.getCivInfo())) { // Deprecated since 3.17.5
modifiers.add(unique.params[2], unique.params[0].toInt()) for (unique in unit.getCivInfo().getMatchingUniques(UniqueType.StrengthUnitsTiles)) {
if (unit.matchesCategory(unique.params[1]) && tile.matchesFilter(unique.params[2], unit.getCivInfo())) {
modifiers.add(unique.params[2], unique.params[0].toInt())
}
} }
} //
return modifiers return modifiers
} }
@ -299,11 +313,12 @@ object BattleDamage {
private fun getHealthDependantDamageRatio(combatant: ICombatant): Float { private fun getHealthDependantDamageRatio(combatant: ICombatant): Float {
return if (combatant !is MapUnitCombatant // is city return if (combatant !is MapUnitCombatant // is city
|| combatant.getCivInfo() || (combatant.getCivInfo().hasUnique("Units fight as though they were at full strength even when damaged")
.hasUnique("Units fight as though they were at full strength even when damaged") && !combatant.unit.baseUnit.movesLikeAirUnits()
&& !combatant.unit.baseUnit.movesLikeAirUnits() )
) ) {
1f 1f
}
else 1 - (100 - combatant.getHealth()) / 300f// Each 3 points of health reduces damage dealt by 1% like original game else 1 - (100 - combatant.getHealth()) / 300f// Each 3 points of health reduces damage dealt by 1% like original game
} }

View File

@ -352,10 +352,9 @@ class MapUnit {
visibilityRange += getMatchingUniques(UniqueType.VisibilityRange).sumOf { it.params[0].toInt() } visibilityRange += getMatchingUniques(UniqueType.VisibilityRange).sumOf { it.params[0].toInt() }
//
// Should this be consolidated as "[-1] Sight"? if (hasUnique(UniqueType.LimitedVisibility)) visibilityRange -= 1
if (hasUnique("Limited Visibility")) visibilityRange -= 1 //
// Maybe add the uniques of the tile a unit is standing on to the tempUniques of the unit? // Maybe add the uniques of the tile a unit is standing on to the tempUniques of the unit?
for (unique in getTile().getAllTerrains().flatMap { it.uniqueObjects }) for (unique in getTile().getAllTerrains().flatMap { it.uniqueObjects })

View File

@ -12,8 +12,8 @@ data class StateForConditionals(
val cityInfo: CityInfo? = null, val cityInfo: CityInfo? = null,
val unit: MapUnit? = null, val unit: MapUnit? = null,
val attacker: ICombatant? = null, val ourCombatant: ICombatant? = null,
val defender: ICombatant? = null, val theirCombatant: ICombatant? = null,
// val attackedTile: TileInfo? = null, val attackedTile: TileInfo? = null,
val combatAction: CombatAction? = null, val combatAction: CombatAction? = null,
) )

View File

@ -56,17 +56,19 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
state.cityInfo != null && state.cityInfo.population.getNumberOfSpecialists() >= condition.params[0].toInt() state.cityInfo != null && state.cityInfo.population.getNumberOfSpecialists() >= condition.params[0].toInt()
UniqueType.ConditionalVsCity -> UniqueType.ConditionalVsCity ->
state.defender != null && state.defender.matchesCategory("City") state.theirCombatant != null && state.theirCombatant.matchesCategory("City")
UniqueType.ConditionalVsUnits -> UniqueType.ConditionalVsUnits ->
state.defender != null && state.defender.matchesCategory(condition.params[0]) state.theirCombatant != null && state.theirCombatant.matchesCategory(condition.params[0])
UniqueType.ConditionalOurUnit -> UniqueType.ConditionalOurUnit ->
(state.attacker != null && state.attacker.matchesCategory(condition.params[0])) (state.ourCombatant != null && state.ourCombatant.matchesCategory(condition.params[0]))
|| (state.unit != null && state.unit.matchesFilter(condition.params[0])) || (state.unit != null && state.unit.matchesFilter(condition.params[0]))
UniqueType.ConditionalAttacking -> state.combatAction == CombatAction.Attack UniqueType.ConditionalAttacking -> state.combatAction == CombatAction.Attack
UniqueType.ConditionalDefending -> state.combatAction == CombatAction.Defend UniqueType.ConditionalDefending -> state.combatAction == CombatAction.Defend
UniqueType.ConditionalInTiles ->
state.attackedTile != null && state.attackedTile.matchesFilter(condition.params[0])
UniqueType.ConditionalVsLargerCiv -> { UniqueType.ConditionalVsLargerCiv -> {
val yourCities = state.civInfo?.cities?.size ?: 1 val yourCities = state.civInfo?.cities?.size ?: 1
val theirCities = state.defender?.getCivInfo()?.cities?.size ?: 0 val theirCities = state.theirCombatant?.getCivInfo()?.cities?.size ?: 0
yourCities < theirCities yourCities < theirCities
} }
@ -76,11 +78,11 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
it.matchesFilter(condition.params[2], state.civInfo) it.matchesFilter(condition.params[2], state.civInfo)
} in (condition.params[0].toInt())..(condition.params[1].toInt()) } in (condition.params[0].toInt())..(condition.params[1].toInt())
UniqueType.ConditionalNeighborTilesAnd -> UniqueType.ConditionalNeighborTilesAnd ->
state.cityInfo != null && state.cityInfo != null
state.cityInfo.getCenterTile().neighbors.count { && state.cityInfo.getCenterTile().neighbors.count {
it.matchesFilter(condition.params[2], state.civInfo) && it.matchesFilter(condition.params[2], state.civInfo) &&
it.matchesFilter(condition.params[3], state.civInfo) it.matchesFilter(condition.params[3], state.civInfo)
} in (condition.params[0].toInt())..(condition.params[1].toInt()) } in (condition.params[0].toInt())..(condition.params[1].toInt())
else -> false else -> false
} }
} }

View File

@ -59,6 +59,7 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
//////////////////////////////////////// GLOBAL UNIQUES //////////////////////////////////////// //////////////////////////////////////// GLOBAL UNIQUES ////////////////////////////////////////
/////// Stat providing uniques /////// Stat providing uniques
Stats("[stats]", UniqueTarget.Global), Stats("[stats]", UniqueTarget.Global),
@ -115,9 +116,9 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
FreeExtraBeliefs("May choose [amount] additional [beliefType] beliefs when [foundingOrEnhancing] a religion", UniqueTarget.Global), FreeExtraBeliefs("May choose [amount] additional [beliefType] beliefs when [foundingOrEnhancing] a religion", UniqueTarget.Global),
FreeExtraAnyBeliefs("May choose [amount] additional of any type when [foundingOrEnhancing] a religion", UniqueTarget.Global), FreeExtraAnyBeliefs("May choose [amount] additional of any type when [foundingOrEnhancing] a religion", UniqueTarget.Global),
///////////////////////////////////////// UNIT UNIQUES ///////////////////////////////////////// ///////////////////////////////////////// UNIT UNIQUES /////////////////////////////////////////
Strength("[amount]% Strength", UniqueTarget.Unit, UniqueTarget.Global), Strength("[amount]% Strength", UniqueTarget.Unit, UniqueTarget.Global),
StrengthNearCapital("[amount]% Strength decreasing with distance from the capital", UniqueTarget.Unit), StrengthNearCapital("[amount]% Strength decreasing with distance from the capital", UniqueTarget.Unit),
@ -141,6 +142,15 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
DamageForUnits("[mapUnitFilter] units deal +[amount]% damage", UniqueTarget.Global), DamageForUnits("[mapUnitFilter] units deal +[amount]% damage", UniqueTarget.Global),
@Deprecated("As of 3.17.5", ReplaceWith("[+10]% Strength <for [All] units> <during a Golden Age>"), DeprecationLevel.WARNING) @Deprecated("As of 3.17.5", ReplaceWith("[+10]% Strength <for [All] units> <during a Golden Age>"), DeprecationLevel.WARNING)
StrengthGoldenAge("+10% Strength for all units during Golden Age", UniqueTarget.Global), StrengthGoldenAge("+10% Strength for all units during Golden Age", UniqueTarget.Global),
@Deprecated("As of 3.17.5", ReplaceWith("[amount]% Strength <when fighting in [tileFilter] tiles> <when defending>"), DeprecationLevel.WARNING)
StrengthDefenseTiles("+[amount]% defence in [tileFilter] tiles", UniqueTarget.Unit),
@Deprecated("As of 3.17.5", ReplaceWith("[amount]% Strength <when fighting in [tileFilter] tiles>"), DeprecationLevel.WARNING)
StrengthIn("+[amount]% Strength in [tileFilter]", UniqueTarget.Unit),
@Deprecated("As of 3.17.5", ReplaceWith("[amount]% Strength <for [mapUnitFilter] units> <when fighting in [tileFilter] tiles>"))
StrengthUnitsTiles("[amount]% Strength for [mapUnitFilter] units in [tileFilter]", UniqueTarget.Global),
@Deprecated("As of 3.17.5", ReplaceWith("[+15]% Strength <for [All] units> <vs cities> <when attacking>"))
StrengthVsCities("+15% Combat Strength for all units when attacking Cities", UniqueTarget.Global),
Movement("[amount] Movement", UniqueTarget.Unit, UniqueTarget.Global), Movement("[amount] Movement", UniqueTarget.Unit, UniqueTarget.Global),
Sight("[amount] Sight", UniqueTarget.Unit, UniqueTarget.Global), Sight("[amount] Sight", UniqueTarget.Unit, UniqueTarget.Global),
@ -155,6 +165,8 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
SightUnits("[amount] Sight for all [mapUnitFilter] units", UniqueTarget.Global), SightUnits("[amount] Sight for all [mapUnitFilter] units", UniqueTarget.Global),
@Deprecated("As of 3.17.5", ReplaceWith("[amount] Sight"), DeprecationLevel.WARNING) @Deprecated("As of 3.17.5", ReplaceWith("[amount] Sight"), DeprecationLevel.WARNING)
VisibilityRange("[amount] Visibility Range", UniqueTarget.Unit), VisibilityRange("[amount] Visibility Range", UniqueTarget.Unit),
@Deprecated("As of 3.17.5", ReplaceWith("[-1] Sight"), DeprecationLevel.WARNING)
LimitedVisibility("Limited Visibility", UniqueTarget.Unit),
@Deprecated("As of 3.17.5", ReplaceWith("[amount]% Spread Religion Strength <for [mapUnitFilter] units>"), DeprecationLevel.WARNING) @Deprecated("As of 3.17.5", ReplaceWith("[amount]% Spread Religion Strength <for [mapUnitFilter] units>"), DeprecationLevel.WARNING)
SpreadReligionStrengthUnits("[amount]% Spread Religion Strength for [mapUnitFilter] units", UniqueTarget.Global), SpreadReligionStrengthUnits("[amount]% Spread Religion Strength for [mapUnitFilter] units", UniqueTarget.Global),
@ -177,9 +189,9 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
CannotEnterOcean("Cannot enter ocean tiles", UniqueTarget.Unit), CannotEnterOcean("Cannot enter ocean tiles", UniqueTarget.Unit),
CannotEnterOceanUntilAstronomy("Cannot enter ocean tiles until Astronomy", UniqueTarget.Unit), CannotEnterOceanUntilAstronomy("Cannot enter ocean tiles until Astronomy", UniqueTarget.Unit),
//////////////////////////////////////// TERRAIN UNIQUES //////////////////////////////////////// //////////////////////////////////////// TERRAIN UNIQUES ////////////////////////////////////////
NaturalWonderNeighborCount("Must be adjacent to [amount] [simpleTerrain] tiles", UniqueTarget.Terrain), NaturalWonderNeighborCount("Must be adjacent to [amount] [simpleTerrain] tiles", UniqueTarget.Terrain),
NaturalWonderNeighborsRange("Must be adjacent to [amount] to [amount] [simpleTerrain] tiles", UniqueTarget.Terrain), NaturalWonderNeighborsRange("Must be adjacent to [amount] to [amount] [simpleTerrain] tiles", UniqueTarget.Terrain),
NaturalWonderSmallerLandmass("Must not be on [amount] largest landmasses", UniqueTarget.Terrain), NaturalWonderSmallerLandmass("Must not be on [amount] largest landmasses", UniqueTarget.Terrain),
@ -200,6 +212,7 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
///////////////////////////////////////// CONDITIONALS ///////////////////////////////////////// ///////////////////////////////////////// CONDITIONALS /////////////////////////////////////////
// civ conditionals // civ conditionals
ConditionalWar("when at war", UniqueTarget.Conditional), ConditionalWar("when at war", UniqueTarget.Conditional),
ConditionalNotWar("when not at war", UniqueTarget.Conditional), ConditionalNotWar("when not at war", UniqueTarget.Conditional),
@ -216,8 +229,8 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
ConditionalVsLargerCiv("when fighting units from a Civilization with more Cities than you", UniqueTarget.Conditional), ConditionalVsLargerCiv("when fighting units from a Civilization with more Cities than you", UniqueTarget.Conditional),
ConditionalAttacking("when attacking", UniqueTarget.Conditional), ConditionalAttacking("when attacking", UniqueTarget.Conditional),
ConditionalDefending("when defending", UniqueTarget.Conditional), ConditionalDefending("when defending", UniqueTarget.Conditional),
ConditionalInTiles("when fighting in [tileFilter] tiles", UniqueTarget.Conditional),
// ConditionalIntercepting("when intercepting", UniqueTarget.Conditional), // ConditionalIntercepting("when intercepting", UniqueTarget.Conditional),
// ConditionalInTiles("fighting in [tileFilter] tiles", UniqueTarget.Conditional),
// tile conditionals // tile conditionals
ConditionalNeighborTiles("with [amount] to [amount] neighboring [tileFilter] tiles", UniqueTarget.Conditional), ConditionalNeighborTiles("with [amount] to [amount] neighboring [tileFilter] tiles", UniqueTarget.Conditional),

View File

@ -601,9 +601,11 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsUnits) } ) { // Bonus vs some units - a quarter of the bonus if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsUnits) } ) { // Bonus vs some units - a quarter of the bonus
power *= (unique.params[0].toInt() / 4f).toPercent() power *= (unique.params[0].toInt() / 4f).toPercent()
} else if ( } else if (
unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsCity) } || // City Attack - half the bonus unique.conditionals.any {
unique.conditionals.any { it.isOfType(UniqueType.ConditionalAttacking) } || // Attack - half the bonus it.isOfType(UniqueType.ConditionalVsCity) // City Attack - half the bonus
unique.conditionals.any { it.isOfType(UniqueType.ConditionalDefending) } // Defense - half the bonus || it.isOfType(UniqueType.ConditionalAttacking) // Attack - half the bonus
|| it.isOfType(UniqueType.ConditionalDefending) // Defense - half the bonus
|| it.isOfType(UniqueType.ConditionalInTiles) } // Bonus in terrain or feature - half the bonus
) { ) {
power *= (unique.params[0].toInt() / 2f).toPercent() power *= (unique.params[0].toInt() / 2f).toPercent()
} }
@ -629,8 +631,10 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
-> power -= power / 5 -> power -= power / 5
unique.placeholderText == "[] additional attacks per turn" // Extra attacks - 20% bonus per extra attack unique.placeholderText == "[] additional attacks per turn" // Extra attacks - 20% bonus per extra attack
-> power += (power * unique.params[0].toInt()) / 5 -> power += (power * unique.params[0].toInt()) / 5
unique.placeholderText == "+[]% Strength in []" // Bonus in terrain or feature - half the bonus // Deprecated since 3.17.5
-> power += (power * unique.params[0].toInt()) / 200 unique.isOfType(UniqueType.StrengthIn) // Bonus in terrain or feature - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
//
} }
} }

View File

@ -212,7 +212,8 @@ class Translations : LinkedHashMap<String, TranslationEntry>(){
companion object { companion object {
// Whenever this string is changed, it should also be changed in the translation files! // Whenever this string is changed, it should also be changed in the translation files!
// It is mostly used as the template for translating the order of conditionals // It is mostly used as the template for translating the order of conditionals
const val englishConditionalOrderingString = "<if this city has at least [amount] specialists> <when at war> <when not at war> <while the empire is happy>" const val englishConditionalOrderingString =
"<for [mapUnitFilter] units> <vs cities> <vs [mapUnitFilter] units> <when fighting in [tileFilter] tiles> <when attacking> <when defending> <if this city has at least [amount] specialists> <when at war> <when not at war> <while the empire is happy> <during a Golden Age>"
const val conditionalUniqueOrderString = "ConditionalsPlacement" const val conditionalUniqueOrderString = "ConditionalsPlacement"
const val shouldCapitalizeString = "StartWithCapitalLetter" const val shouldCapitalizeString = "StartWithCapitalLetter"
} }