Air Units take damage in Combat (#6533)

* Changes required
Add isAirUnit to iCombatant

* Updating XP Rewards to match Civ5
Add isAirUnit() Boolean for ICombatant

* Air Units can only Intercept if they haven't moved this turn

* Change Intercept to only have the highest chance unit take the chance

* Exempt the defending unit from being able to Intercept
Add back in tryInterceptAirAttack() for all units of a civ to attempt intercept

* Combine back to 1 function.
Add filtering for defender

* Fix logic bug

* Change base damage to be centered on 25f (instead of 30f) if City is attacking. addRandomDamage is a guess

* Revert dmanageModifier code

* XP fix

Co-authored-by: itanasi <spellman23@gmail.com>
This commit is contained in:
itanasi 2022-04-16 13:06:30 -07:00 committed by GitHub
parent 8457c7ab1d
commit 7a89c84790
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 18 deletions

View File

@ -70,7 +70,7 @@ object Battle {
}
if (attacker is MapUnitCombatant && attacker.unit.baseUnit.isAirUnit()) {
tryInterceptAirAttack(attacker, attackedTile, defender.getCivInfo())
tryInterceptAirAttack(attacker, attackedTile, defender.getCivInfo(), defender)
if (attacker.isDefeated()) return
}
@ -221,7 +221,7 @@ object Battle {
if (defender is MapUnitCombatant && defender.unit.isCivilian() && attacker.isMelee()) {
captureCivilianUnit(attacker, defender)
} else if (attacker.isRanged()) {
} else if (attacker.isRanged() && !attacker.isAirUnit()) { // Air Units are Ranged, but take damage as well
defender.takeDamage(potentialDamageToDefender) // straight up
} else {
//melee attack is complicated, because either side may defeat the other midway
@ -662,7 +662,7 @@ object Battle {
.filter{it != attackingCiv}) {
tryDeclareWar(civWhoseUnitWasAttacked)
if (attacker.unit.baseUnit.isAirUnit() && !attacker.isDefeated()) {
tryInterceptAirAttack(attacker, targetTile, civWhoseUnitWasAttacked)
tryInterceptAirAttack(attacker, targetTile, civWhoseUnitWasAttacked, null)
}
}
if (attacker.isDefeated()) return
@ -790,17 +790,22 @@ object Battle {
targetedCity.population.addPopulation(-populationLoss.toInt())
if (targetedCity.population.population < 1) targetedCity.population.setPopulation(1)
}
private fun tryInterceptAirAttack(attacker: MapUnitCombatant, attackedTile:TileInfo, interceptingCiv:CivilizationInfo) {
private fun tryInterceptAirAttack(attacker: MapUnitCombatant, attackedTile: TileInfo, interceptingCiv: CivilizationInfo, defender: ICombatant?) {
if (attacker.unit.hasUnique("Cannot be intercepted")) return
// Pick highest chance interceptor
for (interceptor in interceptingCiv.getCivUnits()
.filter { it.canIntercept(attackedTile) }) {
if (Random().nextFloat() > interceptor.interceptChance() / 100f) continue
.filter { it.canIntercept(attackedTile) }
.sortedByDescending { it.interceptChance() }) {
// defender can't also intercept
if (defender != null && defender is MapUnitCombatant && interceptor == defender.unit) continue
// Does Intercept happen? If not, exit
if (Random().nextFloat() > interceptor.interceptChance() / 100f) return
var damage = BattleDamage.calculateDamageToDefender(
MapUnitCombatant(interceptor),
null,
attacker
MapUnitCombatant(interceptor),
null,
attacker
)
var damageFactor = 1f + interceptor.interceptDamagePercentBonus().toFloat() / 100f
@ -813,19 +818,22 @@ object Battle {
if (damage > 0)
addXp(MapUnitCombatant(interceptor), 2, attacker)
if (damage > 0)
addXp(MapUnitCombatant(interceptor), 2, attacker)
val attackerName = attacker.getName()
val interceptorName = interceptor.name
val locations = LocationAction(interceptor.currentTile.position, attacker.unit.currentTile.position)
val attackerText = if (attacker.isDefeated())
"Our [$attackerName] was destroyed by an intercepting [$interceptorName]"
else "Our [$attackerName] was attacked by an intercepting [$interceptorName]"
else "Our [$attackerName] was attacked by an intercepting [$interceptorName]"
val interceptorText = if (attacker.isDefeated())
"Our [$interceptorName] intercepted and destroyed an enemy [$attackerName]"
else "Our [$interceptorName] intercepted and attacked an enemy [$attackerName]"
else "Our [$interceptorName] intercepted and attacked an enemy [$attackerName]"
attacker.getCivInfo().addNotification(attackerText, interceptor.currentTile.position,
attackerName, NotificationIcon.War, interceptorName)
attackerName, NotificationIcon.War, interceptorName)
interceptingCiv.addNotification(interceptorText, locations,
interceptorName, NotificationIcon.War, attackerName)
interceptorName, NotificationIcon.War, attackerName)
return
}
}

View File

@ -264,7 +264,7 @@ object BattleDamage {
defender: ICombatant,
ignoreRandomness: Boolean = false,
): Int {
if (attacker.isRanged()) return 0
if (attacker.isRanged() && !attacker.isAirUnit()) return 0
if (defender.isCivilian()) return 0
val ratio =
getAttackingStrength(attacker, defender) / getDefendingStrength(
@ -302,9 +302,9 @@ object BattleDamage {
if (damageToAttacker && attackerToDefenderRatio > 1 || !damageToAttacker && attackerToDefenderRatio < 1) // damage ratio from the weaker party is inverted
ratioModifier = ratioModifier.pow(-1)
val randomSeed = attacker.getCivInfo().gameInfo.turns * attacker.getTile().position.hashCode() // so people don't save-scum to get optimal results
val randomCenteredAround30 = 24 +
if (ignoreRandomness) 6f
else 12 * Random(randomSeed.toLong()).nextFloat()
val randomCenteredAround30 = 24 +
if (ignoreRandomness) 6f
else 12 * Random(randomSeed.toLong()).nextFloat()
return randomCenteredAround30 * ratioModifier
}
}

View File

@ -979,6 +979,8 @@ class MapUnit {
fun canIntercept(): Boolean {
if (interceptChance() == 0) return false
// 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() }
if (attacksThisTurn >= maxAttacksPerTurn) return false