WordScreenTopBar rework, portrait-friendlier (#6945)

* WordScreenTopBar rework, portrait-friendlier

* WordScreenTopBar rework, portrait-friendlier - NotificationsScroll

* WordScreenTopBar rework, portrait-friendlier - Rounded corners

* WordScreenTopBar rework, portrait-friendlier - Tweak padding

* WordScreenTopBar rework, portrait-friendlier - review
This commit is contained in:
SomeTroglodyte
2022-06-08 09:18:30 +02:00
committed by GitHub
parent 29c55cd393
commit 0ac89a906d
4 changed files with 342 additions and 193 deletions

View File

@ -0,0 +1,61 @@
package com.unciv.ui.worldscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.Batch
import com.badlogic.gdx.graphics.g2d.NinePatch
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.utils.Drawable
import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable
import com.badlogic.gdx.utils.Align
import com.unciv.ui.images.ImageGetter
/** An Actor that just draws a Drawable [background], preferably a [NinePatchDrawable] created
* by [BackgroundActor.getRoundedEdgeRectangle], meant to work in Table Cells and to be overlaid with other Widgets.
* The drawable's center can be moved to any of the corners or vertex centers using `align`, which will also scale the
* drawable up by factor 2 and clip to the original rectangle. This can be used to draw rectangles with one or two rounded corners.
* @param align An [Align] constant - In which corner of the [BackgroundActor] rectangle the center of the [background] should be.
*/
class BackgroundActor(val background: Drawable, align: Int) : Actor() {
private val widthMultiplier = if (Align.isCenterHorizontal(align)) 1f else 2f
private val heightMultiplier = if (Align.isCenterVertical(align)) 1f else 2f
private val noClip = Align.isCenterHorizontal(align) && Align.isCenterVertical(align)
private val xOffset = if (Align.isLeft(align)) 0.5f else 0f
private val yOffset = if (Align.isBottom(align)) 0.5f else 0f
init {
touchable = Touchable.disabled
}
override fun hit(x: Float, y: Float, touchable: Boolean): Actor? = null
override fun draw(batch: Batch?, parentAlpha: Float) {
if (batch == null) return
if (noClip) return drawBackground(batch, parentAlpha)
batch.flush()
if (!clipBegin()) return
val w = width * widthMultiplier
val h = height * heightMultiplier
drawBackground(batch, parentAlpha, x - xOffset * w, y - yOffset * h, w, h)
batch.flush()
clipEnd()
}
private fun drawBackground(batch: Batch, parentAlpha: Float) =
drawBackground(batch, parentAlpha, x, y, width, height)
private fun drawBackground(batch: Batch, parentAlpha: Float, x: Float, y: Float, w: Float, h: Float) {
val color = color
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha)
background.draw(batch, x, y, w, h)
}
companion object {
fun getRoundedEdgeRectangle(tintColor: Color): NinePatchDrawable {
val region = ImageGetter.getDrawable("Skin/roundedEdgeRectangle").region
val drawable = NinePatchDrawable(NinePatch(region, 25, 25, 24, 24))
drawable.setPadding(15f, 15f, 15f, 15f)
return drawable.tint(tintColor)
}
}
}

View File

@ -14,8 +14,7 @@ import kotlin.math.min
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
class NotificationsScroll(
private val worldScreen: WorldScreen,
private val maxNotificationsHeight: Float
private val worldScreen: WorldScreen
) : ScrollPane(null) {
private companion object {
/** Scale the entire ScrollPane by this factor */
@ -41,15 +40,26 @@ class NotificationsScroll(
setScale(scaleFactor)
}
internal fun update(notifications: MutableList<Notification>, tileInfoTableHeight: Float) {
/**
* Update widget contents if necessary and recalculate layout
* @param notifications Data to display
* @param maxNotificationsHeight Total height in world screen coordinates
* @param tileInfoTableHeight Height of the portion that may be covered on the bottom - make sure we can scroll up far enough so the bottom entry is visible above this
*/
internal fun update(
notifications: MutableList<Notification>,
maxNotificationsHeight: Float,
tileInfoTableHeight: Float
) {
updateContent(notifications)
updateLayout(maxNotificationsHeight, tileInfoTableHeight)
}
private fun updateContent(notifications: MutableList<Notification>) {
// no news? - keep our list as it is, especially don't reset scroll position
if (notificationsHash == notifications.hashCode()) {
sizeScrollingSpacer(tileInfoTableHeight)
layout()
return
}
notificationsHash = notifications.hashCode()
val newHash = notifications.hashCode()
if (notificationsHash == newHash) return
notificationsHash = newHash
notificationsTable.clearChildren()
endOfTableSpacerCell = null
@ -93,12 +103,17 @@ class NotificationsScroll(
}
notificationsTable.pack() // needed to get height - prefHeight is set and close but not quite the same value
val filledHeight = notificationsTable.height
}
private fun updateLayout(maxNotificationsHeight: Float, tileInfoTableHeight: Float) {
val newHeight = min(notificationsTable.height, maxNotificationsHeight * inverseScaleFactor)
sizeScrollingSpacer(tileInfoTableHeight)
pack()
height = min(filledHeight, maxNotificationsHeight * inverseScaleFactor) // after this, maxY is still incorrect until layout()
if (height == newHeight) return
height = newHeight // after this, maxY is still incorrect until layout()
invalidateHierarchy()
}
/** Add some empty space that can be scrolled under the TileInfoTable which is covering our lower part */

View File

@ -16,7 +16,6 @@ import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.MainMenuScreen
import com.unciv.UncivGame
import com.unciv.utils.debug
import com.unciv.logic.GameInfo
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.ReligionState
@ -58,7 +57,6 @@ import com.unciv.ui.trade.DiplomacyScreen
import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.KeyCharAndCode
import com.unciv.ui.utils.UncivDateFormat.formatDate
import com.unciv.ui.utils.centerX
import com.unciv.ui.utils.colorFromRGB
import com.unciv.ui.utils.darken
@ -79,8 +77,8 @@ import com.unciv.ui.worldscreen.status.NextTurnButton
import com.unciv.ui.worldscreen.status.StatusButtons
import com.unciv.ui.worldscreen.unit.UnitActionsTable
import com.unciv.ui.worldscreen.unit.UnitTable
import com.unciv.utils.debug
import kotlinx.coroutines.Job
import java.util.*
/**
* Unciv's world screen
@ -122,8 +120,8 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
private val statusButtons = StatusButtons(nextTurnButton)
private val tutorialTaskTable = Table().apply { background = ImageGetter.getBackground(
ImageGetter.getBlue().darken(0.5f)) }
private val notificationsScroll = NotificationsScroll(this)
private val notificationsScroll: NotificationsScroll
var shouldUpdate = false
private val zoomController = ZoomButtonPair(mapHolder)
@ -134,12 +132,6 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
init {
topBar.setPosition(0f, stage.height - topBar.height)
topBar.width = stage.width
val maxNotificationsHeight = topBar.y - nextTurnButton.height -
(if (game.settings.showMinimap) minimapWrapper.height else 0f) - 25f
notificationsScroll = NotificationsScroll(this, maxNotificationsHeight)
// notifications are right-aligned, they take up only as much space as necessary.
notificationsScroll.width = stage.width / 2
@ -443,8 +435,6 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
techPolicyAndVictoryHolder.setPosition(10f, topBar.y - techPolicyAndVictoryHolder.height - 5f)
updateDiplomacyButton(viewingCiv)
topBar.unitSupplyImage.isVisible = selectedCiv.stats().getUnitSupplyDeficit() > 0
if (!hasOpenPopups() && isPlayersTurn) {
when {
viewingCiv.shouldShowDiplomaticVotingResults() ->
@ -465,8 +455,12 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
}
}
}
updateGameplayButtons()
notificationsScroll.update(viewingCiv.notifications, bottomTileInfoTable.height)
val maxNotificationsHeight = statusButtons.y -
(if (game.settings.showMinimap) minimapWrapper.height else 0f) - 5f
notificationsScroll.update(viewingCiv.notifications, maxNotificationsHeight, bottomTileInfoTable.height)
notificationsScroll.setTopRight(stage.width - 10f, statusButtons.y - 5f)
val posZoomFromRight = if (game.settings.showMinimap) minimapWrapper.width

View File

@ -3,9 +3,15 @@ package com.unciv.ui.worldscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Container
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.logic.civilization.CivilizationInfo
import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TileResource
import com.unciv.models.stats.Stats
import com.unciv.models.translations.tr
import com.unciv.ui.civilopedia.CivilopediaCategories
@ -15,31 +21,39 @@ import com.unciv.ui.overviewscreen.EmpireOverviewScreen
import com.unciv.ui.pickerscreens.PolicyPickerScreen
import com.unciv.ui.pickerscreens.TechPickerScreen
import com.unciv.ui.popup.popups
import com.unciv.ui.utils.*
import com.unciv.ui.utils.BaseScreen
import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.MayaCalendar
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import com.unciv.ui.utils.colorFromRGB
import com.unciv.ui.utils.darken
import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.setFontColor
import com.unciv.ui.utils.setFontSize
import com.unciv.ui.utils.toLabel
import com.unciv.ui.utils.toTextButton
import com.unciv.ui.victoryscreen.VictoryScreen
import com.unciv.ui.worldscreen.mainmenu.WorldScreenMenuPopup
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.roundToInt
/**
* Table consisting of the menu button, current civ, some stats and the overview button for the top of [WorldScreen]
*/
//region Fields
class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
private var selectedCivLabel = worldScreen.selectedCiv.civName.toLabel()
private var selectedCivIconHolder = Container<Actor>()
private val turnsLabel = "Turns: 0/400".toLabel()
private val goldLabel = "0".toLabel(colorFromRGB(225, 217, 71))
private val scienceLabel = "0".toLabel(colorFromRGB(78, 140, 151))
private val happinessLabel = "0".toLabel()
private val cultureLabel = "0".toLabel(colorFromRGB(210, 94, 210))
private val faithLabel = "0".toLabel(colorFromRGB(210, 94, 210)) // TODO: This colour should be changed at some point
private val resourceLabels = HashMap<String, Label>()
private val resourceImages = HashMap<String, Actor>()
private val faithLabel = "0".toLabel(colorFromRGB(168, 196, 241))
private data class ResourceActors(val resource: TileResource, val Label: Label, val icon: Group)
private val resourceActors = ArrayList<ResourceActors>(12)
private val happinessImage = Group()
// These are all to improve performance IE reduce update time (was 150 ms on my phone, which is a lot!)
@ -48,27 +62,81 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
private val malcontentGroup = ImageGetter.getStatIcon("Malcontent")
private val happinessGroup = ImageGetter.getStatIcon("Happiness")
val unitSupplyImage = ImageGetter.getImage("OtherIcons/ExclamationMark")
.apply { color = Color.FIREBRICK }
private val statsTable = getStatsTable()
private val resourcesWrapper = Table()
private val resourceTable = getResourceTable()
private val selectedCivTable = SelectedCivilizationTable(worldScreen)
private val overviewButton = OverviewAndSupplyTable(worldScreen)
private val leftFillerCell: Cell<BackgroundActor>
private val rightFillerCell: Cell<BackgroundActor>
//endregion
init {
background = ImageGetter.getBackground(ImageGetter.getBlue().darken(0.5f))
add(getStatsTable()).row()
add(getResourceTable())
pad(5f)
// Not the Table, the Cells (all except one) have the background. To avoid gaps, _no_
// padding except inside the cell actors, and all actors need to _fill_ their cell.
val backColor = ImageGetter.getBlue().darken(0.5f)
val backgroundDrawable = ImageGetter.getBackground(backColor)
statsTable.background = backgroundDrawable
resourceTable.background = backgroundDrawable
add(statsTable).colspan(3).growX().row()
add(resourceTable).colspan(3).growX().row()
val leftFillerBG = BackgroundActor.getRoundedEdgeRectangle(backColor)
leftFillerCell = add(BackgroundActor(leftFillerBG, Align.topLeft))
add().growX()
val rightFillerBG = BackgroundActor.getRoundedEdgeRectangle(backColor)
rightFillerCell = add(BackgroundActor(rightFillerBG, Align.topRight))
pack()
addActor(getMenuButton()) // needs to be after pack
}
addActor(getSelectedCivilizationTable())
private fun getStatsTable(): Table {
val statsTable = Table()
statsTable.defaults().pad(8f, 3f, 3f, 3f)
addActor(getOverviewAndSupplyButton())
fun addStat(label: Label, icon: String, isLast: Boolean = false, screenFactory: ()->BaseScreen) {
val image = ImageGetter.getStatIcon(icon)
val action = {
worldScreen.game.setScreen(screenFactory())
}
label.onClick(action)
image.onClick(action)
statsTable.add(label)
statsTable.add(image).padBottom(6f).size(20f).apply {
if (!isLast) padRight(20f)
}
}
fun addStat(label: Label, icon: String, overviewPage: String, isLast: Boolean = false) =
addStat(label, icon, isLast) { EmpireOverviewScreen(worldScreen.selectedCiv, overviewPage) }
addStat(goldLabel, "Gold", "Stats")
addStat(scienceLabel, "Science") { TechPickerScreen(worldScreen.selectedCiv) }
statsTable.add(happinessImage).padBottom(6f).size(20f)
statsTable.add(happinessLabel).padRight(20f)
val invokeResourcesPage = {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Resources"))
}
happinessImage.onClick(invokeResourcesPage)
happinessLabel.onClick(invokeResourcesPage)
addStat(cultureLabel, "Culture") { PolicyPickerScreen(worldScreen, worldScreen.selectedCiv) }
if (worldScreen.gameInfo.isReligionEnabled()) {
addStat(faithLabel, "Faith", "Religion", isLast = true)
} else {
statsTable.add("Religion: Off".toLabel())
}
statsTable.pack()
return statsTable
}
private fun getResourceTable(): Table {
// Since cells with invisible actors still occupy the full actor dimensions, we only prepare
// the future contents for resourcesWrapper here, they're added to the Table in updateResourcesTable
val resourceTable = Table()
resourceTable.defaults().pad(5f)
resourcesWrapper.defaults().pad(5f, 5f, 10f, 5f)
resourcesWrapper.touchable = Touchable.enabled
turnsLabel.onClick {
if (worldScreen.selectedCiv.isLongCountDisplay()) {
val gameInfo = worldScreen.selectedCiv.gameInfo
@ -77,169 +145,171 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
worldScreen.game.setScreen(VictoryScreen(worldScreen))
}
}
resourceTable.add(turnsLabel).padRight(20f)
val revealedStrategicResources = worldScreen.gameInfo.ruleSet.tileResources.values
.filter { it.resourceType == ResourceType.Strategic } // && currentPlayerCivInfo.tech.isResearched(it.revealedBy!!) }
for (resource in revealedStrategicResources) {
val resourceImage = ImageGetter.getResourceImage(resource.name, 20f)
resourceImages[resource.name] = resourceImage
resourceTable.add(resourceImage).padRight(0f)
val resourceLabel = "0".toLabel()
resourceLabels[resource.name] = resourceLabel
resourceTable.add(resourceLabel)
val invokeResourcesPage = {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Resources"))
}
resourceLabel.onClick(invokeResourcesPage)
resourceImage.onClick(invokeResourcesPage)
resourcesWrapper.onClick {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Resources"))
}
resourceTable.pack()
val strategicResources = worldScreen.gameInfo.ruleSet.tileResources.values
.filter { it.resourceType == ResourceType.Strategic }
for (resource in strategicResources) {
val resourceImage = ImageGetter.getResourceImage(resource.name, 20f)
val resourceLabel = "0".toLabel()
resourceActors += ResourceActors(resource, resourceLabel, resourceImage)
}
// in case the icons are configured higher than a label, we add a dummy - height will be measured once before it's updated
resourcesWrapper.add(resourceActors[0].icon)
resourceTable.add(turnsLabel).pad(5f, 5f, 10f, 5f)
resourceTable.add(resourcesWrapper)
return resourceTable
}
private fun getStatsTable(): Table {
val statsTable = Table()
statsTable.defaults().pad(3f)//.align(Align.top)
private class OverviewAndSupplyTable(worldScreen: WorldScreen) : Table(BaseScreen.skin) {
val unitSupplyImage = ImageGetter.getImage("OtherIcons/ExclamationMark")
.apply { color = Color.FIREBRICK }
val unitSupplyCell: Cell<Actor?>
statsTable.add(goldLabel)
val goldImage = ImageGetter.getStatIcon("Gold")
statsTable.add(goldImage).padRight(20f).padBottom(6f).size(20f)
val invokeStatsPage = {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Stats"))
}
goldLabel.onClick(invokeStatsPage)
goldImage.onClick(invokeStatsPage)
statsTable.add(scienceLabel) //.apply { setAlignment(Align.center) }).align(Align.top)
val scienceImage = ImageGetter.getStatIcon("Science")
statsTable.add(scienceImage).padRight(20f).padBottom(6f).size(20f)
val invokeTechScreen = {
worldScreen.game.setScreen(TechPickerScreen(worldScreen.selectedCiv))
}
scienceLabel.onClick(invokeTechScreen)
scienceImage.onClick(invokeTechScreen)
statsTable.add(happinessImage).padBottom(6f).size(20f)
statsTable.add(happinessLabel).padRight(20f)
val invokeResourcesPage = {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Resources"))
}
happinessImage.onClick(invokeResourcesPage)
happinessLabel.onClick(invokeResourcesPage)
statsTable.add(cultureLabel)
val cultureImage = ImageGetter.getStatIcon("Culture")
statsTable.add(cultureImage).padBottom(6f).size(20f)
val invokePoliciesPage = {
worldScreen.game.setScreen(PolicyPickerScreen(worldScreen, worldScreen.selectedCiv))
}
cultureLabel.onClick(invokePoliciesPage)
cultureImage.onClick(invokePoliciesPage)
if(worldScreen.gameInfo.isReligionEnabled()) {
statsTable.add(faithLabel).padLeft(20f)
val faithImage = ImageGetter.getStatIcon("Faith")
statsTable.add(faithImage).padBottom(6f).size(20f)
val invokeFaithOverview = {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Religion"))
init {
unitSupplyImage.onClick {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Units"))
}
faithLabel.onClick(invokeFaithOverview)
faithImage.onClick(invokeFaithOverview)
} else {
statsTable.add("Religion: Off".toLabel()).padLeft(20f)
val overviewButton = "Overview".toTextButton()
overviewButton.addTooltip('e')
overviewButton.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) }
unitSupplyCell = add()
add(overviewButton).pad(10f)
pack()
}
statsTable.pack()
statsTable.width = worldScreen.stage.width - 20
return statsTable
fun update(worldScreen: WorldScreen) {
val newVisible = worldScreen.selectedCiv.stats().getUnitSupplyDeficit() > 0
if (newVisible == unitSupplyCell.hasActor()) return
if (newVisible) unitSupplyCell.setActor(unitSupplyImage)
.size(50f).padLeft(10f)
else unitSupplyCell.setActor(null).size(0f).pad(0f)
invalidate()
pack()
}
}
private fun getMenuButton(): Image {
val menuButton = ImageGetter.getImage("OtherIcons/MenuIcon")
.apply { setSize(50f, 50f) }
menuButton.color = Color.WHITE
menuButton.onClick {
val worldScreenMenuPopup = worldScreen.popups.firstOrNull { it is WorldScreenMenuPopup }
if(worldScreenMenuPopup!=null)
worldScreenMenuPopup.close()
else WorldScreenMenuPopup(worldScreen).open(force = true)
private class SelectedCivilizationTable(worldScreen: WorldScreen) : Table(BaseScreen.skin) {
private var selectedCiv = ""
private val selectedCivLabel = "".toLabel()
private val selectedCivIconHolder = Container<Actor>()
private val menuButton = ImageGetter.getImage("OtherIcons/MenuIcon")
init {
left()
defaults().pad(10f)
menuButton.color = Color.WHITE
menuButton.onClick {
val worldScreenMenuPopup = worldScreen.popups.firstOrNull { it is WorldScreenMenuPopup }
if (worldScreenMenuPopup != null) worldScreenMenuPopup.close()
else WorldScreenMenuPopup(worldScreen).open(force = true)
}
selectedCivLabel.setFontSize(25)
selectedCivLabel.onClick {
val civilopediaScreen = CivilopediaScreen(
worldScreen.selectedCiv.gameInfo.ruleSet,
worldScreen,
CivilopediaCategories.Nation,
worldScreen.selectedCiv.civName
)
worldScreen.game.setScreen(civilopediaScreen)
}
selectedCivIconHolder.onClick {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv))
}
add(menuButton).size(50f).padRight(0f)
add(selectedCivLabel).padRight(0f)
add(selectedCivIconHolder).size(35f)
pack()
}
fun update(worldScreen: WorldScreen) {
val newCiv = worldScreen.selectedCiv.civName
if (this.selectedCiv == newCiv) return
this.selectedCiv = newCiv
selectedCivLabel.setText(newCiv.tr())
val nation = worldScreen.gameInfo.ruleSet.nations[worldScreen.selectedCiv.civName]!!
val selectedCivIcon = ImageGetter.getNationIndicator(nation, 35f)
selectedCivIconHolder.actor = selectedCivIcon
invalidate()
pack()
}
menuButton.centerY(this)
menuButton.x = menuButton.y
return menuButton
}
private fun getOverviewAndSupplyButton(): Table {
val rightTable = Table(BaseScreen.skin).apply{ defaults().pad(10f) }
private fun layoutButtons() {
removeActor(selectedCivTable)
removeActor(overviewButton)
validate()
unitSupplyImage.onClick {
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Units"))
}
unitSupplyImage.isVisible = worldScreen.selectedCiv.stats().getUnitSupplyDeficit() > 0
val statsWidth = statsTable.minWidth
val resourceWidth = resourceTable.minWidth
val overviewWidth = overviewButton.minWidth
val selectedCivWidth = selectedCivTable.minWidth
val leftRightNeeded = max(selectedCivWidth, overviewWidth)
val statsRowHeight = getRowHeight(0)
val baseHeight = statsRowHeight + getRowHeight(1)
val overviewButton = "Overview".toTextButton()
overviewButton.addTooltip('e')
overviewButton.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) }
rightTable.add(unitSupplyImage).size(50f)
rightTable.add(overviewButton)
rightTable.pack()
rightTable.centerY(this)
rightTable.x = worldScreen.stage.width - rightTable.width - 10
return rightTable
}
private fun getSelectedCivilizationTable(): Table {
val selectedCivTable = Table()
selectedCivTable.centerY(this)
selectedCivTable.left()
selectedCivTable.x = getMenuButton().width + 20f
selectedCivLabel.setFontSize(25)
selectedCivLabel.onClick {
val civilopeidaScreen = CivilopediaScreen(
worldScreen.selectedCiv.gameInfo.ruleSet,
worldScreen,
CivilopediaCategories.Nation,
worldScreen.selectedCiv.civName
)
worldScreen.game.setScreen(civilopeidaScreen)
// Check whether it gets cramped on narrow aspect ratios
val fillerHeight: Float // Height of the background filler cells
val buttonY: Float // Vertical center of Civ+Overview buttons relative to this.y
when {
leftRightNeeded * 2f > stage.width - resourceWidth -> {
// Need to shift buttons down to below both stats and resources
fillerHeight = baseHeight
buttonY = overviewButton.minHeight / 2f
}
leftRightNeeded * 2f > stage.width - statsWidth -> {
// Shifting buttons down to below stats row is enough
fillerHeight = statsRowHeight
buttonY = overviewButton.minHeight / 2f
}
else -> {
// Enough space to keep buttons to the left and right of stats and resources
fillerHeight = 0f
buttonY = baseHeight / 2f
}
}
val nation = worldScreen.gameInfo.ruleSet.nations[worldScreen.selectedCiv.civName]!!
val selectedCivIcon = ImageGetter.getNationIndicator(nation, 35f)
selectedCivIconHolder.actor = selectedCivIcon
selectedCivIconHolder.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) }
val leftFillerWidth = if (fillerHeight > 0f) selectedCivWidth else 0f
val rightFillerWidth = if (fillerHeight > 0f) overviewWidth else 0f
if (leftFillerCell.minHeight != fillerHeight
|| leftFillerCell.minWidth != leftFillerWidth
|| rightFillerCell.minWidth != rightFillerWidth) {
// Gdx fail: containing Table isn't invalidated when setting Cell size
leftFillerCell.width(leftFillerWidth).height(fillerHeight)
rightFillerCell.width(rightFillerWidth).height(fillerHeight)
invalidate() // Without this all attempts to get a recalculated height are doomed
pack() // neither validate nor layout will include the new row height in height
}
selectedCivTable.add(selectedCivLabel).padRight(10f)
selectedCivTable.add(selectedCivIconHolder)
return selectedCivTable
width = stage.width
setPosition(0f, stage.height, Align.topLeft)
selectedCivTable.setPosition(1f, buttonY, Align.left)
overviewButton.setPosition(stage.width, buttonY, Align.right)
addActor(selectedCivTable) // needs to be after pack
addActor(overviewButton)
}
internal fun update(civInfo: CivilizationInfo) {
val revealedStrategicResources = civInfo.gameInfo.ruleSet.tileResources.values
.filter { it.resourceType == ResourceType.Strategic }
val civResources = civInfo.getCivResources()
for (resource in revealedStrategicResources) {
val isRevealed = resource.revealedBy == null || civInfo.tech.isResearched(resource.revealedBy!!)
resourceLabels[resource.name]!!.isVisible = isRevealed
resourceImages[resource.name]!!.isVisible = isRevealed
val amountText = (civResources.get(resource, "All")?.amount ?: 0).toString()
resourceLabels[resource.name]!!.setText(amountText)
}
val year = civInfo.gameInfo.getYear()
val yearText = if (civInfo.isLongCountDisplay()) MayaCalendar.yearToMayaDate(year)
else "[" + abs(year) + "] " + (if (year < 0) "BC" else "AD")
turnsLabel.setText(Fonts.turn + "" + civInfo.gameInfo.turns + " | " + yearText.tr())
updateStatsTable(civInfo)
updateResourcesTable(civInfo)
selectedCivTable.update(worldScreen)
overviewButton.update(worldScreen)
layoutButtons()
}
private fun updateStatsTable(civInfo: CivilizationInfo) {
val nextTurnStats = civInfo.statsForNextTurn
val goldPerTurn = "(" + (if (nextTurnStats.gold > 0) "+" else "") + nextTurnStats.gold.roundToInt() + ")"
goldLabel.setText(civInfo.gold.toString() + goldPerTurn)
@ -260,19 +330,28 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
cultureLabel.setText(getCultureText(civInfo, nextTurnStats))
faithLabel.setText(civInfo.religionManager.storedFaith.toString() + "(+" + nextTurnStats.faith.roundToInt() + ")")
updateSelectedCivTable()
}
private fun updateSelectedCivTable() {
if (selectedCivLabel.text.toString() == worldScreen.selectedCiv.civName.tr()) return
private fun updateResourcesTable(civInfo: CivilizationInfo) {
val year = civInfo.gameInfo.getYear()
val yearText = if (civInfo.isLongCountDisplay()) MayaCalendar.yearToMayaDate(year)
else "[" + abs(year) + "] " + (if (year < 0) "BC" else "AD")
turnsLabel.setText(Fonts.turn + "" + civInfo.gameInfo.turns + " | " + yearText.tr())
selectedCivLabel.setText(worldScreen.selectedCiv.civName.tr())
resourcesWrapper.clearChildren()
var firstPadLeft = 20f // We want a distance from the turns entry to the first resource, but only if any resource is displayed
val civResources = civInfo.getCivResources()
for ((resource, label, icon) in resourceActors) {
if (resource.revealedBy != null && !civInfo.tech.isResearched(resource.revealedBy!!))
continue
resourcesWrapper.add(icon).padLeft(firstPadLeft).padRight(0f)
firstPadLeft = 5f
val amount = civResources.get(resource, "All")?.amount ?: 0
label.setText(amount)
resourcesWrapper.add(label).padTop(8f) // digits don't have descenders, so push them down a little
}
val nation = worldScreen.gameInfo.ruleSet.nations[worldScreen.selectedCiv.civName]!!
val selectedCivIcon = ImageGetter.getNationIndicator(nation, 35f)
selectedCivIconHolder.actor = selectedCivIcon
selectedCivIconHolder.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) }
resourceTable.pack()
}
private fun getCultureText(civInfo: CivilizationInfo, nextTurnStats: Stats): String {
@ -288,7 +367,7 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
private fun getHappinessText(civInfo: CivilizationInfo): String {
var happinessText = civInfo.getHappiness().toString()
val goldenAges = civInfo.goldenAges
happinessText +=
happinessText +=
if (goldenAges.isGoldenAge())
" {GOLDEN AGE}(${goldenAges.turnsLeftForCurrentGoldenAge})".tr()
else