mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-29 14:20:17 +07:00
* Resolved #6767 - AI will not declare war if it definitely can't take a city * Not sure what would happen if you try to calculate damage for civilian attacker and don't care to find out
This commit is contained in:
@ -18,9 +18,10 @@ object BattleHelper {
|
||||
val attackableEnemies = getAttackableEnemies(unit, unit.movement.getDistanceToTiles(), stayOnTile=stayOnTile)
|
||||
// Only take enemies we can fight without dying
|
||||
.filter {
|
||||
BattleDamage.calculateDamageToAttacker(MapUnitCombatant(unit),
|
||||
it.tileToAttackFrom,
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!) < unit.health
|
||||
BattleDamage.calculateDamageToAttacker(
|
||||
MapUnitCombatant(unit),
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!
|
||||
) < unit.health
|
||||
}
|
||||
|
||||
val enemyTileToAttack = chooseAttackTarget(unit, attackableEnemies)
|
||||
@ -129,9 +130,10 @@ object BattleHelper {
|
||||
val attackableEnemiesNextTurn = getAttackableEnemies(unit, unitDistanceToTiles)
|
||||
// Only take enemies we can fight without dying
|
||||
.filter {
|
||||
BattleDamage.calculateDamageToAttacker(MapUnitCombatant(unit),
|
||||
it.tileToAttackFrom,
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!) < unit.health
|
||||
BattleDamage.calculateDamageToAttacker(
|
||||
MapUnitCombatant(unit),
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!
|
||||
) < unit.health
|
||||
}
|
||||
.filter { it.tileToAttackFrom.isLand }
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
package com.unciv.logic.automation
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.battle.BattleDamage
|
||||
import com.unciv.logic.battle.CityCombatant
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.logic.city.PerpetualConstruction
|
||||
@ -687,17 +690,33 @@ object NextTurnAutomation {
|
||||
}
|
||||
|
||||
private fun motivationToAttack(civInfo: CivilizationInfo, otherCiv: CivilizationInfo): Int {
|
||||
if(civInfo.cities.isEmpty() || otherCiv.cities.isEmpty()) return 0
|
||||
val baseForce = 30f
|
||||
|
||||
val ourCombatStrength = civInfo.getStatForRanking(RankingType.Force).toFloat() + baseForce
|
||||
var theirCombatStrength = otherCiv.getStatForRanking(RankingType.Force).toFloat() + baseForce
|
||||
|
||||
//for city-states, also consider there protectors
|
||||
if(otherCiv.isCityState() and otherCiv.getProtectorCivs().isNotEmpty()) {
|
||||
if (otherCiv.isCityState() and otherCiv.getProtectorCivs().isNotEmpty()) {
|
||||
theirCombatStrength += otherCiv.getProtectorCivs().sumOf{it.getStatForRanking(RankingType.Force)}
|
||||
}
|
||||
|
||||
if (theirCombatStrength > ourCombatStrength) return 0
|
||||
|
||||
val closestCities = getClosestCities(civInfo, otherCiv)
|
||||
val ourCity = closestCities.city1
|
||||
val theirCity = closestCities.city2
|
||||
|
||||
if (civInfo.getCivUnits().filter { it.isMilitary() }.none {
|
||||
val damageRecievedWhenAttacking =
|
||||
BattleDamage.calculateDamageToAttacker(
|
||||
MapUnitCombatant(it),
|
||||
CityCombatant(theirCity)
|
||||
)
|
||||
damageRecievedWhenAttacking < 100
|
||||
})
|
||||
return 0 // You don't have any units that can attack this city without dying, don't declare war.
|
||||
|
||||
fun isTileCanMoveThrough(tileInfo: TileInfo): Boolean {
|
||||
val owner = tileInfo.getOwner()
|
||||
return !tileInfo.isImpassible()
|
||||
@ -721,9 +740,6 @@ object NextTurnAutomation {
|
||||
}
|
||||
modifierMap["Relative combat strength"] = combatStrengthModifier
|
||||
|
||||
val closestCities = getClosestCities(civInfo, otherCiv)
|
||||
val ourCity = closestCities.city1
|
||||
val theirCity = closestCities.city2
|
||||
|
||||
if (closestCities.aerialDistance > 7)
|
||||
modifierMap["Far away cities"] = -10
|
||||
|
@ -356,9 +356,10 @@ object UnitAutomation {
|
||||
tilesToCheck = unit.getTile().getTilesInDistance(CLOSE_ENEMY_TILES_AWAY_LIMIT).toList()
|
||||
).filter {
|
||||
// Ignore units that would 1-shot you if you attacked. Account for taking terrain damage after the fact.
|
||||
BattleDamage.calculateDamageToAttacker(MapUnitCombatant(unit),
|
||||
it.tileToAttackFrom,
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!)
|
||||
BattleDamage.calculateDamageToAttacker(
|
||||
MapUnitCombatant(unit),
|
||||
Battle.getMapCombatantOfTile(it.tileToAttack)!!
|
||||
)
|
||||
+ unit.getDamageFromTerrain(it.tileToAttackFrom) < unit.health
|
||||
}
|
||||
|
||||
@ -413,25 +414,13 @@ object UnitAutomation {
|
||||
|
||||
// only focus on *attacking* 1 enemy at a time otherwise you'll lose on both fronts
|
||||
|
||||
val enemies = unit.civInfo.getKnownCivs().filter { unit.civInfo.isAtWarWith(it) && it.cities.isNotEmpty() }
|
||||
val enemies = unit.civInfo.getKnownCivs()
|
||||
.filter { unit.civInfo.isAtWarWith(it) && it.cities.isNotEmpty() }
|
||||
|
||||
val ourCities = unit.civInfo.cities
|
||||
|
||||
var closestEnemyCity:CityInfo?=null
|
||||
var closestDistance = 10000
|
||||
for (enemy in enemies) {
|
||||
val knownEnemyCities = enemy.cities.filter { it.location in unit.civInfo.exploredTiles }
|
||||
if (knownEnemyCities.isEmpty()) continue
|
||||
for(enemyCity in knownEnemyCities) {
|
||||
val distanceToClosestCityOfOurs = ourCities.minOf {
|
||||
it.getCenterTile().aerialDistanceTo(enemyCity.getCenterTile())
|
||||
}
|
||||
if (distanceToClosestCityOfOurs < closestDistance){
|
||||
closestDistance = distanceToClosestCityOfOurs
|
||||
closestEnemyCity = enemyCity
|
||||
}
|
||||
}
|
||||
}
|
||||
val closestEnemyCity = enemies
|
||||
.map { NextTurnAutomation.getClosestCities(unit.civInfo, it) }
|
||||
.minByOrNull { it.aerialDistance }?.city2
|
||||
|
||||
if (closestEnemyCity==null) return false // no attackable cities found
|
||||
|
||||
// Our main attack target is the closest city, but we're fine with deviating from that a bit
|
||||
@ -451,6 +440,7 @@ object UnitAutomation {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private fun headTowardsEnemyCity(unit: MapUnit, closestReachableEnemyCity: TileInfo): Boolean {
|
||||
val unitDistanceToTiles = unit.movement.getDistanceToTiles()
|
||||
|
@ -214,8 +214,8 @@ object Battle {
|
||||
}
|
||||
|
||||
private fun takeDamage(attacker: ICombatant, defender: ICombatant) {
|
||||
var potentialDamageToDefender = BattleDamage.calculateDamageToDefender(attacker, attacker.getTile(), defender)
|
||||
var potentialDamageToAttacker = BattleDamage.calculateDamageToAttacker(attacker, attacker.getTile(), defender)
|
||||
var potentialDamageToDefender = BattleDamage.calculateDamageToDefender(attacker, defender)
|
||||
var potentialDamageToAttacker = BattleDamage.calculateDamageToAttacker(attacker, defender)
|
||||
|
||||
val defenderHealthBefore = defender.getHealth()
|
||||
|
||||
@ -803,9 +803,8 @@ object Battle {
|
||||
if (Random().nextFloat() > interceptor.interceptChance() / 100f) return
|
||||
|
||||
var damage = BattleDamage.calculateDamageToDefender(
|
||||
MapUnitCombatant(interceptor),
|
||||
null,
|
||||
attacker
|
||||
MapUnitCombatant(interceptor),
|
||||
attacker
|
||||
)
|
||||
|
||||
var damageFactor = 1f + interceptor.interceptDamagePercentBonus().toFloat() / 100f
|
||||
|
@ -260,7 +260,6 @@ object BattleDamage {
|
||||
|
||||
fun calculateDamageToAttacker(
|
||||
attacker: ICombatant,
|
||||
tileToAttackFrom: TileInfo?,
|
||||
defender: ICombatant,
|
||||
ignoreRandomness: Boolean = false,
|
||||
): Int {
|
||||
@ -273,7 +272,6 @@ object BattleDamage {
|
||||
|
||||
fun calculateDamageToDefender(
|
||||
attacker: ICombatant,
|
||||
tileToAttackFrom: TileInfo?,
|
||||
defender: ICombatant,
|
||||
ignoreRandomness: Boolean = false,
|
||||
): Int {
|
||||
|
@ -180,8 +180,8 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
row()
|
||||
}
|
||||
|
||||
var damageToDefender = BattleDamage.calculateDamageToDefender(attacker, null, defender, true)
|
||||
var damageToAttacker = BattleDamage.calculateDamageToAttacker(attacker, null, defender, true)
|
||||
var damageToDefender = BattleDamage.calculateDamageToDefender(attacker, defender, true)
|
||||
var damageToAttacker = BattleDamage.calculateDamageToAttacker(attacker, defender, true)
|
||||
|
||||
|
||||
if (damageToAttacker>attacker.getHealth() && damageToDefender>defender.getHealth()) // when damage exceeds health, we don't want to show negative health numbers
|
||||
|
Reference in New Issue
Block a user