Merge 45d251ce7e3442342bb5eb18ceaefd82a9d528cd into d51ef24c205b6b05330b3c4d7ce79c402db44447

This commit is contained in:
PhiRite 2025-09-18 13:09:14 +00:00 committed by GitHub
commit 13c200d7f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 99 additions and 1 deletions

View File

@ -183,10 +183,93 @@ object Battle {
.firstOrNull { it.text == "Your city [${attacker.getName()}] can bombard the enemy!" }
attacker.getCivInfo().notifications.remove(cityCanBombardNotification)
}
//Aoe attack
if (attacker is MapUnitCombatant && (attacker.unit.hasUnique(UniqueType.AoeDegradeAttack) || attacker.unit.hasUnique(UniqueType.AoeFlatAttack))) {
applyAoeAttack(attacker, defender)
}
return damageDealt + interceptDamage
}
//Aoe Logic function
fun applyAoeAttack(attacker: MapUnitCombatant, defender: ICombatant) {
//degrading AOE is used if both AoeDegradeAttack and AoeFlatAttack uniques are present
val degradeUnique = attacker.unit.getMatchingUniques(UniqueType.AoeDegradeAttack).firstOrNull()
val flatUnique = attacker.unit.getMatchingUniques(UniqueType.AoeFlatAttack).firstOrNull()
val aoeUnique = degradeUnique ?: flatUnique ?: return
val isDegrade = degradeUnique != null
val radius = aoeUnique.params.getOrNull(0)?.toIntOrNull() ?: return
if (radius <= 0) return
val includeAllies = attacker.unit.hasUnique(UniqueType.CanDamageAlliesInAOE)
val excludeSelf = !attacker.unit.hasUnique(UniqueType.CanDamageSelfInAOE)
val receivesCounterDamage = attacker.unit.hasUnique(UniqueType.TakeCounterDamageFromAOE)
val centerTile = defender.getTile()
val attackerCiv = attacker.getCivInfo()
for (tile in centerTile.getTilesInDistance(radius)) {
val distance = centerTile.aerialDistanceTo(tile)
val distanceFactor = if (isDegrade)
(1.0 - distance.toDouble() / (radius + 1)).coerceAtLeast(0.0)
else 1.0
for (unit in tile.getUnits()) {
if (excludeSelf && unit == attacker.unit) continue
if (unit == (defender as? MapUnitCombatant)?.unit && unit != attacker.unit) continue
val isAlly = !unit.civ.isAtWarWith(attackerCiv)
if (!includeAllies && isAlly && unit != attacker.unit) continue
val aoeDefender = MapUnitCombatant(unit)
val damage = (BattleDamage.calculateDamageToDefender(attacker, aoeDefender) * distanceFactor).toInt().coerceAtLeast(1)
if (aoeDefender.isCivilian() && attacker.isMelee()) {
// Capture civilian units if attacker can capture
BattleUnitCapture.captureCivilianUnit(attacker, aoeDefender)
continue
}
// Apply combat effects but no counter-damage
triggerCombatUniques(attacker, aoeDefender, tile)
aoeDefender.takeDamage(damage)
triggerDamageUniquesForUnit(attacker, aoeDefender, tile, CombatAction.Attack)
if (aoeDefender.isDefeated() && !aoeDefender.isCivilian()) {
// Try to capture first - if successful, skip other defeat effects
val captured = BattleUnitCapture.tryCaptureMilitaryUnit(attacker, aoeDefender, tile)
if (!captured) {
// If not captured, proceed with normal defeat effects
triggerPostKillingUniques(aoeDefender, attacker, tile)
}
}
if (receivesCounterDamage && !aoeDefender.isCivilian()) {
val isMainTarget =
(defender is MapUnitCombatant && unit == defender.unit) ||
(defender is CityCombatant && unit == defender.city)
val isSelf = (unit == attacker.unit)
// Counter damage from every valid target except main defender + self
if (!isMainTarget && !isSelf) {
val baseCounterDamage = BattleDamage.calculateDamageToAttacker(attacker, aoeDefender)
val finalCounterDamage =
if (isDegrade) (baseCounterDamage * distanceFactor).toInt().coerceAtLeast(1)
else baseCounterDamage
attacker.takeDamage(finalCounterDamage)
if (attacker.isDefeated()) return
}
}
if (attacker.isDefeated()) return
}
}
}
private fun triggerPostKillingUniques(
defender: ICombatant,
attacker: ICombatant,

View File

@ -426,6 +426,21 @@ enum class UniqueType(
IndirectFire("Ranged attacks may be performed over obstacles", UniqueTarget.Unit, UniqueTarget.Global),
NuclearWeapon("Nuclear weapon of Strength [amount]", UniqueTarget.Unit),
//Aoe Attacks
AoeFlatAttack("Attacks also target all units within [positiveAmount] tiles", UniqueTarget.Unit,
docDescription = "Performs an attack against every unit in the radius, dealing equal damage. Status effects and on-hit abilities apply to all affected units. " +
"If both this and decreasing area attacks are present, only decreasing area attacks will be used."),
AoeDegradeAttack("Attacks also target units within [positiveAmount] tiles, with damage decreasing by distance", UniqueTarget.Unit,
docDescription = "Performs an attack against every unit in the radius. Status effects and on-hit abilities apply. " +
"Damage decreases with distance from the main target. If both this and equal area attacks are present, only this will be used. " +
"Damage formula: Damage = (1 - (distance / radius)) * baseDamage"),
CanDamageSelfInAOE("Damages self with Aoe attacks", UniqueTarget.Unit,
docDescription = "This unit takes damage from its own Aoe attacks, does not mean it will take damage from allied Aoe attacks."),
CanDamageAlliesInAOE("Damages allied units with Aoe attacks", UniqueTarget.Unit,
docDescription = "This unit damages allied units with Aoe attacks, does not mean it will take damage from its own Aoe attacks unless Damages self with Aoe attacks is also set."),
TakeCounterDamageFromAOE("Takes counter damage from each unit hit by its area attacks", UniqueTarget.Unit,
docDescription = "Only works for melee units, counter damage does not activate on self if \"Damages self with Aoe attacks\" unique is also present."),
NoDefensiveTerrainBonus("No defensive terrain bonus", UniqueTarget.Unit, UniqueTarget.Global),
NoDefensiveTerrainPenalty("No defensive terrain penalty", UniqueTarget.Unit, UniqueTarget.Global),
NoDamagePenaltyWoundedUnits("No damage penalty for wounded units", UniqueTarget.Unit, UniqueTarget.Global),