Added the paratrooper unit (#4025)

* Added the paratrooper unit

* Reverted accidental reordering of action table

* Fixed Github build errors

* Hopefully actually fixed the build errors

* Added a Dutch translation, finally fixing the error

* Paratroopers can no longer actually paradrop on a tile they shouldn't be able to paradrop onto

* Removed double update action
This commit is contained in:
Xander Lenstra
2021-06-03 06:33:38 +02:00
committed by GitHub
parent a765caa97c
commit aa9dda2eea
14 changed files with 116 additions and 45 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 284 KiB

View File

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

View File

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

View File

@ -504,6 +504,7 @@ Sleep =
Sleep until healed = Sleep until healed =
Moving = Moving =
Set up = Set up =
Paradrop =
Upgrade to [unitType] ([goldCost] gold) = Upgrade to [unitType] ([goldCost] gold) =
Found city = Found city =
Promote = Promote =

View File

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

View File

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

View File

@ -2,6 +2,7 @@ package com.unciv.logic.map
import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.math.Vector2
import com.unciv.Constants import com.unciv.Constants
import com.unciv.logic.HexMath.getDistance
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
class UnitMovementAlgorithms(val unit:MapUnit) { class UnitMovementAlgorithms(val unit:MapUnit) {
@ -169,8 +170,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
val currentTile = unit.getTile() val currentTile = unit.getTile()
if (currentTile == finalDestination) return currentTile if (currentTile == finalDestination) return currentTile
// head there directly // If we can fly, head there directly
if (unit.type.isAirUnit()) return finalDestination if (unit.type.isAirUnit() || unit.action == Constants.unitActionParadrop) return finalDestination
val distanceToTiles = getDistanceToTiles() val distanceToTiles = getDistanceToTiles()
@ -209,6 +210,8 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun canReach(destination: TileInfo): Boolean { fun canReach(destination: TileInfo): Boolean {
if (unit.type.isAirUnit()) if (unit.type.isAirUnit())
return unit.currentTile.aerialDistanceTo(destination) <= unit.getRange()*2 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() return getShortestPath(destination).any()
} }
@ -239,13 +242,27 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
fun moveToTile(destination: TileInfo) { fun moveToTile(destination: TileInfo) {
if (destination == unit.getTile()) return // already here! 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.action = null
unit.removeFromTile() unit.removeFromTile()
unit.isTransported = false // it has left the carrier by own means unit.isTransported = false // it has left the carrier by own means
unit.putInTile(destination) unit.putInTile(destination)
unit.currentMovement = 0f unit.currentMovement = 0f
return 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() val distanceToTiles = getDistanceToTiles()
@ -326,7 +343,14 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
} }
return false 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, // This is the most called function in the entire game,
// so multiple callees of this function have been optimized, // so multiple callees of this function have been optimized,

View File

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

View File

@ -218,7 +218,11 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
val turnsToGetThere = if (unit.type.isAirUnit()) { val turnsToGetThere = if (unit.type.isAirUnit()) {
if (unit.movement.canReach(tileInfo)) 1 if (unit.movement.canReach(tileInfo)) 1
else 0 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 unitToTurnsToTile[unit] = turnsToGetThere
} }
@ -393,11 +397,15 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
} }
val isAirUnit = unit.type.isAirUnit() val isAirUnit = unit.type.isAirUnit()
val tilesInMoveRange = val moveTileOverlayColor = if (unit.action == Constants.unitActionParadrop) Color.BLUE else Color.WHITE
if (isAirUnit) val tilesInMoveRange =
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2)) if (isAirUnit)
else unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2))
unit.movement.getDistanceToTiles().keys.asSequence() 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 (tile in tilesInMoveRange) {
for (tileToColor in tileGroups[tile]!!) { for (tileToColor in tileGroups[tile]!!) {
@ -411,7 +419,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
} }
if (unit.movement.canMoveTo(tile) || if (unit.movement.canMoveTo(tile) ||
unit.movement.isUnknownTileWeShouldAssumeToBePassable(tile) && !unit.type.isAirUnit()) 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) if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
} }
} }

View File

@ -51,6 +51,7 @@ object UnitActions {
addPromoteAction(unit, actionList) addPromoteAction(unit, actionList)
addUnitUpgradeAction(unit, actionList) addUnitUpgradeAction(unit, actionList)
addPillageAction(unit, actionList, worldScreen) addPillageAction(unit, actionList, worldScreen)
addParadropAction(unit, actionList, worldScreen)
addSetupAction(unit, actionList) addSetupAction(unit, actionList)
addFoundCityAction(unit, actionList, tile) addFoundCityAction(unit, actionList, tile)
addWorkerActions(unit, actionList, tile, worldScreen, unitTable) addWorkerActions(unit, actionList, tile, worldScreen, unitTable)
@ -152,6 +153,26 @@ object UnitActions {
}.takeIf { unit.currentMovement > 0 && !isSetUp }) }.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) { private fun addPillageAction(unit: MapUnit, actionList: ArrayList<UnitAction>, worldScreen: WorldScreen) {
val pillageAction = getPillageAction(unit) val pillageAction = getPillageAction(unit)
if (pillageAction == null) return 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') "Start Golden Age" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Artist"), 'g')
"Hurry Wonder" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'g') "Hurry Wonder" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'g')
"Conduct Trade Mission" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Merchant"), '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') "Set up" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Catapult"), 't')
"Disband unit" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/DisbandUnit"))
"Explore" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Scout"), 'x') "Explore" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Scout"), 'x')
"Stop exploration" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), 'x') "Stop exploration" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Stop"), 'x')
"Pillage" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Pillage"), 'p') "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")) else -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Star"))
} }
} }

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 * [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 * 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 * [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 ### Great People