diff --git a/android/assets/jsons/Tutorials.json b/android/assets/jsons/Tutorials.json index 75deb580d7..6d0507fccb 100644 --- a/android/assets/jsons/Tutorials.json +++ b/android/assets/jsons/Tutorials.json @@ -118,7 +118,7 @@ ], Great_People: [ "Certain buildings, and specialists in cities, generate Great Person points per turn.\nThere are several types of Great People, and their points accumulate separately.\nThe number of points per turn and accumulated points can be viewed in the Overview screen.", - "Once enough points have been accumulaated, a Great Person of that type will be created!\nEach Great Person can construct a certain Great Improvement which gives large yields over time, or immediately consumed to provide a certain bonus now.", + "Once enough points have been accumulated, a Great Person of that type will be created!\nEach Great Person can construct a certain Great Improvement which gives large yields over time, or immediately consumed to provide a certain bonus now.", "Great Improvements also provide any strategic resources that are under them, so you don't need to worry if resources are revealed underneath your improvements!" ], Removing_Terrain_Features: [ @@ -126,5 +126,11 @@ ], Natural_Wonders: [ "Natural Wonders, such as the Mt. Fuji, the Rock of Gibraltar and the Great Barrier Reef, are unique, impassable terrain features, masterpieces of mother Nature, which possess exceptional qualities that make them very different from the average terrain.\nThey benefit by giving you large sums of Culture, Science, Gold or Production if worked by your Cities, which is why you might need to bring them under your empire as soon as possible." + ], + "Keyboard": [ + "If you have a keyboard, some shortcut keys become available. Unit command or improvement picker keys, for example, are shown directly in their corresponding buttons.", + "On the world screen the hotkeys are as follows:", "Space or 'N' - Next unit or turn\n'E' - Empire overview (last viewed page)\n'+', '-' - Zoom in / out\nHome - center on capital", + "F1 - Open Civilopedia\nF2 - Empire overview Trades\nF3 - Empire overview Units\nF4 - Empire overview Diplomacy\nF5 - Social policies\nF6 - Technologies\nF7 - Empire overview Cities\nF8 - Victory Progress\nF9 - Empire overview Stats\nF10 - Empire overview Resources\nF11 - Quicksave\nF12 - Quickload", + "Ctrl-R - Toggle tile resource display\nCtrl-Y - Toggle tile yield display\nCtrl-O - Game options\nCtrl-S - Save game\nCtrl-L - Load game" ] -} \ No newline at end of file +} diff --git a/core/src/com/unciv/models/Tutorial.kt b/core/src/com/unciv/models/Tutorial.kt index 0eece7c901..bd6c51e156 100644 --- a/core/src/com/unciv/models/Tutorial.kt +++ b/core/src/com/unciv/models/Tutorial.kt @@ -33,10 +33,11 @@ enum class Tutorial(val value: String, val isCivilopedia: Boolean = !value.start NaturalWonders("Natural_Wonders"), CityExpansion("City_Expansion"), GreatPeople("Great_People"), - RemovingTerrainFeatures("Removing_Terrain_Features") + RemovingTerrainFeatures("Removing_Terrain_Features"), + Keyboard("Keyboard") ; companion object { fun findByName(name: String): Tutorial? = values().find { it.value == name } } -} \ No newline at end of file +} diff --git a/core/src/com/unciv/ui/utils/ZoomableScrollPane.kt b/core/src/com/unciv/ui/utils/ZoomableScrollPane.kt index 42858af83b..36e73f6bb3 100644 --- a/core/src/com/unciv/ui/utils/ZoomableScrollPane.kt +++ b/core/src/com/unciv/ui/utils/ZoomableScrollPane.kt @@ -19,16 +19,22 @@ open class ZoomableScrollPane: ScrollPane(null) { } open fun zoom(zoomScale: Float) { - if (zoomScale < 0.5f || zoomScale > 2) return + if (zoomScale < 0.5f || zoomScale > 2f) return setScale(zoomScale) } + fun zoomIn() { + zoom(scaleX / 0.8f) + } + fun zoomOut() { + zoom(scaleX * 0.8f) + } private fun addZoomListeners() { addListener(object : InputListener() { override fun scrolled(event: InputEvent?, x: Float, y: Float, amountX: Float, amountY: Float): Boolean { - if (amountX > 0 || amountY > 0) zoom(scaleX * 0.8f) - else zoom(scaleX / 0.8f) + if (amountX > 0 || amountY > 0) zoomOut() + else zoomIn() return false } }) diff --git a/core/src/com/unciv/ui/worldscreen/Minimap.kt b/core/src/com/unciv/ui/worldscreen/Minimap.kt index 31a303697f..d2725129b2 100644 --- a/core/src/com/unciv/ui/worldscreen/Minimap.kt +++ b/core/src/com/unciv/ui/worldscreen/Minimap.kt @@ -34,8 +34,8 @@ class Minimap(val mapHolder: WorldMapHolder) : Table(){ var bottomY = 0f fun hexRow(vector2: Vector2) = vector2.x + vector2.y - val maxHexRow = mapHolder.tileMap.values.asSequence().map { hexRow(it.position) }.max()!! - val minHexRow = mapHolder.tileMap.values.asSequence().map { hexRow(it.position) }.min()!! + val maxHexRow = mapHolder.tileMap.values.asSequence().map { hexRow(it.position) }.maxOrNull()!! + val minHexRow = mapHolder.tileMap.values.asSequence().map { hexRow(it.position) }.minOrNull()!! val totalHexRows = maxHexRow - minHexRow for (tileInfo in mapHolder.tileMap.values) { @@ -127,6 +127,53 @@ class MinimapHolder(mapHolder: WorldMapHolder): Table() { pack() } + enum class MinimapToggleButtons(val icon: String) { + YIELD ("Food"), + WORKED ("Population"), + RESOURCES ("ResourceIcons/Cattle"); + } + + // "Api" when external code wants to toggle something together with our buttons + private fun getButtonState(button: MinimapToggleButtons): Boolean { + val info = toggleButtonInfo[button] ?: return false + return info.getSetting() + } + private fun setButtonState(button: MinimapToggleButtons, value: Boolean) { + val info = toggleButtonInfo[button] ?: return + info.setSetting(value) + info.actor.color.a = if (value) 1f else 0.5f + worldScreen.shouldUpdate = true + } + private fun syncButtonState(button: MinimapToggleButtons) = setButtonState(button,getButtonState(button)) + internal fun syncButtonStates() { + MinimapToggleButtons.values().forEach { syncButtonState(it) } + } + fun toggleButtonState(button: MinimapToggleButtons) = setButtonState(button,!getButtonState(button)) + + private fun addToggleButton(table:Table, button: MinimapToggleButtons) { + val image = + if ('/' in button.icon) { + ImageGetter.getImage(button.icon) + .surroundWithCircle(30f).apply { circle.color = Color.GREEN } + .surroundWithCircle(40f, false) + } else { + ImageGetter.getStatIcon(button.icon).surroundWithCircle(40f) + } + image.apply { circle.color = Color.BLACK } + toggleButtonInfo[button] = with(UncivGame.Current.settings) { + when (button) { + MinimapToggleButtons.YIELD -> ToggleButtonInfo(image, {showTileYields}, {showTileYields = it}) + MinimapToggleButtons.WORKED -> ToggleButtonInfo(image, {showWorkedTiles}, {showWorkedTiles = it}) + else -> ToggleButtonInfo(image, {showResourcesAndImprovements}, {showResourcesAndImprovements = it}) + } + } + syncButtonState(button) + image.onClick { + toggleButtonState(button) + } + table.add(image).row() + } + private fun getWrappedMinimap(): Table { val internalMinimapWrapper = Table() internalMinimapWrapper.add(minimap) diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 084326d6b8..437c2ff0b5 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -15,6 +15,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.badlogic.gdx.utils.Align import com.unciv.Constants +import com.unciv.UncivGame import com.unciv.logic.GameInfo import com.unciv.logic.GameSaver import com.unciv.logic.civilization.CivilizationInfo @@ -26,16 +27,21 @@ import com.unciv.models.ruleset.unit.UnitType import com.unciv.models.translations.tr import com.unciv.ui.CivilopediaScreen import com.unciv.ui.cityscreen.CityScreen +import com.unciv.ui.overviewscreen.EmpireOverviewScreen import com.unciv.ui.pickerscreens.GreatPersonPickerScreen import com.unciv.ui.pickerscreens.PolicyPickerScreen import com.unciv.ui.pickerscreens.TechButton import com.unciv.ui.pickerscreens.TechPickerScreen +import com.unciv.ui.saves.LoadGameScreen +import com.unciv.ui.saves.SaveGameScreen import com.unciv.ui.trade.DiplomacyScreen import com.unciv.ui.utils.* import com.unciv.ui.victoryscreen.VictoryScreen +import com.unciv.ui.worldscreen.MinimapHolder.MinimapToggleButtons 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.* @@ -175,8 +181,70 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam } private fun addKeyboardPresses() { + // Note these helpers might need unification with similar code e.g. in: + // GameSaver.autoSave, SaveGameScreen.saveGame, LoadGameScreen.rightSideButton.onClick,... + val quickSave = { + val toast = ToastPopup("Quicksaving...", this) + thread(name = "SaveGame") { + GameSaver.saveGame(gameInfo, "QuickSave") { + Gdx.app.postRunnable { + toast.close() + if (it != null) + ToastPopup("Could not save game!", this) + else { + ToastPopup("Quicksave successful.", this) + } + } + } + } + Unit // change type of anonymous fun from ()->Thread to ()->Unit without unchecked cast + } + val quickLoad = { + val toast = ToastPopup("Quickloading...", this) + thread(name = "SaveGame") { + try { + val loadedGame = GameSaver.loadGameByName("QuickSave") + Gdx.app.postRunnable { + toast.close() + UncivGame.Current.loadGame(loadedGame) + ToastPopup("Quickload successful.", this) + } + } catch (ex: Exception) { + Gdx.app.postRunnable { + ToastPopup("Could not load game!", this) + } + } + } + Unit // change type to ()->Unit + } + // Space and N are assigned in createNextTurnButton keyPressDispatcher[Input.Keys.F1] = { game.setScreen(CivilopediaScreen(gameInfo.ruleSet)) } + keyPressDispatcher['E'] = { game.setScreen(EmpireOverviewScreen(selectedCiv)) } // Empire overview last used page + /* + * These try to be faithful to default Civ5 key bindings as found in several places online + * Some are a little arbitrary, e.g. Economic info, Military info + * Some are very much so as Unciv *is* Strategic View and the Notification log is always visible + */ + keyPressDispatcher[Input.Keys.F2] = { game.setScreen(EmpireOverviewScreen(selectedCiv, "Trades")) } // Economic info + keyPressDispatcher[Input.Keys.F3] = { game.setScreen(EmpireOverviewScreen(selectedCiv, "Units")) } // Military info + keyPressDispatcher[Input.Keys.F4] = { game.setScreen(EmpireOverviewScreen(selectedCiv, "Diplomacy")) } // Diplomacy info + keyPressDispatcher[Input.Keys.F5] = { game.setScreen(PolicyPickerScreen(this, selectedCiv)) } // Social Policies Screen + keyPressDispatcher[Input.Keys.F6] = { game.setScreen(TechPickerScreen(viewingCiv)) } // Tech Screen + keyPressDispatcher[Input.Keys.F7] = { game.setScreen(EmpireOverviewScreen(selectedCiv, "Cities")) } // originally Notification Log + keyPressDispatcher[Input.Keys.F8] = { game.setScreen(VictoryScreen(this)) } // Victory Progress + keyPressDispatcher[Input.Keys.F9] = { game.setScreen(EmpireOverviewScreen(selectedCiv, "Stats")) } // Demographics + keyPressDispatcher[Input.Keys.F10] = { game.setScreen(EmpireOverviewScreen(selectedCiv, "Resources")) } // originally Strategic View + keyPressDispatcher[Input.Keys.F11] = quickSave // Quick Save + keyPressDispatcher[Input.Keys.F12] = quickLoad // Quick Load + keyPressDispatcher[Input.Keys.HOME] = { mapHolder.setCenterPosition(gameInfo.currentPlayerCiv.getCapital().location) } // Capital City View + keyPressDispatcher['\u0012'] = { minimapWrapper.toggleButtonState(MinimapToggleButtons.RESOURCES) } // Ctrl-R: Show Resources Icons + keyPressDispatcher['\u0019'] = { minimapWrapper.toggleButtonState(MinimapToggleButtons.YIELD) } // Ctrl-Y: Yield Icons + keyPressDispatcher['\u000F'] = { OptionsPopup(this).open() } // Ctrl-O: Game Options + keyPressDispatcher['\u0013'] = { game.setScreen(SaveGameScreen(gameInfo)) } // Ctrl-S: Save + keyPressDispatcher['\u000C'] = { game.setScreen(LoadGameScreen(this)) } // Ctrl-L: Load + keyPressDispatcher['+'] = { this.mapHolder.zoomIn() } // '+' Zoom - Input.Keys.NUMPAD_ADD would need dispatcher patch + keyPressDispatcher['-'] = { this.mapHolder.zoomOut() } // '-' Zoom keyPressDispatcher.setCheckpoint() } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt b/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt index ace87d7ed1..36878c5ba0 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt @@ -1,5 +1,7 @@ package com.unciv.ui.worldscreen +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.Input import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Group @@ -144,9 +146,12 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() { return menuButton } - private fun getOverviewButton(): TextButton { - val overviewButton = "Overview".toTextButton() - overviewButton.labelCell.pad(10f) + private fun getOverviewButton(): Button { + val overviewButton = Button(CameraStageBaseScreen.skin) + overviewButton.add("Overview".toLabel()).pad(10f) + if (Gdx.app.input.isPeripheralAvailable(Input.Peripheral.HardwareKeyboard)) { + overviewButton.add("(E)".toLabel(Color.WHITE)) + } overviewButton.pack() overviewButton.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) } overviewButton.centerY(this)