mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-05 21:11:35 +07:00
Ai nuke improvement (#9968)
* Improved Nuke AI * AI can only nuke visible tiles now * Removed an extra space * Removed commented changes from another feature in testing * Removed commented changes from another feature in testing again * AI now doesn't calculate the value of nuking a tile while at peace * Removed extra change related to attacking cities.
This commit is contained in:
parent
c8de5a7de3
commit
0db070a25f
@ -7,6 +7,7 @@ import com.unciv.logic.battle.GreatGeneralImplementation
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.battle.TargetHelper
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
@ -494,27 +495,78 @@ object SpecificUnitAutomation {
|
||||
}
|
||||
|
||||
fun automateNukes(unit: MapUnit) {
|
||||
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange())
|
||||
for (tile in tilesInRange) {
|
||||
// For now AI will only use nukes against cities because in all honesty that's the best use for them.
|
||||
if (tile.isCityCenter()
|
||||
&& tile.getOwner()!!.isAtWarWith(unit.civ)
|
||||
&& tile.getCity()!!.health > tile.getCity()!!.getMaxHealth() / 2
|
||||
&& Battle.mayUseNuke(MapUnitCombatant(unit), tile)) {
|
||||
val blastRadius = unit.getNukeBlastRadius()
|
||||
val tilesInBlastRadius = tile.getTilesInDistance(blastRadius)
|
||||
val civsInBlastRadius = tilesInBlastRadius.mapNotNull { it.getOwner() } +
|
||||
tilesInBlastRadius.mapNotNull { it.getFirstUnit()?.civ }
|
||||
// Don't nuke if it means we will be declaring war on someone!
|
||||
if (civsInBlastRadius.none { it != unit.civ && !it.isAtWarWith(unit.civ) }) {
|
||||
Battle.NUKE(MapUnitCombatant(unit), tile)
|
||||
return
|
||||
}
|
||||
if (!unit.civ.isAtWar()) return
|
||||
// We should *Almost* never want to nuke our own city, so don't consider it
|
||||
val tilesInRange = unit.currentTile.getTilesInDistanceRange(2..unit.getRange())
|
||||
var highestTileNukeValue = 0
|
||||
var tileToNuke: Tile? = null
|
||||
tilesInRange.forEach {
|
||||
val value = getNukeLocationValue(unit, it)
|
||||
if (value > highestTileNukeValue) {
|
||||
highestTileNukeValue = value
|
||||
tileToNuke = it
|
||||
}
|
||||
}
|
||||
if (highestTileNukeValue > 0) {
|
||||
Battle.NUKE(MapUnitCombatant(unit), tileToNuke!!)
|
||||
}
|
||||
tryRelocateToNearbyAttackableCities(unit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Ranks the tile to nuke based off of all tiles in it's blast radius
|
||||
* By default the value is -500 to prevent inefficient nuking.
|
||||
*/
|
||||
fun getNukeLocationValue(nuke: MapUnit, tile: Tile): Int {
|
||||
val civ = nuke.civ
|
||||
if (!Battle.mayUseNuke(MapUnitCombatant(nuke), tile)) return Int.MIN_VALUE
|
||||
val blastRadius = nuke.getNukeBlastRadius()
|
||||
val tilesInBlastRadius = tile.getTilesInDistance(blastRadius)
|
||||
val civsInBlastRadius = tilesInBlastRadius.mapNotNull { it.getOwner() } +
|
||||
tilesInBlastRadius.mapNotNull { it.getFirstUnit()?.civ }
|
||||
|
||||
// Don't nuke if it means we will be declaring war on someone!
|
||||
if (civsInBlastRadius.any { it != civ && !it.isAtWarWith(civ) }) return -100000
|
||||
// If there are no enemies to hit, don't nuke
|
||||
if (!civsInBlastRadius.any { it.isAtWarWith(civ) }) return -100000
|
||||
|
||||
// Launching a Nuke uses resources, therefore don't launch it by default
|
||||
var explosionValue = -500
|
||||
|
||||
// Returns either ourValue or thierValue depending on if the input Civ matches the Nuke's Civ
|
||||
fun evaluateCivValue(targetCiv: Civilization, ourValue: Int, theirValue: Int): Int {
|
||||
if (targetCiv == civ) // We are nuking something that we own!
|
||||
return ourValue
|
||||
return theirValue // We are nuking an enemy!
|
||||
}
|
||||
for (targetTile in tilesInBlastRadius) {
|
||||
// We can only account for visible units
|
||||
if (tile.isVisible(civ)) {
|
||||
if (targetTile.militaryUnit != null && !targetTile.militaryUnit!!.isInvisible(civ))
|
||||
explosionValue += evaluateCivValue(targetTile.militaryUnit?.civ!!, -150, 50)
|
||||
if (targetTile.civilianUnit != null && !targetTile.civilianUnit!!.isInvisible(civ))
|
||||
explosionValue += evaluateCivValue(targetTile.civilianUnit?.civ!!, -100, 25)
|
||||
}
|
||||
// Never nuke our own Civ, don't nuke single enemy civs as well
|
||||
if (targetTile.isCityCenter()
|
||||
&& !(targetTile.getCity()!!.health <= 50f
|
||||
&& targetTile.neighbors.any {it.militaryUnit?.civ == civ})) // Prefer not to nuke cities that we are about to take
|
||||
explosionValue += evaluateCivValue(targetTile.getCity()?.civ!!, -100000, 250)
|
||||
else if (targetTile.owningCity != null) {
|
||||
val owningCiv = targetTile.owningCity?.civ!!
|
||||
// If there is a tile to add fallout to there is a 50% chance it will get fallout
|
||||
if (!(tile.isWater || tile.isImpassible() || targetTile.terrainFeatures.any { it == "Fallout" }))
|
||||
explosionValue += evaluateCivValue(owningCiv, -40, 10)
|
||||
// If there is an improvment to pillage
|
||||
if (targetTile.improvement != null && !targetTile.improvementIsPillaged)
|
||||
explosionValue += evaluateCivValue(owningCiv, -40, 20)
|
||||
}
|
||||
// If the value is too low end the search early
|
||||
if (explosionValue < -1000) return explosionValue
|
||||
}
|
||||
return explosionValue
|
||||
}
|
||||
|
||||
// This really needs to be changed, to have better targeting for missiles
|
||||
fun automateMissile(unit: MapUnit) {
|
||||
if (BattleHelper.tryAttackNearbyEnemy(unit)) return
|
||||
|
@ -178,7 +178,7 @@ object UnitAutomation {
|
||||
if (unit.baseUnit.isAirUnit() && unit.canIntercept())
|
||||
return SpecificUnitAutomation.automateFighter(unit)
|
||||
|
||||
if (unit.baseUnit.isAirUnit())
|
||||
if (unit.baseUnit.isAirUnit() && !unit.baseUnit.isNuclearWeapon())
|
||||
return SpecificUnitAutomation.automateBomber(unit)
|
||||
|
||||
if (unit.baseUnit.isNuclearWeapon())
|
||||
@ -383,7 +383,7 @@ object UnitAutomation {
|
||||
unit.movement.headTowards(encampmentToHeadTowards)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private fun tryHealUnit(unit: MapUnit): Boolean {
|
||||
if (unit.baseUnit.isRanged() && unit.hasUnique(UniqueType.HealsEvenAfterAction))
|
||||
return false // will heal anyway, and attacks don't hurt
|
||||
@ -633,7 +633,7 @@ object UnitAutomation {
|
||||
.sum() // City heals 20 per turn
|
||||
|
||||
if (expectedDamagePerTurn < city.health && // If we can take immediately, go for it
|
||||
(expectedDamagePerTurn <= 20 || city.health / (expectedDamagePerTurn-20) > 5)){ // otherwise check if we can take within a couple of turns
|
||||
(expectedDamagePerTurn <= 20 || city.health / (expectedDamagePerTurn-20) > 5)){ // otherwise check if we can take within a couple of turns
|
||||
|
||||
// We won't be able to take this even with 5 turns of continuous damage!
|
||||
// don't head straight to the city, try to head to landing grounds -
|
||||
|
@ -759,6 +759,8 @@ object Battle {
|
||||
*/
|
||||
fun mayUseNuke(nuke: MapUnitCombatant, targetTile: Tile): Boolean {
|
||||
if (nuke.getTile() == targetTile) return false
|
||||
// Can only nuke visible Tiles
|
||||
if (!targetTile.isVisible(nuke.getCivInfo())) return false
|
||||
|
||||
var canNuke = true
|
||||
val attackerCiv = nuke.getCivInfo()
|
||||
|
Loading…
Reference in New Issue
Block a user