mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-15 10:18:26 +07:00
Rework of City Screen: new current buildings list + misc changes (#8167)
Co-authored-by: tunerzinc@gmail.com <vfylfhby>
This commit is contained in:
@ -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()
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user