mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-22 05:41:11 +07:00
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:
BIN
android/Images/TechIcons/Advanced Ballistics.png
Normal file
BIN
android/Images/TechIcons/Advanced Ballistics.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
android/ImagesToPackSeparately/UnitIcons/Atomic Bomb.png
Normal file
BIN
android/ImagesToPackSeparately/UnitIcons/Atomic Bomb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
@ -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 |
@ -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"]
|
||||
},
|
||||
{
|
||||
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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! =
|
||||
|
@ -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 }
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user