Added Advanced Ballistics Tech, Atomic Bomb Unit, Updated how nukes work (#4211)

* Improved nukes

* Fixed build error (probably)

* Implemented reocmmended changes, fixed some other stuff, the usual

* Implemented requested changes

* Fixed Tech connections

* Fixed nuclear missiles and guided missiles being interceptable

* Missiles are no longer air units. This is, as they cannot be stationed on carriers and cannot be intercepted.
This commit is contained in:
Xander Lenstra
2021-06-24 15:03:59 +02:00
committed by GitHub
parent 1c42e1e730
commit 111d11b9b2
29 changed files with 971 additions and 785 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -32,618 +32,625 @@ Artillery
orig: 100, 100
offset: 0, 0
index: -1
B17
Atomic Bomb
rotate: false
xy: 112, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Ballista
B17
rotate: false
xy: 220, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Battleship
Ballista
rotate: false
xy: 4, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Berserker
Battleship
rotate: false
xy: 112, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Bomber
Berserker
rotate: false
xy: 220, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Bowman
Bomber
rotate: false
xy: 328, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Brute
Bowman
rotate: false
xy: 4, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Camel Archer
Brute
rotate: false
xy: 112, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Cannon
Camel Archer
rotate: false
xy: 220, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Caravel
Cannon
rotate: false
xy: 328, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Carrier
Caravel
rotate: false
xy: 436, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Catapult
Carrier
rotate: false
xy: 4, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Cavalry
Catapult
rotate: false
xy: 112, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Chariot Archer
Cavalry
rotate: false
xy: 220, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Chu-Ko-Nu
Chariot Archer
rotate: false
xy: 328, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Companion Cavalry
Chu-Ko-Nu
rotate: false
xy: 436, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Composite Bowman
Companion Cavalry
rotate: false
xy: 544, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Conquistador
Composite Bowman
rotate: false
xy: 4, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Cossack
Conquistador
rotate: false
xy: 112, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Crossbowman
Cossack
rotate: false
xy: 220, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Destroyer
Crossbowman
rotate: false
xy: 328, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Fighter
Destroyer
rotate: false
xy: 436, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Foreign Legion
Fighter
rotate: false
xy: 544, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Frigate
Foreign Legion
rotate: false
xy: 652, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Galleass
Frigate
rotate: false
xy: 4, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Gatling Gun
Galleass
rotate: false
xy: 112, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Giant Death Robot
Gatling Gun
rotate: false
xy: 220, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Great Artist
Giant Death Robot
rotate: false
xy: 328, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Great Engineer
Great Artist
rotate: false
xy: 436, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Great Engineer
rotate: false
xy: 544, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Great General
rotate: false
xy: 544, 658
xy: 652, 766
size: 100, 94
orig: 100, 94
offset: 0, 0
index: -1
Great Merchant
rotate: false
xy: 652, 760
xy: 760, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Great Scientist
rotate: false
xy: 760, 868
xy: 4, 4
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Great War Bomber
rotate: false
xy: 4, 4
xy: 112, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Great War Infantry
rotate: false
xy: 112, 112
xy: 220, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Guided Missile
rotate: false
xy: 220, 220
xy: 328, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Helicopter Gunship
rotate: false
xy: 328, 328
xy: 436, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Hoplite
rotate: false
xy: 436, 436
xy: 544, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Horseman
rotate: false
xy: 544, 550
xy: 652, 658
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Hwach'a
rotate: false
xy: 652, 652
xy: 760, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Infantry
rotate: false
xy: 760, 760
xy: 868, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Ironclad
rotate: false
xy: 868, 868
xy: 112, 4
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Jaguar
rotate: false
xy: 112, 4
xy: 220, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Janissary
rotate: false
xy: 220, 112
xy: 328, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Jet Fighter
rotate: false
xy: 328, 220
xy: 436, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Keshik
rotate: false
xy: 436, 328
xy: 544, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Khan
rotate: false
xy: 544, 442
xy: 652, 550
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Knight
rotate: false
xy: 652, 544
xy: 760, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Lancer
rotate: false
xy: 760, 652
xy: 868, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Landship
rotate: false
xy: 868, 760
xy: 976, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Landsknecht
rotate: false
xy: 976, 868
xy: 220, 4
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Legion
rotate: false
xy: 220, 4
xy: 328, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Longbowman
rotate: false
xy: 328, 112
xy: 436, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Longswordsman
rotate: false
xy: 436, 220
xy: 544, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Machine Gun
rotate: false
xy: 544, 334
xy: 652, 442
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mandekalu Cavalry
rotate: false
xy: 652, 436
xy: 760, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Maori Warrior
rotate: false
xy: 760, 544
xy: 868, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Marine
rotate: false
xy: 868, 652
xy: 976, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mechanized Infantry
rotate: false
xy: 976, 760
xy: 1084, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Minuteman
rotate: false
xy: 1084, 868
xy: 328, 4
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mobile SAM
rotate: false
xy: 328, 4
xy: 436, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Modern Armor
rotate: false
xy: 436, 112
xy: 544, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Mohawk Warrior
rotate: false
xy: 544, 226
xy: 652, 334
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Musketeer
rotate: false
xy: 652, 328
xy: 760, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Musketman
rotate: false
xy: 760, 437
xy: 868, 545
size: 100, 99
orig: 100, 99
offset: 0, 0
index: -1
Naresuan's Elephant
rotate: false
xy: 868, 544
xy: 976, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Norwegian Ski Infantry
rotate: false
xy: 976, 652
xy: 1084, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Nuclear Missile
rotate: false
xy: 1084, 760
xy: 1192, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Panzer
rotate: false
xy: 1192, 868
xy: 436, 4
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Paratrooper
rotate: false
xy: 436, 4
xy: 544, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Persian Immortal
rotate: false
xy: 544, 118
xy: 652, 226
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Pikeman
rotate: false
xy: 652, 220
xy: 760, 328
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Rifleman
rotate: false
xy: 760, 329
xy: 868, 437
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Rocket Artillery
rotate: false
xy: 868, 436
xy: 976, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Samurai
rotate: false
xy: 976, 544
xy: 1084, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Scout
rotate: false
xy: 1084, 652
xy: 1192, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Settler
rotate: false
xy: 1192, 760
xy: 1300, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Ship of the Line
rotate: false
xy: 1300, 868
xy: 544, 4
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Sipahi
rotate: false
xy: 544, 10
xy: 652, 118
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Slinger
rotate: false
xy: 652, 112
xy: 760, 220
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Spearman
rotate: false
xy: 760, 221
xy: 868, 329
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Stealth Bomber
rotate: false
xy: 868, 328
xy: 976, 436
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Submarine
rotate: false
xy: 976, 436
xy: 1084, 544
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Swordsman
rotate: false
xy: 1084, 544
xy: 1192, 652
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Tank
rotate: false
xy: 1192, 652
xy: 1300, 760
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Tercio
rotate: false
xy: 1300, 760
xy: 1408, 868
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Trebuchet
rotate: false
xy: 1408, 868
xy: 652, 10
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Triplane
rotate: false
xy: 760, 113
xy: 760, 112
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Trireme
rotate: false
xy: 868, 219
xy: 868, 220
size: 100, 101
orig: 100, 101
offset: 0, 0
@ -692,7 +699,7 @@ Worker
index: -1
Zero
rotate: false
xy: 976, 220
xy: 868, 112
size: 100, 100
orig: 100, 100
offset: 0, 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

After

Width:  |  Height:  |  Size: 316 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1000 KiB

After

Width:  |  Height:  |  Size: 1005 KiB

View File

@ -1033,7 +1033,7 @@
{
"name": "SS Booster",
"requiredResource": "Aluminum",
"requiredTech": "Robotics",
"requiredTech": "Advanced Ballistics",
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
},
{

View File

@ -560,9 +560,15 @@
"quote": "'All men can see these tactics whereby I conquer, but what none can see is the strategy out of which victory is evolved.' - Sun Tzu"
},
{
"name": "Satellites",
"name": "Advanced Ballistics",
"row": 5,
"prerequisites": ["Nuclear Fission","Rocketry"],
"quote": "Our scientific power has outrun our spiritual power, we have guided missiles and misguided men. Martin Luther King Jr. "
},
{
"name": "Satellites",
"row": 6,
"prerequisites": ["Rocketry"],
"uniques": ["Reveals the entire map"],
"quote": "'Now, somehow, in some new way, the sky seemed almost alien.' - Lyndon B. Johnson"
},
@ -591,13 +597,13 @@
{
"name": "Particle Physics",
"row": 4,
"prerequisites": ["Mobile Tactics","Satellites"],
"prerequisites": ["Mobile Tactics", "Advanced Ballistics"],
"quote": "'Every particle of matter is attracted by or gravitates to every other particle of matter with a force inversely proportional to the squares of their distances.' - Isaac Newton"
},
{
"name": "Nuclear Fusion",
"row": 6,
"prerequisites": [/*"Advanced Ballistics" ,*/ "Satellites", "Robotics"],
"prerequisites": ["Advanced Ballistics", "Satellites", "Robotics"],
"quote": "'The release of atomic energy has not created a new problem. It has readily made more urgent the necessity of solving an existing one.' - Albert Einstein"
},
{

View File

@ -95,8 +95,8 @@
"unbuildable": true,
"defenceBonus": 0.25,
"occursOn": ["Tundra","Plains","Grassland","Hill"],
"uniques": ["Rough terrain", "Provides a one-time Production bonus to the closest city when cut down",
"Blocks line-of-sight from tiles at same elevation"]
"uniques": ["Provides a one-time Production bonus to the closest city when cut down", "Rough terrain",
"Blocks line-of-sight from tiles at same elevation", "Resistant to nukes", "Can be destroyed by nukes"]
},
{
"name": "Jungle",
@ -107,7 +107,7 @@
"unbuildable": true,
"defenceBonus": 0.25,
"occursOn": ["Plains","Grassland"],
"uniques": ["Rough terrain", "Blocks line-of-sight from tiles at same elevation"]
"uniques": ["Rough terrain", "Blocks line-of-sight from tiles at same elevation", "Resistant to nukes", "Can be destroyed by nukes"]
},
{
"name": "Marsh",

View File

@ -129,9 +129,9 @@
},
{
"name": "Remove Fallout",
"turnsToBuild": 8,
"turnsToBuild": 2,
"terrainsCanBeBuiltOn": ["Fallout"],
"techRequired": "Atomic Theory",
// Has no tile improvements as it can always be built
"uniques": ["Can be built outside your borders"],
"shortcutKey": "X"
},

View File

@ -1195,7 +1195,6 @@
"upgradesTo": "Helicopter Gunship",
"uniques": ["+[100]% Strength vs [Armor]"]
},
/*
{
"name": "Atomic Bomb",
"unitType": "AtomicBomber",
@ -1206,11 +1205,13 @@
"cost": 600,
"requiredTech": "Nuclear Fission",
"requiredResource": "Uranium",
"uniques": ["Nuclear weapon", "Requires [Manhattan Project]", "Self-destructs when attacking"]
// Plane rather than a missile - can be based in city or Carrier only. But cannot be intercepted because of a civ 5 bug that was never fixed.
// Single-use like missile
// No strength/rangedStrength tags in civ 5 xmls. Instead has NukeDamageLevel = 1 tag (Nuclear Missile has NukeDamageLevel = 2)
}, */
"promotions" : ["Evasion"],
"uniques": ["Nuclear weapon of strength [1]", "Requires [Manhattan Project]", "Self-destructs when attacking",
"Blast radius [2]"],
"attackSound": "nuke"
// Plane rather than a missile - can be based in city or Carrier only.
// Can be intercepted, be must die for it not to take effect.
},
{
"name": "Rocket Artillery",
"unitType": "Siege",
@ -1243,7 +1244,7 @@
"rangedStrength": 60,
"range": 8,
"cost": 150,
"requiredTech": "Rocketry",
"requiredTech": "Advanced Ballistics",
"uniques": ["Self-destructs when attacking"]
},
{
@ -1254,9 +1255,9 @@
"rangedStrength": 300,
"range": 12,
"cost": 1000,
"requiredTech": "Rocketry",
"requiredResource": "Uranium",
"uniques": ["Self-destructs when attacking", "Nuclear weapon", "Requires [Manhattan Project]"],
"requiredTech": "Advanced Ballistics",
"uniques": ["Self-destructs when attacking", "Nuclear weapon of strength [2]", "Requires [Manhattan Project]",
"Blast radius [2]", "Consumes [2] [Uranium]"],
"attackSound": "nuke"
},
{
@ -1423,7 +1424,7 @@
"unitType": "Civilian",
"movement": 2,
"cost": 500,
"requiredTech": "Robotics",
"requiredTech": "Advanced Ballistics",
"requiredResource": "Aluminum",
"uniques": ["Spaceship part", "Cannot be purchased", "Requires [Apollo Program]"]
// costs 750 in G&K, 1500 in BNW

View File

@ -435,6 +435,7 @@ An enemy [unit] was spotted near our territory =
An enemy [unit] was spotted in our territory =
[amount] enemy units were spotted near our territory =
[amount] enemy units were spotted in our territory =
A(n) [nukeType] exploded in our territory! =
The civilization of [civName] has been destroyed! =
The City-State of [name] has been destroyed! =
We have captured a barbarian encampment and recovered [goldAmount] gold! =

View File

@ -77,7 +77,7 @@ object Automation {
|| (it.militaryUnit != null && it.militaryUnit!!.civInfo != city.civInfo)
}) // there is absolutely no reason for you to make water units on this body of water.
militaryUnits =
militaryUnits.filter { it.unitType.isLandUnit() || it.unitType.isAirUnit() }
militaryUnits.filter { !it.unitType.isWaterUnit() }
val chosenUnit: BaseUnit
if (!city.civInfo.isAtWar() && city.civInfo.cities.any { it.getCenterTile().militaryUnit == null }

View File

@ -49,7 +49,7 @@ object BattleHelper {
// Silly floats, basically
val unitMustBeSetUp = unit.hasUnique("Must set up to ranged attack")
val tilesToAttackFrom = if (unit.type.isAirUnit()) sequenceOf(unit.currentTile)
val tilesToAttackFrom = if (unit.baseUnit.movesLikeAirUnits()) sequenceOf(unit.currentTile)
else
unitDistanceToTiles.asSequence()
.filter {
@ -63,7 +63,7 @@ object BattleHelper {
for (reachableTile in tilesToAttackFrom) { // tiles we'll still have energy after we reach there
val tilesInAttackRange =
if (unit.hasUnique("Ranged attacks may be performed over obstacles") || unit.type.isAirUnit())
if (unit.hasUnique("Ranged attacks may be performed over obstacles") || unit.baseUnit.movesLikeAirUnits())
reachableTile.getTilesInDistance(rangeOfAttack)
else reachableTile.getViewableTilesList(rangeOfAttack)
.asSequence()

View File

@ -312,11 +312,11 @@ object SpecificUnitAutomation {
// This really needs to be changed, to have better targetting for missiles
fun automateMissile(unit: MapUnit) {
val tilesInRange = unit.currentTile.getTilesInDistance(unit.getRange())
if (unit.hasUnique("Nuclear weapon")) {
if (unit.baseUnit.isNuclearWeapon()) {
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.civInfo)) {
Battle.nuke(MapUnitCombatant(unit), tile)
Battle.NUKE(MapUnitCombatant(unit), tile)
return
}
}

View File

@ -5,12 +5,15 @@ import com.unciv.UncivGame
import com.unciv.logic.city.CityInfo
import com.unciv.logic.civilization.*
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.RoadStatus
import com.unciv.logic.map.TileInfo
import com.unciv.models.AttackableTile
import com.unciv.models.ruleset.Unique
import com.unciv.models.ruleset.unit.UnitType
import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr
import java.util.*
import kotlin.math.max
@ -28,8 +31,9 @@ object Battle {
}
}
if (attacker is MapUnitCombatant && attacker.unit.hasUnique("Nuclear weapon")) {
return nuke(attacker, attackableTile.tileToAttack)
if (attacker is MapUnitCombatant && attacker.unit.baseUnit.isNuclearWeapon()
) {
return NUKE(attacker, attackableTile.tileToAttack)
}
attack(attacker, getMapCombatantOfTile(attackableTile.tileToAttack)!!)
}
@ -250,7 +254,7 @@ object Battle {
// if it was a melee attack and we won, then the unit ALREADY got movement points deducted,
// for the movement to the enemy's tile!
// and if it's an air unit, it only has 1 movement anyway, so...
if (!attacker.getUnitType().isAirUnit() && !(attacker.getUnitType().isMelee() && defender.isDefeated()))
if (!attacker.unit.baseUnit.movesLikeAirUnits() && !(attacker.getUnitType().isMelee() && defender.isDefeated()))
unit.useMovementPoints(1f)
} else unit.currentMovement = 0f
unit.attacksThisTurn += 1
@ -379,63 +383,201 @@ object Battle {
}
}
const val NUKE_RADIUS = 2
fun nuke(attacker: ICombatant, targetTile: TileInfo) {
fun NUKE(attacker: MapUnitCombatant, targetTile: TileInfo) {
val attackingCiv = attacker.getCivInfo()
for (tile in targetTile.getTilesInDistance(NUKE_RADIUS)) {
val city = tile.getCity()
if (city != null && city.location == tile.position) {
city.health = 1
if (city.population.population <= 5 && !city.isOriginalCapital) {
city.destroyCity()
} else {
city.population.population = max(city.population.population - 5, 1)
city.population.unassignExtraPopulation()
continue
}
destroyIfDefeated(city.civInfo, attackingCiv)
fun tryDeclareWar(civSuffered: CivilizationInfo) {
if (civSuffered != attackingCiv
&& civSuffered.knows(attackingCiv)
&& civSuffered.getDiplomacyManager(attackingCiv).diplomaticStatus != DiplomaticStatus.War
) {
attackingCiv.getDiplomacyManager(civSuffered).declareWar()
}
fun declareWar(civSuffered: CivilizationInfo) {
if (civSuffered != attackingCiv
&& civSuffered.knows(attackingCiv)
&& civSuffered.getDiplomacyManager(attackingCiv).canDeclareWar()) {
attackingCiv.getDiplomacyManager(civSuffered).declareWar()
}
}
for (unit in tile.getUnits()) {
unit.destroy()
postBattleNotifications(attacker, MapUnitCombatant(unit), unit.currentTile)
declareWar(unit.civInfo)
destroyIfDefeated(unit.civInfo, attackingCiv)
}
// this tile belongs to some civilization who is not happy of nuking it
if (city != null)
declareWar(city.civInfo)
tile.improvement = null
tile.improvementInProgress = null
tile.turnsToImprovement = 0
tile.roadStatus = RoadStatus.None
if (tile.isLand && !tile.isImpassible() && !tile.terrainFeatures.contains("Fallout"))
tile.terrainFeatures.add("Fallout")
}
for (civ in attacker.getCivInfo().getKnownCivs()) {
civ.getDiplomacyManager(attackingCiv)
.setModifier(DiplomaticModifiers.UsedNuclearWeapons, -50f)
val blastRadius =
if (!attacker.unit.hasUnique("Blast radius []")) 2
else attacker.unit.getMatchingUniques("Blast radius []").first().params[0].toInt()
val strength = when {
(attacker.unit.hasUnique("Nuclear weapon of strength []")) ->
attacker.unit.getMatchingUniques("Nuclear weapon of strength []").first().params[0].toInt()
// Deprecated since 3.15.3
(attacker.unit.hasUnique("Nuclear weapon")) -> 1
//
else -> return
}
// Instead of postBattleAction() just destroy the missile, all other functions are not relevant
if ((attacker as MapUnitCombatant).unit.hasUnique("Self-destructs when attacking")) {
// Calculate the tiles that are hit
val hitTiles = targetTile.getTilesInDistance(blastRadius)
// Declare war on the owners of all hit tiles
for (hitCiv in hitTiles.map { it.getOwner() }.distinct()) {
hitCiv!!.addNotification("A(n) [${attacker.getName()}] exploded in our territory!".tr(), targetTile.position, NotificationIcon.War)
tryDeclareWar(hitCiv)
}
// Declare war on all potentially hit units. They'll try to intercept the nuke before it drops
for (hitUnit in hitTiles.map { it.getUnits() }.flatten()) {
tryDeclareWar(hitUnit.civInfo)
if (attacker.getUnitType().isAirUnit() && !attacker.isDefeated()) {
tryInterceptAirAttack(attacker, MapUnitCombatant(hitUnit))
}
}
if (attacker.isDefeated()) return
// Destroy units on the target tile
for (defender in targetTile.getUnits().filter { it != attacker.unit }) {
defender.destroy()
postBattleNotifications(attacker, MapUnitCombatant(defender), defender.getTile())
destroyIfDefeated(defender.civInfo, attacker.getCivInfo())
}
for (tile in hitTiles) {
// Handle complicated effects
when (strength) {
1 -> nukeStrength1Effect(attacker, tile)
2 -> nukeStrength2Effect(attacker, tile)
else -> nukeStrength1Effect(attacker, tile)
}
}
// Instead of postBattleAction() just destroy the unit, all other functions are not relevant
if (attacker.unit.hasUnique("Self-destructs when attacking")) {
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)
}
}
private fun nukeStrength1Effect(attacker: MapUnitCombatant, tile: TileInfo) {
// https://forums.civfanatics.com/resources/unit-guide-modern-future-units-g-k.25628/
// https://www.carlsguides.com/strategy/civilization5/units/aircraft-nukes.php
// Testing done by Ravignir
var damageModifierFromMissingResource = 1f
val civResources = attacker.getCivInfo().getCivResourcesByName()
for (resource in attacker.unit.baseUnit.getResourceRequirements().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
}
// Decrease health & population of a hit city
val city = tile.getCity()
if (city != null && tile.position == city.location) {
var populationLoss = city.population.population * (0.3 + Random().nextFloat() * 0.4)
var populationLossReduced = false
for (unique in city.civInfo.getMatchingUniques("Population loss from nuclear attacks -[]%")) {
populationLoss *= 1 - unique.params[0].toFloat() / 100f
populationLossReduced = true
}
if (city.population.population < 5 && !populationLossReduced) {
city.population.population = 1 // For cities that cannot be destroyed, such as original capitals
city.destroyCity()
} else {
city.population.population -= populationLoss.toInt()
if (city.population.population < 1) city.population.population = 1
city.population.unassignExtraPopulation()
city.health -= ((0.5 + 0.25 * Random().nextFloat()) * city.health * damageModifierFromMissingResource).toInt()
if (city.health < 1) city.health = 1
}
postBattleNotifications(attacker, CityCombatant(city), city.getCenterTile())
}
// Damage and/or destroy units on the tile
for (unit in tile.getUnits()) {
val defender = MapUnitCombatant(unit)
if (defender.unit.baseUnit.unitType.isCivilian()) {
unit.destroy() // destroy the unit
} else {
defender.takeDamage(((40 + Random().nextInt(60)) * damageModifierFromMissingResource).toInt())
}
postBattleNotifications(attacker, defender, defender.getTile())
destroyIfDefeated(defender.getCivInfo(), attacker.getCivInfo())
}
// Remove improvements, add fallout
tile.improvement = null
tile.improvementInProgress = null
tile.turnsToImprovement = 0
tile.roadStatus = RoadStatus.None
if (tile.isLand && !tile.isImpassible() && !tile.terrainFeatures.contains("Fallout")) {
if (tile.terrainFeatures.any { attacker.getCivInfo().gameInfo.ruleSet.terrains[it]!!.uniques.contains("Resistant to nukes") }) {
if (Random().nextFloat() < 0.25f) {
tile.terrainFeatures.removeAll { attacker.getCivInfo().gameInfo.ruleSet.terrains[it]!!.uniques.contains("Can be destroyed by nukes") }
tile.terrainFeatures.add("Fallout")
}
} else if (Random().nextFloat() < 0.5f) {
tile.terrainFeatures.removeAll { attacker.getCivInfo().gameInfo.ruleSet.terrains[it]!!.uniques.contains("Can be destroyed by nukes") }
tile.terrainFeatures.add("Fallout")
}
}
}
private fun nukeStrength2Effect(attacker: MapUnitCombatant, tile: TileInfo) {
// https://forums.civfanatics.com/threads/unit-guide-modern-future-units-g-k.429987/#2
// https://www.carlsguides.com/strategy/civilization5/units/aircraft-nukes.php
// Testing done by Ravignir
var damageModifierFromMissingResource = 1f
val civResources = attacker.getCivInfo().getCivResourcesByName()
for (resource in attacker.unit.baseUnit.getResourceRequirements().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
}
// Damage and/or destroy cities
val city = tile.getCity()
if (city != null && city.location == tile.position) {
if (city.population.population < 5) {
city.population.population = 1 // For cities that cannot be destroyed, such as original capitals
city.destroyCity()
} else {
var populationLoss = city.population.population * (0.6 + Random().nextFloat() * 0.2);
var populationLossReduced = false
for (unique in city.civInfo.getMatchingUniques("Population loss from nuclear attacks -[]%")) {
populationLoss *= 1 - unique.params[0].toFloat() / 100f
populationLossReduced = true
}
city.population.population -= populationLoss.toInt()
if (city.population.population < 5 && populationLossReduced) city.population.population = 5
if (city.population.population < 1) city.population.population = 1
city.population.unassignExtraPopulation()
city.health -= (0.5 * city.getMaxHealth() * damageModifierFromMissingResource).toInt()
if (city.health < 1) city.health = 1
}
postBattleNotifications(attacker, CityCombatant(city), city.getCenterTile())
destroyIfDefeated(city.civInfo, attacker.getCivInfo())
}
// Destroy all hit units
for (defender in tile.getUnits()) {
defender.destroy()
postBattleNotifications(attacker, MapUnitCombatant(defender), defender.currentTile)
destroyIfDefeated(defender.civInfo, attacker.getCivInfo())
}
// Remove improvements
tile.improvement = null
tile.improvementInProgress = null
tile.turnsToImprovement = 0
tile.roadStatus = RoadStatus.None
if (tile.isLand && !tile.isImpassible() && !tile.terrainFeatures.contains("Fallout")) {
if (tile.terrainFeatures.any { attacker.getCivInfo().gameInfo.ruleSet.terrains[it]!!.uniques.contains("Resistant to nukes") }) {
if (Random().nextFloat() < 0.25f) {
tile.terrainFeatures.removeAll { attacker.getCivInfo().gameInfo.ruleSet.terrains[it]!!.uniques.contains("Can be destroyed by nukes") }
tile.terrainFeatures.add("Fallout")
}
} else if (Random().nextFloat() < 0.5f) {
tile.terrainFeatures.removeAll { attacker.getCivInfo().gameInfo.ruleSet.terrains[it]!!.uniques.contains("Can be destroyed by nukes") }
tile.terrainFeatures.add("Fallout")
}
}
}
private fun tryInterceptAirAttack(attacker: MapUnitCombatant, defender: ICombatant) {
if (attacker.unit.hasUnique("Can not be intercepted")) return
val attackedTile = defender.getTile()
for (interceptor in defender.getCivInfo().getCivUnits().filter { it.canIntercept(attackedTile) }) {
if (Random().nextFloat() > 100f / interceptor.interceptChance()) continue

View File

@ -87,7 +87,7 @@ object BattleDamage {
if (civInfo.hasUnique("+15% combat strength for melee units which have another military unit in an adjacent tile")
&& combatant.isMelee()
&& combatant.getTile().neighbors.flatMap { it.getUnits() }
.any { it.civInfo == civInfo && !it.type.isCivilian() && !it.type.isAirUnit() }
.any { it.civInfo == civInfo && !it.type.isCivilian() && !it.type.isAirUnit() && !it.type.isMissile() }
)
modifiers["Discipline"] = 15
//
@ -272,6 +272,7 @@ object BattleDamage {
|| combatant.getCivInfo()
.hasUnique("Units fight as though they were at full strength even when damaged")
&& !combatant.getUnitType().isAirUnit()
&& !combatant.getUnitType().isMissile()
)
1f
else 1 - (100 - combatant.getHealth()) / 300f// Each 3 points of health reduces damage dealt by 1% like original game

View File

@ -381,6 +381,9 @@ class CityInfo {
}
fun destroyCity() {
// Original capitals can't be destroyed
if (isOriginalCapital) return
for (airUnit in getCenterTile().airUnits.toList()) airUnit.destroy() //Destroy planes stationed in city
// The relinquish ownership MUST come before removing the city,
@ -460,7 +463,7 @@ class CityInfo {
val tile = getCenterTile()
if (construction.unitType.isCivilian())
return tile.civilianUnit == null
if (construction.unitType.isAirUnit())
if (construction.unitType.isAirUnit() || construction.unitType.isMissile())
return tile.airUnits.filter { !it.isTransported }.size < 6
else return tile.militaryUnit == null
}

View File

@ -347,6 +347,7 @@ class MapUnit {
if (type.isWaterUnit()) return false
if (type.isCivilian()) return false
if (type.isAirUnit()) return false
if (type.isMissile()) return false
if (isEmbarked()) return false
if (hasUnique("No defensive terrain bonus")) return false
if (isFortified()) return false
@ -627,13 +628,13 @@ class MapUnit {
fun putInTile(tile: TileInfo) {
when {
!movement.canMoveTo(tile) -> throw Exception("I can't go there!")
type.isAirUnit() -> tile.airUnits.add(this)
type.isAirUnit() || type.isMissile() -> tile.airUnits.add(this)
type.isCivilian() -> tile.civilianUnit = this
else -> tile.militaryUnit = this
}
// this check is here in order to not load the fresh built unit into carrier right after the build
isTransported = !tile.isCityCenter() &&
type.isAirUnit() // not moving civilians
(type.isAirUnit() || type.isMissile()) // not moving civilians
moveThroughTile(tile)
}

View File

@ -180,7 +180,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
if (currentTile == finalDestination) return currentTile
// If we can fly, head there directly
if (unit.type.isAirUnit() || unit.action == Constants.unitActionParadrop) return finalDestination
if (unit.type.isAirUnit() || unit.type.isMissile() || unit.action == Constants.unitActionParadrop) return finalDestination
val distanceToTiles = getDistanceToTiles()
@ -217,13 +217,13 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
/** This is performance-heavy - use as last resort, only after checking everything else! */
fun canReach(destination: TileInfo): Boolean {
if (unit.type.isAirUnit() || unit.action == Constants.unitActionParadrop)
if (unit.type.isAirUnit() || unit.type.isMissile() || unit.action == Constants.unitActionParadrop)
return canReachInCurrentTurn(destination)
return getShortestPath(destination).any()
}
fun canReachInCurrentTurn(destination: TileInfo): Boolean {
if (unit.type.isAirUnit())
if (unit.type.isAirUnit() || unit.type.isMissile())
return unit.currentTile.aerialDistanceTo(destination) <= unit.getRange()*2
if (unit.action == Constants.unitActionParadrop)
return getDistance(unit.currentTile.position, destination.position) <= unit.paradropRange && canParadropOn(destination)
@ -232,7 +232,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun getReachableTilesInCurrentTurn(): Sequence<TileInfo> {
return when {
unit.type.isAirUnit() ->
unit.type.isAirUnit() || unit.type.isMissile() ->
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2))
unit.action == Constants.unitActionParadrop ->
unit.getTile().getTilesInDistance(unit.paradropRange)
@ -259,7 +259,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
*/
private fun canUnitSwapToReachableTile(reachableTile: TileInfo): Boolean {
// Air units cannot swap
if (unit.type.isAirUnit()) return false
if (unit.type.isAirUnit() || unit.type.isMissile()) return false
// We can't swap with ourself
if (reachableTile == unit.getTile()) return false
// Check whether the tile contains a unit of the same type as us that we own and that can
@ -322,7 +322,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun moveToTile(destination: TileInfo) {
if (destination == unit.getTile()) return // already here!
if (unit.type.isAirUnit()) { // air units move differently from all other units
if (unit.type.isAirUnit() || unit.type.isMissile()) { // air units move differently from all other units
unit.action = null
unit.removeFromTile()
unit.isTransported = false // it has left the carrier by own means
@ -421,7 +421,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
* DOES NOT designate whether we can reach that tile in the current turn
*/
fun canMoveTo(tile: TileInfo): Boolean {
if (unit.type.isAirUnit())
if (unit.type.isAirUnit() || unit.type.isMissile())
return canAirUnitMoveTo(tile, unit)
if (!canPassThrough(tile))

View File

@ -9,6 +9,7 @@ import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.Unique
import com.unciv.models.stats.INamed
import com.unciv.models.translations.Translations
import com.unciv.models.translations.getPlaceholderText
import com.unciv.models.translations.tr
import com.unciv.ui.utils.Fonts
import kotlin.math.pow
@ -160,8 +161,8 @@ class BaseUnit : INamed, IConstruction {
if (uniqueTo != null && uniqueTo != civInfo.civName) return "Unique to $uniqueTo"
if (civInfo.gameInfo.ruleSet.units.values.any { it.uniqueTo == civInfo.civName && it.replaces == name })
return "Our unique unit replaces this"
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled
&& uniques.contains("Nuclear weapon")) return "Disabled by setting"
if (!civInfo.gameInfo.gameParameters.nuclearWeaponsEnabled && isNuclearWeapon()
) return "Disabled by setting"
for (unique in uniqueObjects.filter { it.placeholderText == "Unlocked with []" })
if (civInfo.tech.researchedTechnologies.none { it.era() == unique.params[0] || it.name == unique.params[0] }
@ -220,7 +221,7 @@ class BaseUnit : INamed, IConstruction {
val promotion = unique.params[1]
if (unit.matchesFilter(filter) || (filter == "relevant" && civInfo.gameInfo.ruleSet.unitPromotions.values
.any { unit.type.name in it.unitTypes && it.name == promotion }))
.any { unit.type.name in it.unitTypes && it.name == promotion }))
unit.promotions.addPromotion(promotion, isFree = true)
}
@ -247,8 +248,10 @@ class BaseUnit : INamed, IConstruction {
"Land", "land units" -> unitType.isLandUnit()
"Water", "water units", "Water units" -> unitType.isWaterUnit()
"Air", "air units" -> unitType.isAirUnit()
"Missile" -> unitType.isMissile()
"non-air" -> !unitType.isAirUnit()
"Military", "military units" -> unitType.isMilitary()
"Nuclear Weapon" -> isNuclearWeapon()
// Deprecated as of 3.15.2
"military water" -> unitType.isMilitary() && unitType.isWaterUnit()
else -> {
@ -260,6 +263,11 @@ class BaseUnit : INamed, IConstruction {
fun isGreatPerson() = uniqueObjects.any { it.placeholderText == "Great Person - []" }
// "Nuclear Weapon" unique deprecated since 3.15.4
fun isNuclearWeapon() = uniqueObjects.any { it.placeholderText == "Nuclear Weapon" || it.placeholderText == "Nuclear Weapon of strength []" }
fun movesLikeAirUnits() = unitType.isAirUnit() || unitType.isMissile()
override fun getResourceRequirements(): HashMap<String, Int> {
val resourceRequirements = HashMap<String, Int>()
if (requiredResource != null) resourceRequirements[requiredResource!!] = 1

View File

@ -18,6 +18,7 @@ enum class UnitType{
Fighter,
Bomber,
AtomicBomber,
Missile;
fun isMelee() =
@ -35,6 +36,7 @@ enum class UnitType{
|| this == WaterAircraftCarrier
|| this == City
|| this.isAirUnit()
|| this.isMissile()
fun isLandUnit() =
this == Civilian
@ -59,5 +61,8 @@ enum class UnitType{
fun isAirUnit() =
this == Bomber
|| this == Fighter
|| this == Missile
|| this == AtomicBomber
fun isMissile() =
this == Missile
}

View File

@ -210,7 +210,7 @@ class MapEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(CameraS
unit.updateUniques()
if (unit.movement.canMoveTo(it)) {
when {
unit.type.isAirUnit() -> {
unit.baseUnit.movesLikeAirUnits() -> {
it.airUnits.add(unit)
if (!it.isCityCenter()) unit.isTransported = true // if not city - air unit enters carrier
}

View File

@ -14,7 +14,7 @@ class WorldTileGroup(internal val worldScreen: WorldScreen, tileInfo: TileInfo,
private var cityButton: CityButton? = null
fun selectUnit(unit: MapUnit) {
if(unit.type.isAirUnit()) return // doesn't appear on map so nothing to select
if(unit.type.isAirUnit() || unit.type.isMissile()) return // doesn't appear on map so nothing to select
val unitImage = if (unit.type.isCivilian()) icons.civilianUnitIcon
else icons.militaryUnitIcon
unitImage?.selectUnit()

View File

@ -154,7 +154,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
else
previousSelectedUnits.any {
it.movement.canMoveTo(tileInfo) ||
it.movement.isUnknownTileWeShouldAssumeToBePassable(tileInfo) && !it.type.isAirUnit()
it.movement.isUnknownTileWeShouldAssumeToBePassable(tileInfo) && !it.baseUnit.movesLikeAirUnits()
}
)) {
if (previousSelectedUnitIsSwapping) {
@ -260,7 +260,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
val unitToTurnsToTile = HashMap<MapUnit, Int>()
for (unit in selectedUnits) {
val shortestPath = ArrayList<TileInfo>()
val turnsToGetThere = if (unit.type.isAirUnit()) {
val turnsToGetThere = if (unit.baseUnit.movesLikeAirUnits()) {
if (unit.movement.canReach(tileInfo)) 1
else 0
} else if (unit.action == Constants.unitActionParadrop) {
@ -381,7 +381,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
else {
moveHereButton.onClick(UncivSound.Silent) {
UncivGame.Current.settings.addCompletedTutorialTask("Move unit")
if (unitsThatCanMove.any { it.type.isAirUnit() })
if (unitsThatCanMove.any { it.baseUnit.movesLikeAirUnits() })
UncivGame.Current.settings.addCompletedTutorialTask("Move an air unit")
moveUnitToTargetTile(unitsThatCanMove, dto.tileInfo)
}
@ -402,7 +402,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
swapWithButton.onClick(UncivSound.Silent) {
UncivGame.Current.settings.addCompletedTutorialTask("Move unit")
if (dto.unit.type.isAirUnit())
if (dto.unit.baseUnit.movesLikeAirUnits())
UncivGame.Current.settings.addCompletedTutorialTask("Move an air unit")
swapMoveUnitToTargetTile(dto.unit, dto.tileInfo)
}
@ -510,7 +510,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
return // We don't want to show normal movement or attack overlays in unit-swapping mode
}
val isAirUnit = unit.type.isAirUnit()
val isAirUnit = unit.baseUnit.movesLikeAirUnits()
val moveTileOverlayColor = if (unit.action == Constants.unitActionParadrop) Color.BLUE else Color.WHITE
val tilesInMoveRange = unit.movement.getReachableTilesInCurrentTurn()
@ -525,7 +525,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
tileToColor.showCircle(Color.BLUE, 0.3f)
}
if (unit.movement.canMoveTo(tile) ||
unit.movement.isUnknownTileWeShouldAssumeToBePassable(tile) && !unit.type.isAirUnit())
unit.movement.isUnknownTileWeShouldAssumeToBePassable(tile) && !unit.baseUnit.movesLikeAirUnits())
tileToColor.showCircle(moveTileOverlayColor,
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
}

View File

@ -447,7 +447,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
if (viewingCiv.isAtWar() && !completedTasks.contains("Conquer a city"))
return "Conquer a city!\nBring an enemy city down to low health > " +
"\nEnter the city with a melee unit"
if (viewingCiv.getCivUnits().any { it.type.isAirUnit() } && !completedTasks.contains("Move an air unit"))
if (viewingCiv.getCivUnits().any { it.baseUnit.movesLikeAirUnits() } && !completedTasks.contains("Move an air unit"))
return "Move an air unit!\nSelect an air unit > select another city within range > " +
"\nMove the unit to the other city"
if (!completedTasks.contains("See your stats breakdown"))

View File

@ -39,19 +39,18 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
fun update() {
isVisible = true
if (!worldScreen.canChangeState) { hide(); return }
val attacker = tryGetAttacker()
if(attacker==null || !worldScreen.canChangeState){ hide(); return }
if (attacker == null) { hide(); return }
if (attacker is MapUnitCombatant && attacker.unit.hasUnique("Nuclear weapon")) {
if (attacker is MapUnitCombatant && attacker.unit.baseUnit.isNuclearWeapon()) {
val selectedTile = worldScreen.mapHolder.selectedTile
if (selectedTile == null) { hide(); return } // no selected tile
simulateNuke(attacker, selectedTile)
}
else {
} else {
val defender = tryGetDefender()
if (defender == null) { hide(); return }
simulateBattle(attacker, defender)
}
}
@ -229,7 +228,10 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
add(attackerNameWrapper)
var canNuke = true
val defenderNameWrapper = Table()
for (tile in targetTile.getTilesInDistance(Battle.NUKE_RADIUS)) {
val blastRadius =
if (!attacker.unit.hasUnique("Blast radius []")) 2
else attacker.unit.getMatchingUniques("Blast radius []").first().params[0].toInt()
for (tile in targetTile.getTilesInDistance(blastRadius)) {
//To make sure we dont nuke civilisations we cant declare war with
val attackerCiv = attacker.getCivInfo()
@ -277,7 +279,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
}
else {
attackButton.onClick(attacker.getAttackSound()) {
Battle.nuke(attacker, targetTile)
Battle.NUKE(attacker, targetTile)
worldScreen.mapHolder.removeUnitActionOverlay() // the overlay was one of attacking
worldScreen.shouldUpdate = true
}

View File

@ -69,7 +69,7 @@ object UnitActions {
private fun addSwapAction(unit: MapUnit, actionList: ArrayList<UnitAction>, worldScreen: WorldScreen) {
// Air units cannot swap
if (unit.type.isAirUnit()) return
if (unit.type.isAirUnit() || unit.type.isMissile()) return
// Disable unit swapping if multiple units are selected. It would make little sense.
// In principle, the unit swapping mode /will/ function with multiselect: it will simply
// only consider the first selected unit, and ignore the other selections. However, it does
@ -283,7 +283,7 @@ object UnitActions {
}
private fun addExplorationActions(unit: MapUnit, actionList: ArrayList<UnitAction>) {
if (unit.type.isAirUnit()) return
if (unit.baseUnit.movesLikeAirUnits()) return
if (unit.action != Constants.unitActionExplore) {
actionList += UnitAction(UnitActionType.Explore) {
unit.action = Constants.unitActionExplore

View File

@ -108,6 +108,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* Icon for Carrier made by [JackRainy](https://github.com/JackRainy), based on [Aircraft Carrier](https://thenounproject.com/icolabs/collection/flat-icons-transport/?i=2332914) By IcoLabs, BR
* [Water Gun](https://thenounproject.com/term/water-gun/2121571) by ProSymbols for Marine
* [Parachute](https://thenounproject.com/term/parachute/2025018) by Nociconist for Paratrooper
* [atomic bomb](https://thenounproject.com/search/?q=atomic+bomb&i=3712713) by AmruID for Atomic Bomb
* [Robot](https://thenounproject.com/term/robot/1182459/) by Lluisa Iborra, ES for Giant Death Robot
### Great People
@ -450,6 +451,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Ecology](https://thenounproject.com/term/ecology/1970666/) By ProSymbols
* [Robotic Arm](https://thenounproject.com/term/robotic-arm/1970874/) By Karl Gilbert for Robotics
* [Rocket](https://thenounproject.com/term/rocket/1743642/) By kareemov for Rocketry
* [Rocket](https://thenounproject.com/term/rocket/3999811) Kusdarti for Advanced Ballistics
### Future
* [Nanoparticles](https://thenounproject.com/term/nanoparticles/822286/) By Gyan Lakhwani for Nanotechnology