From 8babec78d91c1e3fe9291e825660a89a27d59b80 Mon Sep 17 00:00:00 2001 From: vegeta1k95 <32207817+vegeta1k95@users.noreply.github.com> Date: Fri, 10 Feb 2023 11:21:14 +0100 Subject: [PATCH] Improvements to construction table (#8643) * Construction resource requirements are depicted with icons * Dimming and coloring * Don't show buildings which require another building in this city * Don't show buildings which require another building in this city which is buildable rn --------- Co-authored-by: vegeta1k95 --- .../com/unciv/models/ruleset/IConstruction.kt | 6 +- .../ui/cityscreen/CityConstructionsTable.kt | 80 ++++++++++++++----- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/core/src/com/unciv/models/ruleset/IConstruction.kt b/core/src/com/unciv/models/ruleset/IConstruction.kt index 73a8a2b380..ec41e109fe 100644 --- a/core/src/com/unciv/models/ruleset/IConstruction.kt +++ b/core/src/com/unciv/models/ruleset/IConstruction.kt @@ -190,7 +190,7 @@ enum class RejectionReasonType(val shouldShow: Boolean, val errorMessage: String open class PerpetualConstruction(override var name: String, val description: String) : IConstruction { override fun shouldBeDisplayed(cityConstructions: CityConstructions) = isBuildable(cityConstructions) - open fun getProductionTooltip(city: City) : String = "" + open fun getProductionTooltip(city: City, withIcon: Boolean = false) : String = "" companion object { val science = PerpetualStatConversion(Stat.Science) @@ -217,8 +217,8 @@ open class PerpetualConstruction(override var name: String, val description: Str open class PerpetualStatConversion(val stat: Stat) : PerpetualConstruction(stat.name, "Convert production to [${stat.name}] at a rate of [rate] to 1") { - override fun getProductionTooltip(city: City) : String - = "\r\n${(city.cityStats.currentCityStats.production / getConversionRate(city)).roundToInt()}/${Fonts.turn}" + override fun getProductionTooltip(city: City, withIcon: Boolean) : String + = "\r\n${(city.cityStats.currentCityStats.production / getConversionRate(city)).roundToInt()}${if (withIcon) stat.character else ""}/${Fonts.turn}" fun getConversionRate(city: City) : Int = (1/city.cityStats.getStatConversionRate(stat)).roundToInt() override fun isBuildable(cityConstructions: CityConstructions): Boolean { diff --git a/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt b/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt index d35c78e5f4..706dfa63dc 100644 --- a/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt @@ -13,6 +13,8 @@ import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.IConstruction import com.unciv.logic.city.INonPerpetualConstruction import com.unciv.logic.city.PerpetualConstruction +import com.unciv.logic.city.RejectionReason +import com.unciv.logic.city.RejectionReasonType import com.unciv.logic.map.tile.Tile import com.unciv.models.UncivSound import com.unciv.models.ruleset.Building @@ -49,6 +51,12 @@ import kotlin.math.max import kotlin.math.min import com.unciv.ui.utils.AutoScrollPane as ScrollPane +private class ConstructionButtonDTO( + val construction: IConstruction, + val buttonText: String, + val resourcesRequired: HashMap? = null, + val rejectionReason: RejectionReason? = null) + /** * Manager to hold and coordinate two widgets for the city screen left side: * - Construction queue with switch to [ConstructionInfoTable] button and the enqueue / buy buttons. @@ -191,21 +199,21 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { city.cityStats.updateTileStats() // only once for (entry in constructionsSequence.filter { it.shouldBeDisplayed(cityConstructions) }) { - val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name) - var buttonText = entry.name.tr() + cityConstructions.getTurnsToConstructionString(entry.name, useStoredProduction) - for ((resource, amount) in entry.getResourceRequirements()) { - buttonText += "\n" + resource.getConsumesAmountString(amount).tr() - } + val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name) + val buttonText = cityConstructions.getTurnsToConstructionString(entry.name, useStoredProduction).trim() + val resourcesRequired = entry.getResourceRequirements() val mostImportantRejection = entry.getRejectionReasons(cityConstructions) .filter { it.isImportantRejection() } .minByOrNull { it.getRejectionPrecedence() } + constructionButtonDTOList.add( ConstructionButtonDTO( entry, buttonText, - mostImportantRejection?.errorMessage + if (resourcesRequired.isEmpty()) null else resourcesRequired, + mostImportantRejection ) ) } @@ -216,7 +224,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { constructionButtonDTOList.add( ConstructionButtonDTO( specialConstruction, - "Produce [${specialConstruction.name}]".tr() + specialConstruction.getProductionTooltip(city) + "Produce [${specialConstruction.name}]".tr() + " " + specialConstruction.getProductionTooltip(city).trim() ) ) } @@ -243,6 +251,15 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { var maxButtonWidth = constructionsQueueTable.width for (dto in constructionButtonDTOList) { + + if (dto.construction is Building + && dto.rejectionReason?.type == RejectionReasonType.RequiresBuildingInThisCity + && constructionButtonDTOList.any { + (it.construction is Building) && (it.construction.name == dto.construction.requiredBuilding + || it.construction.replaces == dto.construction.requiredBuilding || it.construction.hasUnique(dto.construction.requiredBuilding!!)) + }) + continue + val constructionButton = getConstructionButton(dto) when (dto.construction) { is BaseUnit -> units.add(constructionButton) @@ -347,11 +364,9 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { Color.BROWN.brighten(0.5f), Color.WHITE) } - private class ConstructionButtonDTO(val construction: IConstruction, val buttonText: String, val rejectionReason: String? = null) - private fun getConstructionButton(constructionButtonDTO: ConstructionButtonDTO): Table { val construction = constructionButtonDTO.construction - val pickConstructionButton = Table() + val pickConstructionButton = Table().apply { isTransform = false } pickConstructionButton.align(Align.left).pad(5f) pickConstructionButton.background = BaseScreen.skinStrings.getUiBackground( @@ -367,9 +382,29 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { ) } + val icon = ImageGetter.getConstructionPortrait(construction.name, 40f) pickConstructionButton.add(getProgressBar(construction.name)).padRight(5f) - pickConstructionButton.add(ImageGetter.getConstructionPortrait(construction.name, 40f)).padRight(10f) - pickConstructionButton.add(constructionButtonDTO.buttonText.toLabel()).expandX().fillX() + pickConstructionButton.add(icon).padRight(10f) + + val table = Table().apply { isTransform = false } + val tableRes = Table().apply { isTransform = false } + + val textColor = if (constructionButtonDTO.rejectionReason == null) Color.WHITE else Color.RED + table.add(construction.name.tr().toLabel(fontColor = textColor)).expandX().left().row() + + tableRes.add(constructionButtonDTO.buttonText.toLabel()).expandX().left() + if (constructionButtonDTO.resourcesRequired != null) { + for ((resource, amount) in constructionButtonDTO.resourcesRequired) { + if (constructionButtonDTO.rejectionReason?.type == RejectionReasonType.ConsumesResources) + tableRes.add(amount.toString().toLabel(fontColor = Color.RED)).expandX().left().padLeft(5f) + else + tableRes.add(amount.toString().toLabel(fontColor = Color.WHITE)).expandX().left().padLeft(5f) + tableRes.add(ImageGetter.getResourcePortrait(resource, 15f)).padBottom(1f) + } + } + table.add(tableRes).expandX().left() + + pickConstructionButton.add(table).expandX().left() if (!cannotAddConstructionToQueue(construction, cityScreen.city, cityScreen.city.cityConstructions)) { val addToQueueButton = ImageGetter.getImage("OtherIcons/New").apply { color = Color.BLACK }.surroundWithCircle(40f) @@ -384,10 +419,15 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { // no rejection reason means we can build it! if (constructionButtonDTO.rejectionReason != null) { - pickConstructionButton.color = Color.GRAY - pickConstructionButton.add(constructionButtonDTO.rejectionReason.toLabel(Color.RED).apply { wrap = true }) + pickConstructionButton.color.a = 0.9f + icon.color.a = 0.5f + if (constructionButtonDTO.rejectionReason.type != RejectionReasonType.ConsumesResources) { + pickConstructionButton.add(constructionButtonDTO.rejectionReason.errorMessage + .toLabel(Color.RED).apply { wrap = true }) .colspan(pickConstructionButton.columns).fillX().left().padTop(2f) + } } + pickConstructionButton.onClick { if (cityScreen.selectedConstruction == construction) { addConstructionToQueue(construction, cityScreen.city.cityConstructions) @@ -418,12 +458,12 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { if (isSelectedQueueEntry()) { button = "Remove from queue".toTextButton() - button.onClick { - cityConstructions.removeFromQueue(selectedQueueEntry, false) - cityScreen.clearSelection() - selectedQueueEntry = -1 - cityScreen.update() - } + button.onClick { + cityConstructions.removeFromQueue(selectedQueueEntry, false) + cityScreen.clearSelection() + selectedQueueEntry = -1 + cityScreen.update() + } } else { button = "Add to queue".toTextButton() if (construction == null