This commit is contained in:
Yair Morgenstern 2021-06-03 19:08:39 +03:00
commit 7cc440a577
21 changed files with 154 additions and 65 deletions

View File

@ -5,6 +5,7 @@
[![Google Play](https://img.shields.io/static/v1?label=Google&message=Play&color=607D8B&logo=google-play)](https://play.google.com/store/apps/details?id=com.unciv.app)
[![F-Droid](https://img.shields.io/f-droid/v/com.unciv.app)](https://f-droid.org/en/packages/com.unciv.app/)
[![itch.io](https://img.shields.io/static/v1?label=itch.io&message=Unciv&color=607D8B&logo=itch.io)](https://yairm210.itch.io/unciv)
[![Flathub](https://img.shields.io/flathub/v/io.github.yairm210.unciv)](https://flathub.org/apps/details/io.github.yairm210.unciv)
[![Travis CI w/ Logo](https://img.shields.io/travis/yairm210/Unciv/master.svg?logo=travis)](https://travis-ci.com/yairm210/Unciv)
![Build and deploy](https://github.com/yairm210/Unciv/workflows/Build%20and%20deploy/badge.svg)
@ -79,6 +80,8 @@ Yes! Windows and Linux versions are available at [itch.io](https://yairm210.itch
If you have Java 8, and are familiar with the command line, there are (considerably smaller) JARs in [Releases](https://github.com/yairm210/UnCiv/releases) which you can run with `java -jar Unciv.jar`. This is also (currently) the only way to run the game on MacOS.
If you use Flatpaks, there's a Flatpak by [MayeulC](https://github.com/MayeulC) and you can know more about it [here](https://github.com/flathub/io.github.yairm210.unciv). Flathub link is available in the [Downloads](#downloads) section.
If you want to build it from sratch for some reason, [we have instructions for that as well](https://github.com/yairm210/Unciv/wiki/Building-locally-without-Android-Studio)
## How about IOS?
@ -124,4 +127,9 @@ Multiplayer takes advantage of Dropbox, which is *non-free software*, for syncin
Single player does not use this feature.
## Downloads
| [![](https://static.itch.io/images/badge.svg)](https://yairm210.itch.io/unciv) | [![](https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png)](https://play.google.com/store/apps/details?id=com.unciv.app) | [![](https://fdroid.gitlab.io/artwork/badge/get-it-on.png)](https://f-droid.org/en/packages/com.unciv.app/) | [![](https://flathub.org/assets/badges/flathub-badge-en.svg)](https://flathub.org/apps/details/io.github.yairm210.unciv)
|--- |--- |--- |--- |
# [Credits and 3rd parties](docs/Credits.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -487,132 +487,139 @@ Panzer
orig: 100, 100
offset: 0, 0
index: -1
Persian Immortal
Paratrooper
rotate: false
xy: 1430, 308
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Pikeman
Persian Immortal
rotate: false
xy: 1532, 410
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Rifleman
Pikeman
rotate: false
xy: 1328, 105
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Rocket Artillery
Rifleman
rotate: false
xy: 1430, 206
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Samurai
Rocket Artillery
rotate: false
xy: 1532, 308
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Scout
Samurai
rotate: false
xy: 1634, 410
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Settler
Scout
rotate: false
xy: 1328, 3
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Ship of the Line
Settler
rotate: false
xy: 1430, 104
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Sipahi
Ship of the Line
rotate: false
xy: 1430, 2
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Slinger
Sipahi
rotate: false
xy: 1532, 206
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Spearman
Slinger
rotate: false
xy: 1634, 308
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Stealth Bomber
Spearman
rotate: false
xy: 1736, 410
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Submarine
Stealth Bomber
rotate: false
xy: 1532, 104
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Swordsman
Submarine
rotate: false
xy: 1532, 2
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Tank
Swordsman
rotate: false
xy: 1634, 206
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Tercio
Tank
rotate: false
xy: 1736, 308
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Trebuchet
Tercio
rotate: false
xy: 1838, 410
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Triplane
Trebuchet
rotate: false
xy: 1634, 104
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Triplane
rotate: false
xy: 1634, 2
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Trireme
rotate: false
xy: 1736, 205
@ -622,49 +629,49 @@ Trireme
index: -1
Turtle Ship
rotate: false
xy: 1634, 2
xy: 1838, 308
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
War Chariot
rotate: false
xy: 1838, 308
xy: 1940, 410
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
War Elephant
rotate: false
xy: 1940, 410
xy: 1736, 103
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Warrior
rotate: false
xy: 1736, 103
xy: 1838, 206
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Work Boats
rotate: false
xy: 1838, 206
xy: 1940, 308
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Worker
rotate: false
xy: 1940, 308
xy: 1838, 104
size: 100, 100
orig: 100, 100
offset: 0, 0
index: -1
Zero
rotate: false
xy: 1838, 104
xy: 1838, 2
size: 100, 100
orig: 100, 100
offset: 0, 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 284 KiB

View File

@ -110,7 +110,7 @@
"isWonder": true,
"cost": 185,
"greatPersonPoints": {"production": 1},
"uniques": ["+[10]% growth in all cities", "+[15]% Production when constructing [Ranged] units [in all cities]"],
"uniques": ["+[10]% [Food] [in all cities]", "+[15]% Production when constructing [Ranged] units [in all cities]"],
"requiredTech": "Archery",
"quote": "'It is not so much for its beauty that the forest makes a claim upon men's hearts, as for that subtle something, that quality of air, that emanation from old trees, that so wonderfully changes and renews a weary spirit.' - Robert Louis Stevenson"
},
@ -937,7 +937,7 @@
{
"name": "Manhattan Project",
"isNationalWonder": true,
"uniques": ["Enables nuclear weapon"],
"uniques": ["Enables nuclear weapon", "Triggers a global alert upon completion"],
"requiredTech": "Nuclear Fission"
},
{
@ -1014,13 +1014,13 @@
"name": "SS Booster",
"requiredResource": "Aluminum",
"requiredTech": "Robotics",
"uniques": ["Spaceship part", "Cannot be purchased"]
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
},
{
"name": "Apollo Program",
"cost": 1500,
"isNationalWonder": true,
"uniques": ["Enables construction of Spaceship parts"],
"uniques": ["Enables construction of Spaceship parts", "Triggers a global alert upon completion"],
"requiredTech": "Rocketry"
},
@ -1044,18 +1044,18 @@
"name": "SS Cockpit",
"requiredResource": "Aluminum",
"requiredTech": "Satellites",
"uniques": ["Spaceship part", "Cannot be purchased"]
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
},
{
"name": "SS Engine",
"requiredResource": "Aluminum",
"requiredTech": "Particle Physics",
"uniques": ["Spaceship part", "Cannot be purchased"]
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
},
{
"name": "SS Stasis Chamber",
"requiredResource": "Aluminum",
"requiredTech": "Nanotechnology",
"uniques": ["Spaceship part", "Cannot be purchased"]
"uniques": ["Spaceship part", "Triggers a global alert upon completion", "Cannot be purchased"]
}
]

View File

@ -1152,7 +1152,6 @@
"uniques": ["+[20]% Strength in [Foreign Land]"],
"attackSound": "shot"
},
/*
{
"name": "Paratrooper",
"unitType": "Melee",
@ -1160,11 +1159,10 @@
"strength": 40,
"cost": 375,
"requiredTech": "Radar",
"uniques": ["May Paradrop"],
"uniques": ["May Paradrop up to [5] tiles from inside friendly territory"],
"attackSound": "shot"
// 65 strength in expansions, upgradesTo "XCOM Squad", "No Movement Cost to Pillage" in BNW
},
*/
{
"name": "Infantry",
"unitType": "Melee",

View File

@ -515,6 +515,7 @@ Sleep = Slaap
Sleep until healed = Slaap tot geheeld
Moving = Bewegen
Set up = Opstellen
Paradrop = Parachutelanding
Upgrade to [unitType] ([goldCost] gold) = Waardeer [unitType] op ([goldCost] goud)
Found city = Stad stichten
Promote = Promoveren
@ -522,7 +523,7 @@ Health = Gezondheid
Disband unit = Eenheid opheffen
Explore = Ontdekken
Stop exploration = Stop ontdekken
Pillage = Plundering
Pillage = Plunderen
Do you really want to disband this unit? = Wil je echt deze eenheid opheffen
Disband this unit for [goldAmount] gold? = Deze eenheid ontbinden voor [goldAmount] goud?
Create [improvement] = Maak [improvement]
@ -919,7 +920,7 @@ Land = Land
Wounded = Gewond
Marine = Marine
Mobile SAM = Mobiele grond-lucht raket
Paratrooper = Paracommando
Paratrooper = Parachutist
Helicopter Gunship = Helicopter Wapenschip
Atomic Bomb = Atoombom
Unbuildable = Onbouwbaar

View File

@ -395,6 +395,8 @@ Cannot provide unit upkeep for [unitName] - unit has been disbanded! =
[cityName] is starving! =
[construction] has been built in [cityName] =
[wonder] has been built in a faraway land =
[civName] has completed [construction]! =
An unknown civilization has completed [construction]! =
Work has started on [construction] =
[cityName] cannot continue work on [construction] =
[cityName] has expanded its borders! =
@ -502,6 +504,7 @@ Sleep =
Sleep until healed =
Moving =
Set up =
Paradrop =
Upgrade to [unitType] ([goldCost] gold) =
Found city =
Promote =

View File

@ -57,6 +57,8 @@ object Constants {
const val unitActionSleepUntilHealed = "Sleep until healed"
const val unitActionAutomation = "Automate"
const val unitActionExplore = "Explore"
const val unitActionParadrop = "Paradrop"
const val futureTech = "Future Tech"
const val cancelImprovementOrder = "Cancel improvement order"

View File

@ -62,7 +62,6 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
var music: Music? = null
val musicLocation = "music/thatched-villagers.mp3"
private var isSizeRestored = false
var isInitialized = false
@ -118,7 +117,6 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
thread(name="Music") { startMusic() }
restoreSize()
if (settings.isFreshlyCreated) {
setScreen(LanguagePickerScreen())
@ -129,14 +127,6 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
crashController = CrashController.Impl(crashReportSender)
}
fun restoreSize() {
if (!isSizeRestored && Gdx.app.type == Application.ApplicationType.Desktop && settings.windowState.height>39 && settings.windowState.width>39) {
isSizeRestored = true
Gdx.graphics.setWindowedMode(settings.windowState.width, settings.windowState.height)
}
}
fun loadGame(gameInfo: GameInfo) {
this.gameInfo = gameInfo
ImageGetter.setNewRuleset(gameInfo.ruleSet)

View File

@ -347,7 +347,16 @@ class CityConstructions {
cityInfo.civInfo.addNotification("[${construction.name}] has been built in [" + cityInfo.name + "]",
cityInfo.location, NotificationIcon.Construction, icon)
}
if (construction is Building && "Triggers a global alert upon completion" in construction.uniques) {
for (otherCiv in cityInfo.civInfo.gameInfo.civilizations) {
// No need to notify ourself, since we already got the building notification anyway
if (otherCiv == cityInfo.civInfo) continue
val completingCivDescription =
if (otherCiv.knows(cityInfo.civInfo)) "[${cityInfo.civInfo.civName}]" else "An unknown civilization"
otherCiv.addNotification("$completingCivDescription has completed [${construction.name}]!",
NotificationIcon.Construction, buildingIcon)
}
}
}
fun addBuilding(buildingName: String) {

View File

@ -71,6 +71,9 @@ class MapUnit {
@Transient
var cannotEnterOceanTilesUntilAstronomy = false
@Transient
var paradropRange = 0
lateinit var owner: String
/**
@ -547,6 +550,9 @@ class MapUnit {
action = null // wake up when healed
}
if (action == Constants.unitActionParadrop)
action = null
getCitadelDamage()
getTerrainDamage()
}

View File

@ -2,6 +2,7 @@ package com.unciv.logic.map
import com.badlogic.gdx.math.Vector2
import com.unciv.Constants
import com.unciv.logic.HexMath.getDistance
import com.unciv.logic.civilization.CivilizationInfo
class UnitMovementAlgorithms(val unit:MapUnit) {
@ -169,8 +170,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
val currentTile = unit.getTile()
if (currentTile == finalDestination) return currentTile
// head there directly
if (unit.type.isAirUnit()) return finalDestination
// If we can fly, head there directly
if (unit.type.isAirUnit() || unit.action == Constants.unitActionParadrop) return finalDestination
val distanceToTiles = getDistanceToTiles()
@ -209,6 +210,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun canReach(destination: TileInfo): Boolean {
if (unit.type.isAirUnit())
return unit.currentTile.aerialDistanceTo(destination) <= unit.getRange()*2
if (unit.action == Constants.unitActionParadrop)
return getDistance(unit.currentTile.position, destination.position) <= unit.paradropRange && canParadropOn(destination)
return getShortestPath(destination).any()
}
@ -239,13 +242,27 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun moveToTile(destination: TileInfo) {
if (destination == unit.getTile()) return // already here!
if (unit.type.isAirUnit()) { // they move differently from all other units
if (unit.type.isAirUnit()) { // air units move differently from all other units
unit.action = null
unit.removeFromTile()
unit.isTransported = false // it has left the carrier by own means
unit.putInTile(destination)
unit.currentMovement = 0f
return
} else if (unit.action == Constants.unitActionParadrop) { // paratrooping move differently
unit.action = null
unit.removeFromTile()
unit.putInTile(destination)
unit.currentMovement -= 1f
// Check if unit maintenance changed
// Is also done for other units, but because we skip everything else, we have to manually check it
// The reasong we skip everything, is that otherwise `getPathToTile()` throws an exception
// As we can not reach our destination in a single turn
if (unit.canGarrison()
&& (unit.getTile().isCityCenter() || destination.isCityCenter())
&& unit.civInfo.hasUnique("Units in cities cost no Maintenance")
) unit.civInfo.updateStatsForNextTurn()
return
}
val distanceToTiles = getDistanceToTiles()
@ -326,7 +343,14 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
}
return false
}
// Can a paratrooper land at this tile?
fun canParadropOn(destination: TileInfo): Boolean {
// Can only move to land tiles within range that are visible and not impassible
// Based on some testing done in the base game
if (!destination.isLand || destination.isImpassible() || !unit.civInfo.viewableTiles.contains(destination)) return false
return true
}
// This is the most called function in the entire game,
// so multiple callees of this function have been optimized,

View File

@ -10,17 +10,18 @@ data class UnitAction(
enum class UnitActionType(val value: String) {
Automate("Automate"),
StopMovement("Stop movement"),
StopAutomation("Stop automation"),
StopExploration("Stop exploration"),
StopMovement("Stop movement"),
Sleep("Sleep"),
SleepUntilHealed("Sleep until healed"),
Fortify("Fortify"),
FortifyUntilHealed("Fortify until healed"),
Explore("Explore"),
StopExploration("Stop exploration"),
Promote("Promote"),
Upgrade("Upgrade"),
Pillage("Pillage"),
Paradrop("Paradrop"),
SetUp("Set up"),
FoundCity("Found city"),
ConstructImprovement("Construct improvement"),

View File

@ -4,7 +4,7 @@ import com.badlogic.gdx.Application
import com.badlogic.gdx.Gdx
import com.unciv.logic.GameSaver
data class WindowState (val width:Int=0, val height:Int=0)
data class WindowState (val width: Int = 900, val height: Int = 600)
class GameSettings {
var showWorkedTiles: Boolean = false

View File

@ -413,8 +413,8 @@ class Building : NamedStats(), IConstruction {
civInfo.victoryManager.currentsSpaceshipParts.add(name, 1)
return true
}
cityConstructions.addBuilding(name)
cityConstructions.addBuilding(name)
val improvement = getImprovement(civInfo.gameInfo.ruleSet)
if (improvement != null) {

View File

@ -218,7 +218,11 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
val turnsToGetThere = if (unit.type.isAirUnit()) {
if (unit.movement.canReach(tileInfo)) 1
else 0
} else unit.movement.getShortestPath(tileInfo).size // this is what takes the most time, tbh
} else if (unit.action == Constants.unitActionParadrop) {
if (unit.movement.canReach(tileInfo)) 1
else 0
} else
unit.movement.getShortestPath(tileInfo).size // this is what takes the most time, tbh
unitToTurnsToTile[unit] = turnsToGetThere
}
@ -393,11 +397,15 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
}
val isAirUnit = unit.type.isAirUnit()
val tilesInMoveRange =
if (isAirUnit)
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2))
else
unit.movement.getDistanceToTiles().keys.asSequence()
val moveTileOverlayColor = if (unit.action == Constants.unitActionParadrop) Color.BLUE else Color.WHITE
val tilesInMoveRange =
if (isAirUnit)
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2))
else if (unit.action == Constants.unitActionParadrop)
unit.getTile().getTilesInDistance(unit.paradropRange)
.filter { unit.movement.canParadropOn(it) }
else
unit.movement.getDistanceToTiles().keys.asSequence()
for (tile in tilesInMoveRange) {
for (tileToColor in tileGroups[tile]!!) {
@ -411,7 +419,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
}
if (unit.movement.canMoveTo(tile) ||
unit.movement.isUnknownTileWeShouldAssumeToBePassable(tile) && !unit.type.isAirUnit())
tileToColor.showCircle(Color.WHITE,
tileToColor.showCircle(moveTileOverlayColor,
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
}
}

View File

@ -51,6 +51,7 @@ object UnitActions {
addPromoteAction(unit, actionList)
addUnitUpgradeAction(unit, actionList)
addPillageAction(unit, actionList, worldScreen)
addParadropAction(unit, actionList, worldScreen)
addSetupAction(unit, actionList)
addFoundCityAction(unit, actionList, tile)
addWorkerActions(unit, actionList, tile, worldScreen, unitTable)
@ -152,6 +153,26 @@ object UnitActions {
}.takeIf { unit.currentMovement > 0 && !isSetUp })
}
private fun addParadropAction(unit: MapUnit, actionList: ArrayList<UnitAction>, worldScreen: WorldScreen) {
val paradropUniques = unit.getMatchingUniques("May Paradrop up to [] tiles from inside friendly territory")
if (!paradropUniques.any() || unit.isEmbarked()) return
unit.paradropRange = paradropUniques.maxOfOrNull { it.params[0] }!!.toInt()
actionList += UnitAction(UnitActionType.Paradrop,
isCurrentAction = unit.action == Constants.unitActionParadrop,
action = {
if (unit.action != Constants.unitActionParadrop) {
unit.action = Constants.unitActionParadrop
} else {
unit.action = null
}
}.takeIf {
unit.currentMovement == unit.getMaxMovement().toFloat() &&
unit.currentTile.isFriendlyTerritory(unit.civInfo) &&
!unit.isEmbarked()
})
}
private fun addPillageAction(unit: MapUnit, actionList: ArrayList<UnitAction>, worldScreen: WorldScreen) {
val pillageAction = getPillageAction(unit)
if (pillageAction == null) return

View File

@ -49,12 +49,13 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
"Start Golden Age" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Artist"), 'g')
"Hurry Wonder" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'g')
"Conduct Trade Mission" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Merchant"), 'g')
"Construct road" -> return UnitIconAndKey(ImageGetter.getImprovementIcon("Road"), 'r')
"Paradrop" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Paratrooper"), 'p')
"Set up" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Catapult"), 't')
"Disband unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/DisbandUnit"))
"Explore" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Scout"), 'x')
"Stop exploration" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), 'x')
"Pillage" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Pillage"), 'p')
"Construct road" -> return UnitIconAndKey(ImageGetter.getImprovementIcon("Road"), 'r')
"Disband unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/DisbandUnit"))
else -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Star"))
}
}

View File

@ -6,10 +6,14 @@ import club.minnced.discord.rpc.DiscordRichPresence
import com.badlogic.gdx.Files
import com.badlogic.gdx.backends.lwjgl.LwjglApplication
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.tools.texturepacker.TexturePacker
import com.unciv.JsonParser
import com.unciv.UncivGame
import com.unciv.UncivGameParameters
import com.unciv.logic.GameSaver
import com.unciv.models.metadata.GameSettings
import com.unciv.models.translations.tr
import com.unciv.ui.utils.Fonts
import io.ktor.application.*
@ -39,6 +43,11 @@ internal object DesktopLauncher {
config.addIcon("ExtraImages/Icon.png", Files.FileType.Internal)
config.title = "Unciv"
config.useHDPI = true
if (FileHandle(GameSaver.settingsFileName).exists()) {
val settings = JsonParser().getFromJson(GameSettings::class.java, FileHandle(GameSaver.settingsFileName))
config.width = settings.windowState.width
config.height = settings.windowState.height
}
val versionFromJar = DesktopLauncher.javaClass.`package`.specificationVersion ?: "Desktop"

View File

@ -104,6 +104,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [Nuclear Missile](https://thenounproject.com/marialuisa.iborra/collection/missiles-bombs/?i=1022574) By Lluisa Iborra, ES
* 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*
### Great People