From fdf95317f0f7816a0ce49392af1d0fc8b688c035 Mon Sep 17 00:00:00 2001 From: sulai <6741822+sulai@users.noreply.github.com> Date: Mon, 20 May 2019 19:08:59 +0200 Subject: [PATCH] units support single-tap move (#750) * implemented single tap move - units stay selected after move (to go on moving) - and they are de-selectable by taping them again (to abort moving) * unit movement: mark the reachable spots clearly to avoid accidental movement * unit action: when an exclusive decision is made, deselect unit * clicking on the unit information in the UnitTable will show that unit + minor fixes * siege units won't show movement hints when set up, while packing up does not cost any movement points * workers: highlight button when constructing an improvement, won't sleep then * fixed units not being de-selected when clicking the UnitTable or "next unit" button * zooming forwards clicks on to the map, so we need to deselect units in that case * clean up branch * added "Move units with a single tap" to options menu --- android/assets/jsons/Translations/Other.json | 10 ++++- core/src/com/unciv/GameSettings.kt | 1 + core/src/com/unciv/ui/tilegroups/TileGroup.kt | 6 +-- .../com/unciv/ui/worldscreen/TileMapHolder.kt | 44 ++++++++++++------- .../com/unciv/ui/worldscreen/WorldScreen.kt | 3 +- .../optionstable/WorldScreenOptionsTable.kt | 6 +++ .../unciv/ui/worldscreen/unit/UnitTable.kt | 10 +++-- 7 files changed, 55 insertions(+), 25 deletions(-) diff --git a/android/assets/jsons/Translations/Other.json b/android/assets/jsons/Translations/Other.json index 19d470412e..0fccdf848e 100644 --- a/android/assets/jsons/Translations/Other.json +++ b/android/assets/jsons/Translations/Other.json @@ -151,7 +151,7 @@ Russian:"Бомбардная сила" French:"Force de bombardement" Romanian:"Puterea bombardamentului" - German:"Bombardieren Sie die Stärke" + German:"Stärke Bombardierung" Dutch:"Bombard sterkte" Spanish:"Fuerza de bombardeo" Simplified_Chinese:"远程轰击" @@ -803,9 +803,15 @@ "Check for idle units":{ Italian:"Controlla unità inutilizzate" Simplified_Chinese:"查看未行动单位" - Portuguese:"Cheque por unidades sem ordens" + Portuguese:"Cheque por unidades sem ordens", + German: "Untätige Einheiten anzeigen bei Rundenende" } + "Move units with a single tap":{ + German: "Einheiten mit einem Klick bewegen" + }, + + "Close":{ Italian:"Chiudi" Russian:"Закрыть" diff --git a/core/src/com/unciv/GameSettings.kt b/core/src/com/unciv/GameSettings.kt index 7065ca0882..7ae12e29e7 100644 --- a/core/src/com/unciv/GameSettings.kt +++ b/core/src/com/unciv/GameSettings.kt @@ -6,6 +6,7 @@ class GameSettings { var showWorkedTiles: Boolean = false var showResourcesAndImprovements: Boolean = true var checkForDueUnits: Boolean = true + var singleTapMove: Boolean = false var language: String = "English" var resolution: String = "1050x700" var tutorialsShown = ArrayList() diff --git a/core/src/com/unciv/ui/tilegroups/TileGroup.kt b/core/src/com/unciv/ui/tilegroups/TileGroup.kt index c0ec2417f4..1348aea4b4 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroup.kt @@ -445,11 +445,9 @@ open class TileGroup(var tileInfo: TileInfo) : Group() { } - - - fun showCircle(color: Color) { + fun showCircle(color: Color, alpha: Float = 0.3f) { circleImage.isVisible = true - circleImage.color = color.cpy().apply { a=0.3f } + circleImage.color = color.cpy().apply { a=alpha } } fun hideCircle() { diff --git a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt index 8266221db2..7565e8fb67 100644 --- a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt @@ -1,5 +1,6 @@ package com.unciv.ui.worldscreen +import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.math.Interpolation import com.badlogic.gdx.math.Vector2 @@ -30,7 +31,6 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: // Used to transfer data on the "move here" button that should be created, from the side thread to the main thread class MoveHereButtonDto(val unit: MapUnit, val tileInfo: TileInfo, val turnsToGetThere: Int) - var moveHereButtonDto :MoveHereButtonDto?=null internal fun addTiles() { @@ -59,6 +59,8 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: var lastInitialDistance = 0f override fun zoom(event: InputEvent?, initialDistance: Float, distance: Float) { + // deselect any unit, as zooming occasionally forwards clicks on to the map + worldScreen.bottomBar.unitTable.selectedUnit = null if (lastInitialDistance != initialDistance) { lastInitialDistance = initialDistance lastScale = scaleX @@ -79,10 +81,14 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: selectedTile = tileInfo val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit + worldScreen.bottomBar.unitTable.tileSelected(tileInfo) + if (selectedUnit != null && selectedUnit.getTile() != tileInfo - && selectedUnit.canMoveTo(tileInfo) && selectedUnit.movementAlgs().canReach(tileInfo)) { + && selectedUnit.canMoveTo(tileInfo) && selectedUnit.movementAlgs().canReach(tileInfo) + && selectedUnit.action!="Set Up") { // this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread - queueAddMoveHereButton(selectedUnit, tileInfo) + moveHere(selectedUnit, tileInfo) + worldScreen.bottomBar.unitTable.selectedUnit = selectedUnit // keep moved unit selected } if(selectedUnit==null || selectedUnit.type==UnitType.Civilian){ @@ -98,10 +104,11 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: } worldScreen.bottomBar.unitTable.tileSelected(tileInfo) + worldScreen.shouldUpdate = true } - private fun queueAddMoveHereButton(selectedUnit: MapUnit, tileInfo: TileInfo) { + private fun moveHere(selectedUnit: MapUnit, tileInfo: TileInfo) { thread { /** LibGdx sometimes has these weird errors when you try to edit the UI layout from 2 separate threads. * And so, all UI editing will be done on the main thread. @@ -109,9 +116,19 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: * so that and that alone will be relegated to the concurrent thread. */ val turnsToGetThere = selectedUnit.movementAlgs().getShortestPath(tileInfo).size // this is what takes the most time, tbh - moveHereButtonDto = MoveHereButtonDto(selectedUnit, tileInfo, turnsToGetThere) - worldScreen.shouldUpdate = true // when the world screen updates, is calls our updateTiles, - // which will add the move here button *on the main thread*! Problem solved! + + Gdx.app.postRunnable { + if(UnCivGame.Current.settings.singleTapMove && turnsToGetThere==1) { + // single turn instant move + selectedUnit.movementAlgs().headTowards(tileInfo) + } else { + // add "move to" button + val moveHereButtonDto = MoveHereButtonDto(selectedUnit, tileInfo, turnsToGetThere) + addMoveHereButtonToTile(moveHereButtonDto, tileGroups[moveHereButtonDto.tileInfo]!!) + } + worldScreen.shouldUpdate = true + } + } } @@ -202,11 +219,6 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: tileGroup.showCircle(Color.RED) // Display ALL viewable enemies with a red circle so that users don't need to go "hunting" for enemy units } - if(moveHereButtonDto!=null) { - addMoveHereButtonToTile(moveHereButtonDto!!, tileGroups[moveHereButtonDto!!.tileInfo]!!) - moveHereButtonDto=null - } - if (worldScreen.bottomBar.unitTable.selectedCity!=null){ val city = worldScreen.bottomBar.unitTable.selectedCity!! updateTilegroupsForSelectedCity(city, playerViewableTilePositions) @@ -224,11 +236,12 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: } private fun updateTilegroupsForSelectedUnit(unit: MapUnit, playerViewableTilePositions: HashSet) { + tileGroups[unit.getTile()]!!.selectUnit(unit) for (tile: TileInfo in unit.getDistanceToTiles().keys) if (unit.canMoveTo(tile)) - tileGroups[tile]!!.showCircle(colorFromRGB(0, 120, 215)) + tileGroups[tile]!!.showCircle(Color.WHITE, if (UnCivGame.Current.settings.singleTapMove) 0.7f else 0.3f) val unitType = unit.type val attackableTiles: List = when { @@ -260,10 +273,11 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: } } - fun setCenterPosition(vector: Vector2, immediately: Boolean =false) { + fun setCenterPosition(vector: Vector2, immediately: Boolean = false, selectUnit: Boolean = true) { val tileGroup = tileGroups.values.first { it.tileInfo.position == vector } selectedTile = tileGroup.tileInfo - worldScreen.bottomBar.unitTable.tileSelected(selectedTile!!) + if(selectUnit) + worldScreen.bottomBar.unitTable.tileSelected(selectedTile!!) val originalScrollX = scrollX val originalScrollY = scrollY diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index d7bf60a88b..693f1201af 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -230,7 +230,8 @@ class WorldScreen : CameraStageBaseScreen() { if (currentPlayerCiv.shouldGoToDueUnit()) { val nextDueUnit = currentPlayerCiv.getNextDueUnit() if(nextDueUnit!=null) { - tileMapHolder.setCenterPosition(nextDueUnit.currentTile.position) + tileMapHolder.setCenterPosition(nextDueUnit.currentTile.position, false, false) + bottomBar.unitTable.selectedUnit = nextDueUnit shouldUpdate=true } return@onClick diff --git a/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenOptionsTable.kt b/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenOptionsTable.kt index 84913b5b48..8d566dd039 100644 --- a/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenOptionsTable.kt +++ b/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenOptionsTable.kt @@ -56,6 +56,12 @@ class WorldScreenOptionsTable(screen:WorldScreen) : PopupTable(screen){ update() } + add("Move units with a single tap".toLabel()) + addButton(if(settings.singleTapMove) "Yes".tr() else "No".tr()) { + settings.singleTapMove = !settings.singleTapMove + update() + } + addLanguageSelectBox() addResolutionSelectBox() diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index 73dd4af712..4d7a689cff 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -74,7 +74,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ touchable = Touchable.enabled onClick { selectedUnit?.currentTile?.position?.let { - worldScreen.tileMapHolder.setCenterPosition(it) + worldScreen.tileMapHolder.setCenterPosition(it, false, false) } } }).expand() @@ -172,7 +172,6 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ for(promotion in selectedUnit!!.promotions.promotions) promotionsTable.add(ImageGetter.getPromotionIcon(promotion)).size(20f) - unitDescriptionTable.onClick { worldScreen.tileMapHolder.setCenterPosition(selectedUnit!!.getTile().position) } } pack() @@ -205,7 +204,8 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ } else if(selectedTile.militaryUnit!=null && selectedTile.militaryUnit!!.civInfo == worldScreen.currentPlayerCiv - && selectedUnit!=selectedTile.militaryUnit){ + && selectedUnit!=selectedTile.militaryUnit + && (selectedTile.civilianUnit==null || selectedUnit!=selectedTile.civilianUnit)){ selectedUnit = selectedTile.militaryUnit selectedCity = null } @@ -213,6 +213,10 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ && selectedUnit!=selectedTile.civilianUnit){ selectedUnit = selectedTile.civilianUnit selectedCity = null + } else if(selectedTile == previouslySelectedUnit?.currentTile) { + // tapping the same tile again will deselect a unit. + // important for single-tap-move to abort moving easily + selectedUnit = null } if(selectedUnit != previouslySelectedUnit)