Simplified combat uniques further (#5361)

* Simplified combat uniques further

* Reviews & translations

* UniqueType > placeHolderText

* I can't math
This commit is contained in:
Xander Lenstra
2021-10-03 23:29:46 +02:00
committed by GitHub
parent edfc66aa2f
commit 07822de375
8 changed files with 115 additions and 70 deletions

View File

@ -174,13 +174,13 @@
},
{
"name": "Survivalism I",
"uniques": ["[+5] HP when healing in [Foreign Land] tiles", "+[25]% Strength when defending"],
"uniques": ["[+5] HP when healing in [Foreign Land] tiles", "[+25]% Strength <when defending>"],
"unitTypes": ["Scout"]
},
{
"name": "Survivalism II",
"prerequisites": ["Survivalism I"],
"uniques": ["[+5] HP when healing in [Foreign Land] tiles", "+[25]% Strength when defending"],
"uniques": ["[+5] HP when healing in [Foreign Land] tiles", "[+25]% Strength <when defending>"],
"unitTypes": ["Scout"]
},
{
@ -250,38 +250,38 @@
// Submarine
{
"name": "Wolfpack I",
"uniques": ["+[25]% Strength when attacking"],
"uniques": ["[+25]% Strength <when attacking>"],
"unitTypes": ["Submarine"]
},
{
"name": "Wolfpack II",
"prerequisites": ["Wolfpack I"],
"uniques": ["+[25]% Strength when attacking"],
"uniques": ["[+25]% Strength <when attacking>"],
"unitTypes": ["Submarine"]
},
{
"name": "Wolfpack III",
"prerequisites": ["Wolfpack II"],
"uniques": ["+[25]% Strength when attacking"],
"uniques": ["[+25]% Strength <when attacking>"],
"unitTypes": ["Submarine"]
},
// Aircraft Carrier
{
"name": "Armor Plating I",
"uniques": ["+[25]% Strength when defending"],
"uniques": ["[+25]% Strength <when defending>"],
"unitTypes": ["Aircraft Carrier"]
},
{
"name": "Armor Plating II",
"prerequisites": ["Armor Plating I"],
"uniques": ["+[25]% Strength when defending"],
"uniques": ["[+25]% Strength <when defending>"],
"unitTypes": ["Aircraft Carrier"]
},
{
"name": "Armor Plating III",
"prerequisites": ["Armor Plating II"],
"uniques": ["+[25]% Strength when defending"],
"uniques": ["[+25]% Strength <when defending>"],
"unitTypes": ["Aircraft Carrier"]
},
{
@ -433,13 +433,13 @@
// Mixed
{
"name": "Cover I",
"uniques": ["[+33]% Strength when defending vs [Ranged] units"],
"uniques": ["[+33]% Strength <vs [Ranged] units> <when defending>"],
"unitTypes": ["Sword","Gunpowder","Archery","Ranged Gunpowder","Siege"]
},
{
"name": "Cover II",
"prerequisites": ["Cover I"],
"uniques": ["[+33]% Strength when defending vs [Ranged] units"],
"uniques": ["[+33]% Strength <vs [Ranged] units> <when defending>"],
"unitTypes": ["Sword","Gunpowder","Archery","Ranged Gunpowder","Siege"]
},

View File

@ -315,7 +315,7 @@
"requiredTech": "Bronze Working",
"obsoleteTech": "Physics",
"upgradesTo": "Trebuchet",
"uniques": ["[+300]% Strength <vs cities>", "No defensive terrain bonus", "+[-33]% Strength when defending",
"uniques": ["[+300]% Strength <vs cities>", "No defensive terrain bonus", "[-33]% Strength <when defending>",
"-[1] Visibility Range", "Can only attack [City] units"],
"promotions": ["Cover I"],
"attackSound": "throw"
@ -838,7 +838,7 @@
"requiredTech": "Gunpowder",
"upgradesTo": "Rifleman",
"obsoleteTech": "Rifling",
"uniques": ["Heals [50] damage if it kills a unit", "+[25]% Strength when attacking"],
"uniques": ["Heals [50] damage if it kills a unit", "[+25]% Strength <when attacking>"],
"attackSound": "shot"
},
{
@ -1132,7 +1132,7 @@
"cost": 325,
"requiredTech": "Refrigeration",
"upgradesTo": "Nuclear Submarine",
"uniques": ["+[75]% Strength when attacking", "Can only attack [Water] tiles"],
"uniques": ["[+75]% Strength <when attacking>", "Can only attack [Water] tiles"],
"attackSound": "torpedo"
},
{
@ -1490,7 +1490,7 @@
"rangedStrength": 85,
"cost": 425,
"requiredTech": "Telecommunications",
"uniques": ["+[75]% Strength when attacking", "Can only attack [Water] tiles", "[+1] Visibility Range", "Can carry [2] [Missile] units"],
"uniques": ["[+75]% Strength <when attacking>", "Can only attack [Water] tiles", "[+1] Visibility Range", "Can carry [2] [Missile] units"],
"attackSound": "torpedo"
},
{

View File

@ -814,6 +814,9 @@ defence vs [unitType] =
Defensive Bonus =
Stacked with [unitType] =
Unit ability =
National ability =
The following improvements [stats]: =
The following improvements on [tileType] tiles [stats]: =
@ -1302,6 +1305,10 @@ Invisible to others =
when not at war =
when at war =
if this city has at least [amount] specialists =
vs cities =
vs [mapUnitFilter] units =
when attacking =
when defending =
# 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

View File

@ -3,6 +3,8 @@ package com.unciv.logic.battle
import com.unciv.logic.map.TileInfo
import com.unciv.models.Counter
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.ui.utils.toPercent
import java.util.*
@ -18,16 +20,24 @@ class BattleDamageModifier(val vs:String, val modificationAmount:Float){
object BattleDamage {
private fun getGeneralModifiers(combatant: ICombatant, enemy: ICombatant): Counter<String> {
private fun getModifierStringFromUnique(unique: Unique): String {
return when (unique.sourceObjectType) {
UniqueTarget.Unit -> "Unit ability"
UniqueTarget.Nation -> "National ability"
else -> "[${unique.sourceObjectName}] ([${unique.sourceObjectType?.name}])"
}
}
private fun getGeneralModifiers(combatant: ICombatant, enemy: ICombatant, combatAction: CombatAction): Counter<String> {
val modifiers = Counter<String>()
val civInfo = combatant.getCivInfo()
if (combatant is MapUnitCombatant) {
for (unique in combatant.unit.getMatchingUniques(
UniqueType.Strength,
StateForConditionals(civInfo, defender = enemy))
StateForConditionals(civInfo, defender = enemy, combatAction = combatAction))
) {
modifiers.add("${unique.sourceObjectName} (${unique.sourceObjectType})", unique.params[0].toInt())
modifiers.add(getModifierStringFromUnique(unique), unique.params[0].toInt())
}
// Deprecated since 3.17.3
@ -124,14 +134,16 @@ object BattleDamage {
attacker: ICombatant,
defender: ICombatant
): Counter<String> {
val modifiers = getGeneralModifiers(attacker, defender)
val modifiers = getGeneralModifiers(attacker, defender, CombatAction.Attack)
if (attacker is MapUnitCombatant) {
modifiers.add(getTileSpecificModifiers(attacker, defender.getTile()))
for (unique in attacker.unit.getMatchingUniques("+[]% Strength when attacking")) {
// Deprecated since 3.17.4
for (unique in attacker.unit.getMatchingUniques(UniqueType.StrengthAttacking)) {
modifiers.add("Attacker Bonus", unique.params[0].toInt())
}
//
if (attacker.unit.isEmbarked() && !attacker.unit.hasUnique("Eliminates combat penalty for attacking from the sea"))
modifiers["Landing"] = -50
@ -192,7 +204,7 @@ object BattleDamage {
}
fun getDefenceModifiers(attacker: ICombatant, defender: ICombatant): Counter<String> {
val modifiers = getGeneralModifiers(defender, attacker)
val modifiers = getGeneralModifiers(defender, attacker, CombatAction.Defend)
val tile = defender.getTile()
if (defender is MapUnitCombatant) {
@ -215,14 +227,16 @@ object BattleDamage {
)
modifiers["Tile"] = (tileDefenceBonus * 100).toInt()
for (unique in defender.unit.getMatchingUniques("[]% Strength when defending vs [] units")) {
// Deprecated since 3.17.4
for (unique in defender.unit.getMatchingUniques(UniqueType.StrengthDefendingUnitFilter)) {
if (attacker.matchesCategory(unique.params[1]))
modifiers.add("defence vs [${unique.params[1]}] ", unique.params[0].toInt())
}
for (unique in defender.unit.getMatchingUniques("+[]% Strength when defending")) {
for (unique in defender.unit.getMatchingUniques(UniqueType.StrengthDefending)) {
modifiers.add("Defender Bonus", unique.params[0].toInt())
}
//
for (unique in defender.unit.getMatchingUniques("+[]% defence in [] tiles")) {
if (tile.matchesFilter(unique.params[1]))
@ -342,3 +356,9 @@ object BattleDamage {
return randomCenteredAround30 * ratioModifier
}
}
enum class CombatAction {
Attack,
Defend,
Intercept,
}

View File

@ -1,5 +1,6 @@
package com.unciv.models.ruleset.unique
import com.unciv.logic.battle.CombatAction
import com.unciv.logic.battle.ICombatant
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.CivilizationInfo
@ -8,14 +9,8 @@ import com.unciv.logic.map.TileInfo
data class StateForConditionals(
val civInfo: CivilizationInfo? = null,
val cityInfo: CityInfo? = null,
val defender: ICombatant? = null,
// val attacker: ICombatant? = null,
val defender: ICombatant? = null,
// val attackedTile: TileInfo? = null,
// val combatAction: CombatAction? = null,
val combatAction: CombatAction? = null,
)
//enum class CombatAction() {
// Attack,
// Defend,
// Intercept,
//}

View File

@ -1,5 +1,6 @@
package com.unciv.models.ruleset.unique
import com.unciv.logic.battle.CombatAction
import com.unciv.logic.city.CityInfo
import com.unciv.models.stats.Stats
import com.unciv.models.translations.*
@ -54,6 +55,8 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
state.defender != null && state.defender.matchesCategory("City")
UniqueType.ConditionalVsUnits ->
state.defender != null && state.defender.matchesCategory(condition.params[0])
UniqueType.ConditionalAttacking -> state.combatAction == CombatAction.Attack
UniqueType.ConditionalDefending -> state.combatAction == CombatAction.Defend
UniqueType.ConditionalNeighborTiles ->
state.cityInfo != null &&
state.cityInfo.getCenterTile().neighbors.count {

View File

@ -123,12 +123,18 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
StrengthPlus("+[amount]% Strength", UniqueTarget.Unit),
@Deprecated("As of 3.17.3", ReplaceWith("[amount]% Strength"), DeprecationLevel.WARNING)
StrengthMin("-[amount]% Strength", UniqueTarget.Unit),
@Deprecated("As of 3.17.3", ReplaceWith("[amount]% Strength <vs [unitFilter] units> OR [amount]% Strength <vs cities>"), DeprecationLevel.WARNING)
@Deprecated("As of 3.17.3", ReplaceWith("[amount]% Strength <vs [mapUnitFilter] units> OR [amount]% Strength <vs cities>"), DeprecationLevel.WARNING)
StrengthPlusVs("+[amount]% Strength vs [combatantFilter]", UniqueTarget.Unit),
@Deprecated("As of 3.17.3", ReplaceWith("[amount]% Strength <vs [unitFilter] units> OR [amount]% Strength <vs cities>"), DeprecationLevel.WARNING)
@Deprecated("As of 3.17.3", ReplaceWith("[amount]% Strength <vs [mapUnitFilter] units> OR [amount]% Strength <vs cities>"), DeprecationLevel.WARNING)
StrengthMinVs("-[amount]% Strength vs [combatantFilter]", UniqueTarget.Unit),
@Deprecated("As of 3.17.3", ReplaceWith("[amount]% Strength"), DeprecationLevel.WARNING)
CombatBonus("+[amount]% Combat Strength", UniqueTarget.Unit),
@Deprecated("As of 3.17.5", ReplaceWith("[amount]% Strength <when attacking>"), DeprecationLevel.WARNING)
StrengthAttacking("+[amount]% Strength when attacking", UniqueTarget.Unit),
@Deprecated("As of 3.17.5", ReplaceWith("[amount]% Strength <shen defending>"), DeprecationLevel.WARNING)
StrengthDefending("+[amount]% Strength when defending", UniqueTarget.Unit),
@Deprecated("As of 3.17.5", ReplaceWith("[amount]% Strength <when defending> <vs [mapUnitFilter] units>"), DeprecationLevel.WARNING)
StrengthDefendingUnitFilter("[amount]% Strength when defending vs [mapUnitFilter] units", UniqueTarget.Unit),
// The following block gets cached in MapUnit for faster getMovementCostBetweenAdjacentTiles
DoubleMovementOnTerrain("Double movement in [terrainFilter]", UniqueTarget.Unit),
@ -175,10 +181,10 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
ConditionalHappy("while the empire is happy", UniqueTarget.Conditional),
ConditionalVsCity("vs cities", UniqueTarget.Conditional),
ConditionalVsUnits("vs [mapUnitFilter] units", UniqueTarget.Conditional),
// ConditionalInTiles("fighting in [tileFilter] tiles", UniqueTarget.Conditional),
// ConditionalAttacking("when attacking", UniqueTarget.Conditional),
// ConditionalDefending("when defending", UniqueTarget.Conditional),
ConditionalAttacking("when attacking", UniqueTarget.Conditional),
ConditionalDefending("when defending", UniqueTarget.Conditional),
// ConditionalIntercepting("when intercepting", UniqueTarget.Conditional),
// ConditionalInTiles("fighting in [tileFilter] tiles", UniqueTarget.Conditional),
ConditionalNeighborTiles("with [amount] to [amount] neighboring [tileFilter] tiles", UniqueTarget.Conditional),
ConditionalNeighborTilesAnd("with [amount] to [amount] neighboring [tileFilter] [tileFilter] tiles", UniqueTarget.Conditional),

View File

@ -573,8 +573,8 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
return
}
var power = strength.toFloat().pow(1.5f).toInt()
var rangedPower = rangedStrength.toFloat().pow(1.45f).toInt()
var power = strength.toFloat().pow(1.5f)
var rangedPower = rangedStrength.toFloat().pow(1.45f)
// Value ranged naval units less
if (isWaterUnit()) {
@ -585,7 +585,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
// Replicates the formula from civ V, which is a lower multiplier than probably intended, because math
// They did fix it in BNW so it was completely bugged and always 1, again math
power = (power * movement.toFloat().pow(0.3f)).toInt()
power = (power * movement.toFloat().pow(0.3f))
if (uniqueObjects.any { it.placeholderText =="Self-destructs when attacking" } )
power /= 2
@ -597,21 +597,28 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
when {
unique.isOfType(UniqueType.Strength) && unique.params[0].toInt() > 0 -> {
if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsCity) })
power += (power * unique.params[0].toInt()) / 200
else if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsUnits) })
power += (power * unique.params[0].toInt()) / 400
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
unique.conditionals.any { it.isOfType(UniqueType.ConditionalAttacking) } || // Attack - half the bonus
unique.conditionals.any { it.isOfType(UniqueType.ConditionalDefending) } // Defense - half the bonus
) {
power *= (unique.params[0].toInt() / 2f).toPercent()
}
}
// Deprecated since 3.17.3
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] == "City" // City Attack - half the bonus
unique.isOfType(UniqueType.StrengthPlusVs) && unique.params[1] == "City" // City Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] != "City" // Bonus vs something else - a quarter of the bonus
unique.isOfType(UniqueType.StrengthPlusVs) && unique.params[1] != "City" // Bonus vs something else - a quarter of the bonus
-> power += (power * unique.params[0].toInt()) / 400
//
unique.placeholderText == "+[]% Strength when attacking" // Attack - half the bonus
// Deprecated since 3.17.4
unique.isOfType(UniqueType.StrengthAttacking) // Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength when defending" // Defense - half the bonus
unique.isOfType(UniqueType.StrengthDefending) // Defense - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
//
unique.placeholderText == "May Paradrop up to [] tiles from inside friendly territory" // Paradrop - 25% bonus
-> power += power / 4
unique.placeholderText == "Must set up to ranged attack" // Must set up - 20 % penalty
@ -626,21 +633,28 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
for (unique in ruleset.unitPromotions[promotionName]!!.uniqueObjects) {
when {
unique.isOfType(UniqueType.Strength) && unique.params[0].toInt() > 0 -> {
if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsCity) })
power += (power * unique.params[0].toInt()) / 200
else if (unique.conditionals.any { it.isOfType(UniqueType.ConditionalVsUnits) })
power += (power * unique.params[0].toInt()) / 400
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
unique.conditionals.any { it.isOfType(UniqueType.ConditionalAttacking) } || // Attack - half the bonus
unique.conditionals.any { it.isOfType(UniqueType.ConditionalDefending) } // Defense - half the bonus
) {
power *= (unique.params[0].toInt() / 2f).toPercent()
}
}
// Deprecated since 3.17.3
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] == "City" // City Attack - half the bonus
unique.isOfType(UniqueType.StrengthPlusVs) && unique.params[1] == "City" // City Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength vs []" && unique.params[1] != "City" // Bonus vs something else - a quarter of the bonus
unique.isOfType(UniqueType.StrengthPlusVs) && unique.params[1] != "City" // Bonus vs something else - a quarter of the bonus
-> power += (power * unique.params[0].toInt()) / 400
//
unique.placeholderText == "+[]% Strength when attacking" // Attack - half the bonus
// Deprecated since 3.17.4
unique.isOfType(UniqueType.StrengthAttacking) // Attack - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
unique.placeholderText == "+[]% Strength when defending" // Defense - half the bonus
unique.isOfType(UniqueType.StrengthDefending) // Defense - half the bonus
-> power += (power * unique.params[0].toInt()) / 200
//
unique.placeholderText == "[] additional attacks per turn" // Extra attacks - 20% bonus per extra attack
-> power += (power * unique.params[0].toInt()) / 5
unique.placeholderText == "+[]% Strength in []" // Bonus in terrain or feature - half the bonus
@ -649,6 +663,6 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
}
}
cachedForceEvaluation = power
cachedForceEvaluation = power.toInt()
}
}