Rework of City Screen: new current buildings list + misc changes (#8167)

Co-authored-by: tunerzinc@gmail.com <vfylfhby>
This commit is contained in:
vegeta1k95
2022-12-18 18:28:48 +01:00
committed by GitHub
parent 9aa3cb13c5
commit 903d3ab278
6 changed files with 160 additions and 418 deletions

View File

@ -61,7 +61,6 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
private var preferredBuyStat = Stat.Gold // Used for keyboard buy private var preferredBuyStat = Stat.Gold // Used for keyboard buy
private val upperTable = Table(BaseScreen.skin) private val upperTable = Table(BaseScreen.skin)
private val showCityInfoTableButton = "Show stats drilldown".toTextButton()
private val constructionsQueueScrollPane: ScrollPane private val constructionsQueueScrollPane: ScrollPane
private val constructionsQueueTable = Table() private val constructionsQueueTable = Table()
private val buyButtonsTable = Table() private val buyButtonsTable = Table()
@ -84,11 +83,6 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
} }
init { init {
showCityInfoTableButton.onClick {
cityScreen.showConstructionsTable = false
cityScreen.update()
}
constructionsQueueScrollPane = ScrollPane(constructionsQueueTable.addBorder(2f, Color.WHITE)) constructionsQueueScrollPane = ScrollPane(constructionsQueueTable.addBorder(2f, Color.WHITE))
constructionsQueueScrollPane.setOverscroll(false, false) constructionsQueueScrollPane.setOverscroll(false, false)
constructionsQueueTable.background = BaseScreen.skinStrings.getUiBackground( constructionsQueueTable.background = BaseScreen.skinStrings.getUiBackground(
@ -97,7 +91,6 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
) )
upperTable.defaults().left().top() upperTable.defaults().left().top()
upperTable.add(showCityInfoTableButton).padLeft(pad).padBottom(pad).row()
upperTable.add(constructionsQueueScrollPane) upperTable.add(constructionsQueueScrollPane)
.maxHeight(stageHeight / 3 - 10f) .maxHeight(stageHeight / 3 - 10f)
.padBottom(pad).row() .padBottom(pad).row()

View File

@ -1,357 +0,0 @@
package com.unciv.ui.cityscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.city.CityInfo
import com.unciv.logic.city.CityStats
import com.unciv.logic.city.StatTreeNode
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.Building
import com.unciv.models.stats.Stat
import com.unciv.models.translations.tr
import com.unciv.ui.images.IconCircleGroup
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popup.ConfirmPopup
import com.unciv.ui.popup.closeAllPopups
import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.ExpanderTab
import com.unciv.ui.utils.extensions.addBorder
import com.unciv.ui.utils.extensions.addSeparator
import com.unciv.ui.utils.extensions.darken
import com.unciv.ui.utils.extensions.disable
import com.unciv.ui.utils.extensions.onClick
import com.unciv.ui.utils.extensions.surroundWithCircle
import com.unciv.ui.utils.extensions.toLabel
import com.unciv.ui.utils.extensions.toTextButton
import java.text.DecimalFormat
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
class CityInfoTable(private val cityScreen: CityScreen) : Table(BaseScreen.skin) {
private val pad = 10f
private val showConstructionsTableButton = "Show construction queue".toTextButton()
private val scrollPane: ScrollPane
private val innerTable = Table(skin)
private val allExpanders = mutableListOf<ExpanderTab>()
private val hideShowAllCell: Cell<Group>
private var hideShowAllShouldClose = false
init {
align(Align.topLeft)
showConstructionsTableButton.onClick {
cityScreen.showConstructionsTable = true
cityScreen.update()
}
innerTable.width = cityScreen.stage.width / 4
innerTable.background = BaseScreen.skinStrings.getUiBackground(
"CityScreen/CityInfoTable",
tintColor = BaseScreen.skinStrings.skinConfig.baseColor.darken(0.5f)
)
scrollPane = ScrollPane(innerTable.addBorder(2f, Color.WHITE))
scrollPane.setOverscroll(false, false)
val hideShowAllButton = Group()
add(showConstructionsTableButton).left().padLeft(pad).padBottom(pad)
hideShowAllCell = add(hideShowAllButton).size(30f) // size as the cell won't be resized when the actor is replaced
hideShowAllCell.left().padLeft(pad).padBottom(pad).expandX().row()
add(scrollPane).colspan(2).left().row()
}
internal fun update() {
val cityInfo = cityScreen.city
allExpanders.clear()
innerTable.clear()
innerTable.apply {
addBuildingsInfo(cityInfo)
addStatInfo()
addGreatPersonPointInfo(cityInfo)
}
updateHideShowAllButton()
getCell(scrollPane).maxHeight(stage.height - showConstructionsTableButton.height - pad - 10f)
onContentResize()
}
private fun updateHideShowAllButton() {
val anyExpanderOpen = allExpanders.map { it.isOpen }.maxOrNull() ?: false
if (anyExpanderOpen == hideShowAllShouldClose) return
hideShowAllShouldClose = anyExpanderOpen
val hideShowAllButton = getToggleButton(hideShowAllShouldClose)
hideShowAllButton.touchable = Touchable.enabled
hideShowAllButton.onClick {
for (expander in allExpanders) {
if (expander.isOpen == hideShowAllShouldClose) expander.toggle()
}
updateHideShowAllButton()
}
hideShowAllCell.setActor(hideShowAllButton)
}
private fun onContentResize() {
pack()
setPosition(CityScreen.posFromEdge, stage.height - CityScreen.posFromEdge, Align.topLeft)
}
private fun Table.addCategory(category: String, showHideTable: Table) {
val categoryWidth = cityScreen.stage.width / 4
val expander = ExpanderTab(category, persistenceID = "CityInfo.$category"
, onChange = {
onContentResize()
updateHideShowAllButton()
}
) {
it.add(showHideTable).minWidth(categoryWidth)
}
addSeparator()
add(expander).minWidth(categoryWidth).expandX().fillX().row()
allExpanders += expander
}
private fun addBuildingInfo(building: Building, destinationTable: Table) {
val icon = ImageGetter.getPortraitImage(building.name, 30f)
val isFree = building.name in cityScreen.city.civInfo.civConstructions.getFreeBuildings(cityScreen.city.id)
val displayName = if (isFree) "{${building.name}} ({Free})" else building.name
val buildingNameAndIconTable = ExpanderTab(
displayName, Constants.defaultFontSize, icon,
startsOutOpened = false, defaultPad = 5f,
onChange = { onContentResize() }
) {
val detailsString = building.getDescription(cityScreen.city, false)
it.add(detailsString.toLabel().apply { wrap = true })
.width(cityScreen.stage.width / 4 - 2 * pad).row() // when you set wrap, then you need to manually set the size of the label
if (building.isSellable() && !isFree) {
val sellAmount = cityScreen.city.getGoldForSellingBuilding(building.name)
val sellText = "Sell for [$sellAmount] gold"
val sellBuildingButton = sellText.toTextButton()
it.add(sellBuildingButton).pad(5f).row()
sellBuildingButton.onClick(UncivSound.Coin) {
sellBuildingButton.disable()
cityScreen.closeAllPopups()
ConfirmPopup(
cityScreen,
"Are you sure you want to sell this [${building.name}]?",
sellText,
restoreDefault = {
cityScreen.update()
}
) {
cityScreen.city.sellBuilding(building.name)
cityScreen.update()
}.open()
}
if (cityScreen.city.hasSoldBuildingThisTurn && !cityScreen.city.civInfo.gameInfo.gameParameters.godMode
|| cityScreen.city.isPuppet
|| !UncivGame.Current.worldScreen!!.isPlayersTurn || !cityScreen.canChangeState)
sellBuildingButton.disable()
}
it.addSeparator()
}
destinationTable.add(buildingNameAndIconTable).pad(5f).fillX().row()
}
private fun Table.addBuildingsInfo(cityInfo: CityInfo) {
val wonders = mutableListOf<Building>()
val specialistBuildings = mutableListOf<Building>()
val otherBuildings = mutableListOf<Building>()
for (building in cityInfo.cityConstructions.getBuiltBuildings()) {
when {
building.isAnyWonder() -> wonders.add(building)
!building.newSpecialists().isEmpty() -> specialistBuildings.add(building)
else -> otherBuildings.add(building)
}
}
if (wonders.isNotEmpty()) {
val wondersTable = Table()
addCategory("Wonders", wondersTable)
for (building in wonders) addBuildingInfo(building, wondersTable)
}
if (specialistBuildings.isNotEmpty()) {
val specialistBuildingsTable = Table()
addCategory("Specialist Buildings", specialistBuildingsTable)
for (building in specialistBuildings) {
addBuildingInfo(building, specialistBuildingsTable)
val specialistIcons = Table()
specialistIcons.row().size(20f).pad(5f)
for ((specialistName, amount) in building.newSpecialists()) {
val specialist = cityInfo.getRuleset().specialists[specialistName]
?: continue // probably a mod that doesn't have the specialist defined yet
for (i in 0 until amount)
specialistIcons.add(ImageGetter.getSpecialistIcon(specialist.colorObject)).size(20f)
}
specialistBuildingsTable.add(specialistIcons).pad(0f).row()
}
}
if (otherBuildings.isNotEmpty()) {
val regularBuildingsTable = Table()
addCategory("Buildings", regularBuildingsTable)
for (building in otherBuildings) addBuildingInfo(building, regularBuildingsTable)
}
}
private fun addStatsToHashmap(
statTreeNode: StatTreeNode,
hashMap: HashMap<String, Float>,
stat: Stat,
showDetails: Boolean,
indentation: Int = 0
) {
for ((name, child) in statTreeNode.children) {
val statAmount = child.totalStats[stat]
if (statAmount == 0f) continue
hashMap["- ".repeat(indentation) + name.tr()] = statAmount
if (showDetails) addStatsToHashmap(child, hashMap, stat, showDetails, indentation + 1)
}
}
private fun Table.addStatInfo() {
val cityStats = cityScreen.city.cityStats
val showFaith = cityScreen.city.civInfo.gameInfo.isReligionEnabled()
for (stat in Stat.values()) {
if (stat == Stat.Faith && !showFaith) continue
val statValuesTable = Table()
statValuesTable.touchable = Touchable.enabled
addCategory(stat.name, statValuesTable)
updateStatValuesTable(stat, cityStats, statValuesTable)
}
}
private fun updateStatValuesTable(
stat: Stat,
cityStats: CityStats,
statValuesTable: Table,
showDetails:Boolean = false
) {
statValuesTable.clear()
statValuesTable.defaults().pad(2f)
statValuesTable.onClick {
updateStatValuesTable(
stat,
cityStats,
statValuesTable,
!showDetails
)
onContentResize()
}
val relevantBaseStats = LinkedHashMap<String, Float>()
if (stat != Stat.Happiness)
addStatsToHashmap(cityStats.baseStatTree, relevantBaseStats, stat, showDetails)
else relevantBaseStats.putAll(cityStats.happinessList)
for (key in relevantBaseStats.keys.toList())
if (relevantBaseStats[key] == 0f) relevantBaseStats.remove(key)
if (relevantBaseStats.isEmpty()) return
statValuesTable.add("Base values".toLabel(fontSize = FONT_SIZE_STAT_INFO_HEADER)).pad(4f)
.colspan(2).row()
var sumOfAllBaseValues = 0f
for (entry in relevantBaseStats) {
val specificStatValue = entry.value
if (!entry.key.startsWith('-'))
sumOfAllBaseValues += specificStatValue
statValuesTable.add(entry.key.toLabel()).left()
statValuesTable.add(specificStatValue.toOneDecimalLabel()).row()
}
statValuesTable.addSeparator()
statValuesTable.add("Total".toLabel())
statValuesTable.add(sumOfAllBaseValues.toOneDecimalLabel()).row()
val relevantBonuses = LinkedHashMap<String, Float>()
addStatsToHashmap(cityStats.statPercentBonusTree, relevantBonuses, stat, showDetails)
val totalBonusStats = cityStats.statPercentBonusTree.totalStats
if (totalBonusStats[stat] != 0f) {
statValuesTable.add("Bonuses".toLabel(fontSize = FONT_SIZE_STAT_INFO_HEADER)).colspan(2)
.padTop(20f).row()
for ((source, bonusAmount) in relevantBonuses) {
statValuesTable.add(source.toLabel()).left()
statValuesTable.add(bonusAmount.toPercentLabel()).row() // negative bonus
}
statValuesTable.addSeparator()
statValuesTable.add("Total".toLabel())
statValuesTable.add(totalBonusStats[stat].toPercentLabel()).row() // negative bonus
statValuesTable.add("Final".toLabel(fontSize = FONT_SIZE_STAT_INFO_HEADER)).colspan(2)
.padTop(20f).row()
var finalTotal = 0f
for (entry in cityStats.finalStatList) {
val specificStatValue = entry.value[stat]
finalTotal += specificStatValue
if (specificStatValue == 0f) continue
statValuesTable.add(entry.key.toLabel())
statValuesTable.add(specificStatValue.toOneDecimalLabel()).row()
}
statValuesTable.addSeparator()
statValuesTable.add("Total".toLabel())
statValuesTable.add(finalTotal.toOneDecimalLabel()).row()
}
statValuesTable.pack()
if (stat != Stat.Happiness) {
val toggleButton = getToggleButton(showDetails)
statValuesTable.addActor(toggleButton)
toggleButton.setPosition(0f, statValuesTable.height, Align.topLeft)
}
statValuesTable.padBottom(4f)
}
private fun getToggleButton(showDetails: Boolean): IconCircleGroup {
val label = (if (showDetails) "-" else "+").toLabel()
label.setAlignment(Align.center)
return label
.surroundWithCircle(25f, color = BaseScreen.skinStrings.skinConfig.baseColor)
.surroundWithCircle(27f, false)
}
private fun Table.addGreatPersonPointInfo(cityInfo: CityInfo) {
val greatPersonPoints = cityInfo.getGreatPersonPointsForNextTurn()
val allGreatPersonNames = greatPersonPoints.asSequence().flatMap { it.value.keys }.distinct()
for (greatPersonName in allGreatPersonNames) {
val expanderName = "[$greatPersonName] points"
val greatPersonTable = Table()
addCategory(expanderName, greatPersonTable)
for ((source, gppCounter) in greatPersonPoints) {
val gppPointsFromSource = gppCounter[greatPersonName]!!
if (gppPointsFromSource == 0) continue
greatPersonTable.add(source.toLabel()).padRight(10f)
greatPersonTable.add(gppPointsFromSource.toLabel()).row()
}
}
}
companion object {
private const val FONT_SIZE_STAT_INFO_HEADER = 22
private fun Float.toPercentLabel() =
"${if (this>0f) "+" else ""}${DecimalFormat("0.#").format(this)}%".toLabel()
private fun Float.toOneDecimalLabel() =
DecimalFormat("0.#").format(this).toLabel()
}
}

View File

@ -50,9 +50,6 @@ class CityScreen(
/** Toggles or adds/removes all state changing buttons */ /** Toggles or adds/removes all state changing buttons */
val canChangeState = UncivGame.Current.worldScreen!!.canChangeState val canChangeState = UncivGame.Current.worldScreen!!.canChangeState
/** Toggle between Constructions and cityInfo (buildings, specialists etc. */
var showConstructionsTable = true
// Clockwise from the top-left // Clockwise from the top-left
/** Displays current production, production queue and available productions list /** Displays current production, production queue and available productions list
@ -61,9 +58,6 @@ class CityScreen(
*/ */
private var constructionsTable = CityConstructionsTable(this) private var constructionsTable = CityConstructionsTable(this)
/** Displays stats, buildings, specialists and stats drilldown - sits on TOP LEFT, can be toggled to */
private var cityInfoTable = CityInfoTable(this)
/** Displays raze city button - sits on TOP CENTER */ /** Displays raze city button - sits on TOP CENTER */
private var razeCityButtonHolder = Table() private var razeCityButtonHolder = Table()
@ -129,7 +123,6 @@ class CityScreen(
//stage.setDebugTableUnderMouse(true) //stage.setDebugTableUnderMouse(true)
stage.addActor(cityStatsTable) stage.addActor(cityStatsTable)
constructionsTable.addActorsToStage() constructionsTable.addActorsToStage()
stage.addActor(cityInfoTable)
stage.addActor(selectedConstructionTable) stage.addActor(selectedConstructionTable)
stage.addActor(tileTable) stage.addActor(tileTable)
stage.addActor(cityPickerTable) // add late so it's top in Z-order and doesn't get covered in cramped portrait stage.addActor(cityPickerTable) // add late so it's top in Z-order and doesn't get covered in cramped portrait
@ -144,17 +137,8 @@ class CityScreen(
// Recalculate Stats // Recalculate Stats
city.cityStats.update() city.cityStats.update()
// Left side, top and bottom: Construction queue / details constructionsTable.isVisible = true
if (showConstructionsTable) { constructionsTable.update(selectedConstruction)
constructionsTable.isVisible = true
cityInfoTable.isVisible = false
constructionsTable.update(selectedConstruction)
} else {
constructionsTable.isVisible = false
cityInfoTable.isVisible = true
cityInfoTable.update()
// CityInfoTable sets its relative position itself
}
// Bottom right: Tile or selected construction info // Bottom right: Tile or selected construction info
tileTable.update(selectedTile) tileTable.update(selectedTile)
@ -171,8 +155,7 @@ class CityScreen(
} }
val leftMargin = when { val leftMargin = when {
!isPortrait() -> 0f !isPortrait() -> 0f
showConstructionsTable -> constructionsTable.getLowerWidth() else -> constructionsTable.getLowerWidth()
else -> cityInfoTable.packIfNeeded().width
} }
// Bottom center: Name, paging, exit city button // Bottom center: Name, paging, exit city button
@ -434,7 +417,6 @@ class CityScreen(
val indexOfCity = civInfo.cities.indexOf(city) val indexOfCity = civInfo.cities.indexOf(city)
val indexOfNextCity = (indexOfCity + delta + numCities) % numCities val indexOfNextCity = (indexOfCity + delta + numCities) % numCities
val newCityScreen = CityScreen(civInfo.cities[indexOfNextCity]) val newCityScreen = CityScreen(civInfo.cities[indexOfNextCity])
newCityScreen.showConstructionsTable = showConstructionsTable // stay on stats drilldown between cities
newCityScreen.update() newCityScreen.update()
game.replaceCurrentScreen(newCityScreen) game.replaceCurrentScreen(newCityScreen)
} }

View File

@ -6,15 +6,18 @@ import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Label
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.UncivGame import com.unciv.UncivGame
import com.unciv.logic.city.CityFlags import com.unciv.logic.city.CityFlags
import com.unciv.logic.city.CityFocus import com.unciv.logic.city.CityFocus
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
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.civilopedia.CivilopediaScreen import com.unciv.ui.civilopedia.CivilopediaScreen
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.ExpanderTab
import com.unciv.ui.utils.Fonts import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.extensions.addSeparator import com.unciv.ui.utils.extensions.addSeparator
import com.unciv.ui.utils.extensions.colorFromRGB import com.unciv.ui.utils.extensions.colorFromRGB
@ -100,6 +103,8 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
if (cityInfo.religion.getNumberOfFollowers().isNotEmpty() && cityInfo.civInfo.gameInfo.isReligionEnabled()) if (cityInfo.religion.getNumberOfFollowers().isNotEmpty() && cityInfo.civInfo.gameInfo.isReligionEnabled())
addReligionInfo() addReligionInfo()
addBuildingsInfo()
upperTable.pack() upperTable.pack()
lowerTable.pack() lowerTable.pack()
lowerPane.layout() lowerPane.layout()
@ -110,6 +115,15 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
pack() // update self last pack() // update self last
} }
private fun onContentResize() {
pack()
setPosition(
stage.width - CityScreen.posFromEdge,
stage.height - CityScreen.posFromEdge,
Align.topRight
)
}
private fun addText() { private fun addText() {
val unassignedPopString = "{Unassigned population}: ".tr() + val unassignedPopString = "{Unassigned population}: ".tr() +
cityInfo.population.getFreePopulation().toString() + "/" + cityInfo.population.population cityInfo.population.getFreePopulation().toString() + "/" + cityInfo.population.population
@ -171,36 +185,111 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
} }
private fun addCitizenManagement() { private fun addCitizenManagement() {
val expanderTab = CitizenManagementTable(cityScreen).asExpander { val expanderTab = CitizenManagementTable(cityScreen).asExpander { onContentResize() }
pack()
setPosition(
stage.width - CityScreen.posFromEdge,
stage.height - CityScreen.posFromEdge,
Align.topRight
)
}
lowerTable.add(expanderTab).growX().row() lowerTable.add(expanderTab).growX().row()
} }
private fun addSpecialistInfo() { private fun addSpecialistInfo() {
val expanderTab = SpecialistAllocationTable(cityScreen).asExpander { val expanderTab = SpecialistAllocationTable(cityScreen).asExpander { onContentResize() }
pack()
setPosition(
stage.width - CityScreen.posFromEdge,
stage.height - CityScreen.posFromEdge,
Align.topRight
)
}
lowerTable.add(expanderTab).growX().row() lowerTable.add(expanderTab).growX().row()
} }
private fun addReligionInfo() { private fun addReligionInfo() {
val expanderTab = CityReligionInfoTable(cityInfo.religion).asExpander { val expanderTab = CityReligionInfoTable(cityInfo.religion).asExpander { onContentResize() }
pack() lowerTable.add(expanderTab).growX().row()
// We have to re-anchor as our position in the city screen, otherwise it expands upwards. }
// ToDo: This probably should be refactored so its placed somewhere else in due time
setPosition(stage.width - CityScreen.posFromEdge, stage.height - CityScreen.posFromEdge, Align.topRight) private fun addBuildingsInfo() {
val wonders = mutableListOf<Building>()
val specialistBuildings = mutableListOf<Building>()
val otherBuildings = mutableListOf<Building>()
for (building in cityInfo.cityConstructions.getBuiltBuildings()) {
when {
building.isAnyWonder() -> wonders.add(building)
!building.newSpecialists().isEmpty() -> specialistBuildings.add(building)
else -> otherBuildings.add(building)
}
}
if (specialistBuildings.isNotEmpty()) {
val specialistBuildingsTable = Table()
addCategory("Specialist Buildings", specialistBuildingsTable)
for (building in specialistBuildings) addBuildingButton(building, specialistBuildingsTable)
}
if (wonders.isNotEmpty()) {
val wondersTable = Table()
addCategory("Wonders", wondersTable)
for (building in wonders) addBuildingButton(building, wondersTable)
}
if (otherBuildings.isNotEmpty()) {
val regularBuildingsTable = Table()
addCategory("Buildings", regularBuildingsTable)
for (building in otherBuildings) addBuildingButton(building, regularBuildingsTable)
}
}
private fun addBuildingButton(building: Building, destinationTable: Table) {
val button = Table()
val info = Table()
val statsAndSpecialists = Table()
val icon = ImageGetter.getPortraitImage(building.name, 50f)
val isFree = building.name in cityScreen.city.civInfo.civConstructions.getFreeBuildings(cityScreen.city.id)
val displayName = if (isFree) "{${building.name}} ({Free})" else building.name
info.add(displayName.toLabel(fontSize = Constants.defaultFontSize)).padBottom(5f).right().row()
val stats = building.getStats(cityInfo).joinToString(separator = " ") {
"" + it.value.toInt() + it.key.character
}
statsAndSpecialists.add(stats.toLabel(fontSize = Constants.defaultFontSize)).right()
val assignedSpec = cityInfo.population.getNewSpecialists().clone()
val specialistIcons = Table()
for ((specialistName, amount) in building.newSpecialists()) {
val specialist = cityInfo.getRuleset().specialists[specialistName]
?: continue // probably a mod that doesn't have the specialist defined yet
for (i in 0 until amount) {
if (assignedSpec[specialistName]!! > 0) {
specialistIcons.add(ImageGetter.getSpecialistIcon(specialist.colorObject))
.size(20f)
assignedSpec.add(specialistName, -1)
} else {
specialistIcons.add(ImageGetter.getSpecialistIcon(Color.GRAY)).size(20f)
}
}
}
statsAndSpecialists.add(specialistIcons).right()
info.add(statsAndSpecialists).right()
button.add(info).right().top().padRight(10f).padTop(5f)
button.add(icon).right()
button.onClick {
cityScreen.selectConstruction(building)
cityScreen.update()
}
destinationTable.add(button).pad(1f).expandX().right().row()
}
private fun addCategory(category: String, showHideTable: Table) {
val expanderTab = ExpanderTab(
title = category,
fontSize = Constants.defaultFontSize,
persistenceID = "CityInfo.$category",
onChange = { onContentResize() }
) {
it.add(showHideTable).fillX().right()
} }
lowerTable.add(expanderTab).growX().row() lowerTable.add(expanderTab).growX().row()
} }
} }

View File

@ -46,11 +46,11 @@ class CityTileGroup(private val city: CityInfo, tileInfo: TileInfo, tileSetStrin
when { when {
tileInfo.getOwner() != city.civInfo -> { // outside of civ boundary tileInfo.getOwner() != city.civInfo -> { // outside of civ boundary
dim(0.3f) dim(0.3f)
yieldGroup.isVisible = false yieldGroup.isVisible = true
} }
tileInfo !in city.tilesInRange -> { // within city but not close enough to be workable tileInfo !in city.tilesInRange -> { // within city but not close enough to be workable
yieldGroup.isVisible = false yieldGroup.isVisible = true
dim(0.5f) dim(0.5f)
} }
@ -74,7 +74,6 @@ class CityTileGroup(private val city: CityInfo, tileInfo: TileInfo, tileSetStrin
unitLayerGroup.isVisible = false unitLayerGroup.isVisible = false
terrainFeatureLayerGroup.color.a = 0.5f terrainFeatureLayerGroup.color.a = 0.5f
icons.improvementIcon?.setColor(1f, 1f, 1f, 0.5f) icons.improvementIcon?.setColor(1f, 1f, 1f, 0.5f)
resourceImage?.setColor(1f, 1f, 1f, 0.5f)
updatePopulationIcon() updatePopulationIcon()
updateYieldGroup() updateYieldGroup()
} }
@ -94,7 +93,7 @@ class CityTileGroup(private val city: CityInfo, tileInfo: TileInfo, tileSetStrin
private fun updatePopulationIcon() { private fun updatePopulationIcon() {
val populationIcon = icons.populationIcon val populationIcon = icons.populationIcon
if (populationIcon != null) { if (populationIcon != null) {
populationIcon.setSize(30f, 30f) populationIcon.setSize(25f, 25f)
populationIcon.setPosition(width / 2 - populationIcon.width / 2, populationIcon.setPosition(width / 2 - populationIcon.width / 2,
height * 0.85f - populationIcon.height / 2) height * 0.85f - populationIcon.height / 2)

View File

@ -8,16 +8,21 @@ import com.unciv.UncivGame
import com.unciv.logic.city.IConstruction import com.unciv.logic.city.IConstruction
import com.unciv.logic.city.PerpetualConstruction import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.city.PerpetualStatConversion import com.unciv.logic.city.PerpetualStatConversion
import com.unciv.models.UncivSound
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.IRulesetObject import com.unciv.models.ruleset.IRulesetObject
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.civilopedia.CivilopediaScreen import com.unciv.ui.civilopedia.CivilopediaScreen
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popup.ConfirmPopup
import com.unciv.ui.popup.closeAllPopups
import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.extensions.darken import com.unciv.ui.utils.extensions.darken
import com.unciv.ui.utils.extensions.disable
import com.unciv.ui.utils.extensions.onClick import com.unciv.ui.utils.extensions.onClick
import com.unciv.ui.utils.extensions.surroundWithCircle import com.unciv.ui.utils.extensions.toTextButton
class ConstructionInfoTable(val cityScreen: CityScreen): Table() { class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
private val selectedConstructionTable = Table() private val selectedConstructionTable = Table()
@ -56,8 +61,14 @@ class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
selectedConstructionTable.run { selectedConstructionTable.run {
pad(10f) pad(10f)
add(ImageGetter.getPortraitImage(construction.name, 50f)) add(ImageGetter.getPortraitImage(construction.name, 50f).apply {
.pad(5f) val link = (construction as? IRulesetObject)?.makeLink() ?: return
if (link.isEmpty()) return
touchable = Touchable.enabled
this.onClick {
UncivGame.Current.pushScreen(CivilopediaScreen(city.getRuleset(), link = link))
}
}).pad(5f)
var buildingText = construction.name.tr() var buildingText = construction.name.tr()
val specialConstruction = PerpetualConstruction.perpetualConstructionsMap[construction.name] val specialConstruction = PerpetualConstruction.perpetualConstructionsMap[construction.name]
@ -78,11 +89,36 @@ class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
descriptionLabel.wrap = true descriptionLabel.wrap = true
add(descriptionLabel).colspan(2).width(stage.width / 4) add(descriptionLabel).colspan(2).width(stage.width / 4)
val link = (construction as? IRulesetObject)?.makeLink() ?: return // Show sell button if construction is a currently sellable building
if (link.isEmpty()) return if (construction is Building && cityConstructions.isBuilt(construction.name)
touchable = Touchable.enabled && construction.isSellable()) {
onClick { val sellAmount = cityScreen.city.getGoldForSellingBuilding(construction.name)
UncivGame.Current.pushScreen(CivilopediaScreen(city.getRuleset(), link = link)) val sellText = "Sell [$sellAmount] " + Fonts.gold
val sellBuildingButton = sellText.toTextButton()
row()
add(sellBuildingButton).padTop(5f).colspan(2).center()
sellBuildingButton.onClick(UncivSound.Coin) {
sellBuildingButton.disable()
cityScreen.closeAllPopups()
ConfirmPopup(
cityScreen,
"Are you sure you want to sell this [${construction.name}]?",
sellText,
restoreDefault = {
cityScreen.update()
}
) {
cityScreen.city.sellBuilding(construction.name)
cityScreen.clearSelection()
cityScreen.update()
}.open()
}
if (cityScreen.city.hasSoldBuildingThisTurn && !cityScreen.city.civInfo.gameInfo.gameParameters.godMode
|| cityScreen.city.isPuppet
|| !UncivGame.Current.worldScreen!!.isPlayersTurn || !cityScreen.canChangeState)
sellBuildingButton.disable()
} }
} }
} }