Spruced up Civilopedia - phase 1 - linking and category Enum (#3975)

This commit is contained in:
SomeTroglodyte
2021-05-21 14:19:18 +02:00
committed by GitHub
parent 3801a81bf4
commit f86a3fa47b
5 changed files with 443 additions and 208 deletions

View File

@ -1,198 +0,0 @@
package com.unciv.ui
import com.badlogic.gdx.graphics.Color
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.Terrain
import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.models.translations.tr
import com.unciv.ui.tilegroups.TileGroup
import com.unciv.ui.tilegroups.TileSetStrings
import com.unciv.ui.utils.*
import java.util.*
class CivilopediaScreen(ruleset: Ruleset) : CameraStageBaseScreen() {
class CivilopediaEntry(var name: String, var description: String, var image: Actor? = null)
private val categoryToEntries = LinkedHashMap<String, Collection<CivilopediaEntry>>()
private val categoryToButtons = LinkedHashMap<String, Button>()
private val entrySelectTable = Table().apply { defaults().pad(6f).left() }
val description = "".toLabel()
fun select(category: String) {
entrySelectTable.clear()
for (button in categoryToButtons.values) button.color = Color.WHITE
categoryToButtons[category]!!.color = Color.BLUE
var entries = categoryToEntries[category]!!
if (category != "Difficulty levels") // this is the only case where we need them in order
entries = entries.sortedBy { it.name.tr() } // Alphabetical order of localized names
for (entry in entries) {
val entryButton = Table().apply {
background = ImageGetter.getBackground(colorFromRGB(50, 75, 125))
touchable = Touchable.enabled
}
if (entry.image != null)
if (category == "Terrains")
entryButton.add(entry.image).padLeft(20f).padRight(10f)
else
entryButton.add(entry.image).padLeft(10f)
entryButton.left().add(entry.name.toLabel(Color.WHITE, 25)).pad(10f)
entryButton.onClick {
description.setText(entry.description)
entrySelectTable.children.forEach { it.color = Color.WHITE }
entryButton.color = Color.BLUE
}
entrySelectTable.add(entryButton).height(75f).expandX().fillX().row()
}
}
init {
val imageSize = 50f
onBackButtonClicked { UncivGame.Current.setWorldScreen() }
categoryToEntries["Buildings"] = ruleset.buildings.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques && !(it.isWonder || it.isNationalWonder) }
.map {
CivilopediaEntry(it.name, it.getDescription(false, null, ruleset),
ImageGetter.getConstructionImage(it.name).surroundWithCircle(imageSize))
}
categoryToEntries["Wonders"] = ruleset.buildings.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques && (it.isWonder || it.isNationalWonder) }
.map {
CivilopediaEntry(it.name, it.getDescription(false, null, ruleset),
ImageGetter.getConstructionImage(it.name).surroundWithCircle(imageSize))
}
categoryToEntries["Resources"] = ruleset.tileResources.values
.map {
CivilopediaEntry(it.name, it.getDescription(ruleset),
ImageGetter.getResourceImage(it.name, imageSize))
}
categoryToEntries["Terrains"] = ruleset.terrains.values
.map {
CivilopediaEntry(it.name, it.getDescription(ruleset),
terrainImage(it, ruleset, imageSize))
}
categoryToEntries["Tile Improvements"] = ruleset.tileImprovements.values
.map {
CivilopediaEntry(it.name, it.getDescription(ruleset, false),
ImageGetter.getImprovementIcon(it.name, imageSize))
}
categoryToEntries["Units"] = ruleset.units.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques }
.map {
CivilopediaEntry(it.name, it.getDescription(false),
ImageGetter.getConstructionImage(it.name).surroundWithCircle(imageSize))
}
categoryToEntries["Nations"] = ruleset.nations.values
.filter { it.isMajorCiv() }
.map {
CivilopediaEntry(it.name, it.getUniqueString(ruleset, false),
ImageGetter.getNationIndicator(it, imageSize))
}
categoryToEntries["Technologies"] = ruleset.technologies.values
.map {
CivilopediaEntry(it.name, it.getDescription(ruleset),
ImageGetter.getTechIconGroup(it.name, imageSize))
}
categoryToEntries["Promotions"] = ruleset.unitPromotions.values
.map {
CivilopediaEntry(it.name, it.getDescription(ruleset.unitPromotions.values, true, ruleset),
ImageGetter.getPromotionIcon(it.name, imageSize))
}
categoryToEntries["Tutorials"] = tutorialController.getCivilopediaTutorials()
.map { CivilopediaEntry(it.key.replace("_", " "), it.value.joinToString("\n\n") { line -> line.tr() }) }
categoryToEntries["Difficulty levels"] = ruleset.difficulties.values
.map { CivilopediaEntry(it.name, it.getDescription()) }
val buttonTable = Table()
buttonTable.pad(15f)
buttonTable.defaults().pad(10f)
for (category in categoryToEntries.keys) {
val button = category.toTextButton()
button.style = TextButton.TextButtonStyle(button.style)
categoryToButtons[category] = button
button.onClick { select(category) }
buttonTable.add(button)
}
buttonTable.pack()
buttonTable.width = stage.width
val buttonTableScroll = ScrollPane(buttonTable)
buttonTableScroll.setScrollingDisabled(false, true)
val goToGameButton = Constants.close.toTextButton()
goToGameButton.onClick {
game.setWorldScreen()
dispose()
}
val topTable = Table()
topTable.add(goToGameButton).pad(10f)
topTable.add(buttonTableScroll)
topTable.pack()
val entryTable = Table()
val splitPane = SplitPane(topTable, entryTable, true, skin)
splitPane.splitAmount = topTable.prefHeight / stage.height
entryTable.height = stage.height - topTable.prefHeight
splitPane.setFillParent(true)
stage.addActor(splitPane)
description.wrap = true
val entrySelectScroll = ScrollPane(entrySelectTable)
entrySelectTable.top()
entrySelectScroll.setOverscroll(false, false)
val descriptionTable = Table()
descriptionTable.add(description).width(stage.width * 0.5f)
val entrySplitPane = SplitPane(entrySelectScroll, ScrollPane(descriptionTable), false, skin)
entrySplitPane.splitAmount = 0.3f
entryTable.addActor(entrySplitPane)
entrySplitPane.setFillParent(true)
select("Tutorials")
}
private fun terrainImage(terrain: Terrain, ruleset: Ruleset, imageSize: Float): Actor {
val tileInfo = TileInfo()
tileInfo.ruleset = ruleset
when (terrain.type) {
TerrainType.NaturalWonder -> {
tileInfo.naturalWonder = terrain.name
tileInfo.baseTerrain = terrain.turnsInto ?: Constants.grassland
}
TerrainType.TerrainFeature -> {
tileInfo.terrainFeatures.add(terrain.name)
tileInfo.baseTerrain = terrain.occursOn.lastOrNull() ?: Constants.grassland
}
else ->
tileInfo.baseTerrain = terrain.name
}
tileInfo.setTransients()
val group = TileGroup(tileInfo, TileSetStrings(), imageSize)
group.showEntireMap = true
group.forMapEditorIcon = true
group.update()
return group
}
override fun resize(width: Int, height: Int) {
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
game.setScreen(CivilopediaScreen(game.worldScreen.gameInfo.ruleSet))
}
}
}

View File

@ -0,0 +1,107 @@
package com.unciv.ui.civilopedia
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.unciv.Constants
import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.Terrain
import com.unciv.models.ruleset.tile.TerrainType
import com.unciv.ui.tilegroups.TileGroup
import com.unciv.ui.tilegroups.TileSetStrings
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.surroundWithCircle
import java.io.File
/** Encapsulates the knowledge on how to get an icon for each of the Civilopedia categories */
object CivilopediaImageGetters {
private const val policyIconFolder = "PolicyIcons"
// Todo: potential synergy with map editor
fun terrainImage(terrain: Terrain, ruleset: Ruleset, imageSize: Float): Actor {
val tileInfo = TileInfo()
tileInfo.ruleset = ruleset
when (terrain.type) {
TerrainType.NaturalWonder -> {
tileInfo.naturalWonder = terrain.name
tileInfo.baseTerrain = terrain.turnsInto ?: Constants.grassland
}
TerrainType.TerrainFeature -> {
tileInfo.terrainFeatures.add(terrain.name)
tileInfo.baseTerrain = terrain.occursOn.lastOrNull() ?: Constants.grassland
}
else ->
tileInfo.baseTerrain = terrain.name
}
tileInfo.setTerrainTransients()
val group = TileGroup(tileInfo, TileSetStrings(), imageSize * 36f/54f) // TileGroup normally spills out of its bounding box
group.showEntireMap = true
group.forMapEditorIcon = true
group.update()
return group
}
val construction = { name: String, size: Float ->
ImageGetter.getConstructionImage(name)
.surroundWithCircle(size, color = Color.WHITE)
}
val improvement = { name: String, size: Float ->
ImageGetter.getImprovementIcon(name, size)
}
val nation = { name: String, size: Float ->
val nation = ImageGetter.ruleset.nations[name]
if (nation == null) null
else ImageGetter.getNationIndicator(nation, size)
}
val policy = { name: String, size: Float ->
ImageGetter.getImage(policyIconFolder + File.separator + name)
.apply { setSize(size,size) }
}
val resource = { name: String, size: Float ->
ImageGetter.getResourceImage(name, size)
}
val technology = { name: String, size: Float ->
ImageGetter.getTechIconGroup(name, size)
}
val promotion = { name: String, size: Float ->
ImageGetter.getPromotionIcon(name, size)
}
val terrain = { name: String, size: Float ->
val terrain = ImageGetter.ruleset.terrains[name]
if (terrain == null) null
else terrainImage(terrain, ImageGetter.ruleset, size)
}
}
/** Enum used as keys for Civilopedia "pages" (categories).
*
* Note names are singular on purpose - a "link" allows both key and label
*
* @param label Translatable caption for the Civilopedia button
*/
enum class CivilopediaCategories (
val label: String,
val hide: Boolean, // Omitted on CivilopediaScreen
val getImage: ((name: String, size: Float) -> Actor?)?
) {
Building ("Buildings", false, CivilopediaImageGetters.construction ),
Wonder ("Wonders", false, CivilopediaImageGetters.construction ),
Resource ("Resources", false, CivilopediaImageGetters.resource ),
Terrain ("Terrains", false, CivilopediaImageGetters.terrain ),
Improvement ("Tile Improvements", false, CivilopediaImageGetters.improvement ),
Unit ("Units", false, CivilopediaImageGetters.construction ),
Nation ("Nations", false, CivilopediaImageGetters.nation ),
Technology ("Technologies", false, CivilopediaImageGetters.technology ),
Promotion ("Promotions", false, CivilopediaImageGetters.promotion ),
Policy ("Policies", true, CivilopediaImageGetters.policy ),
Tutorial ("Tutorials", false, null ),
Difficulty ("Difficulty levels", false, null ),
;
companion object {
fun fromLink(name: String): CivilopediaCategories? =
values().firstOrNull { it.name == name }
?: values().firstOrNull { it.label == name }
}
}

View File

@ -0,0 +1,303 @@
package com.unciv.ui.civilopedia
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.stats.INamed
import com.unciv.models.translations.tr
import com.unciv.ui.utils.*
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
/** Screen displaying the Civilopedia
* @param ruleset [Ruleset] to display items from
* @param category [CivilopediaCategories] key to select category
* @param link alternate selector to select category and/or entry. Can have the form `category/entry`
* overriding the [category] parameter, or just `entry` to complement it.
*/
class CivilopediaScreen(
val ruleset: Ruleset
, category: CivilopediaCategories = CivilopediaCategories.Tutorial
, link: String = ""
) : CameraStageBaseScreen() {
/** Container collecting data per Civilopedia entry
* @param name From [Ruleset] object [INamed.name]
* @param description Multiline text
* @param image Icon for button
* @param y Y coordinate for scrolling to
* @param height Cell height
*/
private class CivilopediaEntry (
val name: String,
val description: String,
val image: Actor? = null,
val y: Float = 0f, // coordinates of button cell used to scroll to entry
val height: Float = 0f
) {
fun withCoordinates(y: Float, height: Float) = CivilopediaEntry(name, description, image, y, height)
}
private val categoryToEntries = LinkedHashMap<CivilopediaCategories, Collection<CivilopediaEntry>>()
private val categoryToButtons = LinkedHashMap<CivilopediaCategories, Button>()
private val entryIndex = LinkedHashMap<String, CivilopediaEntry>()
private val entrySelectTable = Table().apply { defaults().pad(6f).left() }
private val entrySelectScroll: ScrollPane
private val descriptionLabel = "".toLabel()
private var currentCategory: CivilopediaCategories = CivilopediaCategories.Tutorial
private var currentEntry: String = ""
/** Jump to a "link" selecting both category and entry
*
* Calls [selectCategory] with the substring before the first '/',
*
* and [selectEntry] with the substring after the first '/'
*
* @param link Link in the form Category/Entry
*/
private fun selectLink(link: String) {
val parts = link.split('/', limit = 2)
if (parts.isEmpty()) return
selectCategory(parts[0])
if (parts.size >= 2) selectEntry(parts[1], noScrollAnimation = true)
}
/** Select a specified category
* @param name Category name or label
*/
fun selectCategory(name: String) {
val category = CivilopediaCategories.fromLink(name)
?: return // silently ignore unknown category names in links
selectCategory(category)
}
/** Select a specified category - unselects entry, rebuilds left side buttons.
* @param category Category key
*/
fun selectCategory(category: CivilopediaCategories) {
currentCategory = category
entrySelectTable.clear()
entryIndex.clear()
descriptionLabel.setText("")
for (button in categoryToButtons.values) button.color = Color.WHITE
if (category !in categoryToButtons) return // defense against being passed a bad selector
categoryToButtons[category]!!.color = Color.BLUE
if (category !in categoryToEntries) return // defense, allowing buggy panes to remain emtpy while others work
var entries = categoryToEntries[category]!!
if (category != CivilopediaCategories.Difficulty) // this is the only case where we need them in order
entries = entries.sortedBy { it.name.tr() } // Alphabetical order of localized names
var currentY = -1f
for (entry in entries) {
val entryButton = Table().apply {
background = ImageGetter.getBackground(colorFromRGB(50, 75, 125))
touchable = Touchable.enabled
}
if (entry.image != null)
if (category == CivilopediaCategories.Terrain)
entryButton.add(entry.image).padLeft(20f).padRight(10f)
else
entryButton.add(entry.image).padLeft(10f)
entryButton.left().add(entry.name.toLabel(Color.WHITE, 25)).pad(10f)
entryButton.onClick { selectEntry(entry) }
entryButton.name = entry.name // make button findable
val cell = entrySelectTable.add(entryButton).height(75f).expandX().fillX()
entrySelectTable.row()
if (currentY < 0f) currentY = cell.padTop
entryIndex[entry.name] = entry.withCoordinates(currentY, cell.prefHeight)
currentY += cell.padBottom + cell.prefHeight + cell.padTop
}
entrySelectScroll.layout() // necessary for positioning in selectRow to work
}
/** Select a specified entry within the current category. Unknown strings are ignored!
* @param name Entry (Ruleset object) name
* @param noScrollAnimation Disable scroll animation
*/
fun selectEntry(name: String, noScrollAnimation: Boolean = false) {
val entry = entryIndex[name] ?: return
// fails: entrySelectScroll.scrollTo(0f, entry.y, 0f, entry.h, false, true)
entrySelectScroll.let {
it.scrollY = (entry.y + (entry.height - it.height) / 2).coerceIn(0f, it.maxY)
}
if (noScrollAnimation)
entrySelectScroll.updateVisualScroll() // snap without animation on fresh pedia open
selectEntry(entry)
}
private fun selectEntry(entry: CivilopediaEntry) {
currentEntry = entry.name
descriptionLabel.setText(entry.description)
entrySelectTable.children.forEach {
it.color = if (it.name == entry.name) Color.BLUE else Color.WHITE
}
}
init {
val imageSize = 50f
onBackButtonClicked { UncivGame.Current.setWorldScreen() }
categoryToEntries[CivilopediaCategories.Building] = ruleset.buildings.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques && !(it.isWonder || it.isNationalWonder) }
.map {
CivilopediaEntry(
it.name,
it.getDescription(false, null, ruleset),
CivilopediaCategories.Building.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Wonder] = ruleset.buildings.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques && (it.isWonder || it.isNationalWonder) }
.map {
CivilopediaEntry(
it.name,
it.getDescription(false, null, ruleset),
CivilopediaCategories.Wonder.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Resource] = ruleset.tileResources.values
.map {
CivilopediaEntry(
it.name,
it.getDescription(ruleset),
CivilopediaCategories.Resource.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Terrain] = ruleset.terrains.values
.map {
CivilopediaEntry(
it.name,
it.getDescription(ruleset),
CivilopediaCategories.Terrain.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Improvement] = ruleset.tileImprovements.values
.map {
CivilopediaEntry(
it.name,
it.getDescription(ruleset, false),
CivilopediaCategories.Improvement.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Unit] = ruleset.units.values
.filter { "Will not be displayed in Civilopedia" !in it.uniques }
.map {
CivilopediaEntry(
it.name,
it.getDescription(false),
CivilopediaCategories.Unit.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Nation] = ruleset.nations.values
.filter { it.isMajorCiv() }
.map {
CivilopediaEntry(
it.name,
it.getUniqueString(ruleset, false),
CivilopediaCategories.Nation.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Technology] = ruleset.technologies.values
.map {
CivilopediaEntry(
it.name,
it.getDescription(ruleset),
CivilopediaCategories.Technology.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Promotion] = ruleset.unitPromotions.values
.map {
CivilopediaEntry(
it.name,
it.getDescription(ruleset.unitPromotions.values, true, ruleset),
CivilopediaCategories.Promotion.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Tutorial] = tutorialController.getCivilopediaTutorials()
.map {
CivilopediaEntry(
it.key.replace("_", " "),
it.value.joinToString("\n\n") { line -> line.tr() },
// CivilopediaCategories.Tutorial.getImage?.invoke(it.name, imageSize)
)
}
categoryToEntries[CivilopediaCategories.Difficulty] = ruleset.difficulties.values
.map {
CivilopediaEntry(
it.name,
it.getDescription(),
// CivilopediaCategories.Difficulty.getImage?.invoke(it.name, imageSize)
)
}
val buttonTable = Table()
buttonTable.pad(15f)
buttonTable.defaults().pad(10f)
for (categoryKey in categoryToEntries.keys) {
val button = categoryKey.label.toTextButton()
button.style = TextButton.TextButtonStyle(button.style)
categoryToButtons[categoryKey] = button
button.onClick { selectCategory(categoryKey) }
buttonTable.add(button)
}
buttonTable.pack()
buttonTable.width = stage.width
val buttonTableScroll = ScrollPane(buttonTable)
buttonTableScroll.setScrollingDisabled(false, true)
val goToGameButton = Constants.close.toTextButton()
goToGameButton.onClick {
game.setWorldScreen()
dispose()
}
val topTable = Table()
topTable.add(goToGameButton).pad(10f)
topTable.add(buttonTableScroll)
topTable.pack()
val entryTable = Table()
val splitPane = SplitPane(topTable, entryTable, true, skin)
splitPane.splitAmount = topTable.prefHeight / stage.height
entryTable.height = stage.height - topTable.prefHeight
splitPane.setFillParent(true)
stage.addActor(splitPane)
entrySelectScroll = ScrollPane(entrySelectTable)
entrySelectTable.top()
entrySelectScroll.setOverscroll(false, false)
val descriptionTable = Table()
descriptionLabel.wrap = true // requires explicit cell width!
descriptionTable.add(descriptionLabel).width(stage.width * 0.5f).padTop(10f).row()
val entrySplitPane = SplitPane(entrySelectScroll, ScrollPane(descriptionTable), false, skin)
entrySplitPane.splitAmount = 0.3f
entryTable.addActor(entrySplitPane)
entrySplitPane.setFillParent(true)
if (link.isEmpty() || '/' !in link)
selectCategory(category)
if (link.isNotEmpty())
if ('/' in link)
selectLink(link)
else
selectEntry(link, noScrollAnimation = true)
}
override fun resize(width: Int, height: Int) {
if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) {
game.setScreen(CivilopediaScreen(game.worldScreen.gameInfo.ruleSet, currentCategory, currentEntry))
}
}
}

View File

@ -25,7 +25,7 @@ import com.unciv.models.UncivSound
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.unit.UnitType import com.unciv.models.ruleset.unit.UnitType
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.CivilopediaScreen import com.unciv.ui.civilopedia.CivilopediaScreen
import com.unciv.ui.cityscreen.CityScreen import com.unciv.ui.cityscreen.CityScreen
import com.unciv.ui.overviewscreen.EmpireOverviewScreen import com.unciv.ui.overviewscreen.EmpireOverviewScreen
import com.unciv.ui.pickerscreens.GreatPersonPickerScreen import com.unciv.ui.pickerscreens.GreatPersonPickerScreen
@ -40,19 +40,38 @@ import com.unciv.ui.victoryscreen.VictoryScreen
import com.unciv.ui.worldscreen.bottombar.BattleTable import com.unciv.ui.worldscreen.bottombar.BattleTable
import com.unciv.ui.worldscreen.bottombar.TileInfoTable import com.unciv.ui.worldscreen.bottombar.TileInfoTable
import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
import com.unciv.ui.worldscreen.mainmenu.OptionsPopup
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 java.util.* import java.util.*
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.concurrent.timer import kotlin.concurrent.timer
/**
* Unciv's world screen
* @param gameInfo The game state the screen should represent
* @param viewingCiv The currently active [civilization][CivilizationInfo]
* @see [shouldUpdate]
* @see [isPlayersTurn]
* @see [selectedCiv]
* @see [canChangeState]
* @see [mapHolder]
* @see [bottomUnitTable]
* @see [enableNextTurnButtonAfterOptions]
* @property shouldUpdate When set, causes the screen to update in the next [render][CameraStageBaseScreen.render] event
* @property isPlayersTurn (readonly) Indicates it's the player's ([viewingCiv]) turn
* @property selectedCiv Selected civilization, used in spectator and replay mode, equals viewingCiv in ordinary games
* @property canChangeState (readonly) `true` when it's the player's turn unless he is a spectator
* @property mapHolder A [MinimapHolder] instance
* @property bottomUnitTable Bottom left widget holding information about a selected unit or city
*/
class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
var isPlayersTurn = viewingCiv == gameInfo.currentPlayerCiv var isPlayersTurn = viewingCiv == gameInfo.currentPlayerCiv
var selectedCiv = viewingCiv // Selected civilization, used in spectator and replay mode, equals viewingCiv in ordinary games private set // only this class is allowed to make changes
var selectedCiv = viewingCiv
private var fogOfWar = true private var fogOfWar = true
val canChangeState = isPlayersTurn && !viewingCiv.isSpectator() val canChangeState
get() = isPlayersTurn && !viewingCiv.isSpectator()
private var waitingForAutosave = false private var waitingForAutosave = false
val mapHolder = WorldMapHolder(this, gameInfo.tileMap) val mapHolder = WorldMapHolder(this, gameInfo.tileMap)
@ -140,9 +159,9 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
// Don't select unit and change selectedCiv when centering as spectator // Don't select unit and change selectedCiv when centering as spectator
if (viewingCiv.isSpectator()) if (viewingCiv.isSpectator())
mapHolder.setCenterPosition(tileToCenterOn, true, false) mapHolder.setCenterPosition(tileToCenterOn, immediately = true, selectUnit = false)
else else
mapHolder.setCenterPosition(tileToCenterOn, true, true) mapHolder.setCenterPosition(tileToCenterOn, immediately = true, selectUnit = true)
tutorialController.allTutorialsShowedCallback = { shouldUpdate = true } tutorialController.allTutorialsShowedCallback = { shouldUpdate = true }
@ -544,7 +563,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
} }
fun createNewWorldScreen(gameInfo: GameInfo) { private fun createNewWorldScreen(gameInfo: GameInfo) {
game.gameInfo = gameInfo game.gameInfo = gameInfo
val newWorldScreen = WorldScreen(gameInfo, gameInfo.getPlayerToViewAs()) val newWorldScreen = WorldScreen(gameInfo, gameInfo.getPlayerToViewAs())
@ -596,7 +615,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
val shouldAutoSave = gameInfoClone.turns % game.settings.turnsBetweenAutosaves == 0 val shouldAutoSave = gameInfoClone.turns % game.settings.turnsBetweenAutosaves == 0
// create a new worldscreen to show the new stuff we've changed, and switch out the current screen. // create a new WorldScreen to show the new stuff we've changed, and switch out the current screen.
// do this on main thread - it's the only one that has a GL context to create images from // do this on main thread - it's the only one that has a GL context to create images from
Gdx.app.postRunnable { Gdx.app.postRunnable {
@ -635,6 +654,10 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
nextTurnButton.setPosition(stage.width - nextTurnButton.width - 10f, topBar.y - nextTurnButton.height - 10f) nextTurnButton.setPosition(stage.width - nextTurnButton.width - 10f, topBar.y - nextTurnButton.height - 10f)
} }
/**
* Used by [OptionsPopup][com.unciv.ui.worldscreen.mainmenu.OptionsPopup]
* to re-enable the next turn button within its Close button action
*/
fun enableNextTurnButtonAfterOptions() { fun enableNextTurnButtonAfterOptions() {
nextTurnButton.isEnabled = isPlayersTurn && !waitingForAutosave nextTurnButton.isEnabled = isPlayersTurn && !waitingForAutosave
} }
@ -647,7 +670,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
NextTurnAction("Next unit", Color.LIGHT_GRAY) { NextTurnAction("Next unit", Color.LIGHT_GRAY) {
val nextDueUnit = viewingCiv.getNextDueUnit() val nextDueUnit = viewingCiv.getNextDueUnit()
if (nextDueUnit != null) { if (nextDueUnit != null) {
mapHolder.setCenterPosition(nextDueUnit.currentTile.position, false, false) mapHolder.setCenterPosition(nextDueUnit.currentTile.position, immediately = false, selectUnit = false)
bottomUnitTable.selectUnit(nextDueUnit) bottomUnitTable.selectUnit(nextDueUnit)
shouldUpdate = true shouldUpdate = true
} }

View File

@ -6,7 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Cell
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.MainMenuScreen import com.unciv.MainMenuScreen
import com.unciv.ui.CivilopediaScreen import com.unciv.ui.civilopedia.CivilopediaScreen
import com.unciv.ui.newgamescreen.GameSetupInfo import com.unciv.ui.newgamescreen.GameSetupInfo
import com.unciv.ui.newgamescreen.NewGameScreen import com.unciv.ui.newgamescreen.NewGameScreen
import com.unciv.ui.saves.LoadGameScreen import com.unciv.ui.saves.LoadGameScreen