World Wrap Part 1 (#3604)

* Adding fake infinite scroll functionality

* improve scroll jump

* really smooth jump now

* World wrap has functionality now

This is not compileable on its own!
Needs branches WorldWrap-v1 and WorldWrap-v2 to be mergeable

* Mirror tiles rendering

* changed to run v2-alternative code

* Use generic type

* default worldWrap to false

* forward continousScrollingX as parameter

* added unit movement button for mirror tiles

* Add clickable tile layer

* Smooth wrap jump

* code a bit better to read

* Removed unnecessary overrides and catches

* mirrorTiles are stored in one HashMap now

instead of two seperate ones
This commit is contained in:
GGGuenni
2021-03-03 11:54:21 +01:00
committed by GitHub
parent f65de81c21
commit 9a7e637891
6 changed files with 201 additions and 80 deletions

View File

@ -4,24 +4,44 @@ import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.scenes.scene2d.Group
import com.unciv.logic.HexMath
import com.unciv.logic.map.TileInfo
import com.unciv.ui.tilegroups.TileGroup
import kotlin.math.max
import kotlin.math.min
class TileGroupMap<T: TileGroup>(val tileGroups: Collection<T>, val padding: Float): Group(){
class TileGroupMap<T: TileGroup>(val tileGroups: Collection<T>, val padding: Float, worldWrap: Boolean = false): Group(){
var topX = -Float.MAX_VALUE
var topY = -Float.MAX_VALUE
var bottomX = Float.MAX_VALUE
var bottomY = Float.MAX_VALUE
val groupSize = 50
private val mirrorTileGroups = HashMap<TileInfo, Pair<T, T>>()
init{
for(tileGroup in tileGroups){
if (worldWrap) {
for(tileGroup in tileGroups) {
mirrorTileGroups[tileGroup.tileInfo] = Pair(tileGroup.clone() as T, tileGroup.clone() as T)
}
}
for(tileGroup in tileGroups) {
val positionalVector = HexMath.hex2WorldCoords(tileGroup.tileInfo.position)
tileGroup.setPosition(positionalVector.x * 0.8f * groupSize.toFloat(),
positionalVector.y * 0.8f * groupSize.toFloat())
topX = max(topX, tileGroup.x + groupSize)
topX =
if (worldWrap)
// Well it's not pretty but it works
// This is so topX is the same no matter what worldWrap is
// wrapped worlds are missing one tile width on the right side
// which would result in a smaller topX
// The resulting topX was always missing 1.2 * groupSize in every possible
// combination of map size and shape
max(topX, tileGroup.x + groupSize * 2.2f)
else
max(topX, tileGroup.x + groupSize)
topY = max(topY, tileGroup.y + groupSize)
bottomX = min(bottomX, tileGroup.x)
bottomY = min(bottomY, tileGroup.y)
@ -31,6 +51,20 @@ class TileGroupMap<T: TileGroup>(val tileGroups: Collection<T>, val padding: Flo
group.moveBy(-bottomX + padding, -bottomY + padding * 0.5f)
}
if (worldWrap) {
for (mirrorTiles in mirrorTileGroups.values){
val positionalVector = HexMath.hex2WorldCoords(mirrorTiles.first.tileInfo.position)
mirrorTiles.first.setPosition(positionalVector.x * 0.8f * groupSize.toFloat(),
positionalVector.y * 0.8f * groupSize.toFloat())
mirrorTiles.first.moveBy(-bottomX + padding - bottomX * 2, -bottomY + padding * 0.5f)
mirrorTiles.second.setPosition(positionalVector.x * 0.8f * groupSize.toFloat(),
positionalVector.y * 0.8f * groupSize.toFloat())
mirrorTiles.second.moveBy(-bottomX + padding + bottomX * 2, -bottomY + padding * 0.5f)
}
}
val baseLayers = ArrayList<Group>()
val featureLayers = ArrayList<Group>()
val miscLayers = ArrayList<Group>()
@ -49,12 +83,30 @@ class TileGroupMap<T: TileGroup>(val tileGroups: Collection<T>, val padding: Flo
unitImageLayers.add(group.unitImageLayerGroup.apply { setPosition(group.x,group.y) })
cityButtonLayers.add(group.cityButtonLayerGroup.apply { setPosition(group.x,group.y) })
circleCrosshairFogLayers.add(group.circleCrosshairFogLayerGroup.apply { setPosition(group.x,group.y) })
if (worldWrap){
for (mirrorTile in mirrorTileGroups[group.tileInfo]!!.toList()){
baseLayers.add(mirrorTile.baseLayerGroup.apply { setPosition(mirrorTile.x,mirrorTile.y) })
featureLayers.add(mirrorTile.terrainFeatureLayerGroup.apply { setPosition(mirrorTile.x,mirrorTile.y) })
miscLayers.add(mirrorTile.miscLayerGroup.apply { setPosition(mirrorTile.x,mirrorTile.y) })
unitLayers.add(mirrorTile.unitLayerGroup.apply { setPosition(mirrorTile.x,mirrorTile.y) })
unitImageLayers.add(mirrorTile.unitImageLayerGroup.apply { setPosition(mirrorTile.x,mirrorTile.y) })
cityButtonLayers.add(mirrorTile.cityButtonLayerGroup.apply { setPosition(mirrorTile.x,mirrorTile.y) })
circleCrosshairFogLayers.add(mirrorTile.circleCrosshairFogLayerGroup.apply { setPosition(mirrorTile.x,mirrorTile.y) })
}
}
}
for(group in baseLayers) addActor(group)
for(group in featureLayers) addActor(group)
for(group in miscLayers) addActor(group)
for(group in circleCrosshairFogLayers) addActor(group)
for(group in tileGroups) addActor(group) // The above layers are for the visual layers, this is for the clickability of the tile
if (worldWrap){
for (mirrorTiles in mirrorTileGroups.values){
addActor(mirrorTiles.first)
addActor(mirrorTiles.second)
}
}
for(group in unitLayers) addActor(group) // Aaand units above everything else.
for(group in unitImageLayers) addActor(group) // This is so the individual textures for the units are rendered together
for(group in cityButtonLayers) addActor(group) // city buttons + clickability
@ -62,7 +114,7 @@ class TileGroupMap<T: TileGroup>(val tileGroups: Collection<T>, val padding: Flo
// there are tiles "below the zero",
// so we zero out the starting position of the whole board so they will be displayed as well
setSize(topX - bottomX + padding * 2, topY - bottomY + padding * 2 * 0.5f)
setSize(topX - bottomX + padding * 2 - groupSize, topY - bottomY + padding * 2 * 0.5f)
}
/**
@ -76,6 +128,7 @@ class TileGroupMap<T: TileGroup>(val tileGroups: Collection<T>, val padding: Flo
.scl(1f / trueGroupSize)
}
fun getMirrorTiles(): HashMap<TileInfo, Pair<T, T>> = mirrorTileGroups
// For debugging purposes
override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) }

View File

@ -133,6 +133,8 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
isTransform = false // performance helper - nothing here is rotated or scaled
}
open fun clone(): TileGroup = TileGroup(tileInfo, tileSetStrings)
//region init functions
private fun addCircleImage() {

View File

@ -63,4 +63,5 @@ class WorldTileGroup(internal val worldScreen: WorldScreen, tileInfo: TileInfo,
return worldScreen.bottomUnitTable.citySelected(city)
}
}
override fun clone(): WorldTileGroup = WorldTileGroup(worldScreen, tileInfo , tileSetStrings)
}

View File

@ -1,13 +1,17 @@
package com.unciv.ui.utils
import com.badlogic.gdx.scenes.scene2d.Event
import com.badlogic.gdx.scenes.scene2d.InputEvent
import com.badlogic.gdx.scenes.scene2d.InputListener
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.utils.ActorGestureListener
import kotlin.math.abs
import kotlin.math.sqrt
open class ZoomableScrollPane: ScrollPane(null) {
var continousScrollingX = false
init{
// Remove the existing inputListener
// which defines that mouse scroll = vertical movement
@ -45,4 +49,30 @@ open class ZoomableScrollPane: ScrollPane(null) {
}
})
}
}
override fun getFlickScrollListener(): ActorGestureListener {
//This is mostly just Java code from the ScrollPane class reimplemented as Kotlin code
//Had to change a few things to bypass private access modifiers
return object : ActorGestureListener() {
override fun pan(event: InputEvent, x: Float, y: Float, deltaX: Float, deltaY: Float) {
setScrollbarsVisible(true)
scrollX -= deltaX
scrollY += deltaY
//this is the new feature to fake an infinite scroll
when {
continousScrollingX && scrollPercentX >= 1 && deltaX < 0 -> {
scrollPercentX = 0f
}
continousScrollingX && scrollPercentX <= 0 && deltaX > 0-> {
scrollPercentX = 1f
}
}
//clamp() call is missing here but it doesn't seem to make any big difference in this case
if ((isScrollX && deltaX != 0f || isScrollY && deltaY != 0f)) cancelTouchFocus()
}
}
}
}

View File

@ -26,6 +26,7 @@ import com.unciv.models.AttackableTile
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.unit.UnitType
import com.unciv.ui.map.TileGroupMap
import com.unciv.ui.tilegroups.TileGroup
import com.unciv.ui.tilegroups.TileSetStrings
import com.unciv.ui.tilegroups.WorldTileGroup
import com.unciv.ui.utils.*
@ -34,9 +35,13 @@ import kotlin.concurrent.thread
class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: TileMap): ZoomableScrollPane() {
internal var selectedTile: TileInfo? = null
val tileGroups = HashMap<TileInfo, WorldTileGroup>()
val tileGroups = HashMap<TileInfo, List<WorldTileGroup>>()
//allWorldTileGroups exists to easily access all WordTileGroups
//since tileGroup is a HashMap of Lists and getting all WordTileGroups
//would need a double for loop
val allWorldTileGroups = ArrayList<WorldTileGroup>()
var unitActionOverlay: Actor? = null
val unitActionOverlays: ArrayList<Actor> = ArrayList()
init {
if (Gdx.app.type == Application.ApplicationType.Desktop) this.setFlingTime(0f)
@ -48,12 +53,26 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
internal fun addTiles() {
val tileSetStrings = TileSetStrings()
val daTileGroups = tileMap.values.map { WorldTileGroup(worldScreen, it, tileSetStrings) }
val tileGroupMap = TileGroupMap(daTileGroups, worldScreen.stage.width, continousScrollingX)
val mirrorTileGroups = tileGroupMap.getMirrorTiles()
for (tileGroup in daTileGroups) tileGroups[tileGroup.tileInfo] = tileGroup
for (tileGroup in daTileGroups) {
if (continousScrollingX){
val mirrorTileGroupLeft = mirrorTileGroups[tileGroup.tileInfo]!!.first
val mirrorTileGroupRight = mirrorTileGroups[tileGroup.tileInfo]!!.second
val allTiles = TileGroupMap(daTileGroups, worldScreen.stage.width)
allWorldTileGroups.add(tileGroup)
allWorldTileGroups.add(mirrorTileGroupLeft)
allWorldTileGroups.add(mirrorTileGroupRight)
for (tileGroup in tileGroups.values) {
tileGroups[tileGroup.tileInfo] = listOf(tileGroup, mirrorTileGroupLeft, mirrorTileGroupRight)
} else {
tileGroups[tileGroup.tileInfo] = listOf(tileGroup)
allWorldTileGroups.add(tileGroup)
}
}
for (tileGroup in allWorldTileGroups) {
tileGroup.cityButtonLayerGroup.onClick(UncivSound.Silent) {
onTileClicked(tileGroup.tileInfo)
}
@ -90,7 +109,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
})
}
actor = allTiles
actor = tileGroupMap
setSize(worldScreen.stage.width * 2, worldScreen.stage.height * 2)
setOrigin(width / 2, height / 2)
@ -101,7 +120,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
}
private fun onTileClicked(tileInfo: TileInfo) {
unitActionOverlay?.remove()
removeUnitActionOverlay()
selectedTile = tileInfo
val unitTable = worldScreen.bottomUnitTable
@ -176,7 +195,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
worldScreen.shouldUpdate = true
if (selectedUnits.size > 1) { // We have more tiles to move
moveUnitToTargetTile(selectedUnits.subList(1, selectedUnits.size), targetTile)
} else unitActionOverlay?.remove() //we're done here
} else removeUnitActionOverlay() //we're done here
} catch (e: Exception) {
}
}
@ -230,34 +249,36 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
}
private fun addTileOverlays(tileInfo: TileInfo, moveHereDto: MoveHereButtonDto? = null) {
val table = Table().apply { defaults().pad(10f) }
if (moveHereDto != null && worldScreen.canChangeState)
table.add(getMoveHereButton(moveHereDto))
for (group in tileGroups[tileInfo]!!){
val table = Table().apply { defaults().pad(10f) }
if (moveHereDto != null && worldScreen.canChangeState)
table.add(getMoveHereButton(moveHereDto))
val unitList = ArrayList<MapUnit>()
if (tileInfo.isCityCenter()
&& (tileInfo.getOwner() == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) {
unitList.addAll(tileInfo.getCity()!!.getCenterTile().getUnits())
} else if (tileInfo.airUnits.isNotEmpty()
&& (tileInfo.airUnits.first().civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) {
unitList.addAll(tileInfo.getUnits())
}
for (unit in unitList) {
val unitGroup = UnitGroup(unit, 60f).surroundWithCircle(80f)
unitGroup.circle.color = Color.GRAY.cpy().apply { a = 0.5f }
if (unit.currentMovement == 0f) unitGroup.color.a = 0.5f
unitGroup.touchable = Touchable.enabled
unitGroup.onClick {
worldScreen.bottomUnitTable.selectUnit(unit, Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT))
worldScreen.shouldUpdate = true
unitActionOverlay?.remove()
val unitList = ArrayList<MapUnit>()
if (tileInfo.isCityCenter()
&& (tileInfo.getOwner() == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) {
unitList.addAll(tileInfo.getCity()!!.getCenterTile().getUnits())
} else if (tileInfo.airUnits.isNotEmpty()
&& (tileInfo.airUnits.first().civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) {
unitList.addAll(tileInfo.getUnits())
}
table.add(unitGroup)
}
addOverlayOnTileGroup(tileInfo, table)
table.moveBy(0f, 60f)
for (unit in unitList) {
val unitGroup = UnitGroup(unit, 60f).surroundWithCircle(80f)
unitGroup.circle.color = Color.GRAY.cpy().apply { a = 0.5f }
if (unit.currentMovement == 0f) unitGroup.color.a = 0.5f
unitGroup.touchable = Touchable.enabled
unitGroup.onClick {
worldScreen.bottomUnitTable.selectUnit(unit, Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT))
worldScreen.shouldUpdate = true
removeUnitActionOverlay()
}
table.add(unitGroup)
}
addOverlayOnTileGroup(group, table)
table.moveBy(0f, 60f)
}
}
private fun getMoveHereButton(dto: MoveHereButtonDto): Group {
@ -293,9 +314,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
}
private fun addOverlayOnTileGroup(tileInfo: TileInfo, actor: Actor) {
val group = tileGroups[tileInfo]!!
private fun addOverlayOnTileGroup(group: TileGroup, actor: Actor) {
actor.center(group)
actor.x += group.x
@ -304,7 +323,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
actor.toFront()
actor.y += actor.height
unitActionOverlay = actor
unitActionOverlays.add(actor)
}
@ -319,12 +338,12 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
// Only needs to be done once - this is so the minimap will also be revealed
if (viewingCiv.exploredTiles.size != tileMap.values.size)
viewingCiv.exploredTiles = tileMap.values.map { it.position }.toHashSet()
tileGroups.values.forEach { it.showEntireMap = true } // So we can see all resources, regardless of tech
allWorldTileGroups.forEach { it.showEntireMap = true } // So we can see all resources, regardless of tech
}
val playerViewableTilePositions = viewingCiv.viewableTiles.map { it.position }.toHashSet()
for (tileGroup in tileGroups.values) {
for (tileGroup in allWorldTileGroups) {
tileGroup.update(viewingCiv)
if (tileGroup.tileInfo.improvement == Constants.barbarianEncampment
@ -349,21 +368,25 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
updateTilegroupsForSelectedUnit(unit, playerViewableTilePositions)
}
}
unitActionOverlay != null -> {
unitActionOverlay!!.remove()
unitActionOverlay = null
unitActionOverlays.isNotEmpty() -> {
removeUnitActionOverlay()
}
}
tileGroups[selectedTile]?.showCircle(Color.WHITE)
for (group in tileGroups[selectedTile]!!) {
group.showCircle(Color.WHITE)
}
zoom(scaleX) // zoom to current scale, to set the size of the city buttons after "next turn"
}
private fun updateTilegroupsForSelectedUnit(unit: MapUnit, playerViewableTilePositions: HashSet<Vector2>) {
val tileGroup = tileGroups[unit.getTile()]
if (tileGroup == null) return // Entirely unclear when this happens, but this seems to happen since version 520 (3.12.9)
val tileGroup = tileGroups[unit.getTile()]?: return
// Entirely unclear when this happens, but this seems to happen since version 520 (3.12.9)
// so maybe has to do with the construction list being asyc?
tileGroup.selectUnit(unit)
for (group in tileGroup){
group.selectUnit(unit)
}
val isAirUnit = unit.type.isAirUnit()
val tilesInMoveRange =
@ -373,19 +396,20 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
unit.movement.getDistanceToTiles().keys.asSequence()
for (tile in tilesInMoveRange) {
val tileToColor = tileGroups.getValue(tile)
if (isAirUnit)
if (tile.aerialDistanceTo(unit.getTile()) <= unit.getRange()) {
// The tile is within attack range
tileToColor.showCircle(Color.RED, 0.3f)
} else {
// The tile is within move range
tileToColor.showCircle(Color.BLUE, 0.3f)
}
if (unit.movement.canMoveTo(tile) ||
unit.movement.isUnknownTileWeShouldAssumeToBePassable(tile) && !unit.type.isAirUnit())
tileToColor.showCircle(Color.WHITE,
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
for (tileToColor in tileGroups[tile]!!){
if (isAirUnit)
if (tile.aerialDistanceTo(unit.getTile()) <= unit.getRange()) {
// The tile is within attack range
tileToColor.showCircle(Color.RED, 0.3f)
} else {
// The tile is within move range
tileToColor.showCircle(Color.BLUE, 0.3f)
}
if (unit.movement.canMoveTo(tile) ||
unit.movement.isUnknownTileWeShouldAssumeToBePassable(tile) && !unit.type.isAirUnit())
tileToColor.showCircle(Color.WHITE,
if (UncivGame.Current.settings.singleTapMove || isAirUnit) 0.7f else 0.3f)
}
}
val attackableTiles: List<AttackableTile> = if (unit.type.isCivilian()) listOf()
@ -399,19 +423,22 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
}
for (attackableTile in attackableTiles) {
tileGroups[attackableTile.tileToAttack]!!.showCircle(colorFromRGB(237, 41, 57))
tileGroups[attackableTile.tileToAttack]!!.showCrosshair(
// the targets which cannot be attacked without movements shown as orange-ish
if (attackableTile.tileToAttackFrom != unit.currentTile)
colorFromRGB(255, 75, 0)
else Color.RED
)
for (tileGroup in tileGroups[attackableTile.tileToAttack]!!){
tileGroup.showCircle(colorFromRGB(237, 41, 57))
tileGroup.showCrosshair(
// the targets which cannot be attacked without movements shown as orange-ish
if (attackableTile.tileToAttackFrom != unit.currentTile)
colorFromRGB(255, 75, 0)
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 tileGroups.values) {
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)
@ -426,14 +453,16 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
val attackableTiles = UnitAutomation.getBombardTargets(city)
.filter { (UncivGame.Current.viewEntireMapForDebug || playerViewableTilePositions.contains(it.position)) }
for (attackableTile in attackableTiles) {
tileGroups[attackableTile]!!.showCircle(colorFromRGB(237, 41, 57))
tileGroups[attackableTile]!!.showCrosshair(Color.RED)
for (group in tileGroups[attackableTile]!!) {
group.showCircle(colorFromRGB(237, 41, 57))
group.showCrosshair(Color.RED)
}
}
}
var blinkAction: Action? = null
fun setCenterPosition(vector: Vector2, immediately: Boolean = false, selectUnit: Boolean = true) {
val tileGroup = tileGroups.values.firstOrNull { it.tileInfo.position == vector } ?: return
val tileGroup = allWorldTileGroups.firstOrNull { it.tileInfo.position == vector } ?: return
selectedTile = tileGroup.tileInfo
if (selectUnit)
worldScreen.bottomUnitTable.tileSelected(selectedTile!!)
@ -480,10 +509,10 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
super.zoom(zoomScale)
val scale = 1 / scaleX // don't use zoomScale itself, in case it was out of bounds and not applied
if (scale >= 1)
for (tileGroup in tileGroups.values)
for (tileGroup in allWorldTileGroups)
tileGroup.cityButtonLayerGroup.isTransform = false // to save on rendering time to improve framerate
if (scale < 1 && scale > 0.5f)
for (tileGroup in tileGroups.values) {
for (tileGroup in allWorldTileGroups) {
// ONLY set those groups that have active citybuttons as transformable!
// This is massively framerate-improving!
if (tileGroup.cityButtonLayerGroup.hasChildren())
@ -492,6 +521,12 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
}
}
fun removeUnitActionOverlay(){
for (overlay in unitActionOverlays)
overlay.remove()
unitActionOverlays.clear()
}
// For debugging purposes
override fun draw(batch: Batch?, parentAlpha: Float) = super.draw(batch, parentAlpha)

View File

@ -211,7 +211,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
else {
attackButton.onClick {
Battle.moveAndAttack(attacker, attackableTile)
worldScreen.mapHolder.unitActionOverlay?.remove() // the overlay was one of attacking
worldScreen.mapHolder.removeUnitActionOverlay() // the overlay was one of attacking
worldScreen.shouldUpdate = true
}
}
@ -283,7 +283,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() {
else {
attackButton.onClick {
Battle.nuke(attacker, targetTile)
worldScreen.mapHolder.unitActionOverlay?.remove() // the overlay was one of attacking
worldScreen.mapHolder.removeUnitActionOverlay() // the overlay was one of attacking
worldScreen.shouldUpdate = true
}
}