mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-10 10:58:13 +07:00
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:
parent
a765caa97c
commit
aa9dda2eea
BIN
android/ImagesToPackSeparately/UnitIcons/Paratrooper.png
Normal file
BIN
android/ImagesToPackSeparately/UnitIcons/Paratrooper.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
@ -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 |
@ -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",
|
||||
|
@ -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
|
||||
|
@ -504,6 +504,7 @@ Sleep =
|
||||
Sleep until healed =
|
||||
Moving =
|
||||
Set up =
|
||||
Paradrop =
|
||||
Upgrade to [unitType] ([goldCost] gold) =
|
||||
Found city =
|
||||
Promote =
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user