show long-press context menu (move here, Construct road this way)

This commit is contained in:
martin 2019-05-20 23:44:21 +02:00 committed by Yair Morgenstern
parent c83844f600
commit 86eda6208a
2 changed files with 130 additions and 49 deletions

View File

@ -20,6 +20,7 @@ import com.unciv.logic.map.TileMap
import com.unciv.models.gamebasics.unit.UnitType
import com.unciv.ui.tilegroups.WorldTileGroup
import com.unciv.ui.utils.*
import com.unciv.ui.worldscreen.unit.UnitContextMenu
import kotlin.concurrent.thread
class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: TileMap) : ScrollPane(null) {
@ -41,7 +42,15 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
val allTiles = TileGroupMap(daTileGroups,worldScreen.stage.width)
for(tileGroup in tileGroups.values){
tileGroup.onClick{ onTileClicked(tileGroup.tileInfo)}
tileGroup.addListener (object: ActorGestureListener() {
override fun tap(event: InputEvent?, x: Float, y: Float, count: Int, button: Int) {
onTileClicked(tileGroup.tileInfo)
}
override fun longPress(actor: Actor?, x: Float, y: Float): Boolean {
return onTileLongClicked(tileGroup.tileInfo)
}
})
}
actor = allTiles
@ -77,7 +86,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
private fun onTileClicked(tileInfo: TileInfo) {
worldScreen.displayTutorials("TileClicked")
if (unitActionOverlay != null) unitActionOverlay!!.remove()
unitActionOverlay?.remove()
selectedTile = tileInfo
val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit
@ -122,7 +131,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
} else {
// add "move to" button
val moveHereButtonDto = MoveHereButtonDto(selectedUnit, tileInfo, turnsToGetThere)
addMoveHereButtonToTile(moveHereButtonDto, tileGroups[moveHereButtonDto.tileInfo]!!)
addMoveHereButtonToTile(moveHereButtonDto)
}
worldScreen.shouldUpdate = true
}
@ -131,7 +140,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
}
private fun addMoveHereButtonToTile(dto: MoveHereButtonDto, tileGroup: WorldTileGroup) {
private fun addMoveHereButtonToTile(dto: MoveHereButtonDto) {
val size = 60f
val moveHereButton = Group().apply { width = size;height = size; }
moveHereButton.addActor(ImageGetter.getCircle().apply { width = size; height = size })
@ -148,48 +157,45 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap:
moveHereButton.addActor(unitIcon)
if (dto.unit.currentMovement > 0)
moveHereButton.onClick(""){onMoveButtonClick(dto)}
else moveHereButton.color.a = 0.5f
addOverlayOnTileGroup(tileGroup, moveHereButton)
moveHereButton.y += tileGroup.height
unitActionOverlay = moveHereButton
}
private fun onMoveButtonClick(dto: MoveHereButtonDto) {
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
thread {
if (dto.unit.movementAlgs().canReach(dto.tileInfo)) {
try {
// Because this is darned concurrent (as it MUST be to avoid ANRs),
// there are edge cases where the canReach is true,
// but until it reaches the headTowards the board has changed and so the headTowards fails.
// I can't think of any way to avoid this,
// but it's so rare and edge-case-y that ignoring its failure is actually acceptable, hence the empty catch
dto.unit.movementAlgs().headTowards(dto.tileInfo)
Sounds.play("whoosh")
if (dto.unit.currentTile != dto.tileInfo)
dto.unit.action = "moveTo " + dto.tileInfo.position.x.toInt() + "," + dto.tileInfo.position.y.toInt()
if(dto.unit.currentMovement>0){
worldScreen.bottomBar.unitTable.selectedUnit=dto.unit
}
} catch (e: Exception) {
}
moveHereButton.onClick(""){
UnitContextMenu(this, dto.unit, dto.tileInfo).onMoveButtonClick()
}
// we don't update it directly because we're on a different thread; instead, we tell it to update itself
worldScreen.shouldUpdate = true
removeUnitActionOverlay=true
}
else moveHereButton.color.a = 0.5f
addOverlayOnTileGroup(dto.tileInfo, moveHereButton)
}
private fun addOverlayOnTileGroup(group:WorldTileGroup, actor: Actor) {
fun onTileLongClicked(tileInfo: TileInfo): Boolean {
unitActionOverlay?.remove()
selectedTile = tileInfo
val selectedUnit = worldScreen.bottomBar.unitTable.selectedUnit
worldScreen.bottomBar.unitTable.tileSelected(tileInfo)
worldScreen.shouldUpdate = true
if (selectedUnit != null) {
addOverlayOnTileGroup(tileInfo, UnitContextMenu(this, selectedUnit, tileInfo))
return true
}
return false
}
private fun addOverlayOnTileGroup(tileInfo: TileInfo, actor: Actor) {
val group = tileGroups[tileInfo]!!
actor.center(group)
actor.x+=group.x
actor.y+=group.y
group.parent.addActor(actor)
actor.toFront()
actor.y += actor.height
unitActionOverlay = actor
}
internal fun updateTiles(civInfo: CivilizationInfo) {
@ -217,20 +223,23 @@ 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 (worldScreen.bottomBar.unitTable.selectedCity!=null){
val city = worldScreen.bottomBar.unitTable.selectedCity!!
updateTilegroupsForSelectedCity(city, playerViewableTilePositions)
} else if(worldScreen.bottomBar.unitTable.selectedUnit!=null){
val unit = worldScreen.bottomBar.unitTable.selectedUnit!!
updateTilegroupsForSelectedUnit(unit, playerViewableTilePositions)
}
else if(unitActionOverlay!=null){
unitActionOverlay!!.remove()
unitActionOverlay=null
val unitTable = worldScreen.bottomBar.unitTable
when {
unitTable.selectedCity!=null -> {
val city = unitTable.selectedCity!!
updateTilegroupsForSelectedCity(city, playerViewableTilePositions)
}
unitTable.selectedUnit!=null -> {
val unit = unitTable.selectedUnit!!
updateTilegroupsForSelectedUnit(unit, playerViewableTilePositions)
}
unitActionOverlay!=null -> {
unitActionOverlay!!.remove()
unitActionOverlay=null
}
}
if(selectedTile!=null)
tileGroups[selectedTile!!]!!.showCircle(Color.WHITE)
tileGroups[selectedTile]?.showCircle(Color.WHITE)
}
private fun updateTilegroupsForSelectedUnit(unit: MapUnit, playerViewableTilePositions: HashSet<Vector2>) {

View File

@ -0,0 +1,72 @@
package com.unciv.ui.worldscreen.unit
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo
import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.Sounds
import com.unciv.ui.utils.onClick
import com.unciv.ui.worldscreen.TileMapHolder
import kotlin.concurrent.thread
class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUnit, val targetTile: TileInfo) : VerticalGroup() {
init {
space(10f)
addButton(ImageGetter.getStatIcon("Movement"), "Move here") {
onMoveButtonClick()
}
addButton(ImageGetter.getImprovementIcon("Road"), "Construct Road this way") {
onConstructRoadButtonClick()
}
pack()
}
fun addButton(icon: Actor, label: String, action: () -> Unit) {
val skin = CameraStageBaseScreen.skin
val button = Button(skin)
button.add(icon).size(20f).padRight(10f)
button.add(label)
addActor(button)
button.onClick { action() }
}
fun onMoveButtonClick() {
// this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread
thread {
if (selectedUnit.movementAlgs().canReach(targetTile)) {
try {
// Because this is darned concurrent (as it MUST be to avoid ANRs),
// there are edge cases where the canReach is true,
// but until it reaches the headTowards the board has changed and so the headTowards fails.
// I can't think of any way to avoid this,
// but it's so rare and edge-case-y that ignoring its failure is actually acceptable, hence the empty catch
selectedUnit.movementAlgs().headTowards(targetTile)
Sounds.play("whoosh")
if (selectedUnit.currentTile != targetTile)
selectedUnit.action = "moveTo " + targetTile.position.x.toInt() + "," + targetTile.position.y.toInt()
if(selectedUnit.currentMovement>0){
tileMapHolder.worldScreen.bottomBar.unitTable.selectedUnit=selectedUnit
}
} catch (e: Exception) {
}
}
// we don't update it directly because we're on a different thread; instead, we tell it to update itself
tileMapHolder.worldScreen.shouldUpdate = true
tileMapHolder.removeUnitActionOverlay=true
}
}
private fun onConstructRoadButtonClick() {
// TODO
}
}