mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-08 23:08:35 +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 val upperTable = Table(BaseScreen.skin)
|
||||
private val showCityInfoTableButton = "Show stats drilldown".toTextButton()
|
||||
private val constructionsQueueScrollPane: ScrollPane
|
||||
private val constructionsQueueTable = Table()
|
||||
private val buyButtonsTable = Table()
|
||||
@ -84,11 +83,6 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
}
|
||||
|
||||
init {
|
||||
showCityInfoTableButton.onClick {
|
||||
cityScreen.showConstructionsTable = false
|
||||
cityScreen.update()
|
||||
}
|
||||
|
||||
constructionsQueueScrollPane = ScrollPane(constructionsQueueTable.addBorder(2f, Color.WHITE))
|
||||
constructionsQueueScrollPane.setOverscroll(false, false)
|
||||
constructionsQueueTable.background = BaseScreen.skinStrings.getUiBackground(
|
||||
@ -97,7 +91,6 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
)
|
||||
|
||||
upperTable.defaults().left().top()
|
||||
upperTable.add(showCityInfoTableButton).padLeft(pad).padBottom(pad).row()
|
||||
upperTable.add(constructionsQueueScrollPane)
|
||||
.maxHeight(stageHeight / 3 - 10f)
|
||||
.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 */
|
||||
val canChangeState = UncivGame.Current.worldScreen!!.canChangeState
|
||||
|
||||
/** Toggle between Constructions and cityInfo (buildings, specialists etc. */
|
||||
var showConstructionsTable = true
|
||||
|
||||
// Clockwise from the top-left
|
||||
|
||||
/** Displays current production, production queue and available productions list
|
||||
@ -61,9 +58,6 @@ class CityScreen(
|
||||
*/
|
||||
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 */
|
||||
private var razeCityButtonHolder = Table()
|
||||
|
||||
@ -129,7 +123,6 @@ class CityScreen(
|
||||
//stage.setDebugTableUnderMouse(true)
|
||||
stage.addActor(cityStatsTable)
|
||||
constructionsTable.addActorsToStage()
|
||||
stage.addActor(cityInfoTable)
|
||||
stage.addActor(selectedConstructionTable)
|
||||
stage.addActor(tileTable)
|
||||
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
|
||||
city.cityStats.update()
|
||||
|
||||
// Left side, top and bottom: Construction queue / details
|
||||
if (showConstructionsTable) {
|
||||
constructionsTable.isVisible = true
|
||||
cityInfoTable.isVisible = false
|
||||
constructionsTable.update(selectedConstruction)
|
||||
} else {
|
||||
constructionsTable.isVisible = false
|
||||
cityInfoTable.isVisible = true
|
||||
cityInfoTable.update()
|
||||
// CityInfoTable sets its relative position itself
|
||||
}
|
||||
constructionsTable.isVisible = true
|
||||
constructionsTable.update(selectedConstruction)
|
||||
|
||||
// Bottom right: Tile or selected construction info
|
||||
tileTable.update(selectedTile)
|
||||
@ -171,8 +155,7 @@ class CityScreen(
|
||||
}
|
||||
val leftMargin = when {
|
||||
!isPortrait() -> 0f
|
||||
showConstructionsTable -> constructionsTable.getLowerWidth()
|
||||
else -> cityInfoTable.packIfNeeded().width
|
||||
else -> constructionsTable.getLowerWidth()
|
||||
}
|
||||
|
||||
// Bottom center: Name, paging, exit city button
|
||||
@ -434,7 +417,6 @@ class CityScreen(
|
||||
val indexOfCity = civInfo.cities.indexOf(city)
|
||||
val indexOfNextCity = (indexOfCity + delta + numCities) % numCities
|
||||
val newCityScreen = CityScreen(civInfo.cities[indexOfNextCity])
|
||||
newCityScreen.showConstructionsTable = showConstructionsTable // stay on stats drilldown between cities
|
||||
newCityScreen.update()
|
||||
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.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.city.CityFlags
|
||||
import com.unciv.logic.city.CityFocus
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.CivilopediaScreen
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.utils.BaseScreen
|
||||
import com.unciv.ui.utils.ExpanderTab
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import com.unciv.ui.utils.extensions.addSeparator
|
||||
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())
|
||||
addReligionInfo()
|
||||
|
||||
addBuildingsInfo()
|
||||
|
||||
upperTable.pack()
|
||||
lowerTable.pack()
|
||||
lowerPane.layout()
|
||||
@ -110,6 +115,15 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
pack() // update self last
|
||||
}
|
||||
|
||||
private fun onContentResize() {
|
||||
pack()
|
||||
setPosition(
|
||||
stage.width - CityScreen.posFromEdge,
|
||||
stage.height - CityScreen.posFromEdge,
|
||||
Align.topRight
|
||||
)
|
||||
}
|
||||
|
||||
private fun addText() {
|
||||
val unassignedPopString = "{Unassigned population}: ".tr() +
|
||||
cityInfo.population.getFreePopulation().toString() + "/" + cityInfo.population.population
|
||||
@ -171,36 +185,111 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
|
||||
}
|
||||
|
||||
private fun addCitizenManagement() {
|
||||
val expanderTab = CitizenManagementTable(cityScreen).asExpander {
|
||||
pack()
|
||||
setPosition(
|
||||
stage.width - CityScreen.posFromEdge,
|
||||
stage.height - CityScreen.posFromEdge,
|
||||
Align.topRight
|
||||
)
|
||||
}
|
||||
val expanderTab = CitizenManagementTable(cityScreen).asExpander { onContentResize() }
|
||||
lowerTable.add(expanderTab).growX().row()
|
||||
}
|
||||
|
||||
private fun addSpecialistInfo() {
|
||||
val expanderTab = SpecialistAllocationTable(cityScreen).asExpander {
|
||||
pack()
|
||||
setPosition(
|
||||
stage.width - CityScreen.posFromEdge,
|
||||
stage.height - CityScreen.posFromEdge,
|
||||
Align.topRight
|
||||
)
|
||||
}
|
||||
val expanderTab = SpecialistAllocationTable(cityScreen).asExpander { onContentResize() }
|
||||
lowerTable.add(expanderTab).growX().row()
|
||||
}
|
||||
|
||||
private fun addReligionInfo() {
|
||||
val expanderTab = CityReligionInfoTable(cityInfo.religion).asExpander {
|
||||
pack()
|
||||
// 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)
|
||||
val expanderTab = CityReligionInfoTable(cityInfo.religion).asExpander { onContentResize() }
|
||||
lowerTable.add(expanderTab).growX().row()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -46,11 +46,11 @@ class CityTileGroup(private val city: CityInfo, tileInfo: TileInfo, tileSetStrin
|
||||
when {
|
||||
tileInfo.getOwner() != city.civInfo -> { // outside of civ boundary
|
||||
dim(0.3f)
|
||||
yieldGroup.isVisible = false
|
||||
yieldGroup.isVisible = true
|
||||
}
|
||||
|
||||
tileInfo !in city.tilesInRange -> { // within city but not close enough to be workable
|
||||
yieldGroup.isVisible = false
|
||||
yieldGroup.isVisible = true
|
||||
dim(0.5f)
|
||||
}
|
||||
|
||||
@ -74,7 +74,6 @@ class CityTileGroup(private val city: CityInfo, tileInfo: TileInfo, tileSetStrin
|
||||
unitLayerGroup.isVisible = false
|
||||
terrainFeatureLayerGroup.color.a = 0.5f
|
||||
icons.improvementIcon?.setColor(1f, 1f, 1f, 0.5f)
|
||||
resourceImage?.setColor(1f, 1f, 1f, 0.5f)
|
||||
updatePopulationIcon()
|
||||
updateYieldGroup()
|
||||
}
|
||||
@ -94,7 +93,7 @@ class CityTileGroup(private val city: CityInfo, tileInfo: TileInfo, tileSetStrin
|
||||
private fun updatePopulationIcon() {
|
||||
val populationIcon = icons.populationIcon
|
||||
if (populationIcon != null) {
|
||||
populationIcon.setSize(30f, 30f)
|
||||
populationIcon.setSize(25f, 25f)
|
||||
populationIcon.setPosition(width / 2 - populationIcon.width / 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.PerpetualConstruction
|
||||
import com.unciv.logic.city.PerpetualStatConversion
|
||||
import com.unciv.models.UncivSound
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.IRulesetObject
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.civilopedia.CivilopediaScreen
|
||||
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.Fonts
|
||||
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.toTextButton
|
||||
|
||||
class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
|
||||
private val selectedConstructionTable = Table()
|
||||
@ -56,8 +61,14 @@ class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
|
||||
selectedConstructionTable.run {
|
||||
pad(10f)
|
||||
|
||||
add(ImageGetter.getPortraitImage(construction.name, 50f))
|
||||
.pad(5f)
|
||||
add(ImageGetter.getPortraitImage(construction.name, 50f).apply {
|
||||
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()
|
||||
val specialConstruction = PerpetualConstruction.perpetualConstructionsMap[construction.name]
|
||||
@ -78,11 +89,36 @@ class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
|
||||
descriptionLabel.wrap = true
|
||||
add(descriptionLabel).colspan(2).width(stage.width / 4)
|
||||
|
||||
val link = (construction as? IRulesetObject)?.makeLink() ?: return
|
||||
if (link.isEmpty()) return
|
||||
touchable = Touchable.enabled
|
||||
onClick {
|
||||
UncivGame.Current.pushScreen(CivilopediaScreen(city.getRuleset(), link = link))
|
||||
// Show sell button if construction is a currently sellable building
|
||||
if (construction is Building && cityConstructions.isBuilt(construction.name)
|
||||
&& construction.isSellable()) {
|
||||
val sellAmount = cityScreen.city.getGoldForSellingBuilding(construction.name)
|
||||
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