Empire overview resources (#6442)

* Empire Overview Resources: Vertical option

* Empire Overview Resources: Extra Info

* Empire Overview Resources: Tweaks

Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
SomeTroglodyte
2022-03-28 16:31:19 +02:00
committed by GitHub
parent 870659d142
commit 7526114281
10 changed files with 1097 additions and 914 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -765,7 +765,6 @@ Territory = Territorium
Force = Kampfkraft Force = Kampfkraft
GOLDEN AGE = GOLDENES ZEITALTER GOLDEN AGE = GOLDENES ZEITALTER
Golden Age = Goldenes Zeitalter Golden Age = Goldenes Zeitalter
We Love The King Day = 'Wir lieben den König'-Tag
Global Effect = Globaler Effekt Global Effect = Globaler Effekt
[year] BC = [year] v. Chr. [year] BC = [year] v. Chr.
[year] AD = [year] n. Chr. [year] AD = [year] n. Chr.
@ -983,6 +982,15 @@ Far away = Weit entfernt
Status = Status Status = Status
Location = Standort Location = Standort
Unimproved = Unverbessert
Number of tiles with this resource\nin your territory, without an\nappropriate improvement to use it = Anzahl der Felder mit dieser\nRessource innerhalb Deines Territoriums,\ndenen die entsprechende\nVerbesserung zu ihrer Nutzung fehlt.
We Love The King Day = 'Wir lieben den König'-Tag
WLTK+ = WLDK+
Number of your cities celebrating\n'We Love The King Day' thanks\nto access to this resource = Anzahl Deiner Städte, die den\n'Wir lieben den König'-Tag feiern,\ndank Zugang zu dieser Ressource.
WLTK demand = Bedarf für WLDK-Tag
WLTK- = WLDK-
Number of your cities\ndemanding this resource for\n'We Love The King Day' = Anzahl Deiner Städte, die nach\ndieser Ressource verlangen, um\nden 'Wir lieben den König'-Tag\nfeiern zu können.
# Victory # Victory
Science victory = Wissenschaftssieg Science victory = Wissenschaftssieg
@ -5393,8 +5401,8 @@ The Maya measured time in days from what we would call 11th of August, 3114 BCE.
Unciv only displays ය B'ak'tuns, ඹ K'atuns and ම Tuns (from left to right) since that is enough to approximate gregorian calendar years. The Maya numerals are pretty obvious to understand. Have fun deciphering them! = Unciv zeigt nur ය B'ak'tuns, ඹ K'atuns und ම Tuns (von links nach rechts) an, da dies ausreicht, um gregorianische Kalenderjahre anzunähern. Die Maya-Ziffern sind ziemlich einfach zu verstehen. Viel Spaß beim Entschlüsseln! Unciv only displays ය B'ak'tuns, ඹ K'atuns and ම Tuns (from left to right) since that is enough to approximate gregorian calendar years. The Maya numerals are pretty obvious to understand. Have fun deciphering them! = Unciv zeigt nur ය B'ak'tuns, ඹ K'atuns und ම Tuns (von links nach rechts) an, da dies ausreicht, um gregorianische Kalenderjahre anzunähern. Die Maya-Ziffern sind ziemlich einfach zu verstehen. Viel Spaß beim Entschlüsseln!
Your cities will periodically demand different luxury goods to satisfy their desire for new things in life. = Deine Städte werden in regelmäßigen Abständen verschiedene Luxusgüter verlangen, um ihren Wunsch nach neuen Dingen im Leben zu befriedigen. Your cities will periodically demand different luxury goods to satisfy their desire for new things in life. = Deine Städte werden in regelmäßigen Abständen verschiedene Luxusgüter verlangen, um ihren Wunsch nach neuen Dingen im Leben zu befriedigen.
If you manage to acquire the demanded luxury by trade, expansion, or conquest, the city will celebrate We Love The King Day for 20 turns. = Wenn es dir gelingt, den geforderten Luxus durch Handel, Expansion oder Eroberung zu erwerben, wird die Stadt 20 Runden lang den Wir Lieben Den König Tag feiern. If you manage to acquire the demanded luxury by trade, expansion, or conquest, the city will celebrate We Love The King Day for 20 turns. = Wenn es dir gelingt, den geforderten Luxus durch Handel, Expansion oder Eroberung zu erwerben, wird die Stadt 20 Runden lang den 'Wir Lieben Den König'-Tag feiern.
During the We Love The King Day, the city will grow 25% faster. = Während des Wir Lieben Den König Tags wird die Stadt um 25 % schneller wachsen. During the We Love The King Day, the city will grow 25% faster. = Während des 'Wir Lieben Den König'-Tags wird die Stadt um 25 % schneller wachsen.
This means exploration and trade is important to grow your cities! = Das bedeutet, dass Erkundung und Handel wichtig sind, um deine Städte wachsen zu lassen! This means exploration and trade is important to grow your cities! = Das bedeutet, dass Erkundung und Handel wichtig sind, um deine Städte wachsen zu lassen!

View File

@ -770,7 +770,6 @@ Territory =
Force = Force =
GOLDEN AGE = GOLDEN AGE =
Golden Age = Golden Age =
We Love The King Day =
Global Effect = Global Effect =
[year] BC = [year] BC =
[year] AD = [year] AD =
@ -988,6 +987,14 @@ Somewhere around [city] =
Far away = Far away =
Status = Status =
Location = Location =
Unimproved =
Number of tiles with this resource\nin your territory, without an\nappropriate improvement to use it =
We Love The King Day =
WLTK+ =
Number of your cities celebrating\n'We Love The King Day' thanks\nto access to this resource =
WLTK demand =
WLTK- =
Number of your cities\ndemanding this resource for\n'We Love The King Day' =
# Victory # Victory

View File

@ -44,8 +44,8 @@ enum class EmpireOverviewCategories(
= DiplomacyOverviewTab(viewingPlayer, overviewScreen, persistedData), = DiplomacyOverviewTab(viewingPlayer, overviewScreen, persistedData),
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.diplomacy.isEmpty().toState()), fun (viewingPlayer: CivilizationInfo) = viewingPlayer.diplomacy.isEmpty().toState()),
Resources("StatIcons/Happiness", 'R', Align.topLeft, Resources("StatIcons/Happiness", 'R', Align.topLeft,
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, _: EmpireOverviewTabPersistableData?) fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)
= ResourcesOverviewTab(viewingPlayer, overviewScreen), = ResourcesOverviewTab(viewingPlayer, overviewScreen, persistedData),
fun (viewingPlayer: CivilizationInfo) = viewingPlayer.detailedCivResources.isEmpty().toState()), fun (viewingPlayer: CivilizationInfo) = viewingPlayer.detailedCivResources.isEmpty().toState()),
Religion("StatIcons/Faith", 'F', Align.top, Religion("StatIcons/Faith", 'F', Align.top,
fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?) fun (viewingPlayer: CivilizationInfo, overviewScreen: EmpireOverviewScreen, persistedData: EmpireOverviewTabPersistableData?)

View File

@ -1,75 +1,222 @@
package com.unciv.ui.overviewscreen package com.unciv.ui.overviewscreen
import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup
import com.badlogic.gdx.utils.Align
import com.unciv.UncivGame
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.ruleset.tile.ResourceSupplyList
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.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.utils.ImageGetter import com.unciv.ui.utils.*
import com.unciv.ui.utils.addSeparator import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import com.unciv.ui.utils.onClick
import com.unciv.ui.utils.toLabel
class ResourcesOverviewTab( class ResourcesOverviewTab(
viewingPlayer: CivilizationInfo, viewingPlayer: CivilizationInfo,
overviewScreen: EmpireOverviewScreen overviewScreen: EmpireOverviewScreen,
persistedData: EmpireOverviewTabPersistableData? = null
) : EmpireOverviewTab(viewingPlayer, overviewScreen) { ) : EmpireOverviewTab(viewingPlayer, overviewScreen) {
class ResourcesTabPersistableData(
var vertical: Boolean = false
) : EmpireOverviewTabPersistableData() {
override fun isEmpty() = !vertical
}
override val persistableData = (persistedData as? ResourcesTabPersistableData) ?: ResourcesTabPersistableData()
init { companion object {
defaults().pad(10f) private const val iconSize = 50f
private const val defaultPad = 10f
private const val tooltipSize = 24f
}
val resourceDrilldown = viewingPlayer.detailedCivResources private fun getTurnImage(vertical: Boolean) =
ImageGetter.getImage("OtherIcons/Turn right")
.apply {
color = ImageGetter.getBlue()
if (vertical)
rotateBy(90f)
}
.surroundWithCircle(iconSize, color = Color.LIGHT_GRAY)
private val turnImageH = getTurnImage(false)
private val turnImageV = getTurnImage(true)
// First row of table has all the icons private val resourceDrilldown: ResourceSupplyList = viewingPlayer.detailedCivResources
add() private val extraDrilldown: ResourceSupplyList = getExtraDrilldown()
private val drilldownSequence = resourceDrilldown.asSequence() + extraDrilldown.asSequence()
// Order of source ResourceSupplyList: by tiles, enumerating the map in that spiral pattern // Order of source ResourceSupplyList: by tiles, enumerating the map in that spiral pattern
// UI should not surprise player, thus we need a deterministic and guessable order // UI should not surprise player, thus we need a deterministic and guessable order
val resources = resourceDrilldown.map { it.resource } private val resources: List<TileResource> = drilldownSequence
.filter { it.resourceType != ResourceType.Bonus }.distinct() .map { it.resource }
.sortedWith(compareBy({ it.resourceType }, { it.name.tr() })) .filter { it.resourceType != ResourceType.Bonus }
.distinct()
.sortedWith(
compareBy<TileResource> { it.resourceType }
.thenBy(UncivGame.Current.settings.getCollatorFromLocale()) { it.name.tr() }
)
.toList()
private val origins: List<String> = resourceDrilldown.asSequence()
.map { it.origin }.distinct().toList()
private val extraOrigins: List<ExtraInfoOrigin> = extraDrilldown.asSequence()
.mapNotNull { ExtraInfoOrigin.safeValueOf(it.origin) }.distinct().toList()
for (resource in resources) { private fun ResourceSupplyList.getLabel(resource: TileResource, origin: String): Label? =
// Create a group of label and icon for each resource. firstOrNull { it.resource == resource && it.origin == origin }?.amount?.toLabel()
val resourceImage = ImageGetter.getResourceImage(resource.name, 50f) private fun ResourceSupplyList.getTotalLabel(resource: TileResource): Label =
val labelPadding = 10f filter { it.resource == resource }.sumOf { it.amount }.toLabel()
// Using a table here leads to spacing issues private fun getResourceImage(name: String) =
// due to different label lengths. ImageGetter.getResourceImage(name, iconSize).apply {
val holder = Group() onClick {
resourceImage.onClick { viewingPlayer.gameInfo.notifyExploredResources(viewingPlayer, name, 0, true)
viewingPlayer.gameInfo.notifyExploredResources(viewingPlayer, resource.name, 0, true)
overviewScreen.game.setWorldScreen() overviewScreen.game.setWorldScreen()
} }
holder.addActor(resourceImage) }
holder.setSize(resourceImage.width, resourceImage.height + labelPadding)
// Center-align all labels, but right-align the last couple resources' labels private enum class ExtraInfoOrigin(
// because they may get clipped otherwise. The leftmost label should be fine val horizontalCaption: String,
// center-aligned (if there are more than 2 resources), because the left side val verticalCaption: String,
// has more padding. val tooltip: String
val alignFactor = when { ) {
(resources.indexOf(resource) + 2 >= resources.count()) -> 1 Unimproved("Unimproved", "Unimproved",
else -> 2 "Number of tiles with this resource\nin your territory, without an\nappropriate improvement to use it"),
} CelebratingWLKT("We Love The King Day", "WLTK+",
add(holder) "Number of your cities celebrating\n'We Love The King Day' thanks\nto access to this resource"),
DemandingWLTK("WLTK demand", "WLTK-",
"Number of your cities\ndemanding this resource for\n'We Love The King Day'"),
;
companion object {
fun safeValueOf(name: String) = values().firstOrNull { it.name == name }
}
}
private val fixedContent = Table()
init {
defaults().pad(defaultPad)
fixedContent.defaults().pad(defaultPad)
turnImageH.onClick {
persistableData.vertical = true
update()
}
turnImageV.onClick {
persistableData.vertical = false
update()
}
update()
}
override fun getFixedContent() = fixedContent
private fun update() {
clear()
fixedContent.clear()
if (persistableData.vertical) updateVertical()
else updateHorizontal()
}
private fun updateHorizontal() {
// First row of table has all the icons
add(turnImageH)
for (resource in resources) {
add(getResourceImage(resource.name).apply {
addTooltip(resource.name, tipAlign = Align.topLeft)
})
} }
addSeparator() addSeparator()
val origins = resourceDrilldown.map { it.origin }.distinct() // One detail row per origin
for (origin in origins) { for (origin in origins) {
add(origin.toLabel()) add(origin.toLabel()).left()
for (resource in resources) { for (resource in resources) {
val resourceSupply = resourceDrilldown.firstOrNull { it.resource == resource && it.origin == origin } add(resourceDrilldown.getLabel(resource, origin))
if (resourceSupply == null) add() }
else add(resourceSupply.amount.toString().toLabel()) row()
}
addSeparator(Color.GRAY).pad(0f, defaultPad)
// One row for the totals
add("Total".toLabel()).left()
for (resource in resources) {
add(resourceDrilldown.getTotalLabel(resource))
}
addSeparator()
// Separate rows for origins not part of the totals
for (origin in extraOrigins) {
add(origin.horizontalCaption.toLabel().apply {
addTooltip(origin.tooltip, tooltipSize, tipAlign = Align.left)
}).left()
for (resource in resources) {
add(extraDrilldown.getLabel(resource, origin.name))
}
row()
}
}
private fun updateVertical() {
// First row of table has all the origin labels
fixedContent.apply {
add(turnImageV).size(iconSize)
add()
addSeparatorVertical(Color.GRAY).pad(0f)
for (origin in origins) {
add(origin.toLabel())
}
add("Total".toLabel())
addSeparatorVertical(Color.GRAY).pad(0f)
for (origin in extraOrigins) {
add(origin.verticalCaption.toLabel().apply {
addTooltip(origin.tooltip, tooltipSize, targetAlign = Align.bottom, tipAlign = Align.topRight)
})
}
addSeparator().pad(0f, defaultPad)
}
// One detail row per resource
for (resource in resources) {
add(getResourceImage(resource.name))
add(resource.name.toLabel())
addSeparatorVertical(Color.GRAY).pad(0f)
for (origin in origins) {
add(resourceDrilldown.getLabel(resource, origin))
}
add(resourceDrilldown.getTotalLabel(resource))
addSeparatorVertical(Color.GRAY).pad(0f)
for (origin in extraOrigins) {
add(extraDrilldown.getLabel(resource, origin.name))
} }
row() row()
} }
add("Total".toLabel()) equalizeColumns(fixedContent, this)
for (resource in resources) { overviewScreen.resizePage(this) // Without the height is miscalculated - shouldn't be
val sum = resourceDrilldown.filter { it.resource == resource }.sumOf { it.amount } }
add(sum.toLabel())
private fun getExtraDrilldown(): ResourceSupplyList {
val resourceSupplyList = ResourceSupplyList()
for (city in viewingPlayer.cities) {
if (city.demandedResource.isEmpty()) continue
val wltkResource = gameInfo.ruleSet.tileResources[city.demandedResource] ?: continue
if (city.isWeLoveTheKingDayActive()) {
resourceSupplyList.add(wltkResource, 1, ExtraInfoOrigin.CelebratingWLKT.name)
} else {
resourceSupplyList.add(wltkResource, 1, ExtraInfoOrigin.DemandingWLTK.name)
}
for (tile in city.getTiles()) {
if (tile.isCityCenter()) continue
if (!tile.hasViewableResource(viewingPlayer)) continue
val tileResource = tile.tileResource
if (tileResource.resourceType == ResourceType.Bonus) continue
if (tile.improvement == tileResource.improvement) continue
if (tileResource.resourceType == ResourceType.Strategic && tile.getTileImprovement()?.isGreatImprovement() == true) continue
resourceSupplyList.add(tileResource, 1, ExtraInfoOrigin.Unimproved.name)
}
} }
return resourceSupplyList
} }
} }

View File

@ -96,10 +96,10 @@ class WonderOverviewTab(
row() row()
} }
top()
defaults().pad(10f).align(Align.center) defaults().pad(10f).align(Align.center)
(1..5).forEach { _ -> add() } // dummies so equalizeColumns can work because the first grid cell is colspan(5) (1..5).forEach { _ -> add() } // dummies so equalizeColumns can work because the first grid cell is colspan(5)
row() row()
top()
createGrid() createGrid()

View File

@ -23,6 +23,7 @@ import com.unciv.models.translations.tr
* @param forceContentSize Force virtual [content] width/height for alignment calculation * @param forceContentSize Force virtual [content] width/height for alignment calculation
* - because Gdx auto layout reports wrong dimensions on scaled actors. * - because Gdx auto layout reports wrong dimensions on scaled actors.
*/ */
// region fields
class UncivTooltip <T: Actor>( class UncivTooltip <T: Actor>(
val target: Actor, val target: Actor,
val content: T, val content: T,
@ -33,7 +34,6 @@ class UncivTooltip <T: Actor>(
forceContentSize: Vector2? = null, forceContentSize: Vector2? = null,
) : InputListener() { ) : InputListener() {
// region fields
private val container: Container<T> = Container(content) private val container: Container<T> = Container(content)
enum class TipState { Hidden, Showing, Shown, Hiding } enum class TipState { Hidden, Showing, Shown, Hiding }
/** current visibility state of the Tooltip */ /** current visibility state of the Tooltip */
@ -49,7 +49,9 @@ class UncivTooltip <T: Actor>(
contentHeight = forceContentSize?.y ?: content.height contentHeight = forceContentSize?.y ?: content.height
} }
//region show, hide and positioning //endregion
//region show, hide and positioning
/** Show the Tooltip ([immediate]ly or begin the animation). _Can_ be called programmatically. */ /** Show the Tooltip ([immediate]ly or begin the animation). _Can_ be called programmatically. */
fun show(immediate: Boolean = false) { fun show(immediate: Boolean = false) {
if (target.stage == null) return if (target.stage == null) return
@ -129,9 +131,10 @@ class UncivTooltip <T: Actor>(
} }
private fun Actor.getEdgePoint(align: Int) = private fun Actor.getEdgePoint(align: Int) =
Vector2(getOriginX(width,align),getOriginY(height,align)) Vector2(getOriginX(width,align),getOriginY(height,align))
//endregion
//endregion
//region events //region events
override fun enter(event: InputEvent?, x: Float, y: Float, pointer: Int, fromActor: Actor?) { override fun enter(event: InputEvent?, x: Float, y: Float, pointer: Int, fromActor: Actor?) {
// assert(event?.listenerActor == target) - tested - holds true // assert(event?.listenerActor == target) - tested - holds true
if (fromActor != null && fromActor.isDescendantOf(target)) return if (fromActor != null && fromActor.isDescendantOf(target)) return
@ -147,6 +150,7 @@ class UncivTooltip <T: Actor>(
container.toFront() // this is a no-op if it has no parent container.toFront() // this is a no-op if it has no parent
return super.touchDown(event, x, y, pointer, button) return super.touchDown(event, x, y, pointer, button)
} }
//endregion //endregion
companion object { companion object {
@ -158,9 +162,16 @@ class UncivTooltip <T: Actor>(
* @param text Automatically translated tooltip text * @param text Automatically translated tooltip text
* @param size _Vertical_ size of the entire Tooltip including background * @param size _Vertical_ size of the entire Tooltip including background
* @param always override requirement: presence of physical keyboard * @param always override requirement: presence of physical keyboard
* @param tipAlign Point on the Tooltip to align with the top right of the [target] * @param targetAlign Point on the [target] widget to align the Tooltip to
* @param tipAlign Point on the Tooltip to align with the given point on the [target]
*/ */
fun Actor.addTooltip(text: String, size: Float = 26f, always: Boolean = false, tipAlign: Int = Align.top) { fun Actor.addTooltip(
text: String,
size: Float = 26f,
always: Boolean = false,
targetAlign: Int = Align.topRight,
tipAlign: Int = Align.top
) {
if (!(always || KeyPressDispatcher.keyboardAvailable) || text.isEmpty()) return if (!(always || KeyPressDispatcher.keyboardAvailable) || text.isEmpty()) return
val label = text.toLabel(ImageGetter.getBlue(), 38) val label = text.toLabel(ImageGetter.getBlue(), 38)
@ -175,18 +186,20 @@ class UncivTooltip <T: Actor>(
background.setPadding(4f+skewPadDescenders, horizontalPad, 8f-skewPadDescenders, horizontalPad) background.setPadding(4f+skewPadDescenders, horizontalPad, 8f-skewPadDescenders, horizontalPad)
val widthHeightRatio: Float val widthHeightRatio: Float
val multiRowSize = size * (1 + text.count { it == '\n' })
val labelWithBackground = Container(label).apply { val labelWithBackground = Container(label).apply {
setBackground(background) setBackground(background)
pack() pack()
widthHeightRatio = width / height widthHeightRatio = width / height
isTransform = true // otherwise setScale is ignored isTransform = true // otherwise setScale is ignored
setScale(size / height) setScale(multiRowSize / height)
} }
addListener(UncivTooltip(this, addListener(UncivTooltip(this,
labelWithBackground, labelWithBackground,
forceContentSize = Vector2(size * widthHeightRatio, size), forceContentSize = Vector2(multiRowSize * widthHeightRatio, multiRowSize),
offset = Vector2(-size/4, size/4), offset = Vector2(-multiRowSize/4, size/4),
targetAlign = targetAlign,
tipAlign = tipAlign tipAlign = tipAlign
)) ))
} }

View File

@ -677,6 +677,7 @@ Unless otherwise specified, all the following are from [the Noun Project](https:
* [favor](https://thenounproject.com/icon/favor-1029350/) by MICHAEL G BROWN for WLTK marker on City Overview * [favor](https://thenounproject.com/icon/favor-1029350/) by MICHAEL G BROWN for WLTK marker on City Overview
* [Party](https://thenounproject.com/icon/party-1784941/) by Adrien Coquet for WLTK header on City Overview * [Party](https://thenounproject.com/icon/party-1784941/) by Adrien Coquet for WLTK header on City Overview
* [Party](https://thenounproject.com/icon/party-2955155/) by Lars Meiertoberens as additional WLKT decoration * [Party](https://thenounproject.com/icon/party-2955155/) by Lars Meiertoberens as additional WLKT decoration
* [turn right](https://thenounproject.com/icon/turn-right-1920867/) by Alice Design for Resource Overview
## Main menu ## Main menu