Victory screen reorg/cleanup (#9111)

* VictoryScreen reorg

* VictoryScreen reorg - use TabbedPager

* VictoryScreen reorg - make helpers reusable

* VictoryScreen reorg - fixed headers

* VictoryScreen reorg - debug access to Replay

* Victories reorg - more cleanup

* VictoryScreen reorg - icons and keys

* VictoryScreen reorg - RankingType

* VictoryScreen reorg - RecreateOnResize

* VictoryScreen reorg - Avoid floating text overlapping buttons

* VictoryScreen reorg - remove obsolete todo
This commit is contained in:
SomeTroglodyte 2023-04-04 09:49:58 +02:00 committed by GitHub
parent 8b01498227
commit cd4e25a4f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 461 additions and 322 deletions

View File

@ -27,7 +27,6 @@
}, },
{ {
"name": "Diplomatic", "name": "Diplomatic",
"hiddenInVictoryScreen": false,
"victoryScreenHeader": "Build the UN and be voted\nworld leader to win!", "victoryScreenHeader": "Build the UN and be voted\nworld leader to win!",
"milestones": ["Anyone should build [United Nations]", "Win diplomatic vote"], "milestones": ["Anyone should build [United Nations]", "Win diplomatic vote"],
"victoryString": "You have triumphed over your foes through the art of diplomacy! Your cunning and wisdom have earned you great friends - and divided and sown confusion among your enemies! Forever will you be remembered as the leader who brought peace to this weary world!", "victoryString": "You have triumphed over your foes through the art of diplomacy! Your cunning and wisdom have earned you great friends - and divided and sown confusion among your enemies! Forever will you be remembered as the leader who brought peace to this weary world!",
@ -40,4 +39,4 @@
"victoryString": "The world has been convulsed by war. Many great and powerful civilizations have fallen, but you have survived - and emerged victorious! The world will long remember your glorious triumph!", "victoryString": "The world has been convulsed by war. Many great and powerful civilizations have fallen, but you have survived - and emerged victorious! The world will long remember your glorious triumph!",
"defeatString": "You have been defeated. Your civilization has been overwhelmed by its many foes. But your people do not despair, for they know that one day you shall return - and lead them forward to victory!" "defeatString": "You have been defeated. Your civilization has been overwhelmed by its many foes. But your people do not despair, for they know that one day you shall return - and lead them forward to victory!"
} }
] ]

View File

@ -27,7 +27,6 @@
}, },
{ {
"name": "Diplomatic", "name": "Diplomatic",
"hiddenInVictoryScreen": false,
"victoryScreenHeader": "Build the UN and be voted\nworld leader to win!", "victoryScreenHeader": "Build the UN and be voted\nworld leader to win!",
"milestones": ["Anyone should build [United Nations]", "Win diplomatic vote"], "milestones": ["Anyone should build [United Nations]", "Win diplomatic vote"],
"victoryString": "You have triumphed over your foes through the art of diplomacy! Your cunning and wisdom have earned you great friends - and divided and sown confusion among your enemies! Forever will you be remembered as the leader who brought peace to this weary world!", "victoryString": "You have triumphed over your foes through the art of diplomacy! Your cunning and wisdom have earned you great friends - and divided and sown confusion among your enemies! Forever will you be remembered as the leader who brought peace to this weary world!",
@ -40,4 +39,4 @@
"victoryString": "The world has been convulsed by war. Many great and powerful civilizations have fallen, but you have survived - and emerged victorious! The world will long remember your glorious triumph!", "victoryString": "The world has been convulsed by war. Many great and powerful civilizations have fallen, but you have survived - and emerged victorious! The world will long remember your glorious triumph!",
"defeatString": "You have been defeated. Your civilization has been overwhelmed by its many foes. But your people do not despair, for they know that one day you shall return - and lead them forward to victory!" "defeatString": "You have been defeated. Your civilization has been overwhelmed by its many foes. But your people do not despair, for they know that one day you shall return - and lead them forward to victory!"
} }
] ]

View File

@ -45,7 +45,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private val isAtWar = civInfo.isAtWar() private val isAtWar = civInfo.isAtWar()
private val buildingsForVictory = civInfo.gameInfo.getEnabledVictories().values private val buildingsForVictory = civInfo.gameInfo.getEnabledVictories().values
.mapNotNull { civInfo.victoryManager.getNextMilestone(it.name) } .mapNotNull { civInfo.victoryManager.getNextMilestone(it) }
.filter { it.type == MilestoneType.BuiltBuilding || it.type == MilestoneType.BuildingBuiltGlobally } .filter { it.type == MilestoneType.BuiltBuilding || it.type == MilestoneType.BuildingBuiltGlobally }
.map { it.params[0] } .map { it.params[0] }

View File

@ -572,7 +572,7 @@ object NextTurnAutomation {
* a unit and selling a building to make room. Can happen due to trades etc */ * a unit and selling a building to make room. Can happen due to trades etc */
private fun freeUpSpaceResources(civInfo: Civilization) { private fun freeUpSpaceResources(civInfo: Civilization) {
// No need to build spaceship parts just yet // No need to build spaceship parts just yet
if (civInfo.gameInfo.ruleset.victories.none { civInfo.victoryManager.getNextMilestone(it.key)?.type == MilestoneType.AddedSSPartsInCapital } ) if (civInfo.gameInfo.ruleset.victories.none { civInfo.victoryManager.getNextMilestone(it.value)?.type == MilestoneType.AddedSSPartsInCapital } )
return return
for (resource in civInfo.gameInfo.spaceResources) { for (resource in civInfo.gameInfo.spaceResources) {

View File

@ -541,7 +541,7 @@ class Civilization : IsPartOfGameInfoSerialization {
else when (category) { else when (category) {
RankingType.Score -> calculateTotalScore().toInt() RankingType.Score -> calculateTotalScore().toInt()
RankingType.Population -> cities.sumOf { it.population.population } RankingType.Population -> cities.sumOf { it.population.population }
RankingType.Crop_Yield -> stats.statsForNextTurn.food.roundToInt() RankingType.CropYield -> stats.statsForNextTurn.food.roundToInt()
RankingType.Production -> stats.statsForNextTurn.production.roundToInt() RankingType.Production -> stats.statsForNextTurn.production.roundToInt()
RankingType.Gold -> gold RankingType.Gold -> gold
RankingType.Territory -> cities.sumOf { it.tiles.size } RankingType.Territory -> cities.sumOf { it.tiles.size }

View File

@ -5,6 +5,7 @@ import com.unciv.logic.IsPartOfGameInfoSerialization
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.models.Counter import com.unciv.models.Counter
import com.unciv.models.ruleset.Milestone import com.unciv.models.ruleset.Milestone
import com.unciv.models.ruleset.Victory
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
class VictoryManager : IsPartOfGameInfoSerialization { class VictoryManager : IsPartOfGameInfoSerialization {
@ -57,27 +58,28 @@ class VictoryManager : IsPartOfGameInfoSerialization {
fun getVictoryTypeAchieved(): String? { fun getVictoryTypeAchieved(): String? {
if (!civInfo.isMajorCiv()) return null if (!civInfo.isMajorCiv()) return null
for (victoryName in civInfo.gameInfo.gameParameters.victoryTypes val enabledVictories = civInfo.gameInfo.gameParameters.victoryTypes
.filter { it != Constants.neutralVictoryType && it in civInfo.gameInfo.ruleset.victories}) { val victory = civInfo.gameInfo.ruleset.victories
if (getNextMilestone(victoryName) == null) .filter { it.key != Constants.neutralVictoryType && it.key in enabledVictories }
return victoryName .map { it.value }
} .firstOrNull { getNextMilestone(it) == null }
if (victory != null) return victory.name
if (civInfo.hasUnique(UniqueType.TriggersVictory)) if (civInfo.hasUnique(UniqueType.TriggersVictory))
return Constants.neutralVictoryType return Constants.neutralVictoryType
return null return null
} }
fun getNextMilestone(victory: String): Milestone? { fun getNextMilestone(victory: Victory): Milestone? {
for (milestone in civInfo.gameInfo.ruleset.victories[victory]!!.milestoneObjects) { for (milestone in victory.milestoneObjects) {
if (!milestone.hasBeenCompletedBy(civInfo)) if (!milestone.hasBeenCompletedBy(civInfo))
return milestone return milestone
} }
return null return null
} }
fun amountMilestonesCompleted(victory: String): Int { fun amountMilestonesCompleted(victory: Victory): Int {
var completed = 0 var completed = 0
for (milestone in civInfo.gameInfo.ruleset.victories[victory]!!.milestoneObjects) { for (milestone in victory.milestoneObjects) {
if (milestone.hasBeenCompletedBy(civInfo)) if (milestone.hasBeenCompletedBy(civInfo))
++completed ++completed
else else

View File

@ -9,6 +9,7 @@ import com.badlogic.gdx.scenes.scene2d.InputListener
import com.badlogic.gdx.scenes.scene2d.ui.Button import com.badlogic.gdx.scenes.scene2d.ui.Button
import com.badlogic.gdx.scenes.scene2d.ui.Cell import com.badlogic.gdx.scenes.scene2d.ui.Cell
import com.badlogic.gdx.scenes.scene2d.ui.Image import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.scenes.scene2d.ui.Label
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
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.scenes.scene2d.ui.WidgetGroup
@ -24,6 +25,7 @@ import com.unciv.ui.components.extensions.keyShortcuts
import com.unciv.ui.components.extensions.onActivation import com.unciv.ui.components.extensions.onActivation
import com.unciv.ui.components.extensions.packIfNeeded import com.unciv.ui.components.extensions.packIfNeeded
import com.unciv.ui.components.extensions.pad import com.unciv.ui.components.extensions.pad
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.images.IconTextButton import com.unciv.ui.images.IconTextButton
import com.unciv.ui.popups.Popup import com.unciv.ui.popups.Popup
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
@ -64,7 +66,7 @@ open class TabbedPager(
backgroundColor: Color = BaseScreen.skinStrings.skinConfig.baseColor.darken(0.5f), backgroundColor: Color = BaseScreen.skinStrings.skinConfig.baseColor.darken(0.5f),
private val headerPadding: Float = 10f, private val headerPadding: Float = 10f,
separatorColor: Color = Color.CLEAR, separatorColor: Color = Color.CLEAR,
private val shorcutScreen: BaseScreen? = null, private val shortcutScreen: BaseScreen? = null,
capacity: Int = 4 capacity: Int = 4
) : Table() { ) : Table() {
@ -80,7 +82,7 @@ open class TabbedPager(
private set private set
private val header = Table(BaseScreen.skin) private val header = Table(BaseScreen.skin)
protected val headerScroll = LinkedScrollPane(horizontalOnly = true, header) val headerScroll = LinkedScrollPane(horizontalOnly = true, header)
protected var headerHeight = 0f protected var headerHeight = 0f
private val fixedContentScroll = LinkedScrollPane(horizontalOnly = true) private val fixedContentScroll = LinkedScrollPane(horizontalOnly = true)
@ -105,6 +107,40 @@ open class TabbedPager(
/** @return Optional second content [Actor], will be placed outside the tab's main [ScrollPane] between header and `content`. Scrolls horizontally only. */ /** @return Optional second content [Actor], will be placed outside the tab's main [ScrollPane] between header and `content`. Scrolls horizontally only. */
fun getFixedContent(): Actor? = null fun getFixedContent(): Actor? = null
/** Sets first row cell's minWidth to the max of the widths of that column over all given tables
*
* Notes:
* - This aligns columns only if the tables are arranged vertically with equal X coordinates.
* - first table determines columns processed, all others must have at least the same column count.
* - Tables are left as needsLayout==true, so while equal width is ensured, you may have to pack if you want to see the value before this is rendered.
*/
fun equalizeColumns(vararg tables: Table) {
for (table in tables)
table.packIfNeeded()
val columns = tables.first().columns
if (tables.any { it.columns < columns })
throw IllegalStateException("IPageExtensions.equalizeColumns needs all tables to have at least the same number of columns as the first one")
val widths = (0 until columns)
.mapTo(ArrayList(columns)) { column ->
tables.maxOf { it.getColumnWidth(column) }
}
for (table in tables) {
for (column in 0 until columns)
table.cells[column].run {
if (actor == null)
// Empty cells ignore minWidth, so just doing Table.add() for an empty cell in the top row will break this. Fix!
setActor<Label>("".toLabel())
else if (Align.isCenterHorizontal(align)) (actor as? Label)?.run {
// minWidth acts like fillX, so Labels will fill and then left-align by default. Fix!
if (!Align.isCenterHorizontal(labelAlign))
setAlignment(Align.center)
}
minWidth(widths[column] - padLeft - padRight)
}
table.invalidate()
}
}
} }
//endregion //endregion
@ -472,8 +508,8 @@ open class TabbedPager(
if (index !in 0 until pages.size) return false if (index !in 0 until pages.size) return false
if (index == activePage) selectPage(-1) if (index == activePage) selectPage(-1)
val page = pages.removeAt(index) val page = pages.removeAt(index)
header.getCell(page.button).clearActor() val cell = header.getCell(page.button).clearActor()
header.cells.removeIndex(index) header.cells.removeValue(cell, true)
return true return true
} }
@ -620,15 +656,9 @@ open class TabbedPager(
val buttonCell: Cell<Button> val buttonCell: Cell<Button>
if (insertBefore >= 0 && insertBefore < pages.size) { if (insertBefore >= 0 && insertBefore < pages.size) {
newIndex = insertBefore newIndex = insertBefore
val cellIndex = header.cells.indexOf(header.getCell(pages[insertBefore].button))
pages.add(insertBefore, page) pages.add(insertBefore, page)
// Table.addActorAt breaks the Table, it's a Group method that updates children but not cells insertHeaderCellAt(cellIndex).setActor(page.button)
// So we add an empty cell and move cell actors around
header.add()
for (i in header.cells.size - 1 downTo insertBefore + 1) {
val actor = header.removeActorAt(i - 1, true) as Button
header.cells[i].setActor<Button>(actor)
}
header.cells[insertBefore].setActor<Button>(page.button)
buttonCell = header.getCell(page.button) buttonCell = header.getCell(page.button)
} else { } else {
newIndex = pages.size newIndex = pages.size
@ -645,10 +675,40 @@ open class TabbedPager(
return newIndex return newIndex
} }
private fun insertHeaderCellAt(insertBefore: Int): Cell<Actor?> {
if (insertBefore < 0 || insertBefore >= header.cells.size) return header.add()
// Table.addActorAt breaks the Table, it's a Group method that updates children but not cells
// So we add an empty cell and move cell actors around
header.add()
for (i in header.cells.size - 1 downTo insertBefore + 1) {
val actor = header.removeActorAt(i - 1, true)
header.cells[i].setActor<Actor>(actor)
}
return header.cells[insertBefore]
}
private fun addDeferredSecrets() { private fun addDeferredSecrets() {
while (true) { while (true) {
val page = deferredSecretPages.removeFirstOrNull() ?: return val page = deferredSecretPages.removeFirstOrNull() ?: return
addAndShowPage(page, -1) addAndShowPage(page, -1)
} }
} }
/** Gets total width of the header buttons including their padding.
* Header will be scrollable if getHeaderPrefWidth > width. */
fun getHeaderPrefWidth() = header.prefWidth
/** Adds any Actor to the header, e.g. informative labels.
* Must be called _after_ all pages are final, otherwise effects not guaranteed.
* @param leftSide If `true` then [actor] is inserted on the left, otherwise on the right of the page buttons.
*/
fun decorateHeader(actor: Actor, leftSide: Boolean) {
val cell = insertHeaderCellAt(if (leftSide) 0 else -1)
cell.setActor(actor)
if (!leftSide) return
val addWidth = actor.width
for (page in pages) {
page.buttonX += addWidth
}
}
} }

View File

@ -597,3 +597,6 @@ object GdxKeyCodeFixes {
else -> Input.Keys.valueOf(name) else -> Input.Keys.valueOf(name)
} }
} }
fun Input.areSecretKeysPressed() = isKeyPressed(Input.Keys.SHIFT_RIGHT) &&
(isKeyPressed(Input.Keys.CONTROL_RIGHT) || isKeyPressed(Input.Keys.ALT_RIGHT))

View File

@ -9,6 +9,7 @@ import com.unciv.UncivGame
import com.unciv.models.metadata.BaseRuleset import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.components.TabbedPager import com.unciv.ui.components.TabbedPager
import com.unciv.ui.components.extensions.areSecretKeysPressed
import com.unciv.ui.components.extensions.center import com.unciv.ui.components.extensions.center
import com.unciv.ui.components.extensions.toCheckBox import com.unciv.ui.components.extensions.toCheckBox
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
@ -110,7 +111,7 @@ class OptionsPopup(
val content = ModCheckTab(screen) val content = ModCheckTab(screen)
tabs.addPage("Locate mod errors", content, ImageGetter.getImage("OtherIcons/Mods"), 24f) tabs.addPage("Locate mod errors", content, ImageGetter.getImage("OtherIcons/Mods"), 24f)
} }
if (Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT) && (Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT) || Gdx.input.isKeyPressed(Input.Keys.ALT_RIGHT))) { if (Gdx.input.areSecretKeysPressed()) {
tabs.addPage("Debug", debugTab(this), ImageGetter.getImage("OtherIcons/SecretOptions"), 24f, secret = true) tabs.addPage("Debug", debugTab(this), ImageGetter.getImage("OtherIcons/SecretOptions"), 24f, secret = true)
} }

View File

@ -160,7 +160,7 @@ class DetailedStatsPopup(
} }
totalTable.row() totalTable.row()
// Mini version of EmpireOverviewTab.equalizeColumns - the number columns work thanks to statColMinWidth // Mini version of IPageExtensions.equalizeColumns - the number columns work thanks to statColMinWidth
headerTable.packIfNeeded() headerTable.packIfNeeded()
totalTable.packIfNeeded() totalTable.packIfNeeded()
val firstColumnWidth = max(totalTable.getColumnWidth(0), headerTable.getColumnWidth(0)) val firstColumnWidth = max(totalTable.getColumnWidth(0), headerTable.getColumnWidth(0))

View File

@ -1,13 +1,9 @@
package com.unciv.ui.screens.overviewscreen package com.unciv.ui.screens.overviewscreen
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.utils.Align
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.components.TabbedPager import com.unciv.ui.components.TabbedPager
import com.unciv.ui.components.extensions.packIfNeeded import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.components.extensions.toLabel
abstract class EmpireOverviewTab ( abstract class EmpireOverviewTab (
val viewingPlayer: Civilization, val viewingPlayer: Civilization,
@ -33,37 +29,4 @@ abstract class EmpireOverviewTab (
val gameInfo = viewingPlayer.gameInfo val gameInfo = viewingPlayer.gameInfo
/** Sets first row cell's minWidth to the max of the widths of that column over all given tables
*
* Notes:
* - This aligns columns only if the tables are arranged vertically with equal X coordinates.
* - first table determines columns processed, all others must have at least the same column count.
* - Tables are left as needsLayout==true, so while equal width is ensured, you may have to pack if you want to see the value before this is rendered.
*/
protected fun equalizeColumns(vararg tables: Table) {
for (table in tables)
table.packIfNeeded()
val columns = tables.first().columns
if (tables.any { it.columns < columns })
throw IllegalStateException("EmpireOverviewTab.equalizeColumns needs all tables to have at least the same number of columns as the first one")
val widths = (0 until columns)
.mapTo(ArrayList(columns)) { column ->
tables.maxOf { it.getColumnWidth(column) }
}
for (table in tables) {
for (column in 0 until columns)
table.cells[column].run {
if (actor == null)
// Empty cells ignore minWidth, so just doing Table.add() for an empty cell in the top row will break this. Fix!
setActor<Label>("".toLabel())
else if (Align.isCenterHorizontal(align)) (actor as? Label)?.run {
// minWidth acts like fillX, so Labels will fill and then left-align by default. Fix!
if (!Align.isCenterHorizontal(labelAlign))
setAlignment(Align.center)
}
minWidth(widths[column] - padLeft - padRight)
}
table.invalidate()
}
}
} }

View File

@ -4,21 +4,26 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Image import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
enum class RankingType(val getImage: () -> Image?, val idForSerialization: String) { enum class RankingType(
label: String?,
val getImage: () -> Image?,
val idForSerialization: String
) {
// production, gold, happiness, and culture already have icons added when the line is `tr()`anslated // production, gold, happiness, and culture already have icons added when the line is `tr()`anslated
Score( Score({ ImageGetter.getImage("CityStateIcons/Cultured").apply { color = Color.FIREBRICK } }, "S"),
{ ImageGetter.getImage("CityStateIcons/Cultured").apply { color = Color.FIREBRICK } },
"S"
),
Population({ ImageGetter.getStatIcon("Population") }, "N"), Population({ ImageGetter.getStatIcon("Population") }, "N"),
Crop_Yield({ ImageGetter.getStatIcon("Food") }, "C"), CropYield("Crop Yield", { ImageGetter.getStatIcon("Food") }, "C"),
Production({ null }, "P"), Production("P"),
Gold({ null }, "G"), Gold("G"),
Territory({ ImageGetter.getImage("OtherIcons/Hexagon") }, "T"), Territory({ ImageGetter.getImage("OtherIcons/Hexagon") }, "T"),
Force({ ImageGetter.getImage("OtherIcons/Shield") }, "F"), Force({ ImageGetter.getImage("OtherIcons/Shield") }, "F"),
Happiness({ null }, "H"), Happiness("H"),
Technologies({ ImageGetter.getStatIcon("Science") }, "W"), Technologies({ ImageGetter.getStatIcon("Science") }, "W"),
Culture({ null }, "A"); Culture("A")
;
val label = label ?: name
constructor(getImage: () -> Image?, idForSerialization: String) : this(null, getImage, idForSerialization)
constructor(idForSerialization: String) : this(null, { null }, idForSerialization)
companion object { companion object {
fun fromIdForSerialization(s: String): RankingType? = fun fromIdForSerialization(s: String): RankingType? =

View File

@ -1,7 +1,9 @@
package com.unciv.ui.screens.victoryscreen package com.unciv.ui.screens.victoryscreen
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
@ -9,58 +11,99 @@ import com.unciv.logic.civilization.Civilization
import com.unciv.models.metadata.GameSetupInfo import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.ruleset.Victory import com.unciv.models.ruleset.Victory
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.components.extensions.addSeparator import com.unciv.ui.components.KeyCharAndCode
import com.unciv.ui.components.TabbedPager
import com.unciv.ui.components.extensions.areSecretKeysPressed
import com.unciv.ui.components.extensions.enable import com.unciv.ui.components.extensions.enable
import com.unciv.ui.components.extensions.onClick import com.unciv.ui.components.extensions.onClick
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.basescreen.RecreateOnResize
import com.unciv.ui.screens.newgamescreen.NewGameScreen import com.unciv.ui.screens.newgamescreen.NewGameScreen
import com.unciv.ui.screens.pickerscreens.PickerScreen import com.unciv.ui.screens.pickerscreens.PickerScreen
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
class VictoryScreen(private val worldScreen: WorldScreen) : PickerScreen() { //TODO someoneHasWon should look at gameInfo.victoryData
//TODO replay slider
class VictoryScreen(
private val worldScreen: WorldScreen,
pageNumber: Int = 0
) : PickerScreen(), RecreateOnResize {
private val gameInfo = worldScreen.gameInfo private val gameInfo = worldScreen.gameInfo
private val playerCivInfo = worldScreen.viewingCiv private val playerCiv = worldScreen.viewingCiv
private val tabs = TabbedPager(separatorColor = Color.WHITE, shortcutScreen = this)
private val headerTable = Table() internal class CivWithStat(val civ: Civilization, val value: Int) {
private val contentsTable = Table() constructor(civ: Civilization, category: RankingType) : this(civ, civ.getStatForRanking(category))
}
private var replayTab: VictoryScreenReplay? = null private enum class VictoryTabs(
val key: Char,
val iconName: String = "",
val caption: String? = null,
val align: Int = Align.topLeft,
val syncScroll: Boolean = true,
val allowAsSecret: Boolean = false
) {
OurStatus('O', "StatIcons/Specialist", caption = "Our status") {
override fun getContent(worldScreen: WorldScreen) = VictoryScreenOurVictory(worldScreen)
override fun isHidden(playerCiv: Civilization) = playerCiv.isSpectator()
},
Global('G', "OtherIcons/Nations", caption = "Global status") {
override fun getContent(worldScreen: WorldScreen) = VictoryScreenGlobalVictory(worldScreen)
},
Demographics('D', "CityStateIcons/Cultured", allowAsSecret = true) {
override fun getContent(worldScreen: WorldScreen) = VictoryScreenDemographics(worldScreen)
override fun isHidden(playerCiv: Civilization) = !UncivGame.Current.settings.useDemographics
},
Rankings('R', "CityStateIcons/Cultured", allowAsSecret = true) {
override fun getContent(worldScreen: WorldScreen) = VictoryScreenCivRankings(worldScreen)
override fun isHidden(playerCiv: Civilization) = UncivGame.Current.settings.useDemographics
},
Replay('P', "OtherIcons/Load", align = Align.top, syncScroll = false, allowAsSecret = true) {
override fun getContent(worldScreen: WorldScreen) = VictoryScreenReplay(worldScreen)
override fun isHidden(playerCiv: Civilization) =
!playerCiv.isSpectator() && playerCiv.gameInfo.victoryData == null && playerCiv.isAlive()
};
abstract fun getContent(worldScreen: WorldScreen): Table
open fun isHidden(playerCiv: Civilization) = false
}
init { init {
val difficultyLabel = ("{Difficulty}: {${gameInfo.difficulty}}").toLabel() //**************** Set up the tabs ****************
splitPane.setFirstWidget(tabs)
val iconSize = Constants.defaultFontSize.toFloat()
val tabsTable = Table().apply { defaults().pad(10f) } for (tab in VictoryTabs.values()) {
val tabHidden = tab.isHidden(playerCiv)
val setMyVictoryButton = "Our status".toTextButton().onClick { setOurVictoryTable() } if (tabHidden && !(tab.allowAsSecret && Gdx.input.areSecretKeysPressed()))
if (!playerCivInfo.isSpectator()) tabsTable.add(setMyVictoryButton) continue
val icon = if (tab.iconName.isEmpty()) null else ImageGetter.getImage(tab.iconName)
val setGlobalVictoryButton = "Global status".toTextButton().onClick { setGlobalVictoryTable() } tabs.addPage(
tabsTable.add(setGlobalVictoryButton) tab.caption ?: tab.name,
tab.getContent(worldScreen),
val rankingLabel = if (UncivGame.Current.settings.useDemographics) "Demographics" else "Rankings" icon, iconSize,
val setCivRankingsButton = rankingLabel.toTextButton().onClick { setCivRankingsTable() } scrollAlign = tab.align, syncScroll = tab.syncScroll,
tabsTable.add(setCivRankingsButton) shortcutKey = KeyCharAndCode(tab.key),
secret = tabHidden && tab.allowAsSecret
if (playerCivInfo.isSpectator()) )
setGlobalVictoryTable() }
else tabs.selectPage(pageNumber)
setOurVictoryTable()
//**************** Set up bottom area - buttons and description label ****************
rightSideButton.isVisible = false rightSideButton.isVisible = false
//TODO the following should look at gameInfo.victoryData
var someoneHasWon = false var someoneHasWon = false
val playerVictoryType = playerCivInfo.victoryManager.getVictoryTypeAchieved() val playerVictoryType = playerCiv.victoryManager.getVictoryTypeAchieved()
if (playerVictoryType != null) { if (playerVictoryType != null) {
someoneHasWon = true someoneHasWon = true
wonOrLost("You have won a [$playerVictoryType] Victory!", playerVictoryType, true) wonOrLost("You have won a [$playerVictoryType] Victory!", playerVictoryType, true)
} }
for (civ in gameInfo.civilizations.filter { it.isMajorCiv() && it != playerCivInfo }) { for (civ in gameInfo.civilizations.filter { it.isMajorCiv() && it != playerCiv }) {
val civVictoryType = civ.victoryManager.getVictoryTypeAchieved() val civVictoryType = civ.victoryManager.getVictoryTypeAchieved()
if (civVictoryType != null) { if (civVictoryType != null) {
someoneHasWon = true someoneHasWon = true
@ -68,44 +111,41 @@ class VictoryScreen(private val worldScreen: WorldScreen) : PickerScreen() {
} }
} }
if (playerCivInfo.isDefeated()) { if (playerCiv.isDefeated()) {
wonOrLost("", null, false) wonOrLost("", null, false)
} else if (!someoneHasWon) { } else if (!someoneHasWon) {
setDefaultCloseAction() setDefaultCloseAction()
} }
if (playerCivInfo.isSpectator() || someoneHasWon || playerCivInfo.isDefeated()) { //**************** Set up floating info panels ****************
val replayLabel = "Replay" // When horizontal screen space is scarce so they would overlap, insert
val replayButton = replayLabel.toTextButton().onClick { setReplayTable() } // them into the scrolling portion of the TabbedPager header instead
tabsTable.add(replayButton) tabs.pack()
val topRightPanel = VerticalGroup().apply {
space(5f)
align(Align.right)
addActor("{Game Speed}: {${gameInfo.gameParameters.speed}}".toLabel())
if ("Time" in gameInfo.gameParameters.victoryTypes)
addActor("{Max Turns}: ${gameInfo.gameParameters.maxTurns}".toLabel())
pack()
} }
val difficultyLabel = "{Difficulty}: {${gameInfo.difficulty}}".toLabel()
val headerTableRightCell = Table() val neededSpace = topRightPanel.width.coerceAtLeast(difficultyLabel.width) * 2 + tabs.getHeaderPrefWidth()
val gameSpeedLabel = "{Game Speed}: {${gameInfo.gameParameters.speed}}".toLabel() if (neededSpace > stage.width) {
headerTableRightCell.add(gameSpeedLabel).row() tabs.decorateHeader(difficultyLabel, true)
if (gameInfo.gameParameters.victoryTypes.contains("Time")) { tabs.decorateHeader(topRightPanel, false)
val maxTurnsLabel = "{Max Turns}: ${gameInfo.gameParameters.maxTurns}".toLabel() tabs.headerScroll.fadeScrollBars = false
headerTableRightCell.add(maxTurnsLabel).padTop(5f) } else {
val panelY = stage.height - tabs.getRowHeight(0) * 0.5f
stage.addActor(topRightPanel)
topRightPanel.setPosition(stage.width - 10f, panelY, Align.right)
stage.addActor(difficultyLabel)
difficultyLabel.setPosition(10f, panelY, Align.left)
} }
val leftCell = headerTable.add(difficultyLabel).padLeft(10f).left()
headerTable.add(tabsTable).expandX().center()
val rightCell = headerTable.add(headerTableRightCell).padRight(10f).right()
headerTable.addSeparator()
headerTable.pack()
// Make the outer cells the same so that the middle one is properly centered
if (leftCell.actorWidth > rightCell.actorWidth) rightCell.width(leftCell.actorWidth)
else leftCell.width(rightCell.actorWidth)
pickerPane.clearChildren()
pickerPane.add(headerTable).growX().row()
pickerPane.add(splitPane).expand().fill()
topTable.add(contentsTable)
} }
private fun wonOrLost(description: String, victoryType: String?, hasWon: Boolean) { private fun wonOrLost(description: String, victoryType: String?, hasWon: Boolean) {
val victory = playerCivInfo.gameInfo.ruleset.victories[victoryType] val victory = playerCiv.gameInfo.ruleset.victories[victoryType]
?: Victory() // This contains our default victory/defeat texts ?: Victory() // This contains our default victory/defeat texts
val endGameMessage = when { val endGameMessage = when {
hasWon -> victory.victoryString hasWon -> victory.victoryString
@ -130,72 +170,15 @@ class VictoryScreen(private val worldScreen: WorldScreen) : PickerScreen() {
} }
} }
private fun setOurVictoryTable() { override fun show() {
resetContent(VictoryScreenOurVictory(worldScreen)) super.show()
} tabs.askForPassword(secretHashCode = 2747985)
private fun setGlobalVictoryTable() {
resetContent(VictoryScreenGlobalVictory(worldScreen))
}
private fun setCivRankingsTable() {
resetContent(VictoryScreenCivRankings(worldScreen))
}
private fun setReplayTable() {
if (replayTab == null) replayTab = VictoryScreenReplay(worldScreen)
resetContent(replayTab!!)
replayTab!!.restartTimer()
}
private fun resetContent(newContent: Table) {
replayTab?.resetTimer()
contentsTable.clear()
contentsTable.add(newContent)
} }
override fun dispose() { override fun dispose() {
tabs.selectPage(-1) // Tells Replay page to stop its timer
super.dispose() super.dispose()
replayTab?.resetTimer()
} }
open class VictoryScreenTab(worldScreen: WorldScreen) : Table(skin) { override fun recreate(): BaseScreen = VictoryScreen(worldScreen, tabs.activePage)
protected val gameInfo = worldScreen.gameInfo
protected val playerCivInfo = worldScreen.viewingCiv
// Common "service" for VictoryScreenGlobalVictory and VictoryScreenCivRankings
protected fun getCivGroup(civ: Civilization, afterCivNameText: String, currentPlayer: Civilization): Table {
val civGroup = Table()
var labelText = "{${civ.civName.tr()}}{${afterCivNameText.tr()}}"
var labelColor = Color.WHITE
val backgroundColor: Color
if (civ.isDefeated()) {
civGroup.add(ImageGetter.getImage("OtherIcons/DisbandUnit")).size(30f)
backgroundColor = Color.LIGHT_GRAY
labelColor = Color.BLACK
} else if (currentPlayer == civ // || game.viewEntireMapForDebug
|| currentPlayer.knows(civ)
|| currentPlayer.isDefeated()
|| currentPlayer.victoryManager.hasWon()
) {
civGroup.add(ImageGetter.getNationPortrait(civ.nation, 30f))
backgroundColor = civ.nation.getOuterColor()
labelColor = civ.nation.getInnerColor()
} else {
civGroup.add(ImageGetter.getRandomNationPortrait(30f))
backgroundColor = Color.DARK_GRAY
labelText = Constants.unknownNationName
}
civGroup.background = skinStrings.getUiBackground("VictoryScreen/CivGroup", skinStrings.roundedEdgeRectangleShape, backgroundColor)
val label = labelText.toLabel(labelColor)
label.setAlignment(Align.center)
civGroup.add(label).padLeft(10f)
civGroup.pack()
return civGroup
}
}
} }

View File

@ -0,0 +1,67 @@
package com.unciv.ui.screens.victoryscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.logic.civilization.Civilization
import com.unciv.models.translations.tr
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.screens.basescreen.BaseScreen
/** Element displaying one Civilization as seen by another Civilization on a rounded-edge background.
* @param civ Civilization to show, with nation icon (depending on alive / known) and name, optionally followed by
* @param separator a separator (untranslated, only if additionalInfo isn't empty) and
* @param additionalInfo some additional info, auto-translated.
* @param currentPlayer Viewing Civilization
*/
internal class VictoryScreenCivGroup(
civ: Civilization,
separator: String,
additionalInfo: String,
currentPlayer: Civilization
) : Table() {
// Note this Table has no skin - works as long as no element tries to get its skin from the parent
constructor(civEntry: VictoryScreen.CivWithStat, currentPlayer: Civilization)
: this(civEntry.civ, ": ", civEntry.value.toString(), currentPlayer)
constructor(civ: Civilization, additionalInfo: String, currentPlayer: Civilization)
// That tr() is only needed to support additionalInfo containing {} because tr() doesn't support nested ones.
: this(civ, "\n", additionalInfo.tr(), currentPlayer)
init {
var labelText = if (additionalInfo.isEmpty()) civ.civName
else "{${civ.civName}}$separator{$additionalInfo}"
val labelColor: Color
val backgroundColor: Color
when {
civ.isDefeated() -> {
add(ImageGetter.getImage("OtherIcons/DisbandUnit")).size(30f)
backgroundColor = Color.LIGHT_GRAY
labelColor = Color.BLACK
}
currentPlayer == civ // || game.viewEntireMapForDebug
|| currentPlayer.knows(civ)
|| currentPlayer.isDefeated()
|| currentPlayer.victoryManager.hasWon() -> {
add(ImageGetter.getNationPortrait(civ.nation, 30f))
backgroundColor = civ.nation.getOuterColor()
labelColor = civ.nation.getInnerColor()
}
else -> {
add(ImageGetter.getRandomNationPortrait(30f))
backgroundColor = Color.DARK_GRAY
labelColor = Color.WHITE
labelText = Constants.unknownNationName
}
}
background = BaseScreen.skinStrings.getUiBackground("VictoryScreen/CivGroup", BaseScreen.skinStrings.roundedEdgeRectangleShape, backgroundColor)
val label = labelText.toLabel(labelColor)
label.setAlignment(Align.center)
add(label).padLeft(10f)
}
}

View File

@ -1,87 +1,48 @@
package com.unciv.ui.screens.victoryscreen package com.unciv.ui.screens.victoryscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.ui.components.TabbedPager
import com.unciv.logic.civilization.Civilization
import com.unciv.ui.components.extensions.addSeparator import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
class VictoryScreenCivRankings( class VictoryScreenCivRankings(
private val worldScreen: WorldScreen worldScreen: WorldScreen
) : VictoryScreen.VictoryScreenTab(worldScreen) { ) : Table(BaseScreen.skin), TabbedPager.IPageExtensions {
private val header = Table()
init { init {
defaults().pad(5f) defaults().pad(10f)
val majorCivs = gameInfo.civilizations.filter { it.isMajorCiv() } val majorCivs = worldScreen.gameInfo.civilizations.filter { it.isMajorCiv() }
if (UncivGame.Current.settings.useDemographics) buildDemographicsTable(majorCivs)
else buildRankingsTable(majorCivs)
}
enum class RankLabels { Rank, Value, Best, Average, Worst}
private fun buildDemographicsTable(majorCivs: List<Civilization>) {
buildDemographicsHeaders()
for (rankLabel in RankLabels.values()) {
row()
add(rankLabel.name.toLabel())
for (category in RankingType.values()) {
val aliveMajorCivsSorted = majorCivs.filter{ it.isAlive() }.sortedByDescending { it.getStatForRanking(category) }
fun addRankCivGroup(civ: Civilization) { // local function for reuse of getting and formatting civ stats
add(getCivGroup(civ, ": " + civ.getStatForRanking(category).toString(), playerCivInfo)).fillX()
}
@Suppress("NON_EXHAUSTIVE_WHEN") // RankLabels.Demographic treated above
when (rankLabel) {
RankLabels.Rank -> add((aliveMajorCivsSorted.indexOfFirst { it == worldScreen.viewingCiv } + 1).toLabel())
RankLabels.Value -> addRankCivGroup(worldScreen.viewingCiv)
RankLabels.Best -> addRankCivGroup(aliveMajorCivsSorted.firstOrNull()!!)
RankLabels.Average -> add((aliveMajorCivsSorted.sumOf { it.getStatForRanking(category) } / aliveMajorCivsSorted.size).toLabel())
RankLabels.Worst -> addRankCivGroup(aliveMajorCivsSorted.lastOrNull()!!)
}
}
}
}
private fun buildDemographicsHeaders() {
val demoLabel = Table().apply { defaults().pad(5f) }
demoLabel.add("Demographic".toLabel()).row()
demoLabel.addSeparator().fillX()
add(demoLabel)
for (category in RankingType.values()) { for (category in RankingType.values()) {
val headers = Table().apply { defaults().pad(5f) } val textAndIcon = Table()
val textAndIcon = Table().apply { defaults() }
val columnImage = category.getImage() val columnImage = category.getImage()
if (columnImage != null) textAndIcon.add(columnImage).center().size(Constants.defaultFontSize.toFloat() * 0.75f).padRight(2f).padTop(-2f) if (columnImage != null)
textAndIcon.add(category.name.replace('_', ' ').toLabel()).row() textAndIcon.add(columnImage).size(Constants.defaultFontSize.toFloat() * 0.75f)
headers.add(textAndIcon) .padRight(2f).padTop(-2f)
headers.addSeparator() textAndIcon.add(category.label.toLabel()).row()
add(headers) header.add(textAndIcon).pad(10f)
}
}
private fun buildRankingsTable(majorCivs: List<Civilization>) { val column = Table().apply { defaults().space(10f) }
for (category in RankingType.values()) { val civData = majorCivs
val column = Table().apply { defaults().pad(5f) } .map { VictoryScreen.CivWithStat(it, category) }
val textAndIcon = Table().apply { defaults() } .sortedByDescending { it.value }
val columnImage = category.getImage() for (civEntry in civData) {
if (columnImage != null) textAndIcon.add(columnImage).size(Constants.defaultFontSize.toFloat() * 0.75f).padRight(2f).padTop(-2f) column.add(VictoryScreenCivGroup(civEntry, worldScreen.viewingCiv)).fillX().row()
textAndIcon.add(category.name.replace('_' , ' ').toLabel()).row()
column.add(textAndIcon)
column.addSeparator()
for (civ in majorCivs.sortedByDescending { it.getStatForRanking(category) }) {
column.add(getCivGroup(civ, ": " + civ.getStatForRanking(category).toString(), playerCivInfo)).fillX().row()
} }
add(column) add(column)
} }
header.addSeparator(Color.GRAY)
} }
override fun activated(index: Int, caption: String, pager: TabbedPager) {
equalizeColumns(header, this)
}
override fun getFixedContent() = header
} }

View File

@ -0,0 +1,70 @@
package com.unciv.ui.screens.victoryscreen
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.Constants
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.worldscreen.WorldScreen
import kotlin.math.roundToInt
class VictoryScreenDemographics(
worldScreen: WorldScreen
) : Table(BaseScreen.skin) {
private val playerCiv = worldScreen.viewingCiv
private enum class RankLabels { Rank, Value, Best, Average, Worst }
init {
defaults().pad(5f)
val majorCivs = worldScreen.gameInfo.civilizations.filter { it.isMajorCiv() }
buildDemographicsHeaders()
for (rankLabel in RankLabels.values()) {
row()
add(rankLabel.name.toLabel())
for (category in RankingType.values()) {
val aliveMajorCivsSorted = majorCivs.filter { it.isAlive() || it == playerCiv }
.map { VictoryScreen.CivWithStat(it, category) }
.sortedByDescending { it.value }
fun addRankCivGroup(civEntry: VictoryScreen.CivWithStat) {
add(VictoryScreenCivGroup(civEntry, playerCiv)).fillX()
}
@Suppress("NON_EXHAUSTIVE_WHEN") // RankLabels.Demographic treated above
when (rankLabel) {
RankLabels.Rank -> add((aliveMajorCivsSorted.indexOfFirst { it.civ == playerCiv } + 1).toLabel())
RankLabels.Value -> addRankCivGroup(aliveMajorCivsSorted.first { it.civ == playerCiv })
RankLabels.Best -> addRankCivGroup(aliveMajorCivsSorted.first())
RankLabels.Average -> add((aliveMajorCivsSorted.sumOf { it.value }.toFloat() / aliveMajorCivsSorted.size).roundToInt().toLabel())
RankLabels.Worst -> addRankCivGroup(aliveMajorCivsSorted.last())
}
}
}
}
private fun buildDemographicsHeaders() {
val demoLabel = Table().apply { defaults().pad(5f) }
demoLabel.add("Demographic".toLabel()).row()
demoLabel.addSeparator().fillX()
add(demoLabel)
for (category in RankingType.values()) {
val headers = Table().apply { defaults().pad(5f) }
val textAndIcon = Table().apply { defaults() }
val columnImage = category.getImage()
if (columnImage != null)
textAndIcon.add(columnImage).center()
.size(Constants.defaultFontSize.toFloat() * 0.75f)
.padRight(2f).padTop(-2f)
textAndIcon.add(category.label.toLabel()).row()
headers.add(textAndIcon)
headers.addSeparator()
add(headers)
}
}
}

View File

@ -1,47 +1,54 @@
package com.unciv.ui.screens.victoryscreen package com.unciv.ui.screens.victoryscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.Civilization
import com.unciv.models.translations.tr import com.unciv.models.ruleset.Victory
import com.unciv.ui.components.TabbedPager
import com.unciv.ui.components.extensions.addSeparator import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
class VictoryScreenGlobalVictory( class VictoryScreenGlobalVictory(
worldScreen: WorldScreen worldScreen: WorldScreen
) : VictoryScreen.VictoryScreenTab(worldScreen) { ) : Table(BaseScreen.skin), TabbedPager.IPageExtensions {
private val header = Table()
init { init {
val gameInfo = worldScreen.gameInfo
val majorCivs = gameInfo.civilizations.asSequence().filter { it.isMajorCiv() }
val victoriesToShow = gameInfo.getEnabledVictories()
defaults().pad(10f) defaults().pad(10f)
val majorCivs = gameInfo.civilizations.filter { it.isMajorCiv() } for ((victoryName, victory) in victoriesToShow) {
val enabledVictoryTypes = gameInfo.gameParameters.victoryTypes header.add("[$victoryName] Victory".toLabel()).pad(10f)
val victoriesToShow = gameInfo.ruleset.victories.filter { add(getColumn(majorCivs, victory, worldScreen.viewingCiv))
!it.value.hiddenInVictoryScreen && enabledVictoryTypes.contains(it.key)
} }
header.addSeparator(Color.GRAY)
for (victory in victoriesToShow) {
add(getGlobalVictoryColumn(majorCivs, victory.key))
}
} }
private fun getGlobalVictoryColumn(majorCivs: List<Civilization>, victory: String): Table { private fun getColumn(
val victoryColumn = Table().apply { defaults().pad(10f) } majorCivs: Sequence<Civilization>,
victory: Victory,
victoryColumn.add("[$victory] Victory".toLabel()).row() playerCiv: Civilization
victoryColumn.addSeparator() ) = Table().apply {
defaults().pad(10f)
for (civ in majorCivs.filter { !it.isDefeated() }.sortedByDescending { it.victoryManager.amountMilestonesCompleted(victory) }) { val sortedCivs = majorCivs.sortedWith(
val buttonText = civ.victoryManager.getNextMilestone(victory)?.getVictoryScreenButtonHeaderText(false, civ) ?: "Done!" compareBy<Civilization> { it.isDefeated() }
victoryColumn.add(getCivGroup(civ, "\n" + buttonText.tr(), playerCivInfo)).fillX().row() .thenBy { it.victoryManager.amountMilestonesCompleted(victory) }
)
for (civ in sortedCivs) {
val buttonText = civ.victoryManager.getNextMilestone(victory)
?.getVictoryScreenButtonHeaderText(false, civ)
?: "Done!"
add(VictoryScreenCivGroup(civ, buttonText, playerCiv)).fillX().row()
} }
for (civ in majorCivs.filter { it.isDefeated() }.sortedByDescending { it.victoryManager.amountMilestonesCompleted(victory) }) {
val buttonText = civ.victoryManager.getNextMilestone(victory)?.getVictoryScreenButtonHeaderText(false, civ) ?: "Done!"
victoryColumn.add(getCivGroup(civ, "\n" + buttonText.tr(), playerCivInfo)).fillX().row()
}
return victoryColumn
} }
override fun activated(index: Int, caption: String, pager: TabbedPager) {
equalizeColumns(header, this)
}
override fun getFixedContent() = header
} }

View File

@ -1,54 +1,61 @@
package com.unciv.ui.screens.victoryscreen package com.unciv.ui.screens.victoryscreen
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.logic.civilization.Civilization
import com.unciv.models.ruleset.Victory import com.unciv.models.ruleset.Victory
import com.unciv.ui.components.TabbedPager
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
class VictoryScreenOurVictory( class VictoryScreenOurVictory(
worldScreen: WorldScreen worldScreen: WorldScreen
) : VictoryScreen.VictoryScreenTab(worldScreen) { ) : Table(BaseScreen.skin), TabbedPager.IPageExtensions {
private val header = Table()
init { init {
defaults().pad(10f) val gameInfo = worldScreen.gameInfo
val victoriesToShow = gameInfo.getEnabledVictories() val victoriesToShow = gameInfo.getEnabledVictories()
for (victory in victoriesToShow) { defaults().pad(10f)
add("[${victory.key}] Victory".toLabel()) for ((victoryName, victory) in victoriesToShow) {
header.add("[$victoryName] Victory".toLabel()).pad(10f)
add(getColumn(victory, worldScreen.viewingCiv))
} }
row() row()
for (victory in victoriesToShow.values) {
for (victory in victoriesToShow) { add(victory.victoryScreenHeader.toLabel())
add(getOurVictoryColumn(victory.key))
}
row()
for (victory in victoriesToShow) {
add(victory.value.victoryScreenHeader.toLabel())
} }
header.addSeparator(Color.GRAY)
} }
private fun getOurVictoryColumn(victory: String): Table { private fun getColumn(victory: Victory, playerCiv: Civilization): Table {
val victoryObject = gameInfo.ruleset.victories[victory]!!
val table = Table() val table = Table()
table.defaults().pad(5f) table.defaults().space(10f)
var firstIncomplete = true var firstIncomplete = true
for (milestone in victoryObject.milestoneObjects) { for (milestone in victory.milestoneObjects) {
val completionStatus = val completionStatus = when {
when { milestone.hasBeenCompletedBy(playerCiv) -> Victory.CompletionStatus.Completed
milestone.hasBeenCompletedBy(playerCivInfo) -> Victory.CompletionStatus.Completed firstIncomplete -> {
firstIncomplete -> { firstIncomplete = false
firstIncomplete = false Victory.CompletionStatus.Partially
Victory.CompletionStatus.Partially }
} else -> Victory.CompletionStatus.Incomplete
else -> Victory.CompletionStatus.Incomplete }
} for (button in milestone.getVictoryScreenButtons(completionStatus, playerCiv)) {
for (button in milestone.getVictoryScreenButtons(completionStatus, playerCivInfo)) {
table.add(button).row() table.add(button).row()
} }
} }
return table return table
} }
override fun activated(index: Int, caption: String, pager: TabbedPager) {
equalizeColumns(header, this)
}
override fun getFixedContent() = header
} }

View File

@ -1,27 +1,29 @@
package com.unciv.ui.screens.victoryscreen package com.unciv.ui.screens.victoryscreen
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Timer import com.badlogic.gdx.utils.Timer
import com.unciv.ui.components.TabbedPager
import com.unciv.ui.components.YearTextUtil import com.unciv.ui.components.YearTextUtil
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
class VictoryScreenReplay( class VictoryScreenReplay(
worldScreen: WorldScreen worldScreen: WorldScreen
) : VictoryScreen.VictoryScreenTab(worldScreen) { ) : Table(BaseScreen.skin), TabbedPager.IPageExtensions {
private val gameInfo = worldScreen.gameInfo
private var replayTimer : Timer.Task? = null private var replayTimer : Timer.Task? = null
private val yearLabel = "".toLabel() private val yearLabel = "".toLabel()
private val replayMap = ReplayMap(gameInfo.tileMap) private val replayMap = ReplayMap(gameInfo.tileMap)
private val header = Table()
init { init {
defaults().pad(10f) header.add(yearLabel).pad(10f)
add(replayMap).pad(10f)
add(yearLabel).row()
add(replayMap).row()
// restartTimer() - done later!
} }
internal fun restartTimer() { private fun restartTimer() {
replayTimer?.cancel() replayTimer?.cancel()
val firstTurn = gameInfo.historyStartTurn val firstTurn = gameInfo.historyStartTurn
val finalTurn = gameInfo.turns val finalTurn = gameInfo.turns
@ -39,7 +41,7 @@ class VictoryScreenReplay(
) )
} }
internal fun resetTimer() { private fun resetTimer() {
replayTimer?.cancel() replayTimer?.cancel()
replayTimer = null replayTimer = null
} }
@ -54,4 +56,14 @@ class VictoryScreenReplay(
) )
replayMap.update(turn) replayMap.update(turn)
} }
override fun activated(index: Int, caption: String, pager: TabbedPager) {
restartTimer()
}
override fun deactivated(index: Int, caption: String, pager: TabbedPager) {
resetTimer()
}
override fun getFixedContent() = header
} }