mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 14:24:43 -04:00
chore: Separated Nuke logic into separate object
This commit is contained in:
parent
348910dcf7
commit
cc6ab7f7d5
@ -276,7 +276,7 @@ object NextTurnAutomation {
|
||||
if (popupAlert.type == AlertType.DeclarationOfFriendship) {
|
||||
val requestingCiv = civInfo.gameInfo.getCivilization(popupAlert.value)
|
||||
val diploManager = civInfo.getDiplomacyManager(requestingCiv)
|
||||
if (civInfo.diplomacyFunctions.canSignDeclarationOfFriendshipWith(requestingCiv)
|
||||
if (civInfo.diplomacyFunctions.canSignDeclarationOfFriendshipWith(requestingCiv)
|
||||
&& wantsToSignDeclarationOfFrienship(civInfo,requestingCiv)) {
|
||||
diploManager.signDeclarationOfFriendship()
|
||||
requestingCiv.addNotification("We have signed a Declaration of Friendship with [${civInfo.civName}]!", NotificationCategory.Diplomacy, NotificationIcon.Diplomacy, civInfo.civName)
|
||||
@ -284,7 +284,7 @@ object NextTurnAutomation {
|
||||
diploManager.otherCivDiplomacy().setFlag(DiplomacyFlags.DeclinedDeclarationOfFriendship, 10)
|
||||
requestingCiv.addNotification("[${civInfo.civName}] has denied our Declaration of Friendship!", NotificationCategory.Diplomacy, NotificationIcon.Diplomacy, civInfo.civName)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -433,7 +433,7 @@ object NextTurnAutomation {
|
||||
}
|
||||
}
|
||||
|
||||
private fun valueCityStateAlliance(civInfo: Civilization, cityState: Civilization): Int {
|
||||
internal fun valueCityStateAlliance(civInfo: Civilization, cityState: Civilization): Int {
|
||||
var value = 0
|
||||
|
||||
if (civInfo.wantsToFocusOn(Victory.Focus.Culture) && cityState.cityStateFunctions.canProvideStat(Stat.Culture)) {
|
||||
@ -790,13 +790,13 @@ object NextTurnAutomation {
|
||||
val diploManager = civInfo.getDiplomacyManager(otherCiv)
|
||||
// Shortcut, if it is below favorable then don't consider it
|
||||
if (diploManager.isRelationshipLevelLT(RelationshipLevel.Favorable)) return false
|
||||
|
||||
|
||||
val numOfFriends = civInfo.diplomacy.count { it.value.hasFlag(DiplomacyFlags.DeclarationOfFriendship) }
|
||||
val knownCivs = civInfo.getKnownCivs().count { it.isMajorCiv() && it.isAlive() }
|
||||
val allCivs = civInfo.gameInfo.civilizations.count { it.isMajorCiv() } - 1 // Don't include us
|
||||
val deadCivs = civInfo.gameInfo.civilizations.count { it.isMajorCiv() && !it.isAlive() }
|
||||
val allAliveCivs = allCivs - deadCivs
|
||||
|
||||
|
||||
// Motivation should be constant as the number of civs changes
|
||||
var motivation = diploManager.opinionOfOtherCiv().toInt() - 40
|
||||
|
||||
@ -808,7 +808,7 @@ object NextTurnAutomation {
|
||||
ThreatLevel.VeryLow -> -5
|
||||
else -> 0
|
||||
}
|
||||
|
||||
|
||||
// Try to ally with a fourth of the civs in play
|
||||
val civsToAllyWith = 0.25f * allAliveCivs
|
||||
if (numOfFriends < civsToAllyWith) {
|
||||
@ -828,16 +828,16 @@ object NextTurnAutomation {
|
||||
// Goes from -30 to 0 when we know 75% of allCivs
|
||||
val civsToKnow = 0.75f * allAliveCivs
|
||||
motivation -= ((civsToKnow - knownCivs) / civsToKnow * 30f).toInt().coerceAtLeast(0)
|
||||
|
||||
|
||||
motivation -= hasAtLeastMotivationToAttack(civInfo, otherCiv, motivation / 2) * 2
|
||||
|
||||
|
||||
return motivation > 0
|
||||
}
|
||||
|
||||
|
||||
private fun offerOpenBorders(civInfo: Civilization) {
|
||||
if (!civInfo.hasUnique(UniqueType.EnablesOpenBorders)) return
|
||||
val civsThatWeCanOpenBordersWith = civInfo.getKnownCivs()
|
||||
.filter { it.isMajorCiv() && !civInfo.isAtWarWith(it)
|
||||
.filter { it.isMajorCiv() && !civInfo.isAtWarWith(it)
|
||||
&& it.hasUnique(UniqueType.EnablesOpenBorders)
|
||||
&& !civInfo.getDiplomacyManager(it).hasOpenBorders
|
||||
&& !civInfo.getDiplomacyManager(it).hasFlag(DiplomacyFlags.DeclinedOpenBorders) }
|
||||
@ -853,7 +853,7 @@ object NextTurnAutomation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun wantsToOpenBorders(civInfo: Civilization, otherCiv: Civilization): Boolean {
|
||||
if (civInfo.getDiplomacyManager(otherCiv).isRelationshipLevelLT(RelationshipLevel.Favorable)) return false
|
||||
// Don't accept if they are at war with our friends, they might use our land to attack them
|
||||
@ -863,7 +863,7 @@ object NextTurnAutomation {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private fun offerResearchAgreement(civInfo: Civilization) {
|
||||
if (!civInfo.diplomacyFunctions.canSignResearchAgreement()) return // don't waste your time
|
||||
|
||||
@ -903,12 +903,12 @@ object NextTurnAutomation {
|
||||
val tradeLogic = TradeLogic(civInfo, otherCiv)
|
||||
tradeLogic.currentTrade.ourOffers.add(TradeOffer(Constants.defensivePact, TradeType.Treaty))
|
||||
tradeLogic.currentTrade.theirOffers.add(TradeOffer(Constants.defensivePact, TradeType.Treaty))
|
||||
|
||||
|
||||
otherCiv.tradeRequests.add(TradeRequest(civInfo.civName, tradeLogic.currentTrade.reverse()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun wantsToSignDefensivePact(civInfo: Civilization, otherCiv: Civilization): Boolean {
|
||||
val diploManager = civInfo.getDiplomacyManager(otherCiv)
|
||||
if (diploManager.isRelationshipLevelLT(RelationshipLevel.Ally)) return false
|
||||
@ -926,10 +926,10 @@ object NextTurnAutomation {
|
||||
val allCivs = civInfo.gameInfo.civilizations.count { it.isMajorCiv() } - 1 // Don't include us
|
||||
val deadCivs = civInfo.gameInfo.civilizations.count { it.isMajorCiv() && !it.isAlive() }
|
||||
val allAliveCivs = allCivs - deadCivs
|
||||
|
||||
|
||||
// We have to already be at RelationshipLevel.Ally, so we must have 80 oppinion of them
|
||||
var motivation = diploManager.opinionOfOtherCiv().toInt() - 80
|
||||
|
||||
|
||||
// If they are stronger than us, then we value it a lot more
|
||||
// If they are weaker than us, then we don't value it
|
||||
motivation += when (Automation.threatAssessment(civInfo,otherCiv)) {
|
||||
@ -939,7 +939,7 @@ object NextTurnAutomation {
|
||||
ThreatLevel.VeryLow -> -30
|
||||
else -> 0
|
||||
}
|
||||
|
||||
|
||||
// If they have a defensive pact with another civ then we would get drawn into thier battles as well
|
||||
motivation -= 10 * otherCivNonOverlappingDefensivePacts
|
||||
|
||||
@ -1284,31 +1284,6 @@ object NextTurnAutomation {
|
||||
diplomacyManager.removeFlag(DiplomacyFlags.SettledCitiesNearUs)
|
||||
}
|
||||
|
||||
/** Handle decision making after city conquest, namely whether the AI should liberate, puppet,
|
||||
* or raze a city */
|
||||
fun onConquerCity(civInfo: Civilization, city: City) {
|
||||
if (!city.hasDiplomaticMarriage()) {
|
||||
val foundingCiv = civInfo.gameInfo.getCivilization(city.foundingCiv)
|
||||
var valueAlliance = valueCityStateAlliance(civInfo, foundingCiv)
|
||||
if (civInfo.getHappiness() < 0)
|
||||
valueAlliance -= civInfo.getHappiness() // put extra weight on liberating if unhappy
|
||||
if (foundingCiv.isCityState() && city.civ != civInfo && foundingCiv != civInfo
|
||||
&& !civInfo.isAtWarWith(foundingCiv)
|
||||
&& valueAlliance > 0) {
|
||||
city.liberateCity(civInfo)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
city.puppetCity(civInfo)
|
||||
if ((city.population.population < 4 || civInfo.isCityState())
|
||||
&& city.foundingCiv != civInfo.civName && city.canBeDestroyed(justCaptured = true)) {
|
||||
// raze if attacker is a city state
|
||||
if (!civInfo.hasUnique(UniqueType.MayNotAnnexCities)) { city.annexCity() }
|
||||
city.isBeingRazed = true
|
||||
}
|
||||
}
|
||||
|
||||
fun getMinDistanceBetweenCities(civ1: Civilization, civ2: Civilization): Int {
|
||||
return getClosestCities(civ1, civ2)?.aerialDistance ?: Int.MAX_VALUE
|
||||
}
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.logic.battle.Battle
|
||||
import com.unciv.logic.battle.GreatGeneralImplementation
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.battle.Nuke
|
||||
import com.unciv.logic.battle.TargetHelper
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
@ -496,7 +496,7 @@ object SpecificUnitAutomation {
|
||||
}
|
||||
}
|
||||
if (highestTileNukeValue > 0) {
|
||||
Battle.NUKE(MapUnitCombatant(unit), tileToNuke!!)
|
||||
Nuke.NUKE(MapUnitCombatant(unit), tileToNuke!!)
|
||||
}
|
||||
tryRelocateToNearbyAttackableCities(unit)
|
||||
}
|
||||
@ -507,7 +507,7 @@ object SpecificUnitAutomation {
|
||||
*/
|
||||
fun getNukeLocationValue(nuke: MapUnit, tile: Tile): Int {
|
||||
val civ = nuke.civ
|
||||
if (!Battle.mayUseNuke(MapUnitCombatant(nuke), tile)) return Int.MIN_VALUE
|
||||
if (!Nuke.mayUseNuke(MapUnitCombatant(nuke), tile)) return Int.MIN_VALUE
|
||||
val blastRadius = nuke.getNukeBlastRadius()
|
||||
val tilesInBlastRadius = tile.getTilesInDistance(blastRadius)
|
||||
val civsInBlastRadius = tilesInBlastRadius.mapNotNull { it.getOwner() } +
|
||||
|
@ -4,11 +4,9 @@ import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.automation.civilization.NextTurnAutomation
|
||||
import com.unciv.logic.automation.unit.SpecificUnitAutomation
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.AlertType
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.CivilopediaAction
|
||||
import com.unciv.logic.civilization.LocationAction
|
||||
import com.unciv.logic.civilization.MapUnitAction
|
||||
import com.unciv.logic.civilization.NotificationCategory
|
||||
@ -16,25 +14,19 @@ import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.civilization.PopupAlert
|
||||
import com.unciv.logic.civilization.PromoteUnitAction
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.logic.map.mapunit.MapUnit
|
||||
import com.unciv.logic.map.tile.RoadStatus
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.UnitActionType
|
||||
import com.unciv.ui.components.UnitMovementMemoryType
|
||||
import com.unciv.models.ruleset.unique.StateForConditionals
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.ui.components.extensions.toPercent
|
||||
import com.unciv.ui.screens.worldscreen.bottombar.BattleTable
|
||||
import com.unciv.ui.components.UnitMovementMemoryType
|
||||
import com.unciv.utils.debug
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.ulp
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
@ -92,7 +84,7 @@ object Battle {
|
||||
*/
|
||||
fun attackOrNuke(attacker: ICombatant, attackableTile: AttackableTile): DamageDealt {
|
||||
return if (attacker is MapUnitCombatant && attacker.unit.baseUnit.isNuclearWeapon()) {
|
||||
NUKE(attacker, attackableTile.tileToAttack)
|
||||
Nuke.NUKE(attacker, attackableTile.tileToAttack)
|
||||
DamageDealt.None
|
||||
} else {
|
||||
attack(attacker, getMapCombatantOfTile(attackableTile.tileToAttack)!!)
|
||||
@ -439,7 +431,7 @@ object Battle {
|
||||
}
|
||||
}
|
||||
|
||||
private fun postBattleNotifications(
|
||||
internal fun postBattleNotifications(
|
||||
attacker: ICombatant,
|
||||
defender: ICombatant,
|
||||
attackedTile: Tile,
|
||||
@ -626,9 +618,7 @@ object Battle {
|
||||
} else if (attackerCiv.isHuman()) {
|
||||
// we're not taking our former capital
|
||||
attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered, city.id))
|
||||
} else {
|
||||
NextTurnAutomation.onConquerCity(attackerCiv, city)
|
||||
}
|
||||
} else automateCityConquer(attackerCiv, city)
|
||||
|
||||
if (attackerCiv.isCurrentPlayer())
|
||||
UncivGame.Current.settings.addCompletedTutorialTask("Conquer a city")
|
||||
@ -638,6 +628,31 @@ object Battle {
|
||||
UniqueTriggerActivation.triggerCivwideUnique(unique, attackerCiv, city)
|
||||
}
|
||||
|
||||
/** Handle decision making after city conquest, namely whether the AI should liberate, puppet,
|
||||
* or raze a city */
|
||||
private fun automateCityConquer(civInfo: Civilization, city: City) {
|
||||
if (!city.hasDiplomaticMarriage()) {
|
||||
val foundingCiv = civInfo.gameInfo.getCivilization(city.foundingCiv)
|
||||
var valueAlliance = NextTurnAutomation.valueCityStateAlliance(civInfo, foundingCiv)
|
||||
if (civInfo.getHappiness() < 0)
|
||||
valueAlliance -= civInfo.getHappiness() // put extra weight on liberating if unhappy
|
||||
if (foundingCiv.isCityState() && city.civ != civInfo && foundingCiv != civInfo
|
||||
&& !civInfo.isAtWarWith(foundingCiv)
|
||||
&& valueAlliance > 0) {
|
||||
city.liberateCity(civInfo)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
city.puppetCity(civInfo)
|
||||
if ((city.population.population < 4 || civInfo.isCityState())
|
||||
&& city.foundingCiv != civInfo.civName && city.canBeDestroyed(justCaptured = true)) {
|
||||
// raze if attacker is a city state
|
||||
if (!civInfo.hasUnique(UniqueType.MayNotAnnexCities)) city.annexCity()
|
||||
city.isBeingRazed = true
|
||||
}
|
||||
}
|
||||
|
||||
fun getMapCombatantOfTile(tile: Tile): ICombatant? {
|
||||
if (tile.isCityCenter()) return CityCombatant(tile.getCity()!!)
|
||||
if (tile.militaryUnit != null) return MapUnitCombatant(tile.militaryUnit!!)
|
||||
@ -751,245 +766,6 @@ object Battle {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether [nuke] is allowed to nuke [targetTile]
|
||||
* - Not if we would need to declare war on someone we can't.
|
||||
* - Disallow nuking the tile the nuke is in, as per Civ5 (but not nuking your own tiles/units otherwise)
|
||||
*
|
||||
* Both [BattleTable.simulateNuke] and [SpecificUnitAutomation.automateNukes] check range, so that check is omitted here.
|
||||
*/
|
||||
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()
|
||||
fun checkDefenderCiv(defenderCiv: Civilization?) {
|
||||
if (defenderCiv == null) return
|
||||
// Allow nuking yourself! (Civ5 source: CvUnit::isNukeVictim)
|
||||
if (defenderCiv == attackerCiv || defenderCiv.isDefeated()) return
|
||||
// Gleaned from Civ5 source - this disallows nuking unknown civs even in invisible tiles
|
||||
// https://github.com/Gedemon/Civ5-DLL/blob/master/CvGameCoreDLL_Expansion1/CvUnit.cpp#L5056
|
||||
// https://github.com/Gedemon/Civ5-DLL/blob/master/CvGameCoreDLL_Expansion1/CvTeam.cpp#L986
|
||||
if (attackerCiv.knows(defenderCiv) && attackerCiv.getDiplomacyManager(defenderCiv).canAttack())
|
||||
return
|
||||
canNuke = false
|
||||
}
|
||||
|
||||
val blastRadius = nuke.unit.getNukeBlastRadius()
|
||||
for (tile in targetTile.getTilesInDistance(blastRadius)) {
|
||||
checkDefenderCiv(tile.getOwner())
|
||||
checkDefenderCiv(getMapCombatantOfTile(tile)?.getCivInfo())
|
||||
}
|
||||
return canNuke
|
||||
}
|
||||
|
||||
@Suppress("FunctionName") // Yes we want this name to stand out
|
||||
fun NUKE(attacker: MapUnitCombatant, targetTile: Tile) {
|
||||
val attackingCiv = attacker.getCivInfo()
|
||||
val notifyDeclaredWarCivs = ArrayList<Civilization>()
|
||||
fun tryDeclareWar(civSuffered: Civilization) {
|
||||
if (civSuffered != attackingCiv
|
||||
&& civSuffered.knows(attackingCiv)
|
||||
&& civSuffered.getDiplomacyManager(attackingCiv).diplomaticStatus != DiplomaticStatus.War
|
||||
) {
|
||||
attackingCiv.getDiplomacyManager(civSuffered).declareWar()
|
||||
if (!notifyDeclaredWarCivs.contains(civSuffered)) notifyDeclaredWarCivs.add(civSuffered)
|
||||
}
|
||||
}
|
||||
|
||||
val nukeStrength = attacker.unit.getMatchingUniques(UniqueType.NuclearWeapon)
|
||||
.firstOrNull()?.params?.get(0)?.toInt() ?: return
|
||||
|
||||
val blastRadius = attacker.unit.getMatchingUniques(UniqueType.BlastRadius)
|
||||
.firstOrNull()?.params?.get(0)?.toInt() ?: 2
|
||||
|
||||
// Calculate the tiles that are hit
|
||||
val hitTiles = targetTile.getTilesInDistance(blastRadius)
|
||||
|
||||
val hitCivsTerritory = ArrayList<Civilization>()
|
||||
// Declare war on the owners of all hit tiles
|
||||
for (hitCiv in hitTiles.mapNotNull { it.getOwner() }.distinct()) {
|
||||
hitCivsTerritory.add(hitCiv)
|
||||
tryDeclareWar(hitCiv)
|
||||
}
|
||||
|
||||
// Declare war on all potentially hit units. They'll try to intercept the nuke before it drops
|
||||
for (civWhoseUnitWasAttacked in hitTiles
|
||||
.flatMap { it.getUnits() }
|
||||
.map { it.civ }.distinct()
|
||||
.filter { it != attackingCiv }) {
|
||||
tryDeclareWar(civWhoseUnitWasAttacked)
|
||||
if (attacker.unit.baseUnit.isAirUnit() && !attacker.isDefeated()) {
|
||||
tryInterceptAirAttack(attacker, targetTile, civWhoseUnitWasAttacked, null)
|
||||
}
|
||||
}
|
||||
val nukeNotificationAction = sequenceOf( LocationAction(targetTile.position), CivilopediaAction("Units/" + attacker.getName()))
|
||||
// If the nuke has been intercepted and destroyed then it fails to detonate
|
||||
if (attacker.isDefeated()) {
|
||||
// Notify attacker that they are now at war for the attempt
|
||||
for (defendingCiv in notifyDeclaredWarCivs)
|
||||
attackingCiv.addNotification("After an attempted attack by our [${attacker.getName()}], [${defendingCiv}] has declared war on us!", nukeNotificationAction, NotificationCategory.Diplomacy, defendingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
return
|
||||
}
|
||||
|
||||
// Notify attacker that they are now at war
|
||||
for (defendingCiv in notifyDeclaredWarCivs)
|
||||
attackingCiv.addNotification("After being hit by our [${attacker.getName()}], [${defendingCiv}] has declared war on us!", nukeNotificationAction, NotificationCategory.Diplomacy, defendingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
|
||||
attacker.unit.attacksSinceTurnStart.add(Vector2(targetTile.position))
|
||||
|
||||
for (tile in hitTiles) {
|
||||
// Handle complicated effects
|
||||
doNukeExplosionForTile(attacker, tile, nukeStrength, targetTile == tile)
|
||||
}
|
||||
|
||||
// Message all other civs
|
||||
for (otherCiv in attackingCiv.gameInfo.civilizations) {
|
||||
if (!otherCiv.isAlive() || otherCiv == attackingCiv) continue
|
||||
if (hitCivsTerritory.contains(otherCiv))
|
||||
otherCiv.addNotification("A(n) [${attacker.getName()}] from [${attackingCiv.civName}] has exploded in our territory!",
|
||||
nukeNotificationAction, NotificationCategory.War, attackingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
else if (otherCiv.knows(attackingCiv))
|
||||
otherCiv.addNotification("A(n) [${attacker.getName()}] has been detonated by [${attackingCiv.civName}]!",
|
||||
nukeNotificationAction, NotificationCategory.War, attackingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
else
|
||||
otherCiv.addNotification("A(n) [${attacker.getName()}] has been detonated by an unkown civilization!",
|
||||
nukeNotificationAction, NotificationCategory.War, NotificationIcon.War, attacker.getName())
|
||||
}
|
||||
|
||||
// Instead of postBattleAction() just destroy the unit, all other functions are not relevant
|
||||
if (attacker.unit.hasUnique(UniqueType.SelfDestructs)) attacker.unit.destroy()
|
||||
|
||||
// It's unclear whether using nukes results in a penalty with all civs, or only affected civs.
|
||||
// For now I'll make it give a diplomatic penalty to all known civs, but some testing for this would be appreciated
|
||||
for (civ in attackingCiv.getKnownCivs()) {
|
||||
civ.getDiplomacyManager(attackingCiv).setModifier(DiplomaticModifiers.UsedNuclearWeapons, -50f)
|
||||
}
|
||||
|
||||
if (!attacker.isDefeated()) {
|
||||
attacker.unit.attacksThisTurn += 1
|
||||
}
|
||||
}
|
||||
|
||||
private fun doNukeExplosionForTile(
|
||||
attacker: MapUnitCombatant,
|
||||
tile: Tile,
|
||||
nukeStrength: Int,
|
||||
isGroundZero: Boolean
|
||||
) {
|
||||
// https://forums.civfanatics.com/resources/unit-guide-modern-future-units-g-k.25628/
|
||||
// https://www.carlsguides.com/strategy/civilization5/units/aircraft-nukes.ph
|
||||
// Testing done by Ravignir
|
||||
// original source code: GenerateNuclearExplosionDamage(), ApplyNuclearExplosionDamage()
|
||||
|
||||
var damageModifierFromMissingResource = 1f
|
||||
val civResources = attacker.getCivInfo().getCivResourcesByName()
|
||||
for (resource in attacker.unit.baseUnit.getResourceRequirementsPerTurn().keys) {
|
||||
if (civResources[resource]!! < 0 && !attacker.getCivInfo().isBarbarian())
|
||||
damageModifierFromMissingResource *= 0.5f // I could not find a source for this number, but this felt about right
|
||||
// - Original Civ5 does *not* reduce damage from missing resource, from source inspection
|
||||
}
|
||||
|
||||
var buildingModifier = 1f // Strange, but in Civ5 a bunker mitigates damage to garrison, even if the city is destroyed by the nuke
|
||||
|
||||
// Damage city and reduce its population
|
||||
val city = tile.getCity()
|
||||
if (city != null && tile.position == city.location) {
|
||||
buildingModifier = city.getAggregateModifier(UniqueType.GarrisonDamageFromNukes)
|
||||
doNukeExplosionDamageToCity(city, nukeStrength, damageModifierFromMissingResource)
|
||||
postBattleNotifications(attacker, CityCombatant(city), city.getCenterTile())
|
||||
destroyIfDefeated(city.civ, attacker.getCivInfo())
|
||||
}
|
||||
|
||||
// Damage and/or destroy units on the tile
|
||||
for (unit in tile.getUnits().toList()) { // toList so if it's destroyed there's no concurrent modification
|
||||
val damage = (when {
|
||||
isGroundZero || nukeStrength >= 2 -> 100
|
||||
// The following constants are NUKE_UNIT_DAMAGE_BASE / NUKE_UNIT_DAMAGE_RAND_1 / NUKE_UNIT_DAMAGE_RAND_2 in Civ5
|
||||
nukeStrength == 1 -> 30 + Random.Default.nextInt(40) + Random.Default.nextInt(40)
|
||||
// Level 0 does not exist in Civ5 (it treats units same as level 2)
|
||||
else -> 20 + Random.Default.nextInt(30)
|
||||
} * buildingModifier * damageModifierFromMissingResource + 1f.ulp).toInt()
|
||||
val defender = MapUnitCombatant(unit)
|
||||
if (unit.isCivilian()) {
|
||||
if (unit.health - damage <= 40) unit.destroy() // Civ5: NUKE_NON_COMBAT_DEATH_THRESHOLD = 60
|
||||
} else {
|
||||
defender.takeDamage(damage)
|
||||
}
|
||||
postBattleNotifications(attacker, defender, defender.getTile())
|
||||
destroyIfDefeated(defender.getCivInfo(), attacker.getCivInfo())
|
||||
}
|
||||
|
||||
// Pillage improvements, pillage roads, add fallout
|
||||
if (tile.isCityCenter()) return // Never touch city centers - if they survived
|
||||
fun applyPillageAndFallout() {
|
||||
if (tile.getUnpillagedImprovement() != null && !tile.getTileImprovement()!!.hasUnique(UniqueType.Irremovable)) {
|
||||
if (tile.getTileImprovement()!!.hasUnique(UniqueType.Unpillagable)) {
|
||||
tile.removeImprovement()
|
||||
} else {
|
||||
tile.setPillaged()
|
||||
}
|
||||
}
|
||||
if (tile.getUnpillagedRoad() != RoadStatus.None)
|
||||
tile.setPillaged()
|
||||
if (tile.isWater || tile.isImpassible() || tile.terrainFeatures.contains("Fallout")) return
|
||||
tile.addTerrainFeature("Fallout")
|
||||
}
|
||||
|
||||
if (tile.terrainHasUnique(UniqueType.DestroyableByNukesChance)) {
|
||||
// Note: Safe from concurrent modification exceptions only because removeTerrainFeature
|
||||
// *replaces* terrainFeatureObjects and the loop will continue on the old one
|
||||
for (terrainFeature in tile.terrainFeatureObjects) {
|
||||
for (unique in terrainFeature.getMatchingUniques(UniqueType.DestroyableByNukesChance)) {
|
||||
val chance = unique.params[0].toFloat() / 100f
|
||||
if (!(chance > 0f && isGroundZero) && Random.Default.nextFloat() >= chance) continue
|
||||
tile.removeTerrainFeature(terrainFeature.name)
|
||||
applyPillageAndFallout()
|
||||
}
|
||||
}
|
||||
} else if (isGroundZero || Random.Default.nextFloat() < 0.5f) { // Civ5: NUKE_FALLOUT_PROB
|
||||
applyPillageAndFallout()
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the "protection" modifier from buildings (Bomb Shelter, UniqueType.PopulationLossFromNukes) */
|
||||
private fun doNukeExplosionDamageToCity(targetedCity: City, nukeStrength: Int, damageModifierFromMissingResource: Float) {
|
||||
// Original Capitals must be protected, `canBeDestroyed` is responsible for that check.
|
||||
// The `justCaptured = true` parameter is what allows other Capitals to suffer normally.
|
||||
if ((nukeStrength > 2 || nukeStrength > 1 && targetedCity.population.population < 5)
|
||||
&& targetedCity.canBeDestroyed(true)) {
|
||||
targetedCity.destroyCity()
|
||||
return
|
||||
}
|
||||
|
||||
val cityCombatant = CityCombatant(targetedCity)
|
||||
cityCombatant.takeDamage((cityCombatant.getHealth() * 0.5f * damageModifierFromMissingResource).toInt())
|
||||
|
||||
// Difference to original: Civ5 rounds population loss down twice - before and after bomb shelters
|
||||
val populationLoss = (
|
||||
targetedCity.population.population *
|
||||
targetedCity.getAggregateModifier(UniqueType.PopulationLossFromNukes) *
|
||||
when (nukeStrength) {
|
||||
0 -> 0f
|
||||
1 -> (30 + Random.Default.nextInt(20) + Random.Default.nextInt(20)) / 100f
|
||||
2 -> (60 + Random.Default.nextInt(10) + Random.Default.nextInt(10)) / 100f
|
||||
else -> 1f // hypothetical nukeStrength 3 -> always to 1 pop
|
||||
}
|
||||
).toInt().coerceAtMost(targetedCity.population.population - 1)
|
||||
targetedCity.population.addPopulation(-populationLoss)
|
||||
}
|
||||
|
||||
private fun City.getAggregateModifier(uniqueType: UniqueType): Float {
|
||||
var modifier = 1f
|
||||
for (unique in getMatchingUniques(uniqueType)) {
|
||||
if (!matchesFilter(unique.params[1])) continue
|
||||
modifier *= unique.params[0].toPercent()
|
||||
}
|
||||
return modifier
|
||||
}
|
||||
|
||||
// Should draw an Interception if available on the tile from any Civ
|
||||
// Land Units deal 0 damage, and no XP for either party
|
||||
// Air Interceptors do Air Combat as if Melee (mutual damage) but using Ranged Strength. 5XP to both
|
||||
@ -1103,7 +879,7 @@ object Battle {
|
||||
attacker.unit.action = null
|
||||
}
|
||||
|
||||
private fun tryInterceptAirAttack(
|
||||
internal fun tryInterceptAirAttack(
|
||||
attacker: MapUnitCombatant,
|
||||
attackedTile: Tile,
|
||||
interceptingCiv: Civilization,
|
||||
|
265
core/src/com/unciv/logic/battle/Nuke.kt
Normal file
265
core/src/com/unciv/logic/battle/Nuke.kt
Normal file
@ -0,0 +1,265 @@
|
||||
package com.unciv.logic.battle
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.automation.unit.SpecificUnitAutomation
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.civilization.CivilopediaAction
|
||||
import com.unciv.logic.civilization.LocationAction
|
||||
import com.unciv.logic.civilization.NotificationCategory
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
|
||||
import com.unciv.logic.map.tile.RoadStatus
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.ui.components.extensions.toPercent
|
||||
import com.unciv.ui.screens.worldscreen.bottombar.BattleTable
|
||||
import kotlin.math.ulp
|
||||
import kotlin.random.Random
|
||||
|
||||
object Nuke {
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether [nuke] is allowed to nuke [targetTile]
|
||||
* - Not if we would need to declare war on someone we can't.
|
||||
* - Disallow nuking the tile the nuke is in, as per Civ5 (but not nuking your own tiles/units otherwise)
|
||||
*
|
||||
* Both [BattleTable.simulateNuke] and [SpecificUnitAutomation.automateNukes] check range, so that check is omitted here.
|
||||
*/
|
||||
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()
|
||||
fun checkDefenderCiv(defenderCiv: Civilization?) {
|
||||
if (defenderCiv == null) return
|
||||
// Allow nuking yourself! (Civ5 source: CvUnit::isNukeVictim)
|
||||
if (defenderCiv == attackerCiv || defenderCiv.isDefeated()) return
|
||||
// Gleaned from Civ5 source - this disallows nuking unknown civs even in invisible tiles
|
||||
// https://github.com/Gedemon/Civ5-DLL/blob/master/CvGameCoreDLL_Expansion1/CvUnit.cpp#L5056
|
||||
// https://github.com/Gedemon/Civ5-DLL/blob/master/CvGameCoreDLL_Expansion1/CvTeam.cpp#L986
|
||||
if (attackerCiv.knows(defenderCiv) && attackerCiv.getDiplomacyManager(defenderCiv).canAttack())
|
||||
return
|
||||
canNuke = false
|
||||
}
|
||||
|
||||
val blastRadius = nuke.unit.getNukeBlastRadius()
|
||||
for (tile in targetTile.getTilesInDistance(blastRadius)) {
|
||||
checkDefenderCiv(tile.getOwner())
|
||||
checkDefenderCiv(Battle.getMapCombatantOfTile(tile)?.getCivInfo())
|
||||
}
|
||||
return canNuke
|
||||
}
|
||||
|
||||
@Suppress("FunctionName") // Yes we want this name to stand out
|
||||
fun NUKE(attacker: MapUnitCombatant, targetTile: Tile) {
|
||||
val attackingCiv = attacker.getCivInfo()
|
||||
val notifyDeclaredWarCivs = ArrayList<Civilization>()
|
||||
fun tryDeclareWar(civSuffered: Civilization) {
|
||||
if (civSuffered != attackingCiv
|
||||
&& civSuffered.knows(attackingCiv)
|
||||
&& civSuffered.getDiplomacyManager(attackingCiv).diplomaticStatus != DiplomaticStatus.War
|
||||
) {
|
||||
attackingCiv.getDiplomacyManager(civSuffered).declareWar()
|
||||
if (!notifyDeclaredWarCivs.contains(civSuffered)) notifyDeclaredWarCivs.add(civSuffered)
|
||||
}
|
||||
}
|
||||
|
||||
val nukeStrength = attacker.unit.getMatchingUniques(UniqueType.NuclearWeapon)
|
||||
.firstOrNull()?.params?.get(0)?.toInt() ?: return
|
||||
|
||||
val blastRadius = attacker.unit.getMatchingUniques(UniqueType.BlastRadius)
|
||||
.firstOrNull()?.params?.get(0)?.toInt() ?: 2
|
||||
|
||||
// Calculate the tiles that are hit
|
||||
val hitTiles = targetTile.getTilesInDistance(blastRadius)
|
||||
|
||||
val hitCivsTerritory = ArrayList<Civilization>()
|
||||
// Declare war on the owners of all hit tiles
|
||||
for (hitCiv in hitTiles.mapNotNull { it.getOwner() }.distinct()) {
|
||||
hitCivsTerritory.add(hitCiv)
|
||||
tryDeclareWar(hitCiv)
|
||||
}
|
||||
|
||||
// Declare war on all potentially hit units. They'll try to intercept the nuke before it drops
|
||||
for (civWhoseUnitWasAttacked in hitTiles
|
||||
.flatMap { it.getUnits() }
|
||||
.map { it.civ }.distinct()
|
||||
.filter { it != attackingCiv }) {
|
||||
tryDeclareWar(civWhoseUnitWasAttacked)
|
||||
if (attacker.unit.baseUnit.isAirUnit() && !attacker.isDefeated()) {
|
||||
Battle.tryInterceptAirAttack(attacker, targetTile, civWhoseUnitWasAttacked, null)
|
||||
}
|
||||
}
|
||||
val nukeNotificationAction = sequenceOf( LocationAction(targetTile.position), CivilopediaAction("Units/" + attacker.getName()))
|
||||
// If the nuke has been intercepted and destroyed then it fails to detonate
|
||||
if (attacker.isDefeated()) {
|
||||
// Notify attacker that they are now at war for the attempt
|
||||
for (defendingCiv in notifyDeclaredWarCivs)
|
||||
attackingCiv.addNotification("After an attempted attack by our [${attacker.getName()}], [${defendingCiv}] has declared war on us!", nukeNotificationAction, NotificationCategory.Diplomacy, defendingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
return
|
||||
}
|
||||
|
||||
// Notify attacker that they are now at war
|
||||
for (defendingCiv in notifyDeclaredWarCivs)
|
||||
attackingCiv.addNotification("After being hit by our [${attacker.getName()}], [${defendingCiv}] has declared war on us!", nukeNotificationAction, NotificationCategory.Diplomacy, defendingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
|
||||
attacker.unit.attacksSinceTurnStart.add(Vector2(targetTile.position))
|
||||
|
||||
for (tile in hitTiles) {
|
||||
// Handle complicated effects
|
||||
doNukeExplosionForTile(attacker, tile, nukeStrength, targetTile == tile)
|
||||
}
|
||||
|
||||
// Message all other civs
|
||||
for (otherCiv in attackingCiv.gameInfo.civilizations) {
|
||||
if (!otherCiv.isAlive() || otherCiv == attackingCiv) continue
|
||||
if (hitCivsTerritory.contains(otherCiv))
|
||||
otherCiv.addNotification("A(n) [${attacker.getName()}] from [${attackingCiv.civName}] has exploded in our territory!",
|
||||
nukeNotificationAction, NotificationCategory.War, attackingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
else if (otherCiv.knows(attackingCiv))
|
||||
otherCiv.addNotification("A(n) [${attacker.getName()}] has been detonated by [${attackingCiv.civName}]!",
|
||||
nukeNotificationAction, NotificationCategory.War, attackingCiv.civName, NotificationIcon.War, attacker.getName())
|
||||
else
|
||||
otherCiv.addNotification("A(n) [${attacker.getName()}] has been detonated by an unkown civilization!",
|
||||
nukeNotificationAction, NotificationCategory.War, NotificationIcon.War, attacker.getName())
|
||||
}
|
||||
|
||||
// Instead of postBattleAction() just destroy the unit, all other functions are not relevant
|
||||
if (attacker.unit.hasUnique(UniqueType.SelfDestructs)) attacker.unit.destroy()
|
||||
|
||||
// It's unclear whether using nukes results in a penalty with all civs, or only affected civs.
|
||||
// For now I'll make it give a diplomatic penalty to all known civs, but some testing for this would be appreciated
|
||||
for (civ in attackingCiv.getKnownCivs()) {
|
||||
civ.getDiplomacyManager(attackingCiv).setModifier(DiplomaticModifiers.UsedNuclearWeapons, -50f)
|
||||
}
|
||||
|
||||
if (!attacker.isDefeated()) {
|
||||
attacker.unit.attacksThisTurn += 1
|
||||
}
|
||||
}
|
||||
|
||||
private fun doNukeExplosionForTile(
|
||||
attacker: MapUnitCombatant,
|
||||
tile: Tile,
|
||||
nukeStrength: Int,
|
||||
isGroundZero: Boolean
|
||||
) {
|
||||
// https://forums.civfanatics.com/resources/unit-guide-modern-future-units-g-k.25628/
|
||||
// https://www.carlsguides.com/strategy/civilization5/units/aircraft-nukes.ph
|
||||
// Testing done by Ravignir
|
||||
// original source code: GenerateNuclearExplosionDamage(), ApplyNuclearExplosionDamage()
|
||||
|
||||
var damageModifierFromMissingResource = 1f
|
||||
val civResources = attacker.getCivInfo().getCivResourcesByName()
|
||||
for (resource in attacker.unit.baseUnit.getResourceRequirementsPerTurn().keys) {
|
||||
if (civResources[resource]!! < 0 && !attacker.getCivInfo().isBarbarian())
|
||||
damageModifierFromMissingResource *= 0.5f // I could not find a source for this number, but this felt about right
|
||||
// - Original Civ5 does *not* reduce damage from missing resource, from source inspection
|
||||
}
|
||||
|
||||
var buildingModifier = 1f // Strange, but in Civ5 a bunker mitigates damage to garrison, even if the city is destroyed by the nuke
|
||||
|
||||
// Damage city and reduce its population
|
||||
val city = tile.getCity()
|
||||
if (city != null && tile.position == city.location) {
|
||||
buildingModifier = city.getAggregateModifier(UniqueType.GarrisonDamageFromNukes)
|
||||
doNukeExplosionDamageToCity(city, nukeStrength, damageModifierFromMissingResource)
|
||||
Battle.postBattleNotifications(attacker, CityCombatant(city), city.getCenterTile())
|
||||
Battle.destroyIfDefeated(city.civ, attacker.getCivInfo())
|
||||
}
|
||||
|
||||
// Damage and/or destroy units on the tile
|
||||
for (unit in tile.getUnits().toList()) { // toList so if it's destroyed there's no concurrent modification
|
||||
val damage = (when {
|
||||
isGroundZero || nukeStrength >= 2 -> 100
|
||||
// The following constants are NUKE_UNIT_DAMAGE_BASE / NUKE_UNIT_DAMAGE_RAND_1 / NUKE_UNIT_DAMAGE_RAND_2 in Civ5
|
||||
nukeStrength == 1 -> 30 + Random.Default.nextInt(40) + Random.Default.nextInt(40)
|
||||
// Level 0 does not exist in Civ5 (it treats units same as level 2)
|
||||
else -> 20 + Random.Default.nextInt(30)
|
||||
} * buildingModifier * damageModifierFromMissingResource + 1f.ulp).toInt()
|
||||
val defender = MapUnitCombatant(unit)
|
||||
if (unit.isCivilian()) {
|
||||
if (unit.health - damage <= 40) unit.destroy() // Civ5: NUKE_NON_COMBAT_DEATH_THRESHOLD = 60
|
||||
} else {
|
||||
defender.takeDamage(damage)
|
||||
}
|
||||
Battle.postBattleNotifications(attacker, defender, defender.getTile())
|
||||
Battle.destroyIfDefeated(defender.getCivInfo(), attacker.getCivInfo())
|
||||
}
|
||||
|
||||
// Pillage improvements, pillage roads, add fallout
|
||||
if (tile.isCityCenter()) return // Never touch city centers - if they survived
|
||||
fun applyPillageAndFallout() {
|
||||
if (tile.getUnpillagedImprovement() != null && !tile.getTileImprovement()!!.hasUnique(
|
||||
UniqueType.Irremovable)) {
|
||||
if (tile.getTileImprovement()!!.hasUnique(UniqueType.Unpillagable)) {
|
||||
tile.removeImprovement()
|
||||
} else {
|
||||
tile.setPillaged()
|
||||
}
|
||||
}
|
||||
if (tile.getUnpillagedRoad() != RoadStatus.None)
|
||||
tile.setPillaged()
|
||||
if (tile.isWater || tile.isImpassible() || tile.terrainFeatures.contains("Fallout")) return
|
||||
tile.addTerrainFeature("Fallout")
|
||||
}
|
||||
|
||||
if (tile.terrainHasUnique(UniqueType.DestroyableByNukesChance)) {
|
||||
// Note: Safe from concurrent modification exceptions only because removeTerrainFeature
|
||||
// *replaces* terrainFeatureObjects and the loop will continue on the old one
|
||||
for (terrainFeature in tile.terrainFeatureObjects) {
|
||||
for (unique in terrainFeature.getMatchingUniques(UniqueType.DestroyableByNukesChance)) {
|
||||
val chance = unique.params[0].toFloat() / 100f
|
||||
if (!(chance > 0f && isGroundZero) && Random.Default.nextFloat() >= chance) continue
|
||||
tile.removeTerrainFeature(terrainFeature.name)
|
||||
applyPillageAndFallout()
|
||||
}
|
||||
}
|
||||
} else if (isGroundZero || Random.Default.nextFloat() < 0.5f) { // Civ5: NUKE_FALLOUT_PROB
|
||||
applyPillageAndFallout()
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the "protection" modifier from buildings (Bomb Shelter, UniqueType.PopulationLossFromNukes) */
|
||||
private fun doNukeExplosionDamageToCity(targetedCity: City, nukeStrength: Int, damageModifierFromMissingResource: Float) {
|
||||
// Original Capitals must be protected, `canBeDestroyed` is responsible for that check.
|
||||
// The `justCaptured = true` parameter is what allows other Capitals to suffer normally.
|
||||
if ((nukeStrength > 2 || nukeStrength > 1 && targetedCity.population.population < 5)
|
||||
&& targetedCity.canBeDestroyed(true)) {
|
||||
targetedCity.destroyCity()
|
||||
return
|
||||
}
|
||||
|
||||
val cityCombatant = CityCombatant(targetedCity)
|
||||
cityCombatant.takeDamage((cityCombatant.getHealth() * 0.5f * damageModifierFromMissingResource).toInt())
|
||||
|
||||
// Difference to original: Civ5 rounds population loss down twice - before and after bomb shelters
|
||||
val populationLoss = (
|
||||
targetedCity.population.population *
|
||||
targetedCity.getAggregateModifier(UniqueType.PopulationLossFromNukes) *
|
||||
when (nukeStrength) {
|
||||
0 -> 0f
|
||||
1 -> (30 + Random.Default.nextInt(20) + Random.Default.nextInt(20)) / 100f
|
||||
2 -> (60 + Random.Default.nextInt(10) + Random.Default.nextInt(10)) / 100f
|
||||
else -> 1f // hypothetical nukeStrength 3 -> always to 1 pop
|
||||
}
|
||||
).toInt().coerceAtMost(targetedCity.population.population - 1)
|
||||
targetedCity.population.addPopulation(-populationLoss)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun City.getAggregateModifier(uniqueType: UniqueType): Float {
|
||||
var modifier = 1f
|
||||
for (unique in getMatchingUniques(uniqueType)) {
|
||||
if (!matchesFilter(unique.params[1])) continue
|
||||
modifier *= unique.params[0].toPercent()
|
||||
}
|
||||
return modifier
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import com.unciv.logic.battle.BattleDamage
|
||||
import com.unciv.logic.battle.CityCombatant
|
||||
import com.unciv.logic.battle.ICombatant
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
import com.unciv.logic.battle.Nuke
|
||||
import com.unciv.logic.battle.TargetHelper
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.UncivSound
|
||||
@ -21,9 +22,9 @@ import com.unciv.ui.components.UnitGroup
|
||||
import com.unciv.ui.components.extensions.addBorderAllowOpacity
|
||||
import com.unciv.ui.components.extensions.addSeparator
|
||||
import com.unciv.ui.components.extensions.disable
|
||||
import com.unciv.ui.components.input.onClick
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.extensions.toTextButton
|
||||
import com.unciv.ui.components.input.onClick
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
@ -303,7 +304,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
attackerNameWrapper.add(attackerLabel)
|
||||
add(attackerNameWrapper)
|
||||
|
||||
val canNuke = Battle.mayUseNuke(attacker, targetTile)
|
||||
val canNuke = Nuke.mayUseNuke(attacker, targetTile)
|
||||
|
||||
val blastRadius = attacker.unit.getNukeBlastRadius()
|
||||
|
||||
@ -330,7 +331,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
|
||||
}
|
||||
else {
|
||||
attackButton.onClick(attacker.getAttackSound()) {
|
||||
Battle.NUKE(attacker, targetTile)
|
||||
Nuke.NUKE(attacker, targetTile)
|
||||
worldScreen.mapHolder.removeUnitActionOverlay() // the overlay was one of attacking
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user