mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-06 00:09:23 +07:00
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:
@ -151,7 +151,7 @@
|
|||||||
Russian:"Бомбардная сила"
|
Russian:"Бомбардная сила"
|
||||||
French:"Force de bombardement"
|
French:"Force de bombardement"
|
||||||
Romanian:"Puterea bombardamentului"
|
Romanian:"Puterea bombardamentului"
|
||||||
German:"Bombardieren Sie die Stärke"
|
German:"Stärke Bombardierung"
|
||||||
Dutch:"Bombard sterkte"
|
Dutch:"Bombard sterkte"
|
||||||
Spanish:"Fuerza de bombardeo"
|
Spanish:"Fuerza de bombardeo"
|
||||||
Simplified_Chinese:"远程轰击"
|
Simplified_Chinese:"远程轰击"
|
||||||
@ -803,9 +803,15 @@
|
|||||||
"Check for idle units":{
|
"Check for idle units":{
|
||||||
Italian:"Controlla unità inutilizzate"
|
Italian:"Controlla unità inutilizzate"
|
||||||
Simplified_Chinese:"查看未行动单位"
|
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":{
|
"Close":{
|
||||||
Italian:"Chiudi"
|
Italian:"Chiudi"
|
||||||
Russian:"Закрыть"
|
Russian:"Закрыть"
|
||||||
|
@ -6,6 +6,7 @@ class GameSettings {
|
|||||||
var showWorkedTiles: Boolean = false
|
var showWorkedTiles: Boolean = false
|
||||||
var showResourcesAndImprovements: Boolean = true
|
var showResourcesAndImprovements: Boolean = true
|
||||||
var checkForDueUnits: Boolean = true
|
var checkForDueUnits: Boolean = true
|
||||||
|
var singleTapMove: Boolean = false
|
||||||
var language: String = "English"
|
var language: String = "English"
|
||||||
var resolution: String = "1050x700"
|
var resolution: String = "1050x700"
|
||||||
var tutorialsShown = ArrayList<String>()
|
var tutorialsShown = ArrayList<String>()
|
||||||
|
@ -445,11 +445,9 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun showCircle(color: Color, alpha: Float = 0.3f) {
|
||||||
|
|
||||||
fun showCircle(color: Color) {
|
|
||||||
circleImage.isVisible = true
|
circleImage.isVisible = true
|
||||||
circleImage.color = color.cpy().apply { a=0.3f }
|
circleImage.color = color.cpy().apply { a=alpha }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideCircle() {
|
fun hideCircle() {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.unciv.ui.worldscreen
|
package com.unciv.ui.worldscreen
|
||||||
|
|
||||||
|
import com.badlogic.gdx.Gdx
|
||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.math.Interpolation
|
import com.badlogic.gdx.math.Interpolation
|
||||||
import com.badlogic.gdx.math.Vector2
|
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
|
// 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)
|
class MoveHereButtonDto(val unit: MapUnit, val tileInfo: TileInfo, val turnsToGetThere: Int)
|
||||||
var moveHereButtonDto :MoveHereButtonDto?=null
|
|
||||||
|
|
||||||
internal fun addTiles() {
|
internal fun addTiles() {
|
||||||
|
|
||||||
@ -59,6 +59,8 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
|||||||
var lastInitialDistance = 0f
|
var lastInitialDistance = 0f
|
||||||
|
|
||||||
override fun zoom(event: InputEvent?, initialDistance: Float, distance: Float) {
|
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) {
|
if (lastInitialDistance != initialDistance) {
|
||||||
lastInitialDistance = initialDistance
|
lastInitialDistance = initialDistance
|
||||||
lastScale = scaleX
|
lastScale = scaleX
|
||||||
@ -79,10 +81,14 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
|||||||
selectedTile = tileInfo
|
selectedTile = tileInfo
|
||||||
|
|
||||||
val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit
|
val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit
|
||||||
|
worldScreen.bottomBar.unitTable.tileSelected(tileInfo)
|
||||||
|
|
||||||
if (selectedUnit != null && selectedUnit.getTile() != 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
|
// 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){
|
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.bottomBar.unitTable.tileSelected(tileInfo)
|
||||||
|
|
||||||
worldScreen.shouldUpdate = true
|
worldScreen.shouldUpdate = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun queueAddMoveHereButton(selectedUnit: MapUnit, tileInfo: TileInfo) {
|
private fun moveHere(selectedUnit: MapUnit, tileInfo: TileInfo) {
|
||||||
thread {
|
thread {
|
||||||
/** LibGdx sometimes has these weird errors when you try to edit the UI layout from 2 separate threads.
|
/** 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.
|
* 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.
|
* 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
|
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,
|
Gdx.app.postRunnable {
|
||||||
// which will add the move here button *on the main thread*! Problem solved!
|
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
|
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){
|
if (worldScreen.bottomBar.unitTable.selectedCity!=null){
|
||||||
val city = worldScreen.bottomBar.unitTable.selectedCity!!
|
val city = worldScreen.bottomBar.unitTable.selectedCity!!
|
||||||
updateTilegroupsForSelectedCity(city, playerViewableTilePositions)
|
updateTilegroupsForSelectedCity(city, playerViewableTilePositions)
|
||||||
@ -224,11 +236,12 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTilegroupsForSelectedUnit(unit: MapUnit, playerViewableTilePositions: HashSet<Vector2>) {
|
private fun updateTilegroupsForSelectedUnit(unit: MapUnit, playerViewableTilePositions: HashSet<Vector2>) {
|
||||||
|
|
||||||
tileGroups[unit.getTile()]!!.selectUnit(unit)
|
tileGroups[unit.getTile()]!!.selectUnit(unit)
|
||||||
|
|
||||||
for (tile: TileInfo in unit.getDistanceToTiles().keys)
|
for (tile: TileInfo in unit.getDistanceToTiles().keys)
|
||||||
if (unit.canMoveTo(tile))
|
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 unitType = unit.type
|
||||||
val attackableTiles: List<TileInfo> = when {
|
val attackableTiles: List<TileInfo> = when {
|
||||||
@ -260,9 +273,10 @@ 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 }
|
val tileGroup = tileGroups.values.first { it.tileInfo.position == vector }
|
||||||
selectedTile = tileGroup.tileInfo
|
selectedTile = tileGroup.tileInfo
|
||||||
|
if(selectUnit)
|
||||||
worldScreen.bottomBar.unitTable.tileSelected(selectedTile!!)
|
worldScreen.bottomBar.unitTable.tileSelected(selectedTile!!)
|
||||||
|
|
||||||
val originalScrollX = scrollX
|
val originalScrollX = scrollX
|
||||||
|
@ -230,7 +230,8 @@ class WorldScreen : CameraStageBaseScreen() {
|
|||||||
if (currentPlayerCiv.shouldGoToDueUnit()) {
|
if (currentPlayerCiv.shouldGoToDueUnit()) {
|
||||||
val nextDueUnit = currentPlayerCiv.getNextDueUnit()
|
val nextDueUnit = currentPlayerCiv.getNextDueUnit()
|
||||||
if(nextDueUnit!=null) {
|
if(nextDueUnit!=null) {
|
||||||
tileMapHolder.setCenterPosition(nextDueUnit.currentTile.position)
|
tileMapHolder.setCenterPosition(nextDueUnit.currentTile.position, false, false)
|
||||||
|
bottomBar.unitTable.selectedUnit = nextDueUnit
|
||||||
shouldUpdate=true
|
shouldUpdate=true
|
||||||
}
|
}
|
||||||
return@onClick
|
return@onClick
|
||||||
|
@ -56,6 +56,12 @@ class WorldScreenOptionsTable(screen:WorldScreen) : PopupTable(screen){
|
|||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add("Move units with a single tap".toLabel())
|
||||||
|
addButton(if(settings.singleTapMove) "Yes".tr() else "No".tr()) {
|
||||||
|
settings.singleTapMove = !settings.singleTapMove
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
addLanguageSelectBox()
|
addLanguageSelectBox()
|
||||||
|
|
||||||
addResolutionSelectBox()
|
addResolutionSelectBox()
|
||||||
|
@ -74,7 +74,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
|||||||
touchable = Touchable.enabled
|
touchable = Touchable.enabled
|
||||||
onClick {
|
onClick {
|
||||||
selectedUnit?.currentTile?.position?.let {
|
selectedUnit?.currentTile?.position?.let {
|
||||||
worldScreen.tileMapHolder.setCenterPosition(it)
|
worldScreen.tileMapHolder.setCenterPosition(it, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).expand()
|
}).expand()
|
||||||
@ -172,7 +172,6 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
|||||||
for(promotion in selectedUnit!!.promotions.promotions)
|
for(promotion in selectedUnit!!.promotions.promotions)
|
||||||
promotionsTable.add(ImageGetter.getPromotionIcon(promotion)).size(20f)
|
promotionsTable.add(ImageGetter.getPromotionIcon(promotion)).size(20f)
|
||||||
|
|
||||||
unitDescriptionTable.onClick { worldScreen.tileMapHolder.setCenterPosition(selectedUnit!!.getTile().position) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pack()
|
pack()
|
||||||
@ -205,7 +204,8 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if(selectedTile.militaryUnit!=null && selectedTile.militaryUnit!!.civInfo == worldScreen.currentPlayerCiv
|
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
|
selectedUnit = selectedTile.militaryUnit
|
||||||
selectedCity = null
|
selectedCity = null
|
||||||
}
|
}
|
||||||
@ -213,6 +213,10 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){
|
|||||||
&& selectedUnit!=selectedTile.civilianUnit){
|
&& selectedUnit!=selectedTile.civilianUnit){
|
||||||
selectedUnit = selectedTile.civilianUnit
|
selectedUnit = selectedTile.civilianUnit
|
||||||
selectedCity = null
|
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)
|
if(selectedUnit != previouslySelectedUnit)
|
||||||
|
Reference in New Issue
Block a user