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
This commit is contained in:
sulai 2019-05-20 19:08:59 +02:00 committed by Yair Morgenstern
parent 3cfe3a7f73
commit fdf95317f0
7 changed files with 55 additions and 25 deletions

View File

@ -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:"Закрыть"

View File

@ -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<String>()

View File

@ -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() {

View File

@ -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<Vector2>) {
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<TileInfo> = 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

View File

@ -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

View File

@ -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()

View File

@ -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)