mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-05 21:11:35 +07:00
Air unit automation improvement (#10991)
* Improved AirUnitAutomation * UnitPriority now has special cases for air units * Fighters now Air-sweep * Added extra air sweep logic * Moved airSweepDamagePercentBonus to AirUnitAutomation.kt
This commit is contained in:
parent
5cbc04b63a
commit
d216db5ced
@ -390,9 +390,15 @@ object NextTurnAutomation {
|
||||
for (unit in sortedUnits) UnitAutomation.automateUnitMoves(unit)
|
||||
}
|
||||
|
||||
/** Returns the priority of the unit, a lower value is higher priority **/
|
||||
fun getUnitPriority(unit: MapUnit, isAtWar: Boolean): Int {
|
||||
if (unit.isCivilian() && !unit.isGreatPersonOfType("War")) return 1 // Civilian
|
||||
if (unit.baseUnit.isAirUnit()) return 2
|
||||
if (unit.baseUnit.isAirUnit()) return when {
|
||||
unit.canIntercept() -> 2 // Fighers first
|
||||
unit.baseUnit.isNuclearWeapon() -> 3 // Then Nukes (area damage)
|
||||
!unit.hasUnique(UniqueType.SelfDestructs) -> 4 // Then Bombers (reusable)
|
||||
else -> 5 // Missiles
|
||||
}
|
||||
val distance = if (!isAtWar) 0 else unit.civ.threatManager.getDistanceToClosestEnemyUnit(unit.getTile(),6)
|
||||
// Lower health units should move earlier to swap with higher health units
|
||||
return distance + (unit.health / 10) + when {
|
||||
|
@ -1,21 +1,53 @@
|
||||
package com.unciv.logic.automation.unit
|
||||
|
||||
import com.unciv.logic.battle.AirInterception
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.battle.Nuke
|
||||
import com.unciv.logic.battle.TargetHelper
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
|
||||
object AirUnitAutomation {
|
||||
|
||||
fun automateFighter(unit: MapUnit) {
|
||||
if (unit.health <= 50 && !unit.hasUnique(UniqueType.HealsEvenAfterAction)) return // Wait and heal
|
||||
|
||||
val tilesWithEnemyUnitsInRange = unit.civ.threatManager.getTilesWithEnemyUnitsInDistance(unit.getTile(), unit.getRange())
|
||||
// TODO: Optimize [friendlyAirUnitsInRange] by creating an alternate [ThreatManager.getTilesWithEnemyUnitsInDistance] that handles only friendly units
|
||||
val friendlyAirUnitsInRange = unit.getTile().getTilesInDistance(unit.getRange()).flatMap { it.airUnits }.filter { it.civ == unit.civ }
|
||||
// Find all visible enemy air units
|
||||
val enemyAirUnitsInRange = tilesWithEnemyUnitsInRange
|
||||
.flatMap { it.airUnits.asSequence() }.filter { it.civ.isAtWarWith(unit.civ) }
|
||||
val enemyFighters = enemyAirUnitsInRange.size / 2 // Assume half the planes are fighters
|
||||
val friendlyUnusedFighterCount = friendlyAirUnitsInRange.count { it.health >= 50 && it.canAttack() }
|
||||
val friendlyUsedFighterCount = friendlyAirUnitsInRange.count { it.health >= 50 && !it.canAttack() }
|
||||
|
||||
if (enemyAirUnitsInRange.any()) return // we need to be on standby in case they attack
|
||||
// We need to be on standby in case they attack
|
||||
if (friendlyUnusedFighterCount < enemyFighters) return
|
||||
|
||||
if (friendlyUsedFighterCount <= enemyFighters) {
|
||||
fun airSweepDamagePercentBonus(): Int {
|
||||
return unit.getMatchingUniques(UniqueType.StrengthWhenAirsweep)
|
||||
.sumOf { it.params[0].toInt() }
|
||||
}
|
||||
|
||||
// If we are outnumbered, don't heal after attacking and don't have an Air Sweep bonus
|
||||
// Then we shouldn't speed the air battle by killing our fighters, instead, focus on defending
|
||||
if (friendlyUsedFighterCount + friendlyUnusedFighterCount < enemyFighters
|
||||
&& !unit.hasUnique(UniqueType.HealsEvenAfterAction)
|
||||
&& airSweepDamagePercentBonus() <= 0) {
|
||||
return
|
||||
} else {
|
||||
if (tryAirSweep(unit, tilesWithEnemyUnitsInRange)) return
|
||||
}
|
||||
}
|
||||
|
||||
if (unit.health < 80) {
|
||||
return // Wait and heal up, no point in moving closer to battle if we aren't healed
|
||||
}
|
||||
|
||||
if (BattleHelper.tryAttackNearbyEnemy(unit)) return
|
||||
|
||||
if (tryRelocateToCitiesWithEnemyNearBy(unit)) return
|
||||
@ -46,9 +78,25 @@ object AirUnitAutomation {
|
||||
|
||||
}
|
||||
|
||||
private fun tryAirSweep(unit: MapUnit, tilesWithEnemyUnitsInRange: List<Tile>):Boolean {
|
||||
val targetTile = tilesWithEnemyUnitsInRange.filter {
|
||||
tile -> tile.getUnits().any { it.civ.isAtWarWith(unit.civ)
|
||||
|| (tile.isCityCenter() && tile.getCity()!!.civ.isAtWarWith(unit.civ)) }
|
||||
}.minByOrNull { it.aerialDistanceTo(unit.getTile()) } ?: return false
|
||||
AirInterception.airSweep(MapUnitCombatant(unit),targetTile)
|
||||
if (unit.currentMovement > 0) return false
|
||||
return true
|
||||
}
|
||||
|
||||
fun automateBomber(unit: MapUnit) {
|
||||
if (unit.health <= 50 && !unit.hasUnique(UniqueType.HealsEvenAfterAction)) return // Wait and heal
|
||||
|
||||
if (BattleHelper.tryAttackNearbyEnemy(unit)) return
|
||||
|
||||
if (unit.health <= 90 || (unit.health < 100 && !unit.civ.isAtWar())) {
|
||||
return // Wait and heal
|
||||
}
|
||||
|
||||
if (tryRelocateToCitiesWithEnemyNearBy(unit)) return
|
||||
|
||||
val pathsToCities = unit.movement.getAerialPathsToCities()
|
||||
|
@ -15,8 +15,8 @@ object BattleHelper {
|
||||
fun tryAttackNearbyEnemy(unit: MapUnit, stayOnTile: Boolean = false): Boolean {
|
||||
if (unit.hasUnique(UniqueType.CannotAttack)) return false
|
||||
val attackableEnemies = TargetHelper.getAttackableEnemies(unit, unit.movement.getDistanceToTiles(), stayOnTile=stayOnTile)
|
||||
// Only take enemies we can fight without dying
|
||||
.filter {
|
||||
// Only take enemies we can fight without dying or are made to die
|
||||
.filter {unit.hasUnique(UniqueType.SelfDestructs) ||
|
||||
BattleDamage.calculateDamageToAttacker(
|
||||
MapUnitCombatant(unit),
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!
|
||||
@ -92,7 +92,7 @@ object BattleHelper {
|
||||
|
||||
if (attacker.baseUnit.isMelee()) {
|
||||
val battleDamage = BattleDamage.calculateDamageToAttacker(attackerUnit, cityUnit)
|
||||
if (attacker.health - battleDamage * 2 <= 0) {
|
||||
if (attacker.health - battleDamage * 2 <= 0 && !attacker.hasUnique(UniqueType.SelfDestructs)) {
|
||||
// The more fiendly units around the city, the more willing we should be to just attack the city
|
||||
val friendlyUnitsAroundCity = city.getCenterTile().getTilesInDistance(3).count { it.militaryUnit?.civ == attacker.civ }
|
||||
// If we have more than 4 other units around the city, go for it
|
||||
|
@ -197,15 +197,14 @@ object UnitAutomation {
|
||||
if (unit.canIntercept())
|
||||
return AirUnitAutomation.automateFighter(unit)
|
||||
|
||||
if (!unit.baseUnit.isNuclearWeapon())
|
||||
return AirUnitAutomation.automateBomber(unit)
|
||||
|
||||
// Note that not all nukes have to be air units
|
||||
if (unit.baseUnit.isNuclearWeapon())
|
||||
return AirUnitAutomation.automateNukes(unit)
|
||||
|
||||
if (unit.hasUnique(UniqueType.SelfDestructs))
|
||||
return AirUnitAutomation.automateMissile(unit)
|
||||
|
||||
return AirUnitAutomation.automateBomber(unit)
|
||||
}
|
||||
|
||||
if (tryGoToRuinAndEncampment(unit) && unit.currentMovement == 0f) return
|
||||
|
Loading…
Reference in New Issue
Block a user