mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 15:27:50 +07:00
Refactor BattleDamage object and test it (#9992)
* 💡 add some comments providing examples * ♻ refactor getGeneralModifiers to increase readability * ♻ refactor getAttackModifiers to increase readability * 🏗 move constants expressed as magic number to separated class to increase maintainability and expressivity * ♻ invert condition to remove continue statement and simplify code * 💚 add some tests for battle damage class
This commit is contained in:
15
core/src/com/unciv/logic/battle/BattleConstants.kt
Normal file
15
core/src/com/unciv/logic/battle/BattleConstants.kt
Normal file
@ -0,0 +1,15 @@
|
||||
package com.unciv.logic.battle
|
||||
|
||||
object BattleConstants {
|
||||
//https://www.carlsguides.com/strategy/civilization5/war/combatbonuses.php
|
||||
const val LANDING_MALUS = -50
|
||||
const val BOARDING_MALUS = -50
|
||||
const val ATTACKING_ACROSS_RIVER_MALUS = -20
|
||||
const val BASE_FLANKING_BONUS = 10f
|
||||
const val MISSING_RESOURCES_MALUS = -25
|
||||
const val EMBARKED_DEFENCE_BONUS = 100
|
||||
const val FORTIFICATION_BONUS = 20
|
||||
const val DAMAGE_REDUCTION_WOUNDED_UNIT_RATIO_PERCENTAGE = 300f
|
||||
const val DAMAGE_TO_CIVILIAN_UNIT = 40
|
||||
|
||||
}
|
@ -30,7 +30,7 @@ object BattleDamage {
|
||||
return "$source - $conditionalsText"
|
||||
}
|
||||
|
||||
private fun getGeneralModifiers(combatant: ICombatant, enemy: ICombatant, combatAction: CombatAction, tileToAttackFrom:Tile): Counter<String> {
|
||||
private fun getGeneralModifiers(combatant: ICombatant, enemy: ICombatant, combatAction: CombatAction, tileToAttackFrom: Tile): Counter<String> {
|
||||
val modifiers = Counter<String>()
|
||||
|
||||
val civInfo = combatant.getCivInfo()
|
||||
@ -43,38 +43,9 @@ object BattleDamage {
|
||||
|
||||
if (combatant is MapUnitCombatant) {
|
||||
|
||||
for (unique in combatant.getMatchingUniques(UniqueType.Strength, conditionalState, true)) {
|
||||
modifiers.add(getModifierStringFromUnique(unique), unique.params[0].toInt())
|
||||
}
|
||||
for (unique in combatant.getMatchingUniques(
|
||||
UniqueType.StrengthNearCapital, conditionalState, true
|
||||
)) {
|
||||
if (civInfo.cities.isEmpty() || civInfo.getCapital() == null) break
|
||||
val distance =
|
||||
combatant.getTile().aerialDistanceTo(civInfo.getCapital()!!.getCenterTile())
|
||||
// https://steamcommunity.com/sharedfiles/filedetails/?id=326411722#464287
|
||||
val effect = unique.params[0].toInt() - 3 * distance
|
||||
if (effect <= 0) continue
|
||||
modifiers.add("${unique.sourceObjectName} (${unique.sourceObjectType})", effect)
|
||||
}
|
||||
addUnitUniqueModifiers(combatant, enemy, conditionalState, tileToAttackFrom, modifiers)
|
||||
|
||||
//https://www.carlsguides.com/strategy/civilization5/war/combatbonuses.php
|
||||
var adjacentUnits = combatant.getTile().neighbors.flatMap { it.getUnits() }
|
||||
if (enemy.getTile() !in combatant.getTile().neighbors && tileToAttackFrom in combatant.getTile().neighbors
|
||||
&& enemy is MapUnitCombatant)
|
||||
adjacentUnits += sequenceOf(enemy.unit)
|
||||
val strengthMalus = adjacentUnits.filter { it.civ.isAtWarWith(civInfo) }
|
||||
.flatMap { it.getMatchingUniques(UniqueType.StrengthForAdjacentEnemies) }
|
||||
.filter { combatant.matchesCategory(it.params[1]) && combatant.getTile().matchesFilter(it.params[2]) }
|
||||
.maxByOrNull { it.params[0] }
|
||||
if (strengthMalus != null) {
|
||||
modifiers.add("Adjacent enemy units", strengthMalus.params[0].toInt())
|
||||
}
|
||||
|
||||
val civResources = civInfo.getCivResourcesByName()
|
||||
for (resource in combatant.unit.baseUnit.getResourceRequirementsPerTurn().keys)
|
||||
if (civResources[resource]!! < 0 && !civInfo.isBarbarian())
|
||||
modifiers["Missing resource"] = -25 //todo ModConstants
|
||||
addResourceLackingMalus(combatant, modifiers)
|
||||
|
||||
val (greatGeneralName, greatGeneralBonus) = GreatGeneralImplementation.getGreatGeneralBonus(combatant, enemy, combatAction)
|
||||
if (greatGeneralBonus != 0)
|
||||
@ -88,11 +59,6 @@ object BattleDamage {
|
||||
if (stackedUnitsBonus > 0)
|
||||
modifiers["Stacked with [${unique.params[1]}]"] = stackedUnitsBonus
|
||||
}
|
||||
|
||||
if (enemy.getCivInfo().isCityState()
|
||||
&& civInfo.hasUnique(UniqueType.StrengthBonusVsCityStates)
|
||||
)
|
||||
modifiers["vs [City-States]"] = 30
|
||||
} else if (combatant is CityCombatant) {
|
||||
for (unique in combatant.city.getMatchingUniques(UniqueType.StrengthForCities, conditionalState)) {
|
||||
modifiers.add(getModifierStringFromUnique(unique), unique.params[0].toInt())
|
||||
@ -107,6 +73,58 @@ object BattleDamage {
|
||||
return modifiers
|
||||
}
|
||||
|
||||
private fun addUnitUniqueModifiers(combatant: MapUnitCombatant, enemy: ICombatant, conditionalState: StateForConditionals,
|
||||
tileToAttackFrom: Tile, modifiers: Counter<String>) {
|
||||
val civInfo = combatant.getCivInfo()
|
||||
|
||||
for (unique in combatant.getMatchingUniques(UniqueType.Strength, conditionalState, true)) {
|
||||
modifiers.add(getModifierStringFromUnique(unique), unique.params[0].toInt())
|
||||
}
|
||||
|
||||
// e.g., Mehal Sefari https://civilization.fandom.com/wiki/Mehal_Sefari_(Civ5)
|
||||
for (unique in combatant.getMatchingUniques(
|
||||
UniqueType.StrengthNearCapital, conditionalState, true
|
||||
)) {
|
||||
if (civInfo.cities.isEmpty() || civInfo.getCapital() == null) break
|
||||
val distance =
|
||||
combatant.getTile().aerialDistanceTo(civInfo.getCapital()!!.getCenterTile())
|
||||
// https://steamcommunity.com/sharedfiles/filedetails/?id=326411722#464287
|
||||
val effect = unique.params[0].toInt() - 3 * distance
|
||||
if (effect > 0)
|
||||
modifiers.add("${unique.sourceObjectName} (${unique.sourceObjectType})", effect)
|
||||
}
|
||||
|
||||
//https://www.carlsguides.com/strategy/civilization5/war/combatbonuses.php
|
||||
var adjacentUnits = combatant.getTile().neighbors.flatMap { it.getUnits() }
|
||||
if (enemy.getTile() !in combatant.getTile().neighbors && tileToAttackFrom in combatant.getTile().neighbors
|
||||
&& enemy is MapUnitCombatant
|
||||
)
|
||||
adjacentUnits += sequenceOf(enemy.unit)
|
||||
|
||||
// e.g., Maori Warrior - https://civilization.fandom.com/wiki/Maori_Warrior_(Civ5)
|
||||
val strengthMalus = adjacentUnits.filter { it.civ.isAtWarWith(combatant.getCivInfo()) }
|
||||
.flatMap { it.getMatchingUniques(UniqueType.StrengthForAdjacentEnemies) }
|
||||
.filter { combatant.matchesCategory(it.params[1]) && combatant.getTile().matchesFilter(it.params[2]) }
|
||||
.maxByOrNull { it.params[0] }
|
||||
if (strengthMalus != null) {
|
||||
modifiers.add("Adjacent enemy units", strengthMalus.params[0].toInt())
|
||||
}
|
||||
|
||||
// e.g., Mongolia - https://civilization.fandom.com/wiki/Mongolian_(Civ5)
|
||||
if (enemy.getCivInfo().isCityState()
|
||||
&& civInfo.hasUnique(UniqueType.StrengthBonusVsCityStates)
|
||||
)
|
||||
modifiers["vs [City-States]"] = 30
|
||||
}
|
||||
|
||||
private fun addResourceLackingMalus(combatant: MapUnitCombatant, modifiers: Counter<String>) {
|
||||
val civInfo = combatant.getCivInfo()
|
||||
val civResources = civInfo.getCivResourcesByName()
|
||||
for (resource in combatant.unit.baseUnit.getResourceRequirementsPerTurn().keys)
|
||||
if (civResources[resource]!! < 0 && !civInfo.isBarbarian())
|
||||
modifiers["Missing resource"] = BattleConstants.MISSING_RESOURCES_MALUS
|
||||
}
|
||||
|
||||
fun getAttackModifiers(
|
||||
attacker: ICombatant,
|
||||
defender: ICombatant, tileToAttackFrom: Tile
|
||||
@ -114,18 +132,9 @@ object BattleDamage {
|
||||
val modifiers = getGeneralModifiers(attacker, defender, CombatAction.Attack, tileToAttackFrom)
|
||||
|
||||
if (attacker is MapUnitCombatant) {
|
||||
if (attacker.unit.isEmbarked() && defender.getTile().isLand
|
||||
&& !attacker.unit.hasUnique(UniqueType.AttackAcrossCoast))
|
||||
modifiers["Landing"] = -50
|
||||
|
||||
// Land Melee Unit attacking to Water
|
||||
if (attacker.unit.type.isLandUnit() && !attacker.getTile().isWater && attacker.isMelee() && defender.getTile().isWater
|
||||
&& !attacker.unit.hasUnique(UniqueType.AttackAcrossCoast))
|
||||
modifiers["Boarding"] = -50
|
||||
// Melee Unit on water attacking to Land (not City) unit
|
||||
if (!attacker.unit.type.isAirUnit() && attacker.isMelee() && attacker.getTile().isWater && !defender.getTile().isWater
|
||||
&& !attacker.unit.hasUnique(UniqueType.AttackAcrossCoast) && !defender.isCity())
|
||||
modifiers["Landing"] = -50
|
||||
addTerrainAttackModifiers(attacker, defender, tileToAttackFrom, modifiers)
|
||||
|
||||
// Air unit attacking with Air Sweep
|
||||
if (attacker.unit.isPreparingAirSweep())
|
||||
modifiers.add(getAirSweepAttackModifiers(attacker))
|
||||
@ -137,24 +146,14 @@ object BattleDamage {
|
||||
&& MapUnitCombatant(it.militaryUnit!!).isMelee()
|
||||
}
|
||||
if (numberOfOtherAttackersSurroundingDefender > 0) {
|
||||
var flankingBonus = 10f //https://www.carlsguides.com/strategy/civilization5/war/combatbonuses.php
|
||||
var flankingBonus = BattleConstants.BASE_FLANKING_BONUS
|
||||
|
||||
// e.g., Discipline policy - https://civilization.fandom.com/wiki/Discipline_(Civ5)
|
||||
for (unique in attacker.unit.getMatchingUniques(UniqueType.FlankAttackBonus, checkCivInfoUniques = true))
|
||||
flankingBonus *= unique.params[0].toPercent()
|
||||
modifiers["Flanking"] =
|
||||
(flankingBonus * numberOfOtherAttackersSurroundingDefender).toInt()
|
||||
}
|
||||
if (tileToAttackFrom.aerialDistanceTo(defender.getTile()) == 1 &&
|
||||
tileToAttackFrom.isConnectedByRiver(defender.getTile()) &&
|
||||
!attacker.unit.hasUnique(UniqueType.AttackAcrossRiver)
|
||||
) {
|
||||
if (!tileToAttackFrom
|
||||
.hasConnection(attacker.getCivInfo()) // meaning, the tiles are not road-connected for this civ
|
||||
|| !defender.getTile().hasConnection(attacker.getCivInfo())
|
||||
|| !attacker.getCivInfo().tech.roadsConnectAcrossRivers
|
||||
) {
|
||||
modifiers["Across river"] = -20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -162,6 +161,41 @@ object BattleDamage {
|
||||
return modifiers
|
||||
}
|
||||
|
||||
private fun addTerrainAttackModifiers(attacker: MapUnitCombatant, defender: ICombatant,
|
||||
tileToAttackFrom: Tile, modifiers: Counter<String>) {
|
||||
if (attacker.unit.isEmbarked() && defender.getTile().isLand
|
||||
&& !attacker.unit.hasUnique(UniqueType.AttackAcrossCoast)
|
||||
)
|
||||
modifiers["Landing"] = BattleConstants.LANDING_MALUS
|
||||
|
||||
// Land Melee Unit attacking to Water
|
||||
if (attacker.unit.type.isLandUnit() && !attacker.getTile().isWater && attacker.isMelee() && defender.getTile().isWater
|
||||
&& !attacker.unit.hasUnique(UniqueType.AttackAcrossCoast)
|
||||
)
|
||||
modifiers["Boarding"] = BattleConstants.BOARDING_MALUS
|
||||
|
||||
// Melee Unit on water attacking to Land (not City) unit
|
||||
if (!attacker.unit.type.isAirUnit() && attacker.isMelee() && attacker.getTile().isWater && !defender.getTile().isWater
|
||||
&& !attacker.unit.hasUnique(UniqueType.AttackAcrossCoast) && !defender.isCity()
|
||||
)
|
||||
modifiers["Landing"] = BattleConstants.LANDING_MALUS
|
||||
|
||||
if (isMeleeAttackingAcrossRiverWithNoBridge(attacker, tileToAttackFrom, defender))
|
||||
modifiers["Across river"] = BattleConstants.ATTACKING_ACROSS_RIVER_MALUS
|
||||
}
|
||||
|
||||
private fun isMeleeAttackingAcrossRiverWithNoBridge(attacker: MapUnitCombatant, tileToAttackFrom: Tile, defender: ICombatant) = (
|
||||
attacker.isMelee()
|
||||
&&
|
||||
(tileToAttackFrom.aerialDistanceTo(defender.getTile()) == 1
|
||||
&& tileToAttackFrom.isConnectedByRiver(defender.getTile())
|
||||
&& !attacker.unit.hasUnique(UniqueType.AttackAcrossRiver))
|
||||
&&
|
||||
(!tileToAttackFrom.hasConnection(attacker.getCivInfo()) // meaning, the tiles are not road-connected for this civ
|
||||
|| !defender.getTile().hasConnection(attacker.getCivInfo())
|
||||
|| !attacker.getCivInfo().tech.roadsConnectAcrossRivers)
|
||||
)
|
||||
|
||||
fun getAirSweepAttackModifiers(
|
||||
attacker: ICombatant
|
||||
): Counter<String> {
|
||||
@ -186,7 +220,7 @@ object BattleDamage {
|
||||
// embarked units get no defensive modifiers apart from this unique
|
||||
if (defender.unit.hasUnique(UniqueType.DefenceBonusWhenEmbarked, checkCivInfoUniques = true)
|
||||
)
|
||||
modifiers["Embarked"] = 100
|
||||
modifiers["Embarked"] = BattleConstants.EMBARKED_DEFENCE_BONUS
|
||||
|
||||
return modifiers
|
||||
}
|
||||
@ -199,7 +233,7 @@ object BattleDamage {
|
||||
|
||||
|
||||
if (defender.unit.isFortified())
|
||||
modifiers["Fortification"] = 20 * defender.unit.getFortificationTurns()
|
||||
modifiers["Fortification"] = BattleConstants.FORTIFICATION_BONUS * defender.unit.getFortificationTurns()
|
||||
}
|
||||
|
||||
return modifiers
|
||||
@ -217,7 +251,7 @@ object BattleDamage {
|
||||
|| combatant.unit.hasUnique(UniqueType.NoDamagePenalty, checkCivInfoUniques = true)
|
||||
) 1f
|
||||
// Each 3 points of health reduces damage dealt by 1%
|
||||
else 1 - (100 - combatant.getHealth()) / 300f
|
||||
else 1 - (100 - combatant.getHealth()) / BattleConstants.DAMAGE_REDUCTION_WOUNDED_UNIT_RATIO_PERCENTAGE
|
||||
}
|
||||
|
||||
|
||||
@ -264,7 +298,7 @@ object BattleDamage {
|
||||
randomnessFactor: Float = Random(defender.getCivInfo().gameInfo.turns * defender.getTile().position.hashCode().toLong()).nextFloat()
|
||||
,
|
||||
): Int {
|
||||
if (defender.isCivilian()) return 40
|
||||
if (defender.isCivilian()) return BattleConstants.DAMAGE_TO_CIVILIAN_UNIT
|
||||
val ratio = getAttackingStrength(attacker, defender, tileToAttackFrom) /
|
||||
getDefendingStrength(attacker, defender, tileToAttackFrom)
|
||||
return (damageModifier(ratio, false, randomnessFactor) * getHealthDependantDamageRatio(attacker)).roundToInt()
|
||||
|
158
tests/src/com/unciv/logic/battle/BattleDamageTest.kt
Normal file
158
tests/src/com/unciv/logic/battle/BattleDamageTest.kt
Normal file
@ -0,0 +1,158 @@
|
||||
package com.unciv.logic.battle
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.managers.TurnManager
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.testing.GdxTestRunner
|
||||
import com.unciv.uniques.TestGame
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(GdxTestRunner::class)
|
||||
class BattleDamageTest {
|
||||
private lateinit var attackerCiv: Civilization
|
||||
private lateinit var defenderCiv: Civilization
|
||||
|
||||
private lateinit var defaultAttackerTile: Tile
|
||||
private lateinit var defaultDefenderTile: Tile
|
||||
|
||||
private lateinit var defaultAttackerUnit: MapUnit
|
||||
private lateinit var defaultDefenderUnit: MapUnit
|
||||
|
||||
private val testGame = TestGame()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
testGame.makeHexagonalMap(4)
|
||||
attackerCiv = testGame.addCiv()
|
||||
defenderCiv = testGame.addCiv()
|
||||
|
||||
defaultAttackerTile = testGame.getTile(Vector2(1f, 1f))
|
||||
defaultAttackerUnit = testGame.addUnit("Warrior", attackerCiv, defaultAttackerTile)
|
||||
defaultDefenderTile = testGame.getTile(Vector2(0f, 1f))
|
||||
defaultDefenderUnit = testGame.addUnit("Warrior", defenderCiv, defaultDefenderTile)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should retrieve modifiers from policies`() {
|
||||
// given
|
||||
val policy = testGame.createPolicy("[+25]% Strength <when attacking> <for [Military] units>")
|
||||
attackerCiv.policies.adopt(policy, true)
|
||||
|
||||
// when
|
||||
val attackModifiers = BattleDamage.getAttackModifiers(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defaultDefenderUnit), defaultAttackerTile)
|
||||
|
||||
// then
|
||||
assertEquals(1, attackModifiers.size)
|
||||
assertEquals(25, attackModifiers.sumValues())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should retrieve modifiers from buldings`() {
|
||||
// given
|
||||
val building = testGame.createBuilding("[+15]% Strength <for [Military] units>")
|
||||
val attackerCity = testGame.addCity(attackerCiv, testGame.getTile(Vector2.Zero))
|
||||
attackerCity.cityConstructions.addBuilding(building.name)
|
||||
|
||||
// when
|
||||
val attackModifiers = BattleDamage.getAttackModifiers(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defaultDefenderUnit), defaultAttackerTile)
|
||||
|
||||
// then
|
||||
assertEquals(1, attackModifiers.size)
|
||||
assertEquals(15, attackModifiers.sumValues())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should retrieve modifiers from national abilities`() {
|
||||
// given
|
||||
val civ = testGame.addCiv("[+10]% Strength <for [All] units> <during a Golden Age>") // i.e., Persia national ability
|
||||
civ.goldenAges.enterGoldenAge(2)
|
||||
val attackerTile = testGame.getTile(Vector2.Zero)
|
||||
val attackerUnit = testGame.addUnit("Warrior", civ, attackerTile)
|
||||
|
||||
// when
|
||||
val attackModifiers = BattleDamage.getAttackModifiers(MapUnitCombatant(attackerUnit), MapUnitCombatant(defaultDefenderUnit), attackerTile)
|
||||
|
||||
// then
|
||||
assertEquals(1, attackModifiers.size)
|
||||
assertEquals(10, attackModifiers.sumValues())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should retrieve modifiers from lack of strategic resource`() {
|
||||
// given
|
||||
defaultAttackerTile.militaryUnit = null // otherwise we'll also get a flanking bonus
|
||||
val attackerTile = testGame.getTile(Vector2.Zero)
|
||||
val attackerUnit = testGame.addUnit("Horseman", attackerCiv, attackerTile)
|
||||
|
||||
// when
|
||||
val attackModifiers = BattleDamage.getAttackModifiers(MapUnitCombatant(attackerUnit), MapUnitCombatant(defaultDefenderUnit), attackerTile)
|
||||
|
||||
// then
|
||||
assertEquals(1, attackModifiers.size)
|
||||
assertEquals(BattleConstants.MISSING_RESOURCES_MALUS, attackModifiers.sumValues())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should retrieve attacking flank bonus modifiers`() {
|
||||
// given
|
||||
val flankingAttackerTile = testGame.getTile(Vector2.Zero)
|
||||
testGame.addUnit("Warrior", attackerCiv, flankingAttackerTile)
|
||||
|
||||
// when
|
||||
val attackModifiers = BattleDamage.getAttackModifiers(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defaultDefenderUnit), defaultAttackerTile)
|
||||
|
||||
// then
|
||||
assertEquals(1, attackModifiers.size)
|
||||
assertEquals(BattleConstants.BASE_FLANKING_BONUS.toInt(), attackModifiers.sumValues())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should retrieve defence fortification modifiers`() {
|
||||
// given
|
||||
defaultDefenderUnit.currentMovement = 2f // base warrior max movement points
|
||||
defaultDefenderUnit.fortify()
|
||||
TurnManager(defenderCiv).endTurn()
|
||||
|
||||
// when
|
||||
val defenceModifiers = BattleDamage.getDefenceModifiers(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defaultDefenderUnit), defaultAttackerTile)
|
||||
|
||||
// then
|
||||
assertEquals(1, defenceModifiers.size)
|
||||
assertEquals(BattleConstants.FORTIFICATION_BONUS, defenceModifiers.sumValues())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should retrieve defence terrain modifiers`() {
|
||||
// given
|
||||
testGame.setTileFeatures(defaultDefenderTile.position, "Hill")
|
||||
|
||||
// when
|
||||
val defenceModifiers = BattleDamage.getDefenceModifiers(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defaultDefenderUnit), defaultAttackerTile)
|
||||
|
||||
// then
|
||||
assertEquals(1, defenceModifiers.size)
|
||||
assertEquals(25, defenceModifiers.sumValues())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should not retrieve defence terrain modifiers when unit doesn't get them`() {
|
||||
// given
|
||||
val defenderTile = testGame.getTile(Vector2.Zero)
|
||||
testGame.setTileFeatures(defenderTile.position, "Hill")
|
||||
defenderCiv.resourceStockpiles.add("Horses", 1) // no resource penalty
|
||||
val defenderUnit = testGame.addUnit("Horseman", defenderCiv, defenderTile)
|
||||
|
||||
// when
|
||||
val defenceModifiers = BattleDamage.getDefenceModifiers(MapUnitCombatant(defaultAttackerUnit), MapUnitCombatant(defenderUnit), defaultAttackerTile)
|
||||
|
||||
// then
|
||||
assertTrue(defenceModifiers.isEmpty())
|
||||
assertEquals(0, defenceModifiers.sumValues())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user