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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 import com.unciv.ui.utils.AutoScrollPane as ScrollPane
class NotificationsScroll( class NotificationsScroll(
private val worldScreen: WorldScreen, private val worldScreen: WorldScreen
private val maxNotificationsHeight: Float
) : ScrollPane(null) { ) : ScrollPane(null) {
private companion object { private companion object {
/** Scale the entire ScrollPane by this factor */ /** Scale the entire ScrollPane by this factor */
@ -41,15 +40,26 @@ class NotificationsScroll(
setScale(scaleFactor) 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 // no news? - keep our list as it is, especially don't reset scroll position
if (notificationsHash == notifications.hashCode()) { val newHash = notifications.hashCode()
sizeScrollingSpacer(tileInfoTableHeight) if (notificationsHash == newHash) return
layout() notificationsHash = newHash
return
}
notificationsHash = notifications.hashCode()
notificationsTable.clearChildren() notificationsTable.clearChildren()
endOfTableSpacerCell = null 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 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) sizeScrollingSpacer(tileInfoTableHeight)
pack() 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 */ /** 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.Constants
import com.unciv.MainMenuScreen import com.unciv.MainMenuScreen
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.utils.debug
import com.unciv.logic.GameInfo import com.unciv.logic.GameInfo
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.civilization.ReligionState 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.BaseScreen
import com.unciv.ui.utils.Fonts import com.unciv.ui.utils.Fonts
import com.unciv.ui.utils.KeyCharAndCode import com.unciv.ui.utils.KeyCharAndCode
import com.unciv.ui.utils.UncivDateFormat.formatDate
import com.unciv.ui.utils.centerX import com.unciv.ui.utils.centerX
import com.unciv.ui.utils.colorFromRGB import com.unciv.ui.utils.colorFromRGB
import com.unciv.ui.utils.darken 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.status.StatusButtons
import com.unciv.ui.worldscreen.unit.UnitActionsTable import com.unciv.ui.worldscreen.unit.UnitActionsTable
import com.unciv.ui.worldscreen.unit.UnitTable import com.unciv.ui.worldscreen.unit.UnitTable
import com.unciv.utils.debug
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import java.util.*
/** /**
* Unciv's world screen * Unciv's world screen
@ -122,8 +120,8 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
private val statusButtons = StatusButtons(nextTurnButton) private val statusButtons = StatusButtons(nextTurnButton)
private val tutorialTaskTable = Table().apply { background = ImageGetter.getBackground( private val tutorialTaskTable = Table().apply { background = ImageGetter.getBackground(
ImageGetter.getBlue().darken(0.5f)) } ImageGetter.getBlue().darken(0.5f)) }
private val notificationsScroll = NotificationsScroll(this)
private val notificationsScroll: NotificationsScroll
var shouldUpdate = false var shouldUpdate = false
private val zoomController = ZoomButtonPair(mapHolder) private val zoomController = ZoomButtonPair(mapHolder)
@ -134,12 +132,6 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
init { 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. // notifications are right-aligned, they take up only as much space as necessary.
notificationsScroll.width = stage.width / 2 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) techPolicyAndVictoryHolder.setPosition(10f, topBar.y - techPolicyAndVictoryHolder.height - 5f)
updateDiplomacyButton(viewingCiv) updateDiplomacyButton(viewingCiv)
topBar.unitSupplyImage.isVisible = selectedCiv.stats().getUnitSupplyDeficit() > 0
if (!hasOpenPopups() && isPlayersTurn) { if (!hasOpenPopups() && isPlayersTurn) {
when { when {
viewingCiv.shouldShowDiplomaticVotingResults() -> viewingCiv.shouldShowDiplomaticVotingResults() ->
@ -465,8 +455,12 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas
} }
} }
} }
updateGameplayButtons() 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) notificationsScroll.setTopRight(stage.width - 10f, statusButtons.y - 5f)
val posZoomFromRight = if (game.settings.showMinimap) minimapWrapper.width 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.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Group 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.logic.civilization.CivilizationInfo
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TileResource
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.civilopedia.CivilopediaCategories 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.PolicyPickerScreen
import com.unciv.ui.pickerscreens.TechPickerScreen import com.unciv.ui.pickerscreens.TechPickerScreen
import com.unciv.ui.popup.popups 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.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.victoryscreen.VictoryScreen
import com.unciv.ui.worldscreen.mainmenu.WorldScreenMenuPopup import com.unciv.ui.worldscreen.mainmenu.WorldScreenMenuPopup
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.roundToInt import kotlin.math.roundToInt
/** /**
* Table consisting of the menu button, current civ, some stats and the overview button for the top of [WorldScreen] * 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() { 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 turnsLabel = "Turns: 0/400".toLabel()
private val goldLabel = "0".toLabel(colorFromRGB(225, 217, 71)) private val goldLabel = "0".toLabel(colorFromRGB(225, 217, 71))
private val scienceLabel = "0".toLabel(colorFromRGB(78, 140, 151)) private val scienceLabel = "0".toLabel(colorFromRGB(78, 140, 151))
private val happinessLabel = "0".toLabel() private val happinessLabel = "0".toLabel()
private val cultureLabel = "0".toLabel(colorFromRGB(210, 94, 210)) 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 faithLabel = "0".toLabel(colorFromRGB(168, 196, 241))
private val resourceLabels = HashMap<String, Label>() private data class ResourceActors(val resource: TileResource, val Label: Label, val icon: Group)
private val resourceImages = HashMap<String, Actor>() private val resourceActors = ArrayList<ResourceActors>(12)
private val happinessImage = Group() private val happinessImage = Group()
// These are all to improve performance IE reduce update time (was 150 ms on my phone, which is a lot!) // 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 malcontentGroup = ImageGetter.getStatIcon("Malcontent")
private val happinessGroup = ImageGetter.getStatIcon("Happiness") private val happinessGroup = ImageGetter.getStatIcon("Happiness")
val unitSupplyImage = ImageGetter.getImage("OtherIcons/ExclamationMark") private val statsTable = getStatsTable()
.apply { color = Color.FIREBRICK } 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 { init {
background = ImageGetter.getBackground(ImageGetter.getBlue().darken(0.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.
add(getStatsTable()).row() val backColor = ImageGetter.getBlue().darken(0.5f)
add(getResourceTable()) val backgroundDrawable = ImageGetter.getBackground(backColor)
statsTable.background = backgroundDrawable
pad(5f) 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() 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 { 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() val resourceTable = Table()
resourceTable.defaults().pad(5f) resourcesWrapper.defaults().pad(5f, 5f, 10f, 5f)
resourcesWrapper.touchable = Touchable.enabled
turnsLabel.onClick { turnsLabel.onClick {
if (worldScreen.selectedCiv.isLongCountDisplay()) { if (worldScreen.selectedCiv.isLongCountDisplay()) {
val gameInfo = worldScreen.selectedCiv.gameInfo val gameInfo = worldScreen.selectedCiv.gameInfo
@ -77,169 +145,171 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
worldScreen.game.setScreen(VictoryScreen(worldScreen)) worldScreen.game.setScreen(VictoryScreen(worldScreen))
} }
} }
resourceTable.add(turnsLabel).padRight(20f) resourcesWrapper.onClick {
val revealedStrategicResources = worldScreen.gameInfo.ruleSet.tileResources.values worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Resources"))
.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)
} }
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 return resourceTable
} }
private fun getStatsTable(): Table { private class OverviewAndSupplyTable(worldScreen: WorldScreen) : Table(BaseScreen.skin) {
val statsTable = Table() val unitSupplyImage = ImageGetter.getImage("OtherIcons/ExclamationMark")
statsTable.defaults().pad(3f)//.align(Align.top) .apply { color = Color.FIREBRICK }
val unitSupplyCell: Cell<Actor?>
statsTable.add(goldLabel) init {
val goldImage = ImageGetter.getStatIcon("Gold") unitSupplyImage.onClick {
statsTable.add(goldImage).padRight(20f).padBottom(6f).size(20f) worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Units"))
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"))
} }
faithLabel.onClick(invokeFaithOverview) val overviewButton = "Overview".toTextButton()
faithImage.onClick(invokeFaithOverview) overviewButton.addTooltip('e')
} else { overviewButton.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) }
statsTable.add("Religion: Off".toLabel()).padLeft(20f)
unitSupplyCell = add()
add(overviewButton).pad(10f)
pack()
} }
statsTable.pack() fun update(worldScreen: WorldScreen) {
statsTable.width = worldScreen.stage.width - 20 val newVisible = worldScreen.selectedCiv.stats().getUnitSupplyDeficit() > 0
return statsTable 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 { private class SelectedCivilizationTable(worldScreen: WorldScreen) : Table(BaseScreen.skin) {
val menuButton = ImageGetter.getImage("OtherIcons/MenuIcon") private var selectedCiv = ""
.apply { setSize(50f, 50f) } private val selectedCivLabel = "".toLabel()
menuButton.color = Color.WHITE private val selectedCivIconHolder = Container<Actor>()
menuButton.onClick { private val menuButton = ImageGetter.getImage("OtherIcons/MenuIcon")
val worldScreenMenuPopup = worldScreen.popups.firstOrNull { it is WorldScreenMenuPopup }
if(worldScreenMenuPopup!=null) init {
worldScreenMenuPopup.close() left()
else WorldScreenMenuPopup(worldScreen).open(force = true) 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 { private fun layoutButtons() {
val rightTable = Table(BaseScreen.skin).apply{ defaults().pad(10f) } removeActor(selectedCivTable)
removeActor(overviewButton)
validate()
unitSupplyImage.onClick { val statsWidth = statsTable.minWidth
worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Units")) val resourceWidth = resourceTable.minWidth
} val overviewWidth = overviewButton.minWidth
unitSupplyImage.isVisible = worldScreen.selectedCiv.stats().getUnitSupplyDeficit() > 0 val selectedCivWidth = selectedCivTable.minWidth
val leftRightNeeded = max(selectedCivWidth, overviewWidth)
val statsRowHeight = getRowHeight(0)
val baseHeight = statsRowHeight + getRowHeight(1)
val overviewButton = "Overview".toTextButton() // Check whether it gets cramped on narrow aspect ratios
overviewButton.addTooltip('e') val fillerHeight: Float // Height of the background filler cells
overviewButton.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) } val buttonY: Float // Vertical center of Civ+Overview buttons relative to this.y
when {
rightTable.add(unitSupplyImage).size(50f) leftRightNeeded * 2f > stage.width - resourceWidth -> {
rightTable.add(overviewButton) // Need to shift buttons down to below both stats and resources
fillerHeight = baseHeight
rightTable.pack() buttonY = overviewButton.minHeight / 2f
rightTable.centerY(this) }
rightTable.x = worldScreen.stage.width - rightTable.width - 10 leftRightNeeded * 2f > stage.width - statsWidth -> {
// Shifting buttons down to below stats row is enough
return rightTable fillerHeight = statsRowHeight
} buttonY = overviewButton.minHeight / 2f
}
private fun getSelectedCivilizationTable(): Table { else -> {
val selectedCivTable = Table() // Enough space to keep buttons to the left and right of stats and resources
selectedCivTable.centerY(this) fillerHeight = 0f
selectedCivTable.left() buttonY = baseHeight / 2f
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)
} }
val nation = worldScreen.gameInfo.ruleSet.nations[worldScreen.selectedCiv.civName]!! val leftFillerWidth = if (fillerHeight > 0f) selectedCivWidth else 0f
val selectedCivIcon = ImageGetter.getNationIndicator(nation, 35f) val rightFillerWidth = if (fillerHeight > 0f) overviewWidth else 0f
selectedCivIconHolder.actor = selectedCivIcon if (leftFillerCell.minHeight != fillerHeight
selectedCivIconHolder.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) } || 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) width = stage.width
selectedCivTable.add(selectedCivIconHolder) setPosition(0f, stage.height, Align.topLeft)
return selectedCivTable
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) { internal fun update(civInfo: CivilizationInfo) {
val revealedStrategicResources = civInfo.gameInfo.ruleSet.tileResources.values updateStatsTable(civInfo)
.filter { it.resourceType == ResourceType.Strategic } updateResourcesTable(civInfo)
val civResources = civInfo.getCivResources() selectedCivTable.update(worldScreen)
for (resource in revealedStrategicResources) { overviewButton.update(worldScreen)
val isRevealed = resource.revealedBy == null || civInfo.tech.isResearched(resource.revealedBy!!) layoutButtons()
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())
private fun updateStatsTable(civInfo: CivilizationInfo) {
val nextTurnStats = civInfo.statsForNextTurn val nextTurnStats = civInfo.statsForNextTurn
val goldPerTurn = "(" + (if (nextTurnStats.gold > 0) "+" else "") + nextTurnStats.gold.roundToInt() + ")" val goldPerTurn = "(" + (if (nextTurnStats.gold > 0) "+" else "") + nextTurnStats.gold.roundToInt() + ")"
goldLabel.setText(civInfo.gold.toString() + goldPerTurn) goldLabel.setText(civInfo.gold.toString() + goldPerTurn)
@ -260,19 +330,28 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() {
cultureLabel.setText(getCultureText(civInfo, nextTurnStats)) cultureLabel.setText(getCultureText(civInfo, nextTurnStats))
faithLabel.setText(civInfo.religionManager.storedFaith.toString() + "(+" + nextTurnStats.faith.roundToInt() + ")") faithLabel.setText(civInfo.religionManager.storedFaith.toString() + "(+" + nextTurnStats.faith.roundToInt() + ")")
updateSelectedCivTable()
} }
private fun updateSelectedCivTable() { private fun updateResourcesTable(civInfo: CivilizationInfo) {
if (selectedCivLabel.text.toString() == worldScreen.selectedCiv.civName.tr()) return 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]!! resourceTable.pack()
val selectedCivIcon = ImageGetter.getNationIndicator(nation, 35f)
selectedCivIconHolder.actor = selectedCivIcon
selectedCivIconHolder.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) }
} }
private fun getCultureText(civInfo: CivilizationInfo, nextTurnStats: Stats): String { private fun getCultureText(civInfo: CivilizationInfo, nextTurnStats: Stats): String {