mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-10 10:58:13 +07:00
Unit swapping (#4049)
* Added an icon for unit swapping * Implemented unit swapping In the original Civ V, unit swapping is a supported mechanic. If you try to move a unit to a tile with another of your units, and both units have enough movement points left to reach the other's tile, they will swap places. They will consume only the movement points needed to reach the other's tile in this way. This change implements unit swapping for Unciv. To prevent all kinds of problems from arising with automatic unit movement, unit swapping can only be done explicitly. This also means that it can only be done if the unit-swap movement is possible in a single turn. It is however not limited to adjacent units. Because Unciv supports mobile devices, there is in general no separation between a unit-selection click and a movement click. Clicking on another unit while a unit is selected simply selects that other unit. Because we do not want to make it more difficult to select other units in this way, unit swapping is implemented as a separate "movement mode": to toggle this mode on or off, the new unit action "Swap units" must be used. Newly selected units still always start in the normal movement mode. In the unit-swap movement mode, the possible swap tiles are highlighted instead of the possible movement and attack tiles. Clicking on a highlighted tile will display a swap button, similar to the movement button, or instantly perform the swap if single-click-movement is enabled. This new behavior overrides the selection of the unit on the target tile: if the user wants to select the unit instead, they have to exit the unit-swapping mode first. The swapping code is robust, it can even handle swaps that involve a paradrop! An option to always swap-move when an eligible tile is clicked instead of requiring the unit-swapping mode, similar to the existing single-click-movement option, could perhaps be added in later. * Added some comments to existing movement functions * Fixed a silly mistake Fixed a silly mistake which caused the unit-swapping eligibility detection to sometimes remove units from the world. * Removed some unneeded code * Fixed movement buttons not showing with world wrap Fixed a bug where the "move here" and "swap with" buttons would only show on the leftmost copy of the world when world wrap was enabled. * Made the swap action only display if usable Made the unit swapping button only display if there is at least one possible swap movement.
This commit is contained in:
parent
cc3eeb3c7c
commit
a7afc0718c
BIN
android/Images/OtherIcons/Swap.png
Normal file
BIN
android/Images/OtherIcons/Swap.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 956 KiB After Width: | Height: | Size: 958 KiB |
@ -508,6 +508,7 @@ Bombard strength = Bombard sterkte
|
||||
Range = Bereik
|
||||
Move unit = Eenheid verplaatsen
|
||||
Stop movement = Verplaatsen stoppen
|
||||
Swap units = Verwissel eenheden
|
||||
Construct improvement = Verbetering bouwen
|
||||
Automate = Automatiseren
|
||||
Stop automation = Stop automatiseren
|
||||
|
@ -493,6 +493,7 @@ Bombard strength =
|
||||
Range =
|
||||
Move unit =
|
||||
Stop movement =
|
||||
Swap units =
|
||||
Construct improvement =
|
||||
Automate =
|
||||
Stop automation =
|
||||
|
@ -61,6 +61,10 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
|
||||
fun isUnknownTileWeShouldAssumeToBePassable(tileInfo: TileInfo) = !unit.civInfo.exploredTiles.contains(tileInfo.position)
|
||||
|
||||
/**
|
||||
* Does not consider if tiles can actually be entered, use canMoveTo for that.
|
||||
* If a tile can be reached within the turn, but it cannot be passed through, the total distance to it is set to unitMovement
|
||||
*/
|
||||
fun getDistanceToTilesWithinTurn(origin: Vector2, unitMovement: Float): PathsToTilesWithinTurn {
|
||||
val distanceToTiles = PathsToTilesWithinTurn()
|
||||
if (unitMovement == 0f) return distanceToTiles
|
||||
@ -96,7 +100,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
else
|
||||
totalDistanceToTile = unitMovement
|
||||
// In Civ V, you can always travel between adjacent tiles, even if you don't technically
|
||||
// have enough movement points - it simple depletes what you have
|
||||
// have enough movement points - it simply depletes what you have
|
||||
|
||||
distanceToTiles[neighbor] = ParentTileAndTotalDistance(tileToCheck, totalDistanceToTile)
|
||||
}
|
||||
@ -108,7 +112,10 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
return distanceToTiles
|
||||
}
|
||||
|
||||
/** Returns an empty list if there's no way to get there */
|
||||
/**
|
||||
* Does not consider if the destination tile can actually be entered, use canMoveTo for that.
|
||||
* Returns an empty list if there's no way to get to the destination.
|
||||
*/
|
||||
fun getShortestPath(destination: TileInfo): List<TileInfo> {
|
||||
val currentTile = unit.getTile()
|
||||
if (currentTile.position == destination) return listOf(currentTile) // edge case that's needed, so that workers will know that they can reach their own tile. *sigh*
|
||||
@ -208,11 +215,73 @@ 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)
|
||||
return canReachInCurrentTurn(destination)
|
||||
return getShortestPath(destination).any()
|
||||
}
|
||||
|
||||
fun canReachInCurrentTurn(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()
|
||||
return getDistance(unit.currentTile.position, destination.position) <= unit.paradropRange && canParadropOn(destination)
|
||||
return getDistanceToTiles().containsKey(destination)
|
||||
}
|
||||
|
||||
fun getReachableTilesInCurrentTurn(): Sequence<TileInfo> {
|
||||
return when {
|
||||
unit.type.isAirUnit() ->
|
||||
unit.getTile().getTilesInDistanceRange(IntRange(1, unit.getRange() * 2))
|
||||
unit.action == Constants.unitActionParadrop ->
|
||||
unit.getTile().getTilesInDistance(unit.paradropRange)
|
||||
.filter { unit.movement.canParadropOn(it) }
|
||||
else ->
|
||||
unit.movement.getDistanceToTiles().keys.asSequence()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether we can perform a swap move to the specified tile */
|
||||
fun canUnitSwapTo(destination: TileInfo): Boolean {
|
||||
return canReachInCurrentTurn(destination) && canUnitSwapToReachableTile(destination)
|
||||
}
|
||||
|
||||
/** Returns the tiles to which we can perform a swap move */
|
||||
fun getUnitSwappableTiles(): Sequence<TileInfo> {
|
||||
return getReachableTilesInCurrentTurn().filter { canUnitSwapToReachableTile(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether we can perform a unit swap move to the specified tile, given that it is
|
||||
* reachable in the current turn
|
||||
*/
|
||||
private fun canUnitSwapToReachableTile(reachableTile: TileInfo): Boolean {
|
||||
// Air units cannot swap
|
||||
if (unit.type.isAirUnit()) 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
|
||||
// also reach our tile in its current turn.
|
||||
val otherUnit = (
|
||||
if (unit.type.isCivilian())
|
||||
reachableTile.civilianUnit
|
||||
else
|
||||
reachableTile.militaryUnit
|
||||
) ?: return false
|
||||
val ourPosition = unit.getTile()
|
||||
if (otherUnit.owner != unit.owner || !otherUnit.movement.canReachInCurrentTurn(ourPosition)) return false
|
||||
// Check if we could enter their tile if they wouldn't be there
|
||||
otherUnit.removeFromTile()
|
||||
val weCanEnterTheirTile = canMoveTo(reachableTile)
|
||||
otherUnit.putInTile(reachableTile)
|
||||
if (!weCanEnterTheirTile) return false
|
||||
// Check if they could enter our tile if we wouldn't be here
|
||||
unit.removeFromTile()
|
||||
val theyCanEnterOurTile = otherUnit.movement.canMoveTo(ourPosition)
|
||||
unit.putInTile(ourPosition)
|
||||
if (!theyCanEnterOurTile) return false
|
||||
// All clear!
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@ -249,7 +318,7 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
unit.putInTile(destination)
|
||||
unit.currentMovement = 0f
|
||||
return
|
||||
} else if (unit.action == Constants.unitActionParadrop) { // paratroopers move differently
|
||||
} else if (unit.action == Constants.unitActionParadrop) { // paradropping units move differently
|
||||
unit.action = null
|
||||
unit.removeFromTile()
|
||||
unit.putInTile(destination)
|
||||
@ -312,6 +381,29 @@ class UnitMovementAlgorithms(val unit:MapUnit) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps this unit with the unit on the given tile
|
||||
* Precondition: this unit can swap-move to the given tile, as determined by canUnitSwapTo
|
||||
*/
|
||||
fun swapMoveToTile(destination: TileInfo) {
|
||||
val otherUnit = (
|
||||
if (unit.type.isCivilian())
|
||||
destination.civilianUnit
|
||||
else
|
||||
destination.militaryUnit
|
||||
)!! // The precondition guarantees that there is an eligible same-type unit at the destination
|
||||
|
||||
val ourOldPosition = unit.getTile()
|
||||
val theirOldPosition = otherUnit.getTile()
|
||||
|
||||
// Swap the units
|
||||
otherUnit.removeFromTile()
|
||||
unit.movement.moveToTile(destination)
|
||||
unit.removeFromTile()
|
||||
otherUnit.putInTile(theirOldPosition)
|
||||
otherUnit.movement.moveToTile(ourOldPosition)
|
||||
unit.putInTile(theirOldPosition)
|
||||
}
|
||||
|
||||
/**
|
||||
* Designates whether we can enter the tile - without attacking
|
||||
|
@ -9,6 +9,7 @@ data class UnitAction(
|
||||
)
|
||||
|
||||
enum class UnitActionType(val value: String) {
|
||||
SwapUnits("Swap units"),
|
||||
Automate("Automate"),
|
||||
StopAutomation("Stop automation"),
|
||||
StopMovement("Stop movement"),
|
||||
|
@ -49,8 +49,12 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
continuousScrollingX = tileMap.mapParameters.worldWrap
|
||||
}
|
||||
|
||||
// Used to transfer data on the "move here" button that should be created, from the side thread to the main thread
|
||||
class MoveHereButtonDto(val unitToTurnsToDestination: HashMap<MapUnit, Int>, val tileInfo: TileInfo)
|
||||
// Interface for classes that contain the data required to draw a button
|
||||
interface ButtonDto
|
||||
// Contains the data required to draw a "move here" button
|
||||
class MoveHereButtonDto(val unitToTurnsToDestination: HashMap<MapUnit, Int>, val tileInfo: TileInfo) : ButtonDto
|
||||
// Contains the data required to draw a "swap with" button
|
||||
class SwapWithButtonDto(val unit: MapUnit, val tileInfo: TileInfo) : ButtonDto
|
||||
|
||||
internal fun addTiles() {
|
||||
val tileSetStrings = TileSetStrings()
|
||||
@ -92,6 +96,14 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
thread {
|
||||
val tile = tileGroup.tileInfo
|
||||
|
||||
if (worldScreen.bottomUnitTable.selectedUnitIsSwapping) {
|
||||
if (unit.movement.canUnitSwapTo(tile)) {
|
||||
swapMoveUnitToTargetTile(unit, tile)
|
||||
}
|
||||
// If we are in unit-swapping mode, we don't want to move or attack
|
||||
return@thread
|
||||
}
|
||||
|
||||
val attackableTile = BattleHelper.getAttackableEnemies(unit, unit.movement.getDistanceToTiles())
|
||||
.firstOrNull { it.tileToAttack == tileGroup.tileInfo }
|
||||
if (unit.canAttack() && attackableTile != null) {
|
||||
@ -105,7 +117,6 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
moveUnitToTargetTile(listOf(unit), tile)
|
||||
return@thread
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -128,17 +139,28 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
val unitTable = worldScreen.bottomUnitTable
|
||||
val previousSelectedUnits = unitTable.selectedUnits.toList() // create copy
|
||||
val previousSelectedCity = unitTable.selectedCity
|
||||
val previousSelectedUnitIsSwapping = unitTable.selectedUnitIsSwapping
|
||||
unitTable.tileSelected(tileInfo)
|
||||
val newSelectedUnit = unitTable.selectedUnit
|
||||
|
||||
if (previousSelectedUnits.isNotEmpty() && previousSelectedUnits.any { it.getTile() != tileInfo }
|
||||
&& worldScreen.isPlayersTurn
|
||||
&& previousSelectedUnits.any {
|
||||
it.movement.canMoveTo(tileInfo) ||
|
||||
it.movement.isUnknownTileWeShouldAssumeToBePassable(tileInfo) && !it.type.isAirUnit()
|
||||
}) {
|
||||
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
|
||||
addTileOverlaysWithUnitMovement(previousSelectedUnits, tileInfo)
|
||||
&& (
|
||||
if (previousSelectedUnitIsSwapping)
|
||||
previousSelectedUnits.first().movement.canUnitSwapTo(tileInfo)
|
||||
else
|
||||
previousSelectedUnits.any {
|
||||
it.movement.canMoveTo(tileInfo) ||
|
||||
it.movement.isUnknownTileWeShouldAssumeToBePassable(tileInfo) && !it.type.isAirUnit()
|
||||
}
|
||||
)) {
|
||||
if (previousSelectedUnitIsSwapping) {
|
||||
addTileOverlaysWithUnitSwapping(previousSelectedUnits.first(), tileInfo)
|
||||
}
|
||||
else {
|
||||
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
|
||||
addTileOverlaysWithUnitMovement(previousSelectedUnits, tileInfo)
|
||||
}
|
||||
} else addTileOverlays(tileInfo) // no unit movement but display the units in the tile etc.
|
||||
|
||||
|
||||
@ -157,7 +179,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
}
|
||||
|
||||
|
||||
fun moveUnitToTargetTile(selectedUnits: List<MapUnit>, targetTile: TileInfo) {
|
||||
private fun moveUnitToTargetTile(selectedUnits: List<MapUnit>, targetTile: TileInfo) {
|
||||
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
|
||||
// THIS PART IS REALLY ANNOYING
|
||||
// So lets say you have 2 units you want to move in the same direction, right
|
||||
@ -204,6 +226,20 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
}
|
||||
}
|
||||
|
||||
private fun swapMoveUnitToTargetTile(selectedUnit: MapUnit, targetTile: TileInfo) {
|
||||
selectedUnit.movement.swapMoveToTile(targetTile)
|
||||
|
||||
if (selectedUnit.action == Constants.unitActionExplore || selectedUnit.isMoving())
|
||||
selectedUnit.action = null // remove explore on manual swap-move
|
||||
|
||||
// Perhaps something like a swish-swoosh would be clearer
|
||||
Sounds.play(UncivSound.Whoosh)
|
||||
|
||||
if (selectedUnit.currentMovement > 0) worldScreen.bottomUnitTable.selectUnit(selectedUnit)
|
||||
|
||||
worldScreen.shouldUpdate = true
|
||||
removeUnitActionOverlay()
|
||||
}
|
||||
|
||||
private fun addTileOverlaysWithUnitMovement(selectedUnits: List<MapUnit>, tileInfo: TileInfo) {
|
||||
thread(name = "TurnsToGetThere") {
|
||||
@ -250,15 +286,37 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
}
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTileOverlays(tileInfo: TileInfo, moveHereDto: MoveHereButtonDto? = null) {
|
||||
private fun addTileOverlaysWithUnitSwapping(selectedUnit: MapUnit, tileInfo: TileInfo) {
|
||||
if (!selectedUnit.movement.canUnitSwapTo(tileInfo)) { // give the regular tile overlays with no unit swapping
|
||||
addTileOverlays(tileInfo)
|
||||
worldScreen.shouldUpdate = true
|
||||
return
|
||||
}
|
||||
if (UncivGame.Current.settings.singleTapMove) {
|
||||
swapMoveUnitToTargetTile(selectedUnit, tileInfo)
|
||||
}
|
||||
else {
|
||||
// Add "swap with" button
|
||||
val swapWithButtonDto = SwapWithButtonDto(selectedUnit, tileInfo)
|
||||
addTileOverlays(tileInfo, swapWithButtonDto)
|
||||
}
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
|
||||
private fun addTileOverlays(tileInfo: TileInfo, buttonDto: ButtonDto? = null) {
|
||||
for (group in tileGroups[tileInfo]!!) {
|
||||
val table = Table().apply { defaults().pad(10f) }
|
||||
if (moveHereDto != null && worldScreen.canChangeState)
|
||||
table.add(getMoveHereButton(moveHereDto))
|
||||
if (buttonDto != null && worldScreen.canChangeState)
|
||||
table.add(
|
||||
when (buttonDto) {
|
||||
is MoveHereButtonDto -> getMoveHereButton(buttonDto)
|
||||
is SwapWithButtonDto -> getSwapWithButton(buttonDto)
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
|
||||
val unitList = ArrayList<MapUnit>()
|
||||
if (tileInfo.isCityCenter()
|
||||
@ -297,7 +355,6 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
val numberCircle = ImageGetter.getCircle().apply { width = size / 2; height = size / 2;color = Color.BLUE }
|
||||
moveHereButton.addActor(numberCircle)
|
||||
|
||||
|
||||
moveHereButton.addActor(dto.unitToTurnsToDestination.values.maxOrNull()!!.toLabel().apply { center(numberCircle) })
|
||||
val firstUnit = dto.unitToTurnsToDestination.keys.first()
|
||||
val unitIcon = if (dto.unitToTurnsToDestination.size == 1) UnitGroup(firstUnit, size / 2)
|
||||
@ -319,6 +376,27 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
return moveHereButton
|
||||
}
|
||||
|
||||
private fun getSwapWithButton(dto: SwapWithButtonDto): Group {
|
||||
val size = 60f
|
||||
val swapWithButton = Group().apply { width = size;height = size; }
|
||||
swapWithButton.addActor(ImageGetter.getCircle().apply { width = size; height = size })
|
||||
swapWithButton.addActor(ImageGetter.getImage("OtherIcons/Swap")
|
||||
.apply { color = Color.BLACK; width = size / 2; height = size / 2; center(swapWithButton) })
|
||||
|
||||
val unitIcon = UnitGroup(dto.unit, size / 2)
|
||||
unitIcon.y = size - unitIcon.height
|
||||
swapWithButton.addActor(unitIcon)
|
||||
|
||||
swapWithButton.onClick(UncivSound.Silent) {
|
||||
UncivGame.Current.settings.addCompletedTutorialTask("Move unit")
|
||||
if (dto.unit.type.isAirUnit())
|
||||
UncivGame.Current.settings.addCompletedTutorialTask("Move an air unit")
|
||||
swapMoveUnitToTargetTile(dto.unit, dto.tileInfo)
|
||||
}
|
||||
|
||||
return swapWithButton
|
||||
}
|
||||
|
||||
|
||||
private fun addOverlayOnTileGroup(group: TileGroup, actor: Actor) {
|
||||
|
||||
@ -396,16 +474,32 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
group.selectUnit(unit)
|
||||
}
|
||||
|
||||
// Fade out less relevant images if a military unit is selected
|
||||
val fadeout = if (unit.type.isCivilian()) 1f
|
||||
else 0.5f
|
||||
for (tile in allWorldTileGroups) {
|
||||
if (tile.icons.populationIcon != null) tile.icons.populationIcon!!.color.a = fadeout
|
||||
if (tile.icons.improvementIcon != null && tile.tileInfo.improvement != Constants.barbarianEncampment
|
||||
&& tile.tileInfo.improvement != Constants.ancientRuins)
|
||||
tile.icons.improvementIcon!!.color.a = fadeout
|
||||
if (tile.resourceImage != null) tile.resourceImage!!.color.a = fadeout
|
||||
}
|
||||
|
||||
if (worldScreen.bottomUnitTable.selectedUnitIsSwapping) {
|
||||
val unitSwappableTiles = unit.movement.getUnitSwappableTiles()
|
||||
val swapUnitsTileOverlayColor = Color.PURPLE
|
||||
for (tile in unitSwappableTiles) {
|
||||
for (tileToColor in tileGroups[tile]!!) {
|
||||
tileToColor.showCircle(swapUnitsTileOverlayColor,
|
||||
if (UncivGame.Current.settings.singleTapMove) 0.7f else 0.3f)
|
||||
}
|
||||
}
|
||||
return // We don't want to show normal movement or attack overlays in unit-swapping mode
|
||||
}
|
||||
|
||||
val isAirUnit = unit.type.isAirUnit()
|
||||
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()
|
||||
val tilesInMoveRange = unit.movement.getReachableTilesInCurrentTurn()
|
||||
|
||||
for (tile in tilesInMoveRange) {
|
||||
for (tileToColor in tileGroups[tile]!!) {
|
||||
@ -450,18 +544,6 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
||||
else Color.RED
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Fade out less relevant images if a military unit is selected
|
||||
val fadeout = if (unit.type.isCivilian()) 1f
|
||||
else 0.5f
|
||||
for (tile in allWorldTileGroups) {
|
||||
if (tile.icons.populationIcon != null) tile.icons.populationIcon!!.color.a = fadeout
|
||||
if (tile.icons.improvementIcon != null && tile.tileInfo.improvement != Constants.barbarianEncampment
|
||||
&& tile.tileInfo.improvement != Constants.ancientRuins)
|
||||
tile.icons.improvementIcon!!.color.a = fadeout
|
||||
if (tile.resourceImage != null) tile.resourceImage!!.color.a = fadeout
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,7 @@ object UnitActions {
|
||||
)
|
||||
}
|
||||
|
||||
addSwapAction(unit, actionList, worldScreen)
|
||||
addExplorationActions(unit, actionList)
|
||||
addPromoteAction(unit, actionList)
|
||||
addUnitUpgradeAction(unit, actionList)
|
||||
@ -64,6 +65,27 @@ object UnitActions {
|
||||
return actionList
|
||||
}
|
||||
|
||||
private fun addSwapAction(unit: MapUnit, actionList: ArrayList<UnitAction>, worldScreen: WorldScreen) {
|
||||
// Air units cannot swap
|
||||
if (unit.type.isAirUnit()) 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
|
||||
// have the visual bug that the tile overlays for the eligible swap locations are drawn for
|
||||
// /all/ selected units instead of only the first one. This could be fixed, but again,
|
||||
// swapping makes little sense for multiselect anyway.
|
||||
if (worldScreen.bottomUnitTable.selectedUnits.count() > 1) return
|
||||
// Only show the swap action if there is at least one possible swap movement
|
||||
if (unit.movement.getUnitSwappableTiles().none()) return
|
||||
actionList += UnitAction(
|
||||
type = UnitActionType.SwapUnits,
|
||||
isCurrentAction = worldScreen.bottomUnitTable.selectedUnitIsSwapping,
|
||||
action = {
|
||||
worldScreen.bottomUnitTable.selectedUnitIsSwapping = !worldScreen.bottomUnitTable.selectedUnitIsSwapping
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun addDisbandAction(actionList: ArrayList<UnitAction>, unit: MapUnit, worldScreen: WorldScreen) {
|
||||
actionList += UnitAction(type = UnitActionType.DisbandUnit, action = {
|
||||
|
@ -40,6 +40,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() {
|
||||
else -> when (unitAction) {
|
||||
"Move unit" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement"))
|
||||
"Stop movement" -> return UnitIconAndKey(ImageGetter.getStatIcon("Movement").apply { color = Color.RED }, '.')
|
||||
"Swap units" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Swap"), 'y')
|
||||
"Promote" -> return UnitIconAndKey(ImageGetter.getImage("OtherIcons/Star").apply { color = Color.GOLD }, 'o')
|
||||
"Construct improvement" -> return UnitIconAndKey(ImageGetter.getUnitIcon(Constants.worker), 'i')
|
||||
"Automate" -> return UnitIconAndKey(ImageGetter.getUnitIcon("Great Engineer"), 'm')
|
||||
|
@ -32,12 +32,15 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
||||
/** This is in preparation for multi-select and multi-move */
|
||||
val selectedUnits = ArrayList<MapUnit>()
|
||||
|
||||
// Whether the (first) selected unit is in unit-swapping mode
|
||||
var selectedUnitIsSwapping = false
|
||||
|
||||
/** Sending no unit clears the selected units entirely */
|
||||
fun selectUnit(unit:MapUnit?=null, append:Boolean=false) {
|
||||
if (!append) selectedUnits.clear()
|
||||
selectedCity = null
|
||||
if (unit != null) selectedUnits.add(unit)
|
||||
selectedUnitIsSwapping = false
|
||||
}
|
||||
|
||||
var selectedCity : CityInfo? = null
|
||||
@ -223,13 +226,16 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
||||
val previouslySelectedUnit = selectedUnit
|
||||
val previousNumberOfSelectedUnits = selectedUnits.size
|
||||
|
||||
// Do not select a different unit or city center if we click on it to swap our current unit to it
|
||||
if (selectedUnitIsSwapping && selectedUnit != null && selectedUnit!!.movement.canUnitSwapTo(selectedTile)) return
|
||||
|
||||
if (selectedTile.isCityCenter()
|
||||
&& (selectedTile.getOwner() == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) {
|
||||
citySelected(selectedTile.getCity()!!)
|
||||
} else if (selectedTile.militaryUnit != null
|
||||
&& (selectedTile.militaryUnit!!.civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())
|
||||
&& selectedTile.militaryUnit!! !in selectedUnits
|
||||
&& (selectedTile.civilianUnit == null || selectedUnit != selectedTile.civilianUnit)) {
|
||||
&& (selectedTile.civilianUnit == null || selectedUnit != selectedTile.civilianUnit)) { // Only select the military unit there if we do not currently have the civilian unit selected
|
||||
selectUnit(selectedTile.militaryUnit!!, Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT))
|
||||
} else if (selectedTile.civilianUnit != null
|
||||
&& (selectedTile.civilianUnit!!.civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())
|
||||
|
@ -509,6 +509,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
|
||||
|
||||
* [Circle](https://thenounproject.com/term/circle/1841891/) By Aybige
|
||||
* [Arrow](https://thenounproject.com/term/arrow/18123/) By Joe Mortell for movement
|
||||
* [Swap](https://thenounproject.com/search/?q=swap&i=1259600) By iconomania for swapping units
|
||||
* [Connection](https://thenounproject.com/search/?q=connection&i=1521886) By Travis Avery
|
||||
* [Skull](https://thenounproject.com/search/?q=Skull&i=1030702) By Vladimir Belochkin for disbanding units
|
||||
* [Crosshair](https://thenounproject.com/search/?q=crosshairs&i=916030) By Bakunetsu Kaito for selecting enemies to attack
|
||||
|
Loading…
Reference in New Issue
Block a user