diff --git a/core/src/com/unciv/ui/CivilopediaScreen.kt b/core/src/com/unciv/ui/CivilopediaScreen.kt deleted file mode 100644 index e5d4da4336..0000000000 --- a/core/src/com/unciv/ui/CivilopediaScreen.kt +++ /dev/null @@ -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>() - private val categoryToButtons = LinkedHashMap() - - 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)) - } - } -} \ No newline at end of file diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaCategories.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaCategories.kt new file mode 100644 index 0000000000..b6ba28b3f4 --- /dev/null +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaCategories.kt @@ -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 } + + } +} diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt new file mode 100644 index 0000000000..f185523982 --- /dev/null +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt @@ -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>() + private val categoryToButtons = LinkedHashMap() + private val entryIndex = LinkedHashMap() + + 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)) + } + } +} diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index ae428bbbff..a32ee43210 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -25,7 +25,7 @@ import com.unciv.models.UncivSound import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.unit.UnitType 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.overviewscreen.EmpireOverviewScreen 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.TileInfoTable 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.UnitTable import java.util.* import kotlin.concurrent.thread 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() { 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 - val canChangeState = isPlayersTurn && !viewingCiv.isSpectator() + val canChangeState + get() = isPlayersTurn && !viewingCiv.isSpectator() private var waitingForAutosave = false 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 if (viewingCiv.isSpectator()) - mapHolder.setCenterPosition(tileToCenterOn, true, false) + mapHolder.setCenterPosition(tileToCenterOn, immediately = true, selectUnit = false) else - mapHolder.setCenterPosition(tileToCenterOn, true, true) + mapHolder.setCenterPosition(tileToCenterOn, immediately = true, selectUnit = 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 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 - // 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 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) } + /** + * Used by [OptionsPopup][com.unciv.ui.worldscreen.mainmenu.OptionsPopup] + * to re-enable the next turn button within its Close button action + */ fun enableNextTurnButtonAfterOptions() { nextTurnButton.isEnabled = isPlayersTurn && !waitingForAutosave } @@ -647,7 +670,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam NextTurnAction("Next unit", Color.LIGHT_GRAY) { val nextDueUnit = viewingCiv.getNextDueUnit() if (nextDueUnit != null) { - mapHolder.setCenterPosition(nextDueUnit.currentTile.position, false, false) + mapHolder.setCenterPosition(nextDueUnit.currentTile.position, immediately = false, selectUnit = false) bottomUnitTable.selectUnit(nextDueUnit) shouldUpdate = true } diff --git a/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt b/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt index 8cf9a4847e..d659a1ba1a 100644 --- a/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt +++ b/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt @@ -6,7 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Cell import com.badlogic.gdx.scenes.scene2d.ui.Table import com.unciv.Constants 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.NewGameScreen import com.unciv.ui.saves.LoadGameScreen