Show garrison in City overview (#8906)

* Add a Garrison column to City Overview

* Add a Garrison column to City Overview - clickable
This commit is contained in:
SomeTroglodyte 2023-03-16 10:25:47 +01:00 committed by GitHub
parent 461fc4f191
commit 9262beb9a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 11 deletions

View File

@ -9,13 +9,11 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.city.CityFlags
import com.unciv.logic.city.City import com.unciv.logic.city.City
import com.unciv.logic.city.CityFlags
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.screens.cityscreen.CityScreen
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
import com.unciv.ui.components.extensions.addSeparator import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.center import com.unciv.ui.components.extensions.center
@ -24,6 +22,8 @@ import com.unciv.ui.components.extensions.pad
import com.unciv.ui.components.extensions.surroundWithCircle import com.unciv.ui.components.extensions.surroundWithCircle
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.screens.cityscreen.CityScreen
import kotlin.math.roundToInt import kotlin.math.roundToInt
class CityOverviewTab( class CityOverviewTab(
@ -48,7 +48,8 @@ class CityOverviewTab(
private const val CITY = "City" private const val CITY = "City"
private const val WLTK = "WLTK" private const val WLTK = "WLTK"
private const val CONSTRUCTION = "Construction" private const val CONSTRUCTION = "Construction"
private val alphabeticColumns = listOf(CITY, CONSTRUCTION, WLTK) private const val GARRISON = "Garrison"
private val alphabeticColumns = listOf(CITY, CONSTRUCTION, WLTK, GARRISON)
private val citySortIcon = ImageGetter.getUnitIcon("Settler") private val citySortIcon = ImageGetter.getUnitIcon("Settler")
.surroundWithCircle(iconSize) .surroundWithCircle(iconSize)
@ -61,6 +62,10 @@ class CityOverviewTab(
.apply { color = Color.BLACK } .apply { color = Color.BLACK }
.surroundWithCircle(iconSize, color = Color.LIGHT_GRAY) .surroundWithCircle(iconSize, color = Color.LIGHT_GRAY)
.apply { addTooltip("Current construction", 18f, tipAlign = Align.center) } .apply { addTooltip("Current construction", 18f, tipAlign = Align.center) }
private val garrisonSortIcon = ImageGetter.getImage("OtherIcons/Shield")
.apply { color = Color.BLACK }
.surroundWithCircle(iconSize, color = Color.LIGHT_GRAY)
.apply { addTooltip("Garrisoned by unit", 18f, tipAlign = Align.center) }
// Readability helpers // Readability helpers
private fun String.isStat() = Stat.isStat(this) private fun String.isStat() = Stat.isStat(this)
@ -103,7 +108,7 @@ class CityOverviewTab(
add(cityInfoTableTotal) add(cityInfoTableTotal)
} }
fun toggleSort(sortBy: String) { private fun toggleSort(sortBy: String) {
if (sortBy == persistableData.sortedBy) { if (sortBy == persistableData.sortedBy) {
persistableData.descending = !persistableData.descending persistableData.descending = !persistableData.descending
} else { } else {
@ -112,7 +117,7 @@ class CityOverviewTab(
} }
} }
fun getComparator() = Comparator { city2: City, city1: City -> private fun getComparator() = Comparator { city2: City, city1: City ->
when(persistableData.sortedBy) { when(persistableData.sortedBy) {
CITY -> collator.compare(city2.name.tr(), city1.name.tr()) CITY -> collator.compare(city2.name.tr(), city1.name.tr())
CONSTRUCTION -> collator.compare( CONSTRUCTION -> collator.compare(
@ -120,6 +125,10 @@ class CityOverviewTab(
city1.cityConstructions.currentConstructionFromQueue.tr()) city1.cityConstructions.currentConstructionFromQueue.tr())
"Population" -> city2.population.population - city1.population.population "Population" -> city2.population.population - city1.population.population
WLTK -> city2.isWeLoveTheKingDayActive().compareTo(city1.isWeLoveTheKingDayActive()) WLTK -> city2.isWeLoveTheKingDayActive().compareTo(city1.isWeLoveTheKingDayActive())
GARRISON -> collator.compare(
city2.getCenterTile().militaryUnit?.name?.tr() ?: "",
city1.getCenterTile().militaryUnit?.name?.tr() ?: "",
)
else -> { else -> {
val stat = Stat.safeValueOf(persistableData.sortedBy)!! val stat = Stat.safeValueOf(persistableData.sortedBy)!!
city2.getStat(stat) - city1.getStat(stat) city2.getStat(stat) - city1.getStat(stat)
@ -172,6 +181,7 @@ class CityOverviewTab(
addSortIcon(name) addSortIcon(name)
} }
addSortIcon(WLTK, wltkSortIcon) addSortIcon(WLTK, wltkSortIcon)
addSortIcon(GARRISON, garrisonSortIcon)
cityInfoTableHeader.pack() cityInfoTableHeader.pack()
} }
@ -221,11 +231,23 @@ class CityOverviewTab(
city.demandedResource.isNotEmpty() -> { city.demandedResource.isNotEmpty() -> {
val image = ImageGetter.getResourcePortrait(city.demandedResource, iconSize *0.7f) val image = ImageGetter.getResourcePortrait(city.demandedResource, iconSize *0.7f)
image.addTooltip("Demanding [${city.demandedResource}]", 18f, tipAlign = Align.topLeft) image.addTooltip("Demanding [${city.demandedResource}]", 18f, tipAlign = Align.topLeft)
cityInfoTableDetails.add(image).padLeft(iconSize *0.3f) cityInfoTableDetails.add(image)
} }
else -> cityInfoTableDetails.add() else -> cityInfoTableDetails.add()
} }
val garrisonUnit = city.getCenterTile().militaryUnit
if (garrisonUnit == null) {
cityInfoTableDetails.add()
} else {
val garrisonUnitName = garrisonUnit.displayName()
val garrisonUnitIcon = ImageGetter.getConstructionPortrait(garrisonUnit.baseUnit.getIconName(), iconSize * 0.7f)
garrisonUnitIcon.addTooltip(garrisonUnitName, 18f, tipAlign = Align.topLeft)
garrisonUnitIcon.onClick {
overviewScreen.select(EmpireOverviewCategories.Units, UnitOverviewTab.getUnitIdentifier(garrisonUnit) )
}
cityInfoTableDetails.add(garrisonUnitIcon)
}
cityInfoTableDetails.row() cityInfoTableDetails.row()
} }
@ -248,6 +270,7 @@ class CityOverviewTab(
else cityInfoTableTotal.add(viewingPlayer.cities.sumOf { it.getStat(stat) }.toCenteredLabel()) else cityInfoTableTotal.add(viewingPlayer.cities.sumOf { it.getStat(stat) }.toCenteredLabel())
} }
cityInfoTableTotal.add(viewingPlayer.cities.count { it.isWeLoveTheKingDayActive() }.toCenteredLabel()) cityInfoTableTotal.add(viewingPlayer.cities.count { it.isWeLoveTheKingDayActive() }.toCenteredLabel())
cityInfoTableTotal.add(viewingPlayer.cities.count { it.getCenterTile().militaryUnit != null }.toCenteredLabel())
cityInfoTableTotal.pack() cityInfoTableTotal.pack()
} }
} }

View File

@ -73,7 +73,7 @@ class EmpireOverviewScreen(
) )
if (category.name == page) { if (category.name == page) {
tabbedPager.selectPage(index) tabbedPager.selectPage(index)
pageObject.select(selection) select(pageObject, selection)
} }
} }
@ -94,4 +94,14 @@ class EmpireOverviewScreen(
val category = (pageObjects.entries.find { it.value == tab } ?: return).key val category = (pageObjects.entries.find { it.value == tab } ?: return).key
tabbedPager.replacePage(category.name, tab) tabbedPager.replacePage(category.name, tab)
} }
fun select(category: EmpireOverviewCategories, selection: String) {
tabbedPager.selectPage(category.name)
select(pageObjects[category], selection)
}
private fun select(tab: EmpireOverviewTab?, selection: String) {
if (tab == null) return
val scrollY = tab.select(selection) ?: return
tabbedPager.setPageScrollY(tabbedPager.activePage, scrollY)
}
} }

View File

@ -26,8 +26,10 @@ abstract class EmpireOverviewTab (
settings.lastOverviewPage = caption settings.lastOverviewPage = caption
} }
/** Override if the tab can _select_ something specific */ /** Override if the tab can _select_ something specific.
open fun select(selection: String) {} * @return non-null to set that tab's ScrollPane.scrollY
*/
open fun select(selection: String): Float? = null
val gameInfo = viewingPlayer.gameInfo val gameInfo = viewingPlayer.gameInfo

View File

@ -125,10 +125,11 @@ class ReligionOverviewTab(
} }
} }
override fun select(selection: String) { override fun select(selection: String): Float? {
persistableData.selectedReligion = selection persistableData.selectedReligion = selection
loadReligionButtons() // so the icon is "highlighted" loadReligionButtons() // so the icon is "highlighted"
loadReligion(selection) loadReligion(selection)
return null
} }
private fun loadReligion(religionName: String?) { private fun loadReligion(religionName: String?) {
if (religionName == null) return if (religionName == null) return

View File

@ -2,7 +2,10 @@ package com.unciv.ui.screens.overviewscreen
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.scenes.scene2d.Action
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.actions.Actions
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.Constants import com.unciv.Constants
@ -28,6 +31,7 @@ import com.unciv.ui.components.extensions.darken
import com.unciv.ui.components.extensions.onClick import com.unciv.ui.components.extensions.onClick
import com.unciv.ui.components.extensions.surroundWithCircle import com.unciv.ui.components.extensions.surroundWithCircle
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toPrettyString
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsUpgrade
import kotlin.math.abs import kotlin.math.abs
@ -53,6 +57,7 @@ class UnitOverviewTab(
} }
override fun deactivated(index: Int, caption: String, pager: TabbedPager) { override fun deactivated(index: Int, caption: String, pager: TabbedPager) {
persistableData.scrollY = pager.getPageScrollY(index) persistableData.scrollY = pager.getPageScrollY(index)
removeBlinkAction()
} }
private val supplyTableWidth = (overviewScreen.stage.width * 0.25f).coerceAtLeast(240f) private val supplyTableWidth = (overviewScreen.stage.width * 0.25f).coerceAtLeast(240f)
@ -60,6 +65,16 @@ class UnitOverviewTab(
private val unitHeaderTable = Table() private val unitHeaderTable = Table()
private val fixedContent = Table() private val fixedContent = Table()
// used for select()
private var blinkAction: Action? = null
private var blinkActor: Actor? = null
private fun removeBlinkAction() {
if (blinkAction == null || blinkActor == null) return
blinkActor!!.removeAction(blinkAction)
blinkAction = null
blinkActor = null
}
override fun getFixedContent() = fixedContent override fun getFixedContent() = fixedContent
init { init {
@ -170,6 +185,7 @@ class UnitOverviewTab(
UnitGroup(unit, 20f), UnitGroup(unit, 20f),
fontColor = if (unit.due && unit.isIdle()) Color.WHITE else Color.LIGHT_GRAY fontColor = if (unit.due && unit.isIdle()) Color.WHITE else Color.LIGHT_GRAY
) )
button.name = getUnitIdentifier(unit) // Marker to find a unit in select()
button.onClick { button.onClick {
showWorldScreenAt(unit) showWorldScreenAt(unit)
} }
@ -248,4 +264,27 @@ class UnitOverviewTab(
} }
return this return this
} }
companion object {
fun getUnitIdentifier(unit: MapUnit) = unit.run { "$name@${getTile().position.toPrettyString()}" }
}
override fun select(selection: String): Float? {
val cell = unitListTable.cells.asSequence()
.filter { it.actor is IconTextButton && it.actor.name == selection }
.firstOrNull() ?: return null
val button = cell.actor as IconTextButton
val scrollY = (0 until cell.row)
.map { unitListTable.getRowHeight(it) }.sum() -
(parent.height - unitListTable.getRowHeight(cell.row)) / 2
removeBlinkAction()
blinkAction = Actions.repeat(3, Actions.sequence(
Actions.fadeOut(0.17f),
Actions.fadeIn(0.17f)
))
blinkActor = button
button.addAction(blinkAction)
return scrollY
}
} }