Spies can be moved on map (#11587)

* Added right click city selection to spy move button

* Fixed random indenting again

* Fixed city buttons moving back up

* Added esc binding to close move spy action

* Added spy selected panel

* Fixed not clicking on a city crash

* Fixed spy selection and deselection

* Made viable cities more visible

* Fixed accidental change

* Spy icons can now be right clicked to enter the map moving view

* Added an X button to close out of the moving spy screen easier.

* Removed extra white space
This commit is contained in:
Oskar Niesen 2024-05-15 23:03:35 -05:00 committed by GitHub
parent c7a7bf1474
commit 428edfb12a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 138 additions and 9 deletions

View File

@ -554,7 +554,7 @@ class CityButton(val city: City, private val tileGroup: TileGroup) : Table(BaseS
// when deselected, move city button to its original position
if (unitTable.selectedCity != city
&& unitTable.selectedUnit?.currentTile != city.getCenterTile()) {
&& unitTable.selectedUnit?.currentTile != city.getCenterTile() && unitTable.selectedSpy == null) {
moveButtonUp()
}

View File

@ -23,6 +23,7 @@ import com.unciv.ui.components.input.KeyCharAndCode
import com.unciv.ui.components.input.keyShortcuts
import com.unciv.ui.components.input.onActivation
import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.input.onRightClick
import com.unciv.ui.components.widgets.AutoScrollPane
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.ConfirmPopup
@ -92,6 +93,9 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS
moveSpyButton.onClick {
onSpyClicked(moveSpyButton, spy)
}
moveSpyButton.onRightClick {
onSpyRightClicked(spy)
}
if (!worldScreen.canChangeState || !spy.isAlive()) {
// Spectators aren't allowed to move the spies of the Civs they are viewing
moveSpyButton.disable()
@ -179,6 +183,9 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS
onClick {
onSpyClicked(moveSpyButtons[spy]!!, spy)
}
onRightClick {
onSpyRightClicked(spy)
}
}
private fun getSpyIcons(spies: Iterable<Spy>) = Table().apply {
@ -235,6 +242,12 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS
}
}
private fun onSpyRightClicked(spy: Spy) {
worldScreen.bottomUnitTable.selectSpy(spy)
worldScreen.game.popScreen()
worldScreen.shouldUpdate = true
}
private fun resetSelection() {
selectedSpy = null
selectedSpyButton?.label?.setText("Move".tr())

View File

@ -27,6 +27,7 @@ import com.unciv.logic.map.TileMap
import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.logic.map.mapunit.movement.UnitMovement
import com.unciv.logic.map.tile.Tile
import com.unciv.models.Spy
import com.unciv.models.UncivSound
import com.unciv.models.UnitActionType
import com.unciv.models.ruleset.unique.LocalUniqueCache
@ -57,6 +58,7 @@ import com.unciv.ui.components.widgets.ZoomableScrollPane
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.basescreen.UncivStage
import com.unciv.ui.screens.overviewscreen.EspionageOverviewScreen
import com.unciv.ui.screens.worldscreen.UndoHandler.Companion.recordUndoCheckpoint
import com.unciv.ui.screens.worldscreen.bottombar.BattleTableHelpers.battleAnimation
import com.unciv.utils.Concurrency
@ -122,6 +124,9 @@ class WorldMapHolder(
// Contains the data required to draw a "connect road" button
class ConnectRoadButtonDto(val unit: MapUnit, val tile: Tile) : ButtonDto
// Contains the data required to draw a "move spy" button
class MoveSpyButtonDto(val spy: Spy, val city: City?) : ButtonDto
internal fun addTiles() {
@ -172,10 +177,12 @@ class WorldMapHolder(
val previousSelectedCity = unitTable.selectedCity
val previousSelectedUnitIsSwapping = unitTable.selectedUnitIsSwapping
val previousSelectedUnitIsConnectingRoad = unitTable.selectedUnitIsConnectingRoad
unitTable.tileSelected(tile)
val movingSpyOnMap = unitTable.selectedSpy != null
if (!movingSpyOnMap)
unitTable.tileSelected(tile)
val newSelectedUnit = unitTable.selectedUnit
if (previousSelectedCity != null && tile != previousSelectedCity.getCenterTile())
if (previousSelectedCity != null && tile != previousSelectedCity.getCenterTile() && !movingSpyOnMap)
tileGroups[previousSelectedCity.getCenterTile()]!!.layerCityButton.moveUp()
if (previousSelectedUnits.isNotEmpty()) {
@ -202,6 +209,8 @@ class WorldMapHolder(
else -> addTileOverlaysWithUnitMovement(previousSelectedUnits, tile) // Long-running task
}
}
} else if (movingSpyOnMap) {
addMovingSpyOverlay(unitTable.selectedSpy!!, tile)
} else {
addTileOverlays(tile) // no unit movement but display the units in the tile etc.
}
@ -465,6 +474,13 @@ class WorldMapHolder(
}
}
}
private fun addMovingSpyOverlay(spy: Spy, tile: Tile) {
val city: City? = if (tile.isCityCenter() && spy.canMoveTo(tile.getCity()!!)) tile.getCity() else null
addTileOverlays(tile, MoveSpyButtonDto(spy, city))
worldScreen.shouldUpdate = true
}
private fun addTileOverlays(tile: Tile, buttonDto: ButtonDto? = null) {
val table = Table().apply { defaults().pad(10f) }
if (buttonDto != null && worldScreen.canChangeState)
@ -473,6 +489,7 @@ class WorldMapHolder(
is MoveHereButtonDto -> getMoveHereButton(buttonDto)
is SwapWithButtonDto -> getSwapWithButton(buttonDto)
is ConnectRoadButtonDto -> getConnectRoadButton(buttonDto)
is MoveSpyButtonDto -> getMoveSpyButton(buttonDto)
else -> null
}
)
@ -588,7 +605,49 @@ class WorldMapHolder(
return connectRoadButton
}
private fun getMoveSpyButton(dto: MoveSpyButtonDto): Group {
val spyActionButton = Group()
spyActionButton.setSize(buttonSize, buttonSize)
spyActionButton.addActor(ImageGetter.getCircle(size = buttonSize))
if (dto.city != null) {
spyActionButton.addActor(
ImageGetter.getStatIcon("Movement").apply {
name = "Button"
color = Color.BLACK
setSize(buttonSize / 2)
center(spyActionButton)
}
)
} else {
spyActionButton.addActor(
ImageGetter.getImage("OtherIcons/Close").apply {
name = "Button"
color = Color.RED
setSize(buttonSize / 2)
center(spyActionButton)
}
)
}
spyActionButton.onActivation(UncivSound.Silent) {
if (dto.city != null) {
dto.spy.moveTo(dto.city)
worldScreen.game.pushScreen(EspionageOverviewScreen(worldScreen.selectedCiv, worldScreen))
} else {
worldScreen.game.pushScreen(EspionageOverviewScreen(worldScreen.selectedCiv, worldScreen))
worldScreen.bottomUnitTable.selectedSpy = null
}
removeUnitActionOverlay()
selectedTile = null
worldScreen.shouldUpdate = true
}
spyActionButton.keyShortcuts.add(KeyCharAndCode.TAB)
spyActionButton.keyShortcuts.add(KeyCharAndCode.RETURN)
spyActionButton.keyShortcuts.add(KeyCharAndCode.NUMPAD_ENTER)
return spyActionButton
}
fun addOverlayOnTileGroup(group: TileGroup, actor: Actor) {
@ -666,6 +725,9 @@ class WorldMapHolder(
// Update tiles according to selected unit/city
val unitTable = worldScreen.bottomUnitTable
when {
unitTable.selectedSpy != null -> {
updateTilesForSelectedSpy(unitTable.selectedSpy!!)
}
unitTable.selectedCity != null -> {
val city = unitTable.selectedCity!!
updateBombardableTilesForSelectedCity(city)
@ -865,6 +927,21 @@ class WorldMapHolder(
}
}
private fun updateTilesForSelectedSpy(spy: Spy) {
for (group in tileGroups.values) {
group.layerOverlay.reset()
if (!group.tile.isCityCenter())
group.layerMisc.dimImprovement(true)
group.layerCityButton.moveDown()
}
for (city in worldScreen.gameInfo.getCities()) {
if (spy.canMoveTo(city)) {
tileGroups[city.getCenterTile()]!!.layerOverlay.showHighlight(Color.CYAN, .7f)
}
}
}
private fun updateBombardableTilesForSelectedCity(city: City) {
if (!city.canBombard()) return
for (attackableTile in TargetHelper.getBombardableTiles(city)) {

View File

@ -794,6 +794,12 @@ class WorldScreen(
shouldUpdate = true
return
}
if (bottomUnitTable.selectedSpy != null) {
bottomUnitTable.selectSpy(null)
shouldUpdate = true
return
}
game.popScreen()
}

View File

@ -10,6 +10,7 @@ import com.unciv.logic.battle.CityCombatant
import com.unciv.logic.city.City
import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.logic.map.tile.Tile
import com.unciv.models.Spy
import com.unciv.models.translations.tr
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.center
@ -57,6 +58,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
}
selectedUnitIsSwapping = false
selectedUnitIsConnectingRoad = false
selectedSpy = null
}
var selectedCity : City? = null
@ -65,6 +67,16 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
// Most of the time it's the same unit with the same stats so why waste precious time?
private var selectedUnitHasChanged = false
val separator: Actor
var selectedSpy: Spy? = null
fun selectSpy(spy: Spy?) {
selectedSpy = spy
selectedCity = null
selectedUnits.clear()
selectedUnitIsSwapping = false
selectedUnitIsConnectingRoad = false
}
private var bg = Image(
BaseScreen.skinStrings.getUiBackground("WorldScreen/UnitTable",
@ -206,9 +218,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
unitNameLabel.setText("")
unitDescriptionTable.clear()
}
}
else if (selectedCity != null) {
} else if (selectedCity != null) {
isVisible = true
separator.isVisible = true
val city = selectedCity!!
@ -218,15 +228,14 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
unitNameLabel.clearListeners()
unitNameLabel.onClick {
if (!worldScreen.canChangeState) return@onClick
if (!worldScreen.canChangeState) return@onClick
CityRenamePopup(
screen = worldScreen,
city = city,
actionOnClose = {
unitNameLabel.setText(city.name.tr())
worldScreen.shouldUpdate = true
}
)
})
}
unitDescriptionTable.clear()
@ -237,6 +246,30 @@ class UnitTable(val worldScreen: WorldScreen) : Table() {
unitDescriptionTable.add(CityCombatant(city).getAttackingStrength().toString()).row()
selectedUnitHasChanged = true
} else if (selectedSpy != null) {
val spy = selectedSpy!!
isVisible = true
unitNameLabel.clearListeners()
unitNameLabel.setText(spy.name)
unitDescriptionTable.clear()
unitIconHolder.clear()
unitIconHolder.add (ImageGetter.getImage("OtherIcons/Spy_White").apply {
color = Color.WHITE
}).size(30f)
separator.isVisible = true
val color = when(spy.rank) {
1 -> Color.BROWN
2 -> Color.LIGHT_GRAY
3 -> Color.GOLD
else -> Color.BLACK
}
repeat(spy.rank) {
val star = ImageGetter.getImage("OtherIcons/Star")
star.color = color
unitDescriptionTable.add(star).size(20f).pad(1f)
}
} else {
isVisible = false
}