diff --git a/core/src/com/unciv/ui/components/tilegroups/CityButton.kt b/core/src/com/unciv/ui/components/tilegroups/CityButton.kt index 134054e9bd..9c128f1d4d 100644 --- a/core/src/com/unciv/ui/components/tilegroups/CityButton.kt +++ b/core/src/com/unciv/ui/components/tilegroups/CityButton.kt @@ -12,19 +12,19 @@ import com.badlogic.gdx.utils.Align import com.unciv.GUI import com.unciv.logic.battle.CityCombatant import com.unciv.logic.city.City -import com.unciv.models.ruleset.INonPerpetualConstruction -import com.unciv.models.ruleset.PerpetualConstruction import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.models.TutorialTrigger +import com.unciv.models.ruleset.INonPerpetualConstruction +import com.unciv.models.ruleset.PerpetualConstruction import com.unciv.ui.components.BorderedTable import com.unciv.ui.components.Fonts import com.unciv.ui.components.extensions.center import com.unciv.ui.components.extensions.centerX import com.unciv.ui.components.extensions.colorFromRGB import com.unciv.ui.components.extensions.darken -import com.unciv.ui.components.input.onClick import com.unciv.ui.components.extensions.toGroup import com.unciv.ui.components.extensions.toLabel +import com.unciv.ui.components.input.onClick import com.unciv.ui.images.ImageGetter import com.unciv.ui.popups.Popup import com.unciv.ui.screens.basescreen.BaseScreen @@ -594,7 +594,7 @@ class CityButton(val city: City, private val tileGroup: TileGroup): Table(BaseSc addOKButton("Diplomacy") { openDiplomacy() } add().expandX() addCloseButton() { - GUI.getWorldScreen().run { nextTurnButton.update(this@run) } + GUI.getWorldScreen().run { nextTurnButton.update() } } } popup.open() diff --git a/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt index f61ee9925c..dd76f0a6e2 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/WorldScreen.kt @@ -118,7 +118,7 @@ class WorldScreen( internal val minimapWrapper = MinimapHolder(mapHolder) private val bottomTileInfoTable = TileInfoTable(viewingCiv) internal val notificationsScroll = NotificationsScroll(this) - internal val nextTurnButton = NextTurnButton() + internal val nextTurnButton = NextTurnButton(this) private val statusButtons = StatusButtons(nextTurnButton) private val tutorialTaskTable = Table().apply { background = skinStrings.getUiBackground("WorldScreen/TutorialTaskTable", tintColor = skinStrings.skinConfig.baseColor.darken(0.5f)) @@ -251,9 +251,7 @@ class WorldScreen( game.pushScreen(CityScreen(capital)) } globalShortcuts.add(KeyboardBinding.Options) { // Game Options - this.openOptionsPopup(onClose = { - nextTurnButton.update(this) - }) + openOptionsPopup { nextTurnButton.update() } } globalShortcuts.add(KeyboardBinding.SaveGame) { game.pushScreen(SaveGameScreen(gameInfo)) } // Save globalShortcuts.add(KeyboardBinding.LoadGame) { game.pushScreen(LoadGameScreen()) } // Load @@ -679,7 +677,7 @@ class WorldScreen( } private fun updateGameplayButtons() { - nextTurnButton.update(this) + nextTurnButton.update() updateMultiplayerStatusButton() diff --git a/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnAction.kt b/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnAction.kt new file mode 100644 index 0000000000..d42ed349a1 --- /dev/null +++ b/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnAction.kt @@ -0,0 +1,185 @@ +package com.unciv.ui.screens.worldscreen.status + +import com.badlogic.gdx.graphics.Color +import com.unciv.Constants +import com.unciv.logic.civilization.managers.ReligionManager +import com.unciv.logic.civilization.managers.ReligionState +import com.unciv.models.Counter +import com.unciv.models.ruleset.BeliefType +import com.unciv.ui.components.extensions.disable +import com.unciv.ui.components.extensions.enable +import com.unciv.ui.popups.ConfirmPopup +import com.unciv.ui.screens.cityscreen.CityScreen +import com.unciv.ui.screens.pickerscreens.PantheonPickerScreen +import com.unciv.ui.screens.pickerscreens.PolicyPickerScreen +import com.unciv.ui.screens.pickerscreens.ReligiousBeliefsPickerScreen +import com.unciv.ui.screens.pickerscreens.TechPickerScreen +import com.unciv.ui.screens.worldscreen.WorldScreen +import com.unciv.utils.Concurrency +import com.unciv.utils.launchOnGLThread + +enum class NextTurnAction(protected val text: String, val color: Color) { + Default("", Color.BLACK) { + override val icon get() = null + override fun isChoice(worldScreen: WorldScreen) = false + }, + Working(Constants.working, Color.GRAY) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.isNextTurnUpdateRunning() + }, + Waiting("Waiting for other players...",Color.GRAY) { + override fun getText(worldScreen: WorldScreen) = + if (worldScreen.gameInfo.gameParameters.isOnlineMultiplayer) + "Waiting for [${worldScreen.gameInfo.currentPlayerCiv}]..." + else text + override fun isChoice(worldScreen: WorldScreen) = + !worldScreen.isPlayersTurn + }, + PickConstruction("Pick construction", Color.CORAL) { + override fun isChoice(worldScreen: WorldScreen) = + getCityWithNoProductionSet(worldScreen) != null + override fun action(worldScreen: WorldScreen) { + val city = getCityWithNoProductionSet(worldScreen) ?: return + worldScreen.game.pushScreen(CityScreen(city)) + } + }, + PickTech("Pick a tech", Color.SKY) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.shouldOpenTechPicker() + override fun action(worldScreen: WorldScreen) = + worldScreen.game.pushScreen( + TechPickerScreen(worldScreen.viewingCiv, null, worldScreen.viewingCiv.tech.freeTechs != 0) + ) + }, + PickPolicy("Pick a policy", Color.VIOLET) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.policies.shouldOpenPolicyPicker + override fun action(worldScreen: WorldScreen) { + worldScreen.game.pushScreen(PolicyPickerScreen(worldScreen.selectedCiv, worldScreen.canChangeState)) + worldScreen.viewingCiv.policies.shouldOpenPolicyPicker = false + } + }, + FoundPantheon("Found Pantheon", Color.valueOf(BeliefType.Pantheon.color)) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.religionManager.run { + religionState != ReligionState.Pantheon && canFoundOrExpandPantheon() + } + override fun action(worldScreen: WorldScreen) = + worldScreen.game.pushScreen(PantheonPickerScreen(worldScreen.viewingCiv)) + }, + ExpandPantheon("Expand Pantheon", Color.valueOf(BeliefType.Pantheon.color)) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.religionManager.run { + religionState == ReligionState.Pantheon && canFoundOrExpandPantheon() + } + override fun action(worldScreen: WorldScreen) = + worldScreen.game.pushScreen(PantheonPickerScreen(worldScreen.viewingCiv)) + }, + FoundReligion("Found Religion", Color.valueOf(BeliefType.Founder.color)) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.religionManager.religionState == ReligionState.FoundingReligion + override fun action(worldScreen: WorldScreen) = + openReligionPicker(worldScreen, true) { getBeliefsToChooseAtFounding() } + }, + EnhanceReligion("Enhance a Religion", Color.valueOf(BeliefType.Enhancer.color)) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.religionManager.religionState == ReligionState.EnhancingReligion + override fun action(worldScreen: WorldScreen) = + openReligionPicker(worldScreen, false) { getBeliefsToChooseAtEnhancing() } + }, + ReformReligion("Reform Religion", Color.valueOf(BeliefType.Enhancer.color)) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.religionManager.hasFreeBeliefs() + override fun action(worldScreen: WorldScreen) = + openReligionPicker(worldScreen, false) { freeBeliefsAsEnums() } + }, + WorldCongressVote("Vote for World Leader", Color.MAROON) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.mayVoteForDiplomaticVictory() + }, + NextUnit("Next unit", Color.LIGHT_GRAY) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.viewingCiv.units.shouldGoToDueUnit() + override fun action(worldScreen: WorldScreen) = + worldScreen.switchToNextUnit() + }, + MoveAutomatedUnits("Move automated units", Color.LIGHT_GRAY) { + override fun isChoice(worldScreen: WorldScreen) = + worldScreen.isMoveAutomatedUnits() + override fun action(worldScreen: WorldScreen) = + moveAutomatedUnits(worldScreen) + }, + NextTurn("Next turn", Color.WHITE) { + override fun isChoice(worldScreen: WorldScreen) = + true // When none of the others is active.. + override fun action(worldScreen: WorldScreen) = + worldScreen.confirmedNextTurn() + }, + + ; + open val icon: String? get() = "NotificationIcons/$name" + open fun getText(worldScreen: WorldScreen) = text + abstract fun isChoice(worldScreen: WorldScreen): Boolean + open fun action(worldScreen: WorldScreen) {} + + companion object { + // Readability helpers to allow concise enum instances + private fun getCityWithNoProductionSet(worldScreen: WorldScreen) = + worldScreen.viewingCiv.cities + .firstOrNull { + !it.isPuppet && it.cityConstructions.currentConstructionFromQueue.isEmpty() + } + + private fun openReligionPicker( + worldScreen: WorldScreen, + pickIconAndName: Boolean, + getBeliefs: ReligionManager.() -> Counter + ) = + worldScreen.game.pushScreen( + ReligiousBeliefsPickerScreen( + worldScreen.viewingCiv, + worldScreen.viewingCiv.religionManager.getBeliefs(), + pickIconAndName = pickIconAndName + ) + ) + + private fun WorldScreen.isMoveAutomatedUnits(): Boolean { + if (game.settings.automatedUnitsMoveOnTurnStart || viewingCiv.hasMovedAutomatedUnits) + return false + return viewingCiv.units.getCivUnits() + .any { + it.currentMovement > Constants.minimumMovementEpsilon + && (it.isMoving() || it.isAutomated() || it.isExploring()) + } + } + + private fun moveAutomatedUnits(worldScreen: WorldScreen) { + // Don't allow double-click of 'n' to spawn 2 processes trying to automate units + if (!worldScreen.isPlayersTurn) return + + worldScreen.isPlayersTurn = false // Disable state changes + worldScreen.viewingCiv.hasMovedAutomatedUnits = true + worldScreen.nextTurnButton.disable() + Concurrency.run("Move automated units") { + for (unit in worldScreen.viewingCiv.units.getCivUnits()) + unit.doAction() + launchOnGLThread { + worldScreen.shouldUpdate = true + worldScreen.isPlayersTurn = true //Re-enable state changes + worldScreen.nextTurnButton.enable() + } + } + } + + private fun WorldScreen.confirmedNextTurn() { + fun action() { + game.settings.addCompletedTutorialTask("Pass a turn") + nextTurn() + } + if (game.settings.confirmNextTurn) { + ConfirmPopup(this, "Confirm next turn", "Next turn", + true, action = ::action).open() + } else action() + } + } +} diff --git a/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnButton.kt b/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnButton.kt index de3e5dbfea..d0551fa164 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnButton.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnButton.kt @@ -1,13 +1,7 @@ package com.unciv.ui.screens.worldscreen.status -import com.badlogic.gdx.graphics.Color -import com.unciv.Constants -import com.unciv.logic.civilization.managers.ReligionState -import com.unciv.models.ruleset.BeliefType import com.unciv.models.translations.tr import com.unciv.ui.components.UncivTooltip.Companion.addTooltip -import com.unciv.ui.components.extensions.disable -import com.unciv.ui.components.extensions.enable import com.unciv.ui.components.extensions.isEnabled import com.unciv.ui.components.extensions.setSize import com.unciv.ui.components.input.KeyboardBinding @@ -15,32 +9,25 @@ import com.unciv.ui.components.input.keyShortcuts import com.unciv.ui.components.input.onActivation import com.unciv.ui.images.IconTextButton import com.unciv.ui.images.ImageGetter -import com.unciv.ui.popups.ConfirmPopup import com.unciv.ui.popups.hasOpenPopups -import com.unciv.ui.screens.cityscreen.CityScreen -import com.unciv.ui.screens.pickerscreens.DiplomaticVotePickerScreen -import com.unciv.ui.screens.pickerscreens.PantheonPickerScreen -import com.unciv.ui.screens.pickerscreens.PolicyPickerScreen -import com.unciv.ui.screens.pickerscreens.ReligiousBeliefsPickerScreen -import com.unciv.ui.screens.pickerscreens.TechPickerScreen import com.unciv.ui.screens.worldscreen.WorldScreen -import com.unciv.utils.Concurrency -import com.unciv.utils.launchOnGLThread -class NextTurnButton : IconTextButton("", null, 30) { +class NextTurnButton( + private val worldScreen: WorldScreen +) : IconTextButton("", null, 30) { private var nextTurnAction = NextTurnAction.Default init { // label.setFontSize(30) labelCell.pad(10f) - onActivation { nextTurnAction.action() } + onActivation { nextTurnAction.action(worldScreen) } keyShortcuts.add(KeyboardBinding.NextTurn) keyShortcuts.add(KeyboardBinding.NextTurnAlternate) // Let unit actions override this for command "Wait". keyShortcuts.add(KeyboardBinding.Wait, -99) } - fun update(worldScreen: WorldScreen) { + fun update() { nextTurnAction = getNextTurnAction(worldScreen) updateButton(nextTurnAction) @@ -49,156 +36,18 @@ class NextTurnButton : IconTextButton("", null, 30) { if (isEnabled) addTooltip(KeyboardBinding.NextTurn) else addTooltip("") } + internal fun updateButton(nextTurnAction: NextTurnAction) { - label.setText(nextTurnAction.text.tr()) + label.setText(nextTurnAction.getText(worldScreen).tr()) label.color = nextTurnAction.color - if (nextTurnAction.icon != null && ImageGetter.imageExists(nextTurnAction.icon)) + if (nextTurnAction.icon != null && ImageGetter.imageExists(nextTurnAction.icon!!)) iconCell.setActor(ImageGetter.getImage(nextTurnAction.icon).apply { setSize(30f) }) else iconCell.clearActor() pack() } - - private fun getNextTurnAction(worldScreen: WorldScreen): NextTurnAction { - return when { - worldScreen.isNextTurnUpdateRunning() -> - NextTurnAction.Working - !worldScreen.isPlayersTurn && worldScreen.gameInfo.gameParameters.isOnlineMultiplayer -> - NextTurnAction("Waiting for [${worldScreen.gameInfo.currentPlayerCiv}]...", Color.GRAY, - "NotificationIcons/Waiting") {} - !worldScreen.isPlayersTurn && !worldScreen.gameInfo.gameParameters.isOnlineMultiplayer -> - NextTurnAction.Waiting - - worldScreen.viewingCiv.cities.any { - !it.isPuppet && - it.cityConstructions.currentConstructionFromQueue == "" - } -> - NextTurnAction("Pick construction", Color.CORAL, - "NotificationIcons/PickConstruction") { - val cityWithNoProductionSet = worldScreen.viewingCiv.cities - .firstOrNull { - !it.isPuppet && - it.cityConstructions.currentConstructionFromQueue == "" - } - if (cityWithNoProductionSet != null) worldScreen.game.pushScreen( - CityScreen(cityWithNoProductionSet) - ) - } - - worldScreen.viewingCiv.shouldOpenTechPicker() -> - NextTurnAction("Pick a tech", Color.SKY, "NotificationIcons/PickTech") { - worldScreen.game.pushScreen( - TechPickerScreen(worldScreen.viewingCiv, null, worldScreen.viewingCiv.tech.freeTechs != 0) - ) - } - - worldScreen.viewingCiv.policies.shouldOpenPolicyPicker - || worldScreen.viewingCiv.policies.freePolicies > 0 && worldScreen.viewingCiv.policies.canAdoptPolicy() -> - NextTurnAction("Pick a policy", Color.VIOLET, "NotificationIcons/PickPolicy") { - worldScreen.game.pushScreen(PolicyPickerScreen(worldScreen.selectedCiv, worldScreen.canChangeState)) - worldScreen.viewingCiv.policies.shouldOpenPolicyPicker = false - } - - worldScreen.viewingCiv.religionManager.canFoundOrExpandPantheon() -> { - val displayString = if (worldScreen.viewingCiv.religionManager.religionState == ReligionState.Pantheon) - "Expand Pantheon" - else "Found Pantheon" - NextTurnAction(displayString, Color.valueOf(BeliefType.Pantheon.color), - "NotificationIcons/FoundPantheon") { - worldScreen.game.pushScreen(PantheonPickerScreen(worldScreen.viewingCiv)) - } - } - - worldScreen.viewingCiv.religionManager.religionState == ReligionState.FoundingReligion -> - NextTurnAction("Found Religion", Color.valueOf(BeliefType.Founder.color), - "NotificationIcons/FoundReligion") { - worldScreen.game.pushScreen( - ReligiousBeliefsPickerScreen( - worldScreen.viewingCiv, - worldScreen.viewingCiv.religionManager.getBeliefsToChooseAtFounding(), - pickIconAndName = true - ) - ) - } - - worldScreen.viewingCiv.religionManager.religionState == ReligionState.EnhancingReligion -> - NextTurnAction("Enhance a Religion", Color.valueOf(BeliefType.Enhancer.color), - "NotificationIcons/EnhanceReligion") { - worldScreen.game.pushScreen( - ReligiousBeliefsPickerScreen( - worldScreen.viewingCiv, - worldScreen.viewingCiv.religionManager.getBeliefsToChooseAtEnhancing(), - pickIconAndName = false - ) - ) - } - - worldScreen.viewingCiv.religionManager.hasFreeBeliefs() -> - NextTurnAction("Reform Religion", Color.valueOf(BeliefType.Enhancer.color), - "NotificationIcons/ReformReligion") { - worldScreen.game.pushScreen( - ReligiousBeliefsPickerScreen( - worldScreen.viewingCiv, - worldScreen.viewingCiv.religionManager.freeBeliefsAsEnums(), - pickIconAndName = false - ) - ) - } - - worldScreen.viewingCiv.mayVoteForDiplomaticVictory() -> - NextTurnAction("Vote for World Leader", Color.MAROON, - "NotificationIcons/WorldCongressVote") { - worldScreen.game.pushScreen(DiplomaticVotePickerScreen(worldScreen.viewingCiv)) - } - - worldScreen.viewingCiv.units.shouldGoToDueUnit() -> - NextTurnAction("Next unit", Color.LIGHT_GRAY, - "NotificationIcons/NextUnit") { worldScreen.switchToNextUnit() } - - !worldScreen.game.settings.automatedUnitsMoveOnTurnStart - && !worldScreen.viewingCiv.hasMovedAutomatedUnits - && worldScreen.viewingCiv.units.getCivUnits() - .any { it.currentMovement > Constants.minimumMovementEpsilon && (it.isMoving() || it.isAutomated() || it.isExploring()) } -> - NextTurnAction("Move automated units", Color.LIGHT_GRAY, - "NotificationIcons/MoveAutomatedUnits") { - // Don't allow double-click of 'n' to spawn 2 processes trying to automate units - if (!worldScreen.isPlayersTurn) return@NextTurnAction - - worldScreen.isPlayersTurn = false // Disable state changes - worldScreen.viewingCiv.hasMovedAutomatedUnits = true - worldScreen.nextTurnButton.disable() - Concurrency.run("Move automated units") { - for (unit in worldScreen.viewingCiv.units.getCivUnits()) - unit.doAction() - launchOnGLThread { - worldScreen.shouldUpdate = true - worldScreen.isPlayersTurn = true //Re-enable state changes - worldScreen.nextTurnButton.enable() - } - } - } - - else -> - NextTurnAction("Next turn", Color.WHITE, - "NotificationIcons/NextTurn") { - val action = { - worldScreen.game.settings.addCompletedTutorialTask("Pass a turn") - worldScreen.nextTurn() - } - if (worldScreen.game.settings.confirmNextTurn) { - ConfirmPopup(worldScreen, "Confirm next turn", "Next turn", - true, action = action).open() - } else action() - } - } - } -} - -class NextTurnAction(val text: String, val color: Color, val icon: String? = null, val action: () -> Unit) { - companion object Prefabs { - val Default = NextTurnAction("", Color.BLACK) {} - val Working = NextTurnAction(Constants.working, Color.GRAY, "NotificationIcons/Working") {} - val Waiting = NextTurnAction("Waiting for other players...",Color.GRAY, "NotificationIcons/Waiting") {} - } + private fun getNextTurnAction(worldScreen: WorldScreen) = + // Guaranteed to return a non-null NextTurnAction because the last isChoice always returns true + NextTurnAction.values().first { it.isChoice(worldScreen) } } diff --git a/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnProgress.kt b/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnProgress.kt index 01a141c9a8..470ba5acdc 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnProgress.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/status/NextTurnProgress.kt @@ -10,7 +10,6 @@ import com.unciv.ui.images.ImageGetter import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.utils.Concurrency -import com.unciv.utils.Log class NextTurnProgress( // nullable so we can free the reference once the ProgressBar is shown