From 9008d242a3f7e07d5859f7d1d9adfa884f94d26d Mon Sep 17 00:00:00 2001 From: Timo T Date: Sat, 11 Jun 2022 21:14:44 +0200 Subject: [PATCH] Refactor: Change UncivGame.worldScreen and UncivGame.gameInfo to be of nullable type (#7114) * Refactor: Make Popups work on Stages instead of BaseScreens * Refactor: Change UncivGame.worldScreen and UncivGame.gameInfo to be of nullable type * Fix "Resume" game loading not fully handling exceptions * Fix one missed refactoring * Refactor: remove useless postRunnable --- android/src/com/unciv/app/AndroidLauncher.kt | 4 +- core/src/com/unciv/MainMenuScreen.kt | 2 +- core/src/com/unciv/UncivGame.kt | 101 +++++++++++------- core/src/com/unciv/logic/battle/Battle.kt | 4 +- .../com/unciv/logic/city/CityConstructions.kt | 4 +- .../logic/city/CityInfoConquestFunctions.kt | 6 +- .../logic/civilization/CivilizationInfo.kt | 7 +- .../unciv/logic/civilization/Notification.kt | 2 +- .../unciv/logic/civilization/QuestManager.kt | 4 +- .../logic/multiplayer/OnlineMultiplayer.kt | 5 +- core/src/com/unciv/logic/trade/TradeOffer.kt | 14 +-- core/src/com/unciv/models/ruleset/Belief.kt | 5 +- core/src/com/unciv/models/ruleset/Nation.kt | 9 +- .../unciv/models/ruleset/tech/Technology.kt | 14 +-- .../models/ruleset/tile/TileImprovement.kt | 6 +- .../com/unciv/models/simulation/Simulation.kt | 4 +- .../unciv/models/translations/Translations.kt | 32 +++--- core/src/com/unciv/ui/LanguagePickerScreen.kt | 1 - core/src/com/unciv/ui/audio/SoundPlayer.kt | 9 +- .../ui/cityscreen/CityConstructionsTable.kt | 3 +- .../com/unciv/ui/cityscreen/CityInfoTable.kt | 14 +-- .../ui/cityscreen/CityReligionInfoTable.kt | 2 +- .../src/com/unciv/ui/cityscreen/CityScreen.kt | 6 +- .../ui/cityscreen/CityScreenTileTable.kt | 13 ++- .../cityscreen/SpecialistAllocationTable.kt | 4 +- .../unciv/ui/civilopedia/CivilopediaScreen.kt | 8 +- .../unciv/ui/civilopedia/CivilopediaText.kt | 11 +- .../com/unciv/ui/crashhandling/CrashScreen.kt | 10 +- .../unciv/ui/mapeditor/MapEditorLoadTab.kt | 4 +- .../unciv/ui/mapeditor/MapEditorModsTab.kt | 2 +- .../unciv/ui/mapeditor/MapEditorSaveTab.kt | 4 +- .../com/unciv/ui/mapeditor/MapEditorScreen.kt | 4 +- .../unciv/ui/multiplayer/EditFriendScreen.kt | 4 +- .../EditMultiplayerGameInfoScreen.kt | 8 +- .../unciv/ui/newgamescreen/NewGameScreen.kt | 13 +-- .../ui/newgamescreen/PlayerPickerTable.kt | 4 +- core/src/com/unciv/ui/options/AdvancedTab.kt | 25 ++--- core/src/com/unciv/ui/options/DebugTab.kt | 62 ++++++----- core/src/com/unciv/ui/options/DisplayTab.kt | 10 +- core/src/com/unciv/ui/options/GameplayTab.kt | 13 +-- core/src/com/unciv/ui/options/LanguageTab.kt | 3 +- .../com/unciv/ui/options/MultiplayerTab.kt | 6 +- core/src/com/unciv/ui/options/OptionsPopup.kt | 15 +-- core/src/com/unciv/ui/options/SoundTab.kt | 32 +++--- .../overviewscreen/DiplomacyOverviewTable.kt | 1 - .../ui/overviewscreen/StatsOverviewTable.kt | 2 +- .../ui/overviewscreen/UnitOverviewTable.kt | 5 +- .../ui/overviewscreen/WonderOverviewTable.kt | 7 +- .../pickerscreens/ImprovementPickerScreen.kt | 1 - .../ui/pickerscreens/ModManagementScreen.kt | 2 - .../com/unciv/ui/pickerscreens/PickerPane.kt | 2 +- .../unciv/ui/pickerscreens/PickerScreen.kt | 1 - .../ui/pickerscreens/PolicyPickerScreen.kt | 1 - .../ui/pickerscreens/PromotionPickerScreen.kt | 4 +- .../ReligionPickerScreenCommon.kt | 1 - .../ui/pickerscreens/TechPickerScreen.kt | 4 +- core/src/com/unciv/ui/popup/Popup.kt | 52 +++++---- core/src/com/unciv/ui/popup/ToastPopup.kt | 8 +- core/src/com/unciv/ui/popup/YesNoPopup.kt | 31 +++--- core/src/com/unciv/ui/saves/QuickSave.kt | 14 +++ core/src/com/unciv/ui/saves/SaveGameScreen.kt | 2 +- .../com/unciv/ui/tilegroups/TileGroupIcons.kt | 2 +- .../src/com/unciv/ui/trade/DiplomacyScreen.kt | 31 +++--- .../com/unciv/ui/trade/OffersListScroll.kt | 2 +- core/src/com/unciv/ui/utils/BaseScreen.kt | 12 ++- core/src/com/unciv/ui/utils/TabbedPager.kt | 4 +- .../ui/utils/extensions/Scene2dExtensions.kt | 3 + .../unciv/ui/worldscreen/PlayerReadyScreen.kt | 15 ++- .../unciv/ui/worldscreen/WorldMapHolder.kt | 2 +- .../com/unciv/ui/worldscreen/WorldScreen.kt | 23 ++-- .../ui/worldscreen/bottombar/TileInfoTable.kt | 2 +- .../minimap/MapOverlayToggleButton.kt | 2 +- .../unciv/ui/worldscreen/unit/UnitActions.kt | 10 +- .../ui/worldscreen/unit/UnitActionsTable.kt | 2 +- .../com/unciv/app/desktop/DesktopLauncher.kt | 12 ++- 75 files changed, 427 insertions(+), 346 deletions(-) diff --git a/android/src/com/unciv/app/AndroidLauncher.kt b/android/src/com/unciv/app/AndroidLauncher.kt index 8bf4f1cbb6..0b848a79e3 100644 --- a/android/src/com/unciv/app/AndroidLauncher.kt +++ b/android/src/com/unciv/app/AndroidLauncher.kt @@ -70,13 +70,13 @@ open class AndroidLauncher : AndroidApplication() { override fun onPause() { if (UncivGame.isCurrentInitialized() - && UncivGame.Current.isGameInfoInitialized() + && UncivGame.Current.gameInfo != null && UncivGame.Current.settings.multiplayer.turnCheckerEnabled && UncivGame.Current.gameSaver.getMultiplayerSaves().any() ) { MultiplayerTurnCheckWorker.startTurnChecker( applicationContext, UncivGame.Current.gameSaver, - UncivGame.Current.gameInfo, UncivGame.Current.settings.multiplayer + UncivGame.Current.gameInfo!!, UncivGame.Current.settings.multiplayer ) } super.onPause() diff --git a/core/src/com/unciv/MainMenuScreen.kt b/core/src/com/unciv/MainMenuScreen.kt index cc1695f1a3..703348b20b 100644 --- a/core/src/com/unciv/MainMenuScreen.kt +++ b/core/src/com/unciv/MainMenuScreen.kt @@ -198,7 +198,7 @@ class MainMenuScreen: BaseScreen() { private fun resumeGame() { - val curWorldScreen = game.getWorldScreenOrNull() + val curWorldScreen = game.worldScreen if (curWorldScreen != null) { game.resetToWorldScreen() curWorldScreen.popups.filterIsInstance(WorldScreenMenuPopup::class.java).forEach(Popup::close) diff --git a/core/src/com/unciv/UncivGame.kt b/core/src/com/unciv/UncivGame.kt index fe0c9ed3d2..864322018b 100644 --- a/core/src/com/unciv/UncivGame.kt +++ b/core/src/com/unciv/UncivGame.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.Application import com.badlogic.gdx.Game import com.badlogic.gdx.Gdx import com.badlogic.gdx.Input +import com.badlogic.gdx.Screen import com.badlogic.gdx.scenes.scene2d.actions.Actions import com.badlogic.gdx.utils.Align import com.unciv.logic.GameInfo @@ -22,13 +23,13 @@ import com.unciv.ui.audio.SoundPlayer import com.unciv.ui.crashhandling.closeExecutors import com.unciv.ui.crashhandling.launchCrashHandling import com.unciv.ui.crashhandling.postCrashHandlingRunnable +import com.unciv.ui.crashhandling.wrapCrashHandlingUnit import com.unciv.ui.images.ImageGetter import com.unciv.ui.multiplayer.LoadDeepLinkScreen import com.unciv.ui.multiplayer.MultiplayerHelpers import com.unciv.ui.popup.Popup import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.extensions.center -import com.unciv.ui.crashhandling.wrapCrashHandlingUnit import com.unciv.ui.worldscreen.PlayerReadyScreen import com.unciv.ui.worldscreen.WorldScreen import com.unciv.utils.debug @@ -49,8 +50,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() { private val audioExceptionHelper = parameters.audioExceptionHelper var deepLinkedMultiplayerGame: String? = null - lateinit var gameInfo: GameInfo - fun isGameInfoInitialized() = this::gameInfo.isInitialized + var gameInfo: GameInfo? = null lateinit var settings: GameSettings lateinit var musicController: MusicController lateinit var onlineMultiplayer: OnlineMultiplayer @@ -70,9 +70,8 @@ class UncivGame(parameters: UncivGameParameters) : Game() { */ var simulateUntilTurnForDebug: Int = 0 - lateinit var worldScreen: WorldScreen + var worldScreen: WorldScreen? = null private set - fun getWorldScreenOrNull() = if (this::worldScreen.isInitialized) worldScreen else null var isInitialized = false @@ -105,7 +104,7 @@ class UncivGame(parameters: UncivGameParameters) : Game() { * - Font (hence Fonts.resetFont() inside setSkin()) */ settings = gameSaver.getGeneralSettings() // needed for the screen - screen = LoadingScreen() // NOT dependent on any atlas or skin + setScreen(LoadingScreen()) // NOT dependent on any atlas or skin GameSounds.init() musicController = MusicController() // early, but at this point does only copy volume from settings audioExceptionHelper?.installHooks( @@ -152,49 +151,66 @@ class UncivGame(parameters: UncivGameParameters) : Game() { } } - fun loadGame(gameInfo: GameInfo) { + fun loadGame(gameInfo: GameInfo): WorldScreen { this.gameInfo = gameInfo ImageGetter.setNewRuleset(gameInfo.ruleSet) // Clone the mod list and add the base ruleset to it val fullModList = gameInfo.gameParameters.getModsAndBaseRuleset() musicController.setModList(fullModList) Gdx.input.inputProcessor = null // Since we will set the world screen when we're ready, - if (gameInfo.civilizations.count { it.playerType == PlayerType.Human } > 1 && !gameInfo.gameParameters.isOnlineMultiplayer) - setScreen(PlayerReadyScreen(gameInfo, gameInfo.getPlayerToViewAs())) + val worldScreen = WorldScreen(gameInfo, gameInfo.getPlayerToViewAs()) + val newScreen = if (gameInfo.civilizations.count { it.playerType == PlayerType.Human } > 1 && !gameInfo.gameParameters.isOnlineMultiplayer) + PlayerReadyScreen(worldScreen) else { - resetToWorldScreen(WorldScreen(gameInfo, gameInfo.getPlayerToViewAs())) + worldScreen } - } - - fun setScreen(screen: BaseScreen) { - val oldScreen = getScreen() - Gdx.input.inputProcessor = screen.stage - super.setScreen(screen) - if (oldScreen != getWorldScreenOrNull()) oldScreen.dispose() + setScreen(newScreen) + return worldScreen } /** - * If called with null [newWorldScreen], disposes of the current screen and sets it to the current stored world screen. - * If the current screen is already the world screen, the only thing that happens is that the world screen updates. + * Sets the screen of the game and automatically disposes the old screen as long as it isn't the world screen. + * + * @param screen must be a subclass of [BaseScreen]. */ - fun resetToWorldScreen(newWorldScreen: WorldScreen? = null) { - if (newWorldScreen != null) { - debug("Reset to new WorldScreen, gameId: %s, turn: %s, curCiv: %s", - newWorldScreen.gameInfo.gameId, newWorldScreen.gameInfo.turns, newWorldScreen.gameInfo.currentPlayer) - val oldWorldScreen = getWorldScreenOrNull() - worldScreen = newWorldScreen - // setScreen disposes the current screen, but the old world screen is not the current screen, so need to dispose here - if (screen != oldWorldScreen) { - oldWorldScreen?.dispose() - } + override fun setScreen(screen: Screen) { + if (screen !is BaseScreen) throw IllegalArgumentException("Call to setScreen with screen that does not inherit BaseScreen: " + screen.javaClass.simpleName) + setScreen(screen) + } + + override fun getScreen(): BaseScreen? { + val curScreen = super.getScreen() + return if (curScreen == null) { null } else { curScreen as BaseScreen } + } + + /** Sets the screen of the game and automatically [disposes][Screen.dispose] the old screen as long as it isn't the world screen. */ + fun setScreen(newScreen: BaseScreen) { + if (newScreen is WorldScreen) { + debug( + "Setting new world screen: gameId: %s, turn: %s, curCiv: %s", + newScreen.gameInfo.gameId, newScreen.gameInfo.turns, newScreen.gameInfo.currentPlayer + ) + if (newScreen != worldScreen) worldScreen?.dispose() + worldScreen = newScreen + newScreen.shouldUpdate = true + Gdx.graphics.requestRendering() } else { - val oldWorldScreen = getWorldScreenOrNull()!! - debug("Reset to old WorldScreen, gameId: %s, turn: %s, curCiv: %s", - oldWorldScreen.gameInfo.gameId, oldWorldScreen.gameInfo.turns, oldWorldScreen.gameInfo.currentPlayer) + debug("Setting new screen: %s", newScreen) } - setScreen(worldScreen) - worldScreen.shouldUpdate = true // This can set the screen to the policy picker or tech picker screen, so the input processor must come before - Gdx.graphics.requestRendering() + + val oldScreen = screen + Gdx.input.inputProcessor = newScreen.stage + super.setScreen(newScreen) // This can set the screen to the policy picker or tech picker screen, so the input processor must be set before + if (oldScreen !is WorldScreen) { // we want to keep the world screen around, because it's expensive to re-create it + oldScreen?.dispose() + } + } + + /** + * Resets the game to the stored world screen and automatically [disposes][Screen.dispose] the old screen. + */ + fun resetToWorldScreen() { + setScreen(worldScreen!!) } private fun tryLoadDeepLinkedGame() = launchCrashHandling("LoadDeepLinkedGame") { @@ -233,7 +249,8 @@ class UncivGame(parameters: UncivGameParameters) : Game() { } override fun pause() { - if (isGameInfoInitialized()) gameSaver.autoSave(this.gameInfo) + val curGameInfo = gameInfo + if (curGameInfo != null) gameSaver.autoSave(curGameInfo) musicController.pause() super.pause() } @@ -252,14 +269,15 @@ class UncivGame(parameters: UncivGameParameters) : Game() { if (::musicController.isInitialized) musicController.gracefulShutdown() // Do allow fade-out closeExecutors() - if (isGameInfoInitialized()) { + val curGameInfo = gameInfo + if (curGameInfo != null) { val autoSaveJob = gameSaver.autoSaveJob if (autoSaveJob != null && autoSaveJob.isActive) { // auto save is already in progress (e.g. started by onPause() event) // let's allow it to finish and do not try to autosave second time autoSaveJob.join() } else { - gameSaver.autoSaveSingleThreaded(gameInfo) // NO new thread + gameSaver.autoSaveSingleThreaded(curGameInfo) // NO new thread } } settings.save() @@ -277,10 +295,15 @@ class UncivGame(parameters: UncivGameParameters) : Game() { } } + /** Returns the [worldScreen] if it is the currently active screen of the game */ + fun getWorldScreenIfActive(): WorldScreen? { + return if (screen == worldScreen) worldScreen else null + } + companion object { lateinit var Current: UncivGame fun isCurrentInitialized() = this::Current.isInitialized - fun isCurrentGame(gameId: String): Boolean = isCurrentInitialized() && Current.isGameInfoInitialized() && Current.gameInfo.gameId == gameId + fun isCurrentGame(gameId: String): Boolean = isCurrentInitialized() && Current.gameInfo != null && Current.gameInfo!!.gameId == gameId fun isDeepLinkedGameLoading() = isCurrentInitialized() && Current.deepLinkedMultiplayerGame != null } } diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index e9a1b137ca..f803dcafb4 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -208,7 +208,7 @@ object Battle { // CS friendship from killing barbarians if (defeatedUnit.matchesCategory("Barbarian") && defeatedUnit.matchesCategory("Military") && civUnit.getCivInfo().isMajorCiv()) { - for (cityState in UncivGame.Current.gameInfo.getAliveCityStates()) { + for (cityState in UncivGame.Current.gameInfo!!.getAliveCityStates()) { if (civUnit.getCivInfo().knows(cityState) && defeatedUnit.unit.threatensCiv(cityState)) { cityState.cityStateFunctions.threateningBarbarianKilledBy(civUnit.getCivInfo()) } @@ -216,7 +216,7 @@ object Battle { } // CS war with major pseudo-quest - for (cityState in UncivGame.Current.gameInfo.getAliveCityStates()) { + for (cityState in UncivGame.Current.gameInfo!!.getAliveCityStates()) { cityState.questManager.militaryUnitKilledBy(civUnit.getCivInfo(), defeatedUnit.getCivInfo()) } } diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 963dc44b4f..9f88c92ced 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -663,8 +663,8 @@ class CityConstructions { cityInfo.cityStats.update() cityInfo.civInfo.updateDetailedCivResources() // If bought the worldscreen will not have been marked to update, and the new improvement won't show until later... - if (UncivGame.isCurrentInitialized()) - UncivGame.Current.worldScreen.shouldUpdate = true + if (UncivGame.isCurrentInitialized() && UncivGame.Current.worldScreen != null) + UncivGame.Current.worldScreen!!.shouldUpdate = true } /** Support for [UniqueType.CreatesOneImprovement]: diff --git a/core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt b/core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt index 63578ab9f5..7a6f127850 100644 --- a/core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt +++ b/core/src/com/unciv/logic/city/CityInfoConquestFunctions.kt @@ -155,8 +155,10 @@ class CityInfoConquestFunctions(val city: CityInfo){ city.isPuppet = false city.cityConstructions.inProgressConstructions.clear() // undo all progress of the previous civ on units etc. city.cityStats.update() - if (!UncivGame.Current.consoleMode) - UncivGame.Current.worldScreen.shouldUpdate = true + val worldScreen = UncivGame.Current.worldScreen + if (!UncivGame.Current.consoleMode && worldScreen != null) { + worldScreen.shouldUpdate = true + } } private fun diplomaticRepercussionsForConqueringCity(oldCiv: CivilizationInfo, conqueringCiv: CivilizationInfo) { diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 4884424823..cdee21094a 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -62,8 +62,11 @@ class CivilizationInfo { /** Returns an instance of WorkerAutomation valid for the duration of the current turn * This instance carries cached data common for all Workers of this civ */ fun getWorkerAutomation(): WorkerAutomation { - val currentTurn = if (UncivGame.Current.isInitialized && UncivGame.Current.isGameInfoInitialized()) - UncivGame.Current.gameInfo.turns else 0 + val currentTurn = if (UncivGame.Current.isInitialized && UncivGame.Current.gameInfo != null) { + UncivGame.Current.gameInfo!!.turns + } else { + 0 + } if (workerAutomationCache == null || workerAutomationCache!!.cachedForTurn != currentTurn) workerAutomationCache = WorkerAutomation(this, currentTurn) return workerAutomationCache!! diff --git a/core/src/com/unciv/logic/civilization/Notification.kt b/core/src/com/unciv/logic/civilization/Notification.kt index 5db2bdf40a..60acfedd0b 100644 --- a/core/src/com/unciv/logic/civilization/Notification.kt +++ b/core/src/com/unciv/logic/civilization/Notification.kt @@ -56,7 +56,7 @@ interface NotificationAction { } /** A notification action that cycles through tiles. - * + * * Constructors accept any kind of [Vector2] collection, including [Iterable], [Sequence], `vararg`. * `varargs` allows nulls which are ignored, a resulting empty list is allowed and equivalent to no [NotificationAction]. */ diff --git a/core/src/com/unciv/logic/civilization/QuestManager.kt b/core/src/com/unciv/logic/civilization/QuestManager.kt index d79b84eabf..6e832afdb1 100644 --- a/core/src/com/unciv/logic/civilization/QuestManager.kt +++ b/core/src/com/unciv/logic/civilization/QuestManager.kt @@ -919,11 +919,11 @@ class AssignedQuest(val questName: String = "", when (questName) { QuestName.ClearBarbarianCamp.value -> { game.resetToWorldScreen() - game.worldScreen.mapHolder.setCenterPosition(Vector2(data1.toFloat(), data2.toFloat()), selectUnit = false) + game.worldScreen!!.mapHolder.setCenterPosition(Vector2(data1.toFloat(), data2.toFloat()), selectUnit = false) } QuestName.Route.value -> { game.resetToWorldScreen() - game.worldScreen.mapHolder.setCenterPosition(gameInfo.getCivilization(assigner).getCapital()!!.location, selectUnit = false) + game.worldScreen!!.mapHolder.setCenterPosition(gameInfo.getCivilization(assigner).getCapital()!!.location, selectUnit = false) } } } diff --git a/core/src/com/unciv/logic/multiplayer/OnlineMultiplayer.kt b/core/src/com/unciv/logic/multiplayer/OnlineMultiplayer.kt index 5fb70daf59..2af8fa71f8 100644 --- a/core/src/com/unciv/logic/multiplayer/OnlineMultiplayer.kt +++ b/core/src/com/unciv/logic/multiplayer/OnlineMultiplayer.kt @@ -67,8 +67,9 @@ class OnlineMultiplayer { } private fun getCurrentGame(): OnlineMultiplayerGame? { - if (UncivGame.isCurrentInitialized() && UncivGame.Current.isGameInfoInitialized()) { - return getGameByGameId(UncivGame.Current.gameInfo.gameId) + val gameInfo = UncivGame.Current.gameInfo + if (gameInfo != null) { + return getGameByGameId(gameInfo.gameId) } else { return null } diff --git a/core/src/com/unciv/logic/trade/TradeOffer.kt b/core/src/com/unciv/logic/trade/TradeOffer.kt index 8f908fa37e..9909c4cafc 100644 --- a/core/src/com/unciv/logic/trade/TradeOffer.kt +++ b/core/src/com/unciv/logic/trade/TradeOffer.kt @@ -10,10 +10,10 @@ import com.unciv.logic.trade.TradeType.TradeTypeNumberType data class TradeOffer(val name: String, val type: TradeType, var amount: Int = 1, var duration: Int) { constructor( - name: String, - type: TradeType, - amount: Int = 1, - gameSpeed: GameSpeed = UncivGame.Current.gameInfo.gameParameters.gameSpeed + name: String, + type: TradeType, + amount: Int = 1, + gameSpeed: GameSpeed = UncivGame.Current.gameInfo!!.gameParameters.gameSpeed ) : this(name, type, amount, duration = -1) { duration = when { type.isImmediate -> -1 // -1 for offers that are immediate (e.g. gold transfer) @@ -22,7 +22,7 @@ data class TradeOffer(val name: String, val type: TradeType, var amount: Int = 1 else -> (30 * gameSpeed.modifier).toInt() } } - + constructor() : this("", TradeType.Gold, duration = -1) // so that the json deserializer can work @Suppress("CovariantEquals") // This is an overload, not an override of the built-in equals(Any?) @@ -36,13 +36,13 @@ data class TradeOffer(val name: String, val type: TradeType, var amount: Int = 1 var offerText = when(type){ TradeType.WarDeclaration -> "Declare war on [$name]" TradeType.Introduction -> "Introduction to [$name]" - TradeType.City -> UncivGame.Current.gameInfo.getCities().firstOrNull{ it.id == name }?.name ?: "Non-existent city" + TradeType.City -> UncivGame.Current.gameInfo!!.getCities().firstOrNull{ it.id == name }?.name ?: "Non-existent city" else -> name }.tr() if (type.numberType == TradeTypeNumberType.Simple || name == Constants.researchAgreement) offerText += " ($amount)" else if (type.numberType == TradeTypeNumberType.Gold) offerText += " ($amount)" - + if (duration > 0) offerText += "\n" + duration + Fonts.turn if (untradable == 1) { diff --git a/core/src/com/unciv/models/ruleset/Belief.kt b/core/src/com/unciv/models/ruleset/Belief.kt index 1b71077a79..e4c31a0a86 100644 --- a/core/src/com/unciv/models/ruleset/Belief.kt +++ b/core/src/com/unciv/models/ruleset/Belief.kt @@ -47,8 +47,9 @@ class Belief() : RulesetObject() { // private but potentially reusable, therefore not folded into getCivilopediaTextMatching private fun getBeliefsMatching(name: String, ruleset: Ruleset): Sequence { if (!UncivGame.isCurrentInitialized()) return sequenceOf() - if (!UncivGame.Current.isGameInfoInitialized()) return sequenceOf() - if (!UncivGame.Current.gameInfo.isReligionEnabled()) return sequenceOf() + val gameInfo = UncivGame.Current.gameInfo + if (gameInfo == null) return sequenceOf() + if (!gameInfo.isReligionEnabled()) return sequenceOf() return ruleset.beliefs.asSequence().map { it.value } .filter { belief -> belief.uniqueObjects.any { unique -> unique.params.any { it == name } } } diff --git a/core/src/com/unciv/models/ruleset/Nation.kt b/core/src/com/unciv/models/ruleset/Nation.kt index c1a10e4d10..90b241a689 100644 --- a/core/src/com/unciv/models/ruleset/Nation.kt +++ b/core/src/com/unciv/models/ruleset/Nation.kt @@ -137,10 +137,11 @@ class Nation : RulesetObject() { textList += FormattedLine("{Type}: {$cityStateType}", header = 4, color = cityStateType!!.color) - val era = if (UncivGame.isCurrentInitialized() && UncivGame.Current.isGameInfoInitialized()) - UncivGame.Current.gameInfo.currentPlayerCiv.getEra() - else + val era = if (UncivGame.isCurrentInitialized() && UncivGame.Current.gameInfo != null) { + UncivGame.Current.gameInfo!!.currentPlayerCiv.getEra() + } else { ruleset.eras.values.first() + } var showResources = false val friendBonus = era.friendBonus[cityStateType!!.name] @@ -185,7 +186,7 @@ class Nation : RulesetObject() { when { building.uniqueTo != name -> continue building.hasUnique(UniqueType.HiddenFromCivilopedia) -> continue - UncivGame.Current.isGameInfoInitialized() && !UncivGame.Current.gameInfo.isReligionEnabled() && building.hasUnique(UniqueType.HiddenWithoutReligion) -> continue // This seems consistent with existing behaviour of CivilopediaScreen's init..shouldBeDisplayed(), and Technology().getEnabledUnits(). Otherwise there are broken links in the Civilopedia (E.G. to "Pyramid" and "Shrine", from "The Maya"). + UncivGame.Current.gameInfo != null && !UncivGame.Current.gameInfo!!.isReligionEnabled() && building.hasUnique(UniqueType.HiddenWithoutReligion) -> continue // This seems consistent with existing behaviour of CivilopediaScreen's init..shouldBeDisplayed(), and Technology().getEnabledUnits(). Otherwise there are broken links in the Civilopedia (E.G. to "Pyramid" and "Shrine", from "The Maya"). } yield(FormattedLine("{${building.name}} -", link=building.makeLink())) if (building.replaces != null && ruleset.buildings.containsKey(building.replaces!!)) { diff --git a/core/src/com/unciv/models/ruleset/tech/Technology.kt b/core/src/com/unciv/models/ruleset/tech/Technology.kt index 86127a69ba..5352d54422 100644 --- a/core/src/com/unciv/models/ruleset/tech/Technology.kt +++ b/core/src/com/unciv/models/ruleset/tech/Technology.kt @@ -51,7 +51,7 @@ class Technology: RulesetObject() { } } - val viewingCiv = UncivGame.Current.worldScreen.viewingCiv + val viewingCiv = UncivGame.Current.worldScreen!!.viewingCiv val enabledUnits = getEnabledUnits(ruleset, viewingCiv) if (enabledUnits.any()) { lineList += "{Units enabled}: " @@ -158,11 +158,11 @@ class Technology: RulesetObject() { ruleset.tileImprovements.values .asSequence() .filter { improvement -> - improvement.uniqueObjects.any { - unique -> unique.conditionals.any { - (it.isOfType(UniqueType.ConditionalTech) || it.isOfType(UniqueType.ConditionalNoTech)) - && it.params[0] == name - } + improvement.uniqueObjects.any { + unique -> unique.conditionals.any { + (it.isOfType(UniqueType.ConditionalTech) || it.isOfType(UniqueType.ConditionalNoTech)) + && it.params[0] == name + } } } @@ -225,7 +225,7 @@ class Technology: RulesetObject() { } } - val viewingCiv = UncivGame.Current.getWorldScreenOrNull()?.viewingCiv + val viewingCiv = UncivGame.Current.worldScreen?.viewingCiv val enabledUnits = getEnabledUnits(ruleset, viewingCiv) if (enabledUnits.any()) { lineList += FormattedLine() diff --git a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt index 1420e6303b..7ab86606a6 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt @@ -176,9 +176,9 @@ class TileImprovement : RulesetStatsObject() { if (isAncientRuinsEquivalent() && ruleset.ruinRewards.isNotEmpty()) { val difficulty: String val religionEnabled: Boolean - if (UncivGame.isCurrentInitialized() && UncivGame.Current.isGameInfoInitialized()) { - difficulty = UncivGame.Current.gameInfo.gameParameters.difficulty - religionEnabled = UncivGame.Current.gameInfo.isReligionEnabled() + if (UncivGame.isCurrentInitialized() && UncivGame.Current.gameInfo != null) { + difficulty = UncivGame.Current.gameInfo!!.gameParameters.difficulty + religionEnabled = UncivGame.Current.gameInfo!!.isReligionEnabled() } else { difficulty = "Prince" // most factors == 1 religionEnabled = true diff --git a/core/src/com/unciv/models/simulation/Simulation.kt b/core/src/com/unciv/models/simulation/Simulation.kt index f80f136137..5f91907a29 100644 --- a/core/src/com/unciv/models/simulation/Simulation.kt +++ b/core/src/com/unciv/models/simulation/Simulation.kt @@ -38,7 +38,7 @@ class Simulation( for (civ in civilizations) { this.winRate[civ] = MutableInt(0) winRateByVictory[civ] = mutableMapOf() - for (victory in UncivGame.Current.gameInfo.ruleSet.victories.keys) + for (victory in UncivGame.Current.gameInfo!!.ruleSet.victories.keys) winRateByVictory[civ]!![victory] = MutableInt(0) } } @@ -121,7 +121,7 @@ class Simulation( outString += "\n$civ:\n" val wins = winRate[civ]!!.value * 100 / max(steps.size, 1) outString += "$wins% total win rate \n" - for (victory in UncivGame.Current.gameInfo.ruleSet.victories.keys) { + for (victory in UncivGame.Current.gameInfo!!.ruleSet.victories.keys) { val winsVictory = winRateByVictory[civ]!![victory]!!.value * 100 / max(winRate[civ]!!.value, 1) outString += "$victory: $winsVictory% " } diff --git a/core/src/com/unciv/models/translations/Translations.kt b/core/src/com/unciv/models/translations/Translations.kt index db84142dcb..3499af9f68 100644 --- a/core/src/com/unciv/models/translations/Translations.kt +++ b/core/src/com/unciv/models/translations/Translations.kt @@ -250,22 +250,28 @@ object TranslationActiveModsCache { } private set - private fun getCurrentHash() = UncivGame.Current.run { - if (isGameInfoInitialized()) - gameInfo.gameParameters.mods.hashCode() + gameInfo.gameParameters.baseRuleset.hashCode() * 31 - else translations.translationActiveMods.hashCode() * 31 * 31 + private fun getCurrentHash(): Int { + val gameInfo = UncivGame.Current.gameInfo + return if (gameInfo != null) { + gameInfo.gameParameters.mods.hashCode() + gameInfo.gameParameters.baseRuleset.hashCode() * 31 + } else { + UncivGame.Current.translations.translationActiveMods.hashCode() * 31 * 31 } + } - private fun getCurrentSet() = UncivGame.Current.run { - if (isGameInfoInitialized()) { - val par = gameInfo.gameParameters - // This is equivalent to (par.mods + par.baseRuleset) without the cast down to `Set` - LinkedHashSet(par.mods.size + 1).apply { - addAll(par.mods) - add(par.baseRuleset) - } - } else translations.translationActiveMods + private fun getCurrentSet(): LinkedHashSet { + val gameInfo = UncivGame.Current.gameInfo + return if (gameInfo != null) { + val par = gameInfo.gameParameters + // This is equivalent to (par.mods + par.baseRuleset) without the cast down to `Set` + LinkedHashSet(par.mods.size + 1).apply { + addAll(par.mods) + add(par.baseRuleset) + } + } else { + UncivGame.Current.translations.translationActiveMods } + } } /** diff --git a/core/src/com/unciv/ui/LanguagePickerScreen.kt b/core/src/com/unciv/ui/LanguagePickerScreen.kt index 2c5cfa9ba4..5de755f75d 100644 --- a/core/src/com/unciv/ui/LanguagePickerScreen.kt +++ b/core/src/com/unciv/ui/LanguagePickerScreen.kt @@ -49,6 +49,5 @@ class LanguagePickerScreen : PickerScreen() { game.translations.tryReadTranslationForCurrentLanguage() game.setScreen(MainMenuScreen()) - dispose() } } diff --git a/core/src/com/unciv/ui/audio/SoundPlayer.kt b/core/src/com/unciv/ui/audio/SoundPlayer.kt index 92b41b0d38..cd533797fd 100644 --- a/core/src/com/unciv/ui/audio/SoundPlayer.kt +++ b/core/src/com/unciv/ui/audio/SoundPlayer.kt @@ -59,7 +59,8 @@ object SoundPlayer { val game = UncivGame.Current // Get a hash covering all mods - quickly, so don't map, cast or copy the Set types - val hash1 = if (game.isGameInfoInitialized()) game.gameInfo.ruleSet.mods.hashCode() else 0 + val gameInfo = game.gameInfo + val hash1 = if (gameInfo != null) gameInfo.ruleSet.mods.hashCode() else 0 val newHash = hash1.xor(game.settings.visualMods.hashCode()) // If hash the same, leave the cache as is @@ -91,8 +92,10 @@ object SoundPlayer { // audiovisual mods after game mods but before built-in sounds // (these can already be available when game.gameInfo is not) val modList: MutableSet = mutableSetOf() - if (game.isGameInfoInitialized()) - modList.addAll(game.gameInfo.ruleSet.mods) // Sounds from game mods + val gameInfo = game.gameInfo + if (gameInfo != null) { + modList.addAll(gameInfo.ruleSet.mods) // Sounds from game mods + } modList.addAll(game.settings.visualMods) // Translate the basic mod list into relative folder names so only sounds/name.ext needs diff --git a/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt b/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt index 57d273d2a8..66a4e070d9 100644 --- a/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityConstructionsTable.kt @@ -516,10 +516,9 @@ class CityConstructionsTable(private val cityScreen: CityScreen) { "Would you like to purchase [${construction.name}] for [$constructionBuyCost] [${stat.character}]?".tr() YesNoPopup( purchasePrompt, - action = { purchaseConstruction(construction, stat, tile) }, screen = cityScreen, restoreDefault = { cityScreen.update() } - ).open() + ) { purchaseConstruction(construction, stat, tile) }.open() } /** This tests whether the buy button should be _shown_ */ diff --git a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt index c3c95a02b8..d1cedb0f06 100644 --- a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt @@ -138,17 +138,17 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(BaseScreen.skin) cityScreen.closeAllPopups() YesNoPopup("Are you sure you want to sell this [${building.name}]?".tr(), - { - cityScreen.city.sellBuilding(building.name) + cityScreen, { cityScreen.update() - }, cityScreen, - { - cityScreen.update() - }).open() + } + ) { + cityScreen.city.sellBuilding(building.name) + cityScreen.update() + }.open() } if (cityScreen.city.hasSoldBuildingThisTurn && !cityScreen.city.civInfo.gameInfo.gameParameters.godMode || cityScreen.city.isPuppet - || !UncivGame.Current.worldScreen.isPlayersTurn || !cityScreen.canChangeState) + || !UncivGame.Current.worldScreen!!.isPlayersTurn || !cityScreen.canChangeState) sellBuildingButton.disable() } it.addSeparator() diff --git a/core/src/com/unciv/ui/cityscreen/CityReligionInfoTable.kt b/core/src/com/unciv/ui/cityscreen/CityReligionInfoTable.kt index 010b8eae29..72ecc8ad1e 100644 --- a/core/src/com/unciv/ui/cityscreen/CityReligionInfoTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityReligionInfoTable.kt @@ -81,7 +81,7 @@ class CityReligionInfoTable( icon.onClick { val newScreen = if (religion == iconName) EmpireOverviewScreen(civInfo, EmpireOverviewCategories.Religion.name, religion) - else CivilopediaScreen(gameInfo.ruleSet, UncivGame.Current.screen as BaseScreen, CivilopediaCategories.Belief, religion ) + else CivilopediaScreen(gameInfo.ruleSet, UncivGame.Current.screen!!, CivilopediaCategories.Belief, religion ) UncivGame.Current.setScreen(newScreen) } return icon diff --git a/core/src/com/unciv/ui/cityscreen/CityScreen.kt b/core/src/com/unciv/ui/cityscreen/CityScreen.kt index dc692557bc..b5f681fd8b 100644 --- a/core/src/com/unciv/ui/cityscreen/CityScreen.kt +++ b/core/src/com/unciv/ui/cityscreen/CityScreen.kt @@ -41,7 +41,7 @@ class CityScreen( } /** Toggles or adds/removes all state changing buttons */ - val canChangeState = UncivGame.Current.worldScreen.canChangeState + val canChangeState = UncivGame.Current.worldScreen!!.canChangeState /** Toggle between Constructions and cityInfo (buildings, specialists etc. */ var showConstructionsTable = true @@ -402,8 +402,8 @@ class CityScreen( fun exit() { game.resetToWorldScreen() - game.worldScreen.mapHolder.setCenterPosition(city.location) - game.worldScreen.bottomUnitTable.selectUnit() + game.worldScreen!!.mapHolder.setCenterPosition(city.location) + game.worldScreen!!.bottomUnitTable.selectUnit() } fun page(delta: Int) { diff --git a/core/src/com/unciv/ui/cityscreen/CityScreenTileTable.kt b/core/src/com/unciv/ui/cityscreen/CityScreenTileTable.kt index 53fe9946e3..bec64821fc 100644 --- a/core/src/com/unciv/ui/cityscreen/CityScreenTileTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityScreenTileTable.kt @@ -112,15 +112,14 @@ class CityScreenTileTable(private val cityScreen: CityScreen): Table() { "Would you like to purchase [Tile] for [$goldCostOfTile] [${Stat.Gold.character}]?".tr() YesNoPopup( purchasePrompt, - action = { - SoundPlayer.play(UncivSound.Coin) - city.expansion.buyTile(selectedTile) - // preselect the next tile on city screen rebuild so bulk buying can go faster - UncivGame.Current.setScreen(CityScreen(city, initSelectedTile = city.expansion.chooseNewTileToOwn())) - }, screen = cityScreen, restoreDefault = { cityScreen.update() } - ).open() + ) { + SoundPlayer.play(UncivSound.Coin) + city.expansion.buyTile(selectedTile) + // preselect the next tile on city screen rebuild so bulk buying can go faster + UncivGame.Current.setScreen(CityScreen(city, initSelectedTile = city.expansion.chooseNewTileToOwn())) + }.open() } /** This tests whether the buy button should be _shown_ */ diff --git a/core/src/com/unciv/ui/cityscreen/SpecialistAllocationTable.kt b/core/src/com/unciv/ui/cityscreen/SpecialistAllocationTable.kt index e6edb6195d..2a78c48add 100644 --- a/core/src/com/unciv/ui/cityscreen/SpecialistAllocationTable.kt +++ b/core/src/com/unciv/ui/cityscreen/SpecialistAllocationTable.kt @@ -81,7 +81,7 @@ class SpecialistAllocationTable(val cityScreen: CityScreen) : Table(BaseScreen.s cityInfo.cityStats.update() cityScreen.update() } - if (cityInfo.population.getFreePopulation() == 0 || !UncivGame.Current.worldScreen.isPlayersTurn) + if (cityInfo.population.getFreePopulation() == 0 || !UncivGame.Current.worldScreen!!.isPlayersTurn) assignButton.clear() return assignButton } @@ -98,7 +98,7 @@ class SpecialistAllocationTable(val cityScreen: CityScreen) : Table(BaseScreen.s } if (assignedSpecialists <= 0 || cityInfo.isPuppet) unassignButton.isVisible = false - if (!UncivGame.Current.worldScreen.isPlayersTurn) unassignButton.clear() + if (!UncivGame.Current.worldScreen!!.isPlayersTurn) unassignButton.clear() return unassignButton } diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt index 016cdb82b0..872e9cca66 100644 --- a/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaScreen.kt @@ -183,10 +183,9 @@ class CivilopediaScreen( val imageSize = 50f onBackButtonClicked { game.setScreen(previousScreen) } - val religionEnabled = if (game.isGameInfoInitialized()) game.gameInfo.isReligionEnabled() - else ruleset.beliefs.isNotEmpty() - val victoryTypes = if (game.isGameInfoInitialized()) game.gameInfo.gameParameters.victoryTypes - else listOf() + val curGameInfo = game.gameInfo + val religionEnabled = if (curGameInfo != null) curGameInfo.isReligionEnabled() else ruleset.beliefs.isNotEmpty() + val victoryTypes = if (curGameInfo != null) curGameInfo.gameParameters.victoryTypes else emptyList() fun shouldBeDisplayed(obj: IHasUniques): Boolean { return when { @@ -254,7 +253,6 @@ class CivilopediaScreen( val goToGameButton = Constants.close.toTextButton() goToGameButton.onClick { game.setScreen(previousScreen) - dispose() } val topTable = Table() diff --git a/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt b/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt index 828460cc31..fd3dab402e 100644 --- a/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt +++ b/core/src/com/unciv/ui/civilopedia/CivilopediaText.kt @@ -164,10 +164,13 @@ class FormattedLine ( } return "" } - private fun getCurrentRuleset() = when { - !UncivGame.isCurrentInitialized() -> Ruleset() - !UncivGame.Current.isGameInfoInitialized() -> RulesetCache[BaseRuleset.Civ_V_Vanilla.fullName]!! - else -> UncivGame.Current.gameInfo.ruleSet + private fun getCurrentRuleset(): Ruleset { + val gameInfo = UncivGame.Current.gameInfo + return when { + !UncivGame.isCurrentInitialized() -> Ruleset() + gameInfo == null -> RulesetCache[BaseRuleset.Civ_V_Vanilla.fullName]!! + else -> gameInfo.ruleSet + } } private fun initNamesCategoryMap(ruleSet: Ruleset): HashMap { //val startTime = System.nanoTime() diff --git a/core/src/com/unciv/ui/crashhandling/CrashScreen.kt b/core/src/com/unciv/ui/crashhandling/CrashScreen.kt index 49d883e88c..e28f83e437 100644 --- a/core/src/com/unciv/ui/crashhandling/CrashScreen.kt +++ b/core/src/com/unciv/ui/crashhandling/CrashScreen.kt @@ -47,7 +47,7 @@ class CrashScreen(val exception: Throwable): BaseScreen() { /** Qualified class name of the game screen that was active at the construction of this instance, or an error note. */ private val lastScreenType = try { - UncivGame.Current.screen::class.qualifiedName.toString() + UncivGame.Current.screen!!::class.qualifiedName.toString() } catch (e: Throwable) { "Could not get screen type: $e" } @@ -58,11 +58,11 @@ class CrashScreen(val exception: Throwable): BaseScreen() { /** @return The last active save game serialized as a compressed string if any, or an informational note otherwise. */ private fun tryGetSaveGame(): String { - if (!UncivGame.isCurrentInitialized() || !UncivGame.Current.isGameInfoInitialized()) + if (!UncivGame.isCurrentInitialized() || UncivGame.Current.gameInfo == null) return "" return "\n**Save Data:**\n
Show Saved Game\n\n```\n" + try { - GameSaver.gameInfoToString(UncivGame.Current.gameInfo, forceZip = true) + GameSaver.gameInfoToString(UncivGame.Current.gameInfo!!, forceZip = true) } catch (e: Throwable) { "No save data: $e" // In theory .toString() could still error here. } + "\n```\n
\n" @@ -70,11 +70,11 @@ class CrashScreen(val exception: Throwable): BaseScreen() { /** @return Mods from the last active save game if any, or an informational note otherwise. */ private fun tryGetSaveMods(): String { - if (!UncivGame.isCurrentInitialized() || !UncivGame.Current.isGameInfoInitialized()) + if (!UncivGame.isCurrentInitialized() || UncivGame.Current.gameInfo == null) return "" return "\n**Save Mods:**\n```\n" + try { // Also from old CrashController().buildReport(), also could still error at .toString(). - LinkedHashSet(UncivGame.Current.gameInfo.gameParameters.getModsAndBaseRuleset()).toString() + LinkedHashSet(UncivGame.Current.gameInfo!!.gameParameters.getModsAndBaseRuleset()).toString() } catch (e: Throwable) { "No mod data: $e" } + "\n```\n" diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorLoadTab.kt b/core/src/com/unciv/ui/mapeditor/MapEditorLoadTab.kt index 3bec0c5a9e..7826eb8ee8 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorLoadTab.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorLoadTab.kt @@ -62,10 +62,10 @@ class MapEditorLoadTab( private fun deleteHandler() { if (chosenMap == null) return - YesNoPopup("Are you sure you want to delete this map?", { + YesNoPopup("Are you sure you want to delete this map?", editorScreen) { chosenMap!!.delete() mapFiles.update() - }, editorScreen).open() + }.open() } override fun activated(index: Int, caption: String, pager: TabbedPager) { diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorModsTab.kt b/core/src/com/unciv/ui/mapeditor/MapEditorModsTab.kt index 3c352c4089..3e3e8584de 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorModsTab.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorModsTab.kt @@ -131,7 +131,7 @@ class MapEditorModsTab( add(inc.toLabel()).row() } add(ScrollPane(incompatibilityTable)).colspan(2) - .maxHeight(screen.stage.height * 0.8f).row() + .maxHeight(stage.height * 0.8f).row() addGoodSizedLabel("Change map to fit selected ruleset?", 24).colspan(2).row() addButtonInRow(Constants.yes, 'y') { onOK() diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorSaveTab.kt b/core/src/com/unciv/ui/mapeditor/MapEditorSaveTab.kt index af5df78892..9d4dbbb6fe 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorSaveTab.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorSaveTab.kt @@ -78,10 +78,10 @@ class MapEditorSaveTab( private fun deleteHandler() { if (chosenMap == null) return - YesNoPopup("Are you sure you want to delete this map?", { + YesNoPopup("Are you sure you want to delete this map?", editorScreen) { chosenMap!!.delete() mapFiles.update() - }, editorScreen).open() + }.open() } override fun activated(index: Int, caption: String, pager: TabbedPager) { diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt index ff02c10754..29c6e05653 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt @@ -191,9 +191,9 @@ class MapEditorScreen(map: TileMap? = null): BaseScreen() { fun askIfDirty(question: String, action: ()->Unit) { if (!isDirty) return action() - YesNoPopup(question, action, screen = this, restoreDefault = { + YesNoPopup(question, screen = this, restoreDefault = { keyPressDispatcher[KeyCharAndCode.BACK] = this::closeEditor - }).open() + }, action).open() } fun hideSelection() { diff --git a/core/src/com/unciv/ui/multiplayer/EditFriendScreen.kt b/core/src/com/unciv/ui/multiplayer/EditFriendScreen.kt index 4189f8e594..353a0a1c4e 100644 --- a/core/src/com/unciv/ui/multiplayer/EditFriendScreen.kt +++ b/core/src/com/unciv/ui/multiplayer/EditFriendScreen.kt @@ -40,11 +40,11 @@ class EditFriendScreen(selectedFriend: FriendList.Friend, backScreen: ViewFriend topTable.add(gameIDTable).padBottom(30f).row() deleteFriendButton.onClick { - val askPopup = YesNoPopup("Are you sure you want to delete this friend?", { + val askPopup = YesNoPopup("Are you sure you want to delete this friend?", this) { friendlist.delete(selectedFriend) backScreen.game.setScreen(backScreen) backScreen.refreshFriendsList() - }, this) + } askPopup.open() }.apply { color = Color.RED } topTable.add(deleteFriendButton) diff --git a/core/src/com/unciv/ui/multiplayer/EditMultiplayerGameInfoScreen.kt b/core/src/com/unciv/ui/multiplayer/EditMultiplayerGameInfoScreen.kt index 5e82930040..609b91aa0a 100644 --- a/core/src/com/unciv/ui/multiplayer/EditMultiplayerGameInfoScreen.kt +++ b/core/src/com/unciv/ui/multiplayer/EditMultiplayerGameInfoScreen.kt @@ -27,22 +27,22 @@ class EditMultiplayerGameInfoScreen(val multiplayerGame: OnlineMultiplayerGame, val deleteButton = "Delete save".toTextButton() deleteButton.onClick { - val askPopup = YesNoPopup("Are you sure you want to delete this map?", { + val askPopup = YesNoPopup("Are you sure you want to delete this map?", this) { try { game.onlineMultiplayer.deleteGame(multiplayerGame) game.setScreen(backScreen) } catch (ex: Exception) { ToastPopup("Could not delete game!", this) } - }, this) + } askPopup.open() }.apply { color = Color.RED } val giveUpButton = "Resign".toTextButton() giveUpButton.onClick { - val askPopup = YesNoPopup("Are you sure you want to resign?", { + val askPopup = YesNoPopup("Are you sure you want to resign?", this) { resign(multiplayerGame, backScreen) - }, this) + } askPopup.open() } giveUpButton.apply { color = Color.RED } diff --git a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt index 3922bc6247..8414d42b7d 100644 --- a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt +++ b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt @@ -36,6 +36,7 @@ import com.unciv.ui.utils.extensions.onClick import com.unciv.ui.utils.extensions.pad import com.unciv.ui.utils.extensions.toLabel import com.unciv.ui.utils.extensions.toTextButton +import com.unciv.utils.Log import java.net.URL import java.util.* import com.unciv.ui.utils.AutoScrollPane as ScrollPane @@ -74,9 +75,9 @@ class NewGameScreen( val resetToDefaultsButton = "Reset to defaults".toTextButton() rightSideGroup.addActorAt(0, resetToDefaultsButton) resetToDefaultsButton.onClick { - YesNoPopup("Are you sure you want to reset all game options to defaults?", { + YesNoPopup("Are you sure you want to reset all game options to defaults?", this) { game.setScreen(NewGameScreen(previousScreen, GameSetupInfo())) - }, this).open(true) + }.open(true) } } @@ -155,7 +156,7 @@ class NewGameScreen( val message = mapSize.fixUndesiredSizes(gameSetupInfo.mapParameters.worldWrap) if (message != null) { postCrashHandlingRunnable { - ToastPopup( message, UncivGame.Current.screen as BaseScreen, 4000 ) + ToastPopup( message, UncivGame.Current.screen!!, 4000 ) with (mapOptionsTable.generatedMapOptionsTable) { customMapSizeRadius.text = mapSize.radius.toString() customMapWidth.text = mapSize.width.toString() @@ -275,6 +276,7 @@ class NewGameScreen( rightSideButton.setText("Start game!".tr()) return } catch (ex: Exception) { + Log.error("Error while creating game", ex) postCrashHandlingRunnable { popup.reuseWith("Could not upload game!", true) } @@ -286,13 +288,12 @@ class NewGameScreen( } postCrashHandlingRunnable { - game.loadGame(newGame) - previousScreen.dispose() + val worldScreen = game.loadGame(newGame) if (newGame.gameParameters.isOnlineMultiplayer) { // Save gameId to clipboard because you have to do it anyway. Gdx.app.clipboard.contents = newGame.gameId // Popup to notify the User that the gameID got copied to the clipboard - ToastPopup("Game ID copied to clipboard!".tr(), game.worldScreen, 2500) + ToastPopup("Game ID copied to clipboard!".tr(), worldScreen, 2500) } } } diff --git a/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt b/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt index 84e6669639..80bff7ade7 100644 --- a/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt @@ -344,7 +344,7 @@ private class NationPickerPopup( private val ruleset = previousScreen.ruleset // This Popup's body has two halves of same size, either side by side or arranged vertically // depending on screen proportions - determine height for one of those - private val partHeight = screen.stage.height * (if (screen.isNarrowerThan4to3()) 0.45f else 0.8f) + private val partHeight = stage.height * (if (stage.isNarrowerThan4to3()) 0.45f else 0.8f) private val civBlocksWidth = playerPicker.civBlocksWidth private val nationListTable = Table() private val nationListScroll = ScrollPane(nationListTable) @@ -356,7 +356,7 @@ private class NationPickerPopup( nationListScroll.setOverscroll(false, false) add(nationListScroll).size( civBlocksWidth + 10f, partHeight ) // +10, because the nation table has a 5f pad, for a total of +10f - if (screen.isNarrowerThan4to3()) row() + if (stage.isNarrowerThan4to3()) row() nationDetailsScroll.setOverscroll(false, false) add(nationDetailsScroll).size(civBlocksWidth + 10f, partHeight) // Same here, see above diff --git a/core/src/com/unciv/ui/options/AdvancedTab.kt b/core/src/com/unciv/ui/options/AdvancedTab.kt index 9faab47836..2029e148b5 100644 --- a/core/src/com/unciv/ui/options/AdvancedTab.kt +++ b/core/src/com/unciv/ui/options/AdvancedTab.kt @@ -9,6 +9,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Cell import com.badlogic.gdx.scenes.scene2d.ui.SelectBox import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Array +import com.unciv.UncivGame import com.unciv.models.metadata.GameSettings import com.unciv.models.translations.TranslationFileWriter import com.unciv.models.translations.tr @@ -48,12 +49,12 @@ fun advancedTab( addMaxZoomSlider(this, settings) - val screen = optionsPopup.screen - if (screen.game.platformSpecificHelper != null && Gdx.app.type == Application.ApplicationType.Android) { + val helper = UncivGame.Current.platformSpecificHelper + if (helper != null && Gdx.app.type == Application.ApplicationType.Android) { optionsPopup.addCheckbox(this, "Enable portrait orientation", settings.allowAndroidPortrait) { settings.allowAndroidPortrait = it // Note the following might close the options screen indirectly and delayed - screen.game.platformSpecificHelper.allowPortrait(it) + helper.allowPortrait(it) } } @@ -61,7 +62,7 @@ fun advancedTab( addTranslationGeneration(this, optionsPopup) - addSetUserId(this, settings, screen) + addSetUserId(this, settings) } private fun addAutosaveTurnsSelectBox(table: Table, settings: GameSettings) { @@ -161,22 +162,18 @@ private fun addTranslationGeneration(table: Table, optionsPopup: OptionsPopup) { table.add(generateTranslationsButton).colspan(2).row() } -private fun addSetUserId(table: Table, settings: GameSettings, screen: BaseScreen) { +private fun addSetUserId(table: Table, settings: GameSettings) { val idSetLabel = "".toLabel() val takeUserIdFromClipboardButton = "Take user ID from clipboard".toTextButton() .onClick { try { val clipboardContents = Gdx.app.clipboard.contents.trim() UUID.fromString(clipboardContents) - YesNoPopup( - "Doing this will reset your current user ID to the clipboard contents - are you sure?", - { - settings.multiplayer.userId = clipboardContents - settings.save() - idSetLabel.setFontColor(Color.WHITE).setText("ID successfully set!".tr()) - }, - screen - ).open(true) + YesNoPopup("Doing this will reset your current user ID to the clipboard contents - are you sure?",table.stage) { + settings.multiplayer.userId = clipboardContents + settings.save() + idSetLabel.setFontColor(Color.WHITE).setText("ID successfully set!".tr()) + }.open(true) idSetLabel.isVisible = true } catch (ex: Exception) { idSetLabel.isVisible = true diff --git a/core/src/com/unciv/ui/options/DebugTab.kt b/core/src/com/unciv/ui/options/DebugTab.kt index 90faf49751..79f74870b0 100644 --- a/core/src/com/unciv/ui/options/DebugTab.kt +++ b/core/src/com/unciv/ui/options/DebugTab.kt @@ -19,22 +19,25 @@ fun debugTab() = Table(BaseScreen.skin).apply { defaults().pad(5f) val game = UncivGame.Current - val simulateButton = "Simulate until turn:".toTextButton() - val simulateTextField = TextField(game.simulateUntilTurnForDebug.toString(), BaseScreen.skin) - val invalidInputLabel = "This is not a valid integer!".toLabel().also { it.isVisible = false } - simulateButton.onClick { - val simulateUntilTurns = simulateTextField.text.toIntOrNull() - if (simulateUntilTurns == null) { - invalidInputLabel.isVisible = true - return@onClick + val worldScreen = game.worldScreen + if (worldScreen != null) { + val simulateButton = "Simulate until turn:".toTextButton() + val simulateTextField = TextField(game.simulateUntilTurnForDebug.toString(), BaseScreen.skin) + val invalidInputLabel = "This is not a valid integer!".toLabel().also { it.isVisible = false } + simulateButton.onClick { + val simulateUntilTurns = simulateTextField.text.toIntOrNull() + if (simulateUntilTurns == null) { + invalidInputLabel.isVisible = true + return@onClick + } + game.simulateUntilTurnForDebug = simulateUntilTurns + invalidInputLabel.isVisible = false + worldScreen.nextTurn() } - game.simulateUntilTurnForDebug = simulateUntilTurns - invalidInputLabel.isVisible = false - game.worldScreen.nextTurn() + add(simulateButton) + add(simulateTextField).row() + add(invalidInputLabel).colspan(2).row() } - add(simulateButton) - add(simulateTextField).row() - add(invalidInputLabel).colspan(2).row() add("Supercharged".toCheckBox(game.superchargedForDebug) { game.superchargedForDebug = it @@ -42,9 +45,10 @@ fun debugTab() = Table(BaseScreen.skin).apply { add("View entire map".toCheckBox(game.viewEntireMapForDebug) { game.viewEntireMapForDebug = it }).colspan(2).row() - if (game.isGameInfoInitialized()) { - add("God mode (current game)".toCheckBox(game.gameInfo.gameParameters.godMode) { - game.gameInfo.gameParameters.godMode = it + val curGameInfo = game.gameInfo + if (curGameInfo != null) { + add("God mode (current game)".toCheckBox(curGameInfo.gameParameters.godMode) { + curGameInfo.gameParameters.godMode = it }).colspan(2).row() } add("Save games compressed".toCheckBox(GameSaver.saveZipped) { @@ -73,25 +77,25 @@ fun debugTab() = Table(BaseScreen.skin).apply { val unlockTechsButton = "Unlock all techs".toTextButton() unlockTechsButton.onClick { - if (!game.isGameInfoInitialized()) + if (curGameInfo == null) return@onClick - for (tech in game.gameInfo.ruleSet.technologies.keys) { - if (tech !in game.gameInfo.getCurrentPlayerCivilization().tech.techsResearched) { - game.gameInfo.getCurrentPlayerCivilization().tech.addTechnology(tech) - game.gameInfo.getCurrentPlayerCivilization().popupAlerts.removeLastOrNull() + for (tech in curGameInfo.ruleSet.technologies.keys) { + if (tech !in curGameInfo.getCurrentPlayerCivilization().tech.techsResearched) { + curGameInfo.getCurrentPlayerCivilization().tech.addTechnology(tech) + curGameInfo.getCurrentPlayerCivilization().popupAlerts.removeLastOrNull() } } - game.gameInfo.getCurrentPlayerCivilization().updateSightAndResources() - game.worldScreen.shouldUpdate = true + curGameInfo.getCurrentPlayerCivilization().updateSightAndResources() + if (worldScreen != null) worldScreen.shouldUpdate = true } add(unlockTechsButton).colspan(2).row() val giveResourcesButton = "Get all strategic resources".toTextButton() giveResourcesButton.onClick { - if (!game.isGameInfoInitialized()) + if (curGameInfo == null) return@onClick - val ownedTiles = game.gameInfo.tileMap.values.asSequence().filter { it.getOwner() == game.gameInfo.getCurrentPlayerCivilization() } - val resourceTypes = game.gameInfo.ruleSet.tileResources.values.asSequence().filter { it.resourceType == ResourceType.Strategic } + val ownedTiles = curGameInfo.tileMap.values.asSequence().filter { it.getOwner() == curGameInfo.getCurrentPlayerCivilization() } + val resourceTypes = curGameInfo.ruleSet.tileResources.values.asSequence().filter { it.resourceType == ResourceType.Strategic } for ((tile, resource) in ownedTiles zip resourceTypes) { tile.resource = resource.name tile.resourceAmount = 999 @@ -99,8 +103,8 @@ fun debugTab() = Table(BaseScreen.skin).apply { // If this becomes a problem, check if such an improvement exists and otherwise plop down a great improvement or so tile.improvement = resource.getImprovements().first() } - game.gameInfo.getCurrentPlayerCivilization().updateSightAndResources() - game.worldScreen.shouldUpdate = true + curGameInfo.getCurrentPlayerCivilization().updateSightAndResources() + if (worldScreen != null) worldScreen.shouldUpdate = true } add(giveResourcesButton).colspan(2).row() } diff --git a/core/src/com/unciv/ui/options/DisplayTab.kt b/core/src/com/unciv/ui/options/DisplayTab.kt index 219f60b2b1..1fd6f730da 100644 --- a/core/src/com/unciv/ui/options/DisplayTab.kt +++ b/core/src/com/unciv/ui/options/DisplayTab.kt @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.ui.SelectBox import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Array +import com.unciv.UncivGame import com.unciv.models.metadata.GameSettings import com.unciv.models.tilesets.TileSetCache import com.unciv.models.translations.tr @@ -41,7 +42,7 @@ fun displayTab( optionsPopup.addCheckbox(this, "Experimental Demographics scoreboard", settings.useDemographics, true) { settings.useDemographics = it } optionsPopup.addCheckbox(this, "Show zoom buttons in world screen", settings.showZoomButtons, true) { settings.showZoomButtons = it } - addMinimapSizeSlider(this, settings, optionsPopup.screen, optionsPopup.selectBoxMinWidth) + addMinimapSizeSlider(this, settings, optionsPopup.selectBoxMinWidth) addResolutionSelectBox(this, settings, optionsPopup.selectBoxMinWidth, onResolutionChange) @@ -62,7 +63,7 @@ fun displayTab( } -private fun addMinimapSizeSlider(table: Table, settings: GameSettings, screen: BaseScreen, selectBoxMinWidth: Float) { +private fun addMinimapSizeSlider(table: Table, settings: GameSettings, selectBoxMinWidth: Float) { table.add("Show minimap".toLabel()).left().fillX() // The meaning of the values needs a formula to be synchronized between here and @@ -88,8 +89,9 @@ private fun addMinimapSizeSlider(table: Table, settings: GameSettings, screen: B settings.minimapSize = size } settings.save() - if (screen is WorldScreen) - screen.shouldUpdate = true + val worldScreen = UncivGame.Current.getWorldScreenIfActive() + if (worldScreen != null) + worldScreen.shouldUpdate = true } table.add(minimapSlider).minWidth(selectBoxMinWidth).pad(10f).row() } diff --git a/core/src/com/unciv/ui/options/GameplayTab.kt b/core/src/com/unciv/ui/options/GameplayTab.kt index fbd9c597d9..664cda750f 100644 --- a/core/src/com/unciv/ui/options/GameplayTab.kt +++ b/core/src/com/unciv/ui/options/GameplayTab.kt @@ -1,6 +1,7 @@ package com.unciv.ui.options import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.UncivGame import com.unciv.logic.civilization.PlayerType import com.unciv.ui.utils.BaseScreen import com.unciv.ui.worldscreen.WorldScreen @@ -12,16 +13,16 @@ fun gameplayTab( defaults().pad(5f) val settings = optionsPopup.settings - val screen = optionsPopup.screen optionsPopup.addCheckbox(this, "Check for idle units", settings.checkForDueUnits, true) { settings.checkForDueUnits = it } optionsPopup.addCheckbox(this, "Move units with a single tap", settings.singleTapMove) { settings.singleTapMove = it } - optionsPopup.addCheckbox(this, "Auto-assign city production", settings.autoAssignCityProduction, true) { - settings.autoAssignCityProduction = it - if (it && screen is WorldScreen && - screen.viewingCiv.isCurrentPlayer() && screen.viewingCiv.playerType == PlayerType.Human + optionsPopup.addCheckbox(this, "Auto-assign city production", settings.autoAssignCityProduction, true) { shouldAutoAssignCityProduction -> + settings.autoAssignCityProduction = shouldAutoAssignCityProduction + val worldScreen = UncivGame.Current.getWorldScreenIfActive() + if (shouldAutoAssignCityProduction && worldScreen != null && + worldScreen.viewingCiv.isCurrentPlayer() && worldScreen.viewingCiv.playerType == PlayerType.Human ) { - screen.gameInfo.currentPlayerCiv.cities.forEach { city -> + worldScreen.gameInfo.currentPlayerCiv.cities.forEach { city -> city.cityConstructions.chooseNextConstruction() } } diff --git a/core/src/com/unciv/ui/options/LanguageTab.kt b/core/src/com/unciv/ui/options/LanguageTab.kt index 6d43af1927..4cd66c0a1f 100644 --- a/core/src/com/unciv/ui/options/LanguageTab.kt +++ b/core/src/com/unciv/ui/options/LanguageTab.kt @@ -1,6 +1,7 @@ package com.unciv.ui.options import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.UncivGame import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.LanguageTable.Companion.addLanguageTables import com.unciv.ui.utils.extensions.onClick @@ -17,7 +18,7 @@ fun languageTab( fun selectLanguage() { settings.language = chosenLanguage settings.updateLocaleFromLanguage() - optionsPopup.screen.game.translations.tryReadTranslationForCurrentLanguage() + UncivGame.Current.translations.tryReadTranslationForCurrentLanguage() onLanguageSelected() } diff --git a/core/src/com/unciv/ui/options/MultiplayerTab.kt b/core/src/com/unciv/ui/options/MultiplayerTab.kt index 35cb06dda9..28e3e9604e 100644 --- a/core/src/com/unciv/ui/options/MultiplayerTab.kt +++ b/core/src/com/unciv/ui/options/MultiplayerTab.kt @@ -12,7 +12,6 @@ import com.unciv.models.metadata.GameSetting import com.unciv.models.metadata.GameSettings import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache -import com.unciv.models.translations.tr import com.unciv.ui.crashhandling.launchCrashHandling import com.unciv.ui.crashhandling.postCrashHandlingRunnable import com.unciv.ui.popup.Popup @@ -145,8 +144,7 @@ private fun addMultiplayerServerOptions( settings.save() } - val screen = optionsPopup.screen - serverIpTable.add(multiplayerServerTextField).minWidth(screen.stage.width / 2).growX() + serverIpTable.add(multiplayerServerTextField).minWidth(optionsPopup.stageToShowOn.width / 2).growX() tab.add(serverIpTable).colspan(2).fillX().row() tab.add("Reset to Dropbox".toTextButton().onClick { @@ -156,7 +154,7 @@ private fun addMultiplayerServerOptions( }).colspan(2).row() tab.add(connectionToServerButton.onClick { - val popup = Popup(screen).apply { + val popup = Popup(optionsPopup.stage).apply { addGoodSizedLabel("Awaiting response...").row() } popup.open(true) diff --git a/core/src/com/unciv/ui/options/OptionsPopup.kt b/core/src/com/unciv/ui/options/OptionsPopup.kt index 1bbe8c783a..565ded5ef4 100644 --- a/core/src/com/unciv/ui/options/OptionsPopup.kt +++ b/core/src/com/unciv/ui/options/OptionsPopup.kt @@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener import com.badlogic.gdx.utils.Array import com.unciv.MainMenuScreen +import com.unciv.UncivGame import com.unciv.logic.event.EventBus import com.unciv.models.UncivSound import com.unciv.models.metadata.BaseRuleset @@ -133,20 +134,20 @@ class OptionsPopup( /** Reload this Popup after major changes (resolution, tileset, language, font) */ private fun reloadWorldAndOptions() { settings.save() - if (screen is WorldScreen) { - screen.game.resetToWorldScreen(WorldScreen(screen.gameInfo, screen.viewingCiv)) - } else if (screen is MainMenuScreen) { - screen.game.setScreen(MainMenuScreen()) + val worldScreen = UncivGame.Current.getWorldScreenIfActive() + if (worldScreen != null) { + val newWorldScreen = WorldScreen(worldScreen.gameInfo, worldScreen.viewingCiv) + worldScreen.game.setScreen(newWorldScreen) + newWorldScreen.openOptionsPopup(tabs.activePage) } - (screen.game.screen as BaseScreen).openOptionsPopup(tabs.activePage) } fun addCheckbox(table: Table, text: String, initialState: Boolean, updateWorld: Boolean = false, action: ((Boolean) -> Unit)) { val checkbox = text.toCheckBox(initialState) { action(it) settings.save() - if (updateWorld && screen is WorldScreen) - screen.shouldUpdate = true + val worldScreen = UncivGame.Current.getWorldScreenIfActive() + if (updateWorld && worldScreen != null) worldScreen.shouldUpdate = true } table.add(checkbox).colspan(2).left().row() } diff --git a/core/src/com/unciv/ui/options/SoundTab.kt b/core/src/com/unciv/ui/options/SoundTab.kt index b2786fbc9f..de076413ef 100644 --- a/core/src/com/unciv/ui/options/SoundTab.kt +++ b/core/src/com/unciv/ui/options/SoundTab.kt @@ -2,9 +2,11 @@ package com.unciv.ui.options import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.UncivGame import com.unciv.models.UncivSound import com.unciv.models.metadata.GameSettings import com.unciv.models.translations.tr +import com.unciv.ui.audio.MusicController import com.unciv.ui.audio.MusicTrackChooserFlags import com.unciv.ui.crashhandling.launchCrashHandling import com.unciv.ui.crashhandling.postCrashHandlingRunnable @@ -24,17 +26,17 @@ fun soundTab( defaults().pad(5f) val settings = optionsPopup.settings - val screen = optionsPopup.screen + val music = UncivGame.Current.musicController addSoundEffectsVolumeSlider(this, settings) - if (screen.game.musicController.isMusicAvailable()) { - addMusicVolumeSlider(this, settings, screen) - addMusicPauseSlider(this, settings, screen) - addMusicCurrentlyPlaying(this, screen) + if (UncivGame.Current.musicController.isMusicAvailable()) { + addMusicVolumeSlider(this, settings, music) + addMusicPauseSlider(this, settings, music) + addMusicCurrentlyPlaying(this, music) } - if (!screen.game.musicController.isDefaultFileAvailable()) + if (!UncivGame.Current.musicController.isDefaultFileAvailable()) addDownloadMusic(this, optionsPopup) } @@ -52,11 +54,10 @@ private fun addDownloadMusic(table: Table, optionsPopup: OptionsPopup) { // So the whole game doesn't get stuck while downloading the file launchCrashHandling("MusicDownload") { try { - val screen = optionsPopup.screen - screen.game.musicController.downloadDefaultFile() + UncivGame.Current.musicController.downloadDefaultFile() postCrashHandlingRunnable { optionsPopup.tabs.replacePage("Sound", soundTab(optionsPopup)) - screen.game.musicController.chooseTrack(flags = MusicTrackChooserFlags.setPlayDefault) + UncivGame.Current.musicController.chooseTrack(flags = MusicTrackChooserFlags.setPlayDefault) } } catch (ex: Exception) { postCrashHandlingRunnable { @@ -83,7 +84,7 @@ private fun addSoundEffectsVolumeSlider(table: Table, settings: GameSettings) { table.add(soundEffectsVolumeSlider).pad(5f).row() } -private fun addMusicVolumeSlider(table: Table, settings: GameSettings, screen: BaseScreen) { +private fun addMusicVolumeSlider(table: Table, settings: GameSettings, music: MusicController) { table.add("Music volume".tr()).left().fillX() val musicVolumeSlider = UncivSlider( @@ -95,7 +96,6 @@ private fun addMusicVolumeSlider(table: Table, settings: GameSettings, screen: B settings.musicVolume = it settings.save() - val music = screen.game.musicController music.setVolume(it) if (!music.isPlaying()) music.chooseTrack(flags = MusicTrackChooserFlags.setPlayDefault) @@ -103,9 +103,7 @@ private fun addMusicVolumeSlider(table: Table, settings: GameSettings, screen: B table.add(musicVolumeSlider).pad(5f).row() } -private fun addMusicPauseSlider(table: Table, settings: GameSettings, screen: BaseScreen) { - val music = screen.game.musicController - +private fun addMusicPauseSlider(table: Table, settings: GameSettings, music: MusicController) { // map to/from 0-1-2..10-12-14..30-35-40..60-75-90-105-120 fun posToLength(pos: Float): Float = when (pos) { in 0f..10f -> pos @@ -141,16 +139,16 @@ private fun addMusicPauseSlider(table: Table, settings: GameSettings, screen: Ba table.add(pauseLengthSlider).pad(5f).row() } -private fun addMusicCurrentlyPlaying(table: Table, screen: BaseScreen) { +private fun addMusicCurrentlyPlaying(table: Table, music: MusicController) { val label = WrappableLabel("", table.width - 10f, Color(-0x2f5001), 16) label.wrap = true table.add(label).padTop(20f).colspan(2).fillX().row() - screen.game.musicController.onChange { + music.onChange { postCrashHandlingRunnable { label.setText("Currently playing: [$it]".tr()) } } label.onClick(UncivSound.Silent) { - screen.game.musicController.chooseTrack(flags = MusicTrackChooserFlags.none) + music.chooseTrack(flags = MusicTrackChooserFlags.none) } } diff --git a/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt index 5d03bd05f9..e53d57034c 100644 --- a/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/DiplomacyOverviewTable.kt @@ -127,7 +127,6 @@ class DiplomacyOverviewTab ( table.touchable = Touchable.enabled table.onClick { if (civInfo.isDefeated() || viewingPlayer.isSpectator() || civInfo == viewingPlayer) return@onClick - overviewScreen.dispose() UncivGame.Current.setScreen(DiplomacyScreen(viewingPlayer, civInfo)) } return table diff --git a/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt index 7bf0273319..e734ceb4f4 100644 --- a/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/StatsOverviewTable.kt @@ -138,7 +138,7 @@ class StatsOverviewTab( for (city in viewingPlayer.cities) { city.cityStats.update() } update() } - slider.isDisabled = !UncivGame.Current.worldScreen.canChangeState + slider.isDisabled = !UncivGame.Current.worldScreen!!.canChangeState sliderTable.add(slider).padTop(15f) add(sliderTable).colspan(2) diff --git a/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt index c064f2f7f8..dd8738a11f 100644 --- a/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/UnitOverviewTable.kt @@ -78,7 +78,7 @@ class UnitOverviewTab( private fun showWorldScreenAt(position: Vector2, unit: MapUnit?) { val game = overviewScreen.game game.resetToWorldScreen() - game.worldScreen.mapHolder.setCenterPosition(position, forceSelectUnit = unit) + game.worldScreen!!.mapHolder.setCenterPosition(position, forceSelectUnit = unit) } private fun showWorldScreenAt(unit: MapUnit) = showWorldScreenAt(unit.currentTile.position, unit) private fun showWorldScreenAt(tile: TileInfo) = showWorldScreenAt(tile.position, null) @@ -190,7 +190,7 @@ class UnitOverviewTab( if (unit.promotions.canBePromoted()) promotionsTable.add( ImageGetter.getImage("OtherIcons/Star").apply { - color = if (game.worldScreen.canChangeState && unit.currentMovement > 0f && unit.attacksThisTurn == 0) + color = if (game.worldScreen!!.canChangeState && unit.currentMovement > 0f && unit.attacksThisTurn == 0) Color.GOLDENROD else Color.GOLDENROD.darken(0.25f) } @@ -198,7 +198,6 @@ class UnitOverviewTab( promotionsTable.onClick { if (unit.promotions.canBePromoted() || unit.promotions.promotions.isNotEmpty()) { game.setScreen(PromotionPickerScreen(unit)) - overviewScreen.dispose() } } add(promotionsTable) diff --git a/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt index e44462fc11..36f3087ad4 100644 --- a/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/WonderOverviewTable.kt @@ -238,8 +238,11 @@ class WonderOverviewTab( val locationLabel = locationText.toLabel() if (wonder.location != null) locationLabel.onClick{ - UncivGame.Current.resetToWorldScreen() - UncivGame.Current.worldScreen.mapHolder.setCenterPosition(wonder.location.position) + val worldScreen = UncivGame.Current.worldScreen + if (worldScreen != null) { + UncivGame.Current.resetToWorldScreen() + worldScreen.mapHolder.setCenterPosition(wonder.location.position) + } } add(locationLabel).fillY() } diff --git a/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt index 03c1f6a930..c81fe953e1 100644 --- a/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/ImprovementPickerScreen.kt @@ -59,7 +59,6 @@ class ImprovementPickerScreen( onAccept() } game.resetToWorldScreen() - dispose() } init { diff --git a/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt b/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt index af675a666a..2f827dfe45 100644 --- a/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt @@ -87,7 +87,6 @@ class ModManagementScreen( game.settings.tileSet = tileSets.first() } game.setScreen(MainMenuScreen()) - dispose() } closeButton.onClick(closeAction) onBackButtonClicked(closeAction) @@ -581,7 +580,6 @@ class ModManagementScreen( override fun resize(width: Int, height: Int) { if (stage.viewport.screenWidth != width || stage.viewport.screenHeight != height) { game.setScreen(ModManagementScreen(installedModInfo, onlineModInfo)) - dispose() // interrupt background loader - sorry, the resized new screen won't continue } } diff --git a/core/src/com/unciv/ui/pickerscreens/PickerPane.kt b/core/src/com/unciv/ui/pickerscreens/PickerPane.kt index 64f0f96a7a..6b2d5ab7db 100644 --- a/core/src/com/unciv/ui/pickerscreens/PickerPane.kt +++ b/core/src/com/unciv/ui/pickerscreens/PickerPane.kt @@ -73,7 +73,7 @@ class PickerPane( /** Sets the text of the [rightSideButton] and enables it if it's the player's turn */ fun pick(rightButtonText: String) { - if (UncivGame.Current.worldScreen.isPlayersTurn) rightSideButton.enable() + if (UncivGame.Current.worldScreen!!.isPlayersTurn) rightSideButton.enable() rightSideButton.setText(rightButtonText) } diff --git a/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt index fbbbdbe7cd..202ea3f4fa 100644 --- a/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PickerScreen.kt @@ -43,7 +43,6 @@ open class PickerScreen(disableScroll: Boolean = false) : BaseScreen() { val closeAction = { if (previousScreen != null) game.setScreen(previousScreen) else game.resetToWorldScreen() - dispose() } pickerPane.closeButton.onClick(closeAction) onBackButtonClicked(closeAction) diff --git a/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt index 604623abf7..200a7690de 100644 --- a/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt @@ -58,7 +58,6 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo // If we've moved to another screen in the meantime (great person pick, victory screen) ignore this if (game.screen !is PolicyPickerScreen || !policies.canAdoptPolicy()) { game.resetToWorldScreen() - dispose() } else { val policyScreen = PolicyPickerScreen(worldScreen) policyScreen.scrollPane.scrollPercentX = scrollPane.scrollPercentX diff --git a/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt index 061f43d906..a2cdbf9566 100644 --- a/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt @@ -30,8 +30,6 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen() { game.setScreen(PromotionPickerScreen(unit).setScrollY(scrollPane.scrollY)) else game.resetToWorldScreen() - dispose() - game.worldScreen.shouldUpdate = true } init { @@ -43,7 +41,7 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen() { acceptPromotion(selectedPromotion) } val canBePromoted = unit.promotions.canBePromoted() - val canChangeState = game.worldScreen.canChangeState + val canChangeState = game.worldScreen!!.canChangeState val canPromoteNow = canBePromoted && canChangeState && unit.currentMovement > 0 && unit.attacksThisTurn == 0 rightSideButton.isEnabled = canPromoteNow diff --git a/core/src/com/unciv/ui/pickerscreens/ReligionPickerScreenCommon.kt b/core/src/com/unciv/ui/pickerscreens/ReligionPickerScreenCommon.kt index 83768df33c..8a4a584966 100644 --- a/core/src/com/unciv/ui/pickerscreens/ReligionPickerScreenCommon.kt +++ b/core/src/com/unciv/ui/pickerscreens/ReligionPickerScreenCommon.kt @@ -67,7 +67,6 @@ abstract class ReligionPickerScreenCommon( rightSideButton.onClick(UncivSound.Choir) { choosingCiv.religionManager.action() UncivGame.Current.resetToWorldScreen() - dispose() } } diff --git a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt index bc25aee3f6..69efab2f9b 100644 --- a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt @@ -88,8 +88,6 @@ class TechPickerScreen( game.settings.addCompletedTutorialTask("Pick technology") game.resetToWorldScreen() - game.worldScreen.shouldUpdate = true - dispose() } // per default show current/recent technology, @@ -265,7 +263,7 @@ class TechPickerScreen( return } - if (!UncivGame.Current.worldScreen.canChangeState) { + if (!UncivGame.Current.worldScreen!!.canChangeState) { rightSideButton.disable() return } diff --git a/core/src/com/unciv/ui/popup/Popup.kt b/core/src/com/unciv/ui/popup/Popup.kt index 5203e07ac2..e170afc7de 100644 --- a/core/src/com/unciv/ui/popup/Popup.kt +++ b/core/src/com/unciv/ui/popup/Popup.kt @@ -2,6 +2,7 @@ package com.unciv.ui.popup import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.Actor +import com.badlogic.gdx.scenes.scene2d.Stage import com.badlogic.gdx.scenes.scene2d.Touchable import com.badlogic.gdx.scenes.scene2d.ui.Button import com.badlogic.gdx.scenes.scene2d.ui.Cell @@ -27,7 +28,12 @@ import com.unciv.ui.utils.extensions.toTextButton * Base class for all Popups, i.e. Tables that get rendered in the middle of a screen and on top of everything else */ @Suppress("MemberVisibilityCanBePrivate") -open class Popup(val screen: BaseScreen): Table(BaseScreen.skin) { +open class Popup( + val stageToShowOn: Stage +): Table(BaseScreen.skin) { + + constructor(screen: BaseScreen) : this(screen.stage) + // This exists to differentiate the actual popup (the inner table) // from the 'screen blocking' part of the popup (which covers the entire screen) val innerTable = Table(BaseScreen.skin) @@ -38,11 +44,15 @@ open class Popup(val screen: BaseScreen): Table(BaseScreen.skin) { */ val keyPressDispatcher = KeyPressDispatcher(this.javaClass.simpleName) + val closeListeners = mutableListOf<() -> Unit>() + + val scrollPane: AutoScrollPane + init { // Set actor name for debugging name = javaClass.simpleName - val scrollPane = AutoScrollPane(innerTable, BaseScreen.skin) + scrollPane = AutoScrollPane(innerTable, BaseScreen.skin) background = ImageGetter.getBackground(Color.GRAY.cpy().apply { a=.5f }) innerTable.background = ImageGetter.getBackground(ImageGetter.getBlue().darken(0.5f)) @@ -61,11 +71,11 @@ open class Popup(val screen: BaseScreen): Table(BaseScreen.skin) { * closed. Use [force] = true if you want to open this popup above the other one anyway. */ fun open(force: Boolean = false) { - screen.stage.addActor(this) + stageToShowOn.addActor(this) innerTable.pack() pack() - center(screen.stage) - if (force || !screen.hasOpenPopups()) { + center(stageToShowOn) + if (force || !stageToShowOn.hasOpenPopups()) { show() } } @@ -73,18 +83,19 @@ open class Popup(val screen: BaseScreen): Table(BaseScreen.skin) { /** Subroutine for [open] handles only visibility and [keyPressDispatcher] */ private fun show() { this.isVisible = true - val currentCount = screen.countOpenPopups() + val currentCount = stageToShowOn.countOpenPopups() // the lambda is for stacked key dispatcher precedence: - keyPressDispatcher.install(screen.stage) { screen.countOpenPopups() > currentCount } + keyPressDispatcher.install(stageToShowOn) { stageToShowOn.countOpenPopups() > currentCount } } /** * Close this popup and - if any other popups are pending - display the next one. */ open fun close() { + for (listener in closeListeners) listener() keyPressDispatcher.uninstall() remove() - val nextPopup = screen.stage.actors.firstOrNull { it is Popup } + val nextPopup = stageToShowOn.actors.firstOrNull { it is Popup } if (nextPopup != null) (nextPopup as Popup).show() } @@ -104,7 +115,7 @@ open class Popup(val screen: BaseScreen): Table(BaseScreen.skin) { val label = text.toLabel(fontSize = size) label.wrap = true label.setAlignment(Align.center) - return add(label).width(screen.stage.width / 2) + return add(label).width(stageToShowOn.width / 2) } /** @@ -219,9 +230,9 @@ open class Popup(val screen: BaseScreen): Table(BaseScreen.skin) { * [FocusListener][com.badlogic.gdx.scenes.scene2d.utils.FocusListener] cancels the event. */ var keyboardFocus: Actor? - get() = screen.stage.keyboardFocus + get() = stageToShowOn.keyboardFocus set(value) { - if (screen.stage.setKeyboardFocus(value)) + if (stageToShowOn.setKeyboardFocus(value)) (value as? TextField)?.selectAll() } } @@ -230,25 +241,22 @@ open class Popup(val screen: BaseScreen): Table(BaseScreen.skin) { * Checks if there are visible [Popup]s. * @return `true` if any were found. */ -fun BaseScreen.hasOpenPopups(): Boolean = stage.actors.any { it is Popup && it.isVisible } +fun BaseScreen.hasOpenPopups(): Boolean = stage.hasOpenPopups() +private fun Stage.hasOpenPopups(): Boolean = actors.any { it is Popup && it.isVisible } /** * Counts number of visible[Popup]s. * * Used for key dispatcher precedence. */ -fun BaseScreen.countOpenPopups() = stage.actors.count { it is Popup && it.isVisible } +private fun Stage.countOpenPopups() = actors.count { it is Popup && it.isVisible } /** Closes all [Popup]s. */ -fun BaseScreen.closeAllPopups() = popups.forEach { it.close() } - -/** - * Closes the topmost visible [Popup]. - * @return The [name][Popup.name] of the closed [Popup] if any popup was closed and if it had a name. - */ -fun BaseScreen.closeOneVisiblePopup() = popups.lastOrNull { it.isVisible }?.apply { close() }?.name +fun BaseScreen.closeAllPopups() = stage.popups.forEach { it.close() } /** @return A [List] of currently active or pending [Popup] screens. */ -val BaseScreen.popups: List - get() = stage.actors.filterIsInstance() +val BaseScreen.popups + get() = stage.popups +private val Stage.popups: List + get() = actors.filterIsInstance() diff --git a/core/src/com/unciv/ui/popup/ToastPopup.kt b/core/src/com/unciv/ui/popup/ToastPopup.kt index 42ee8b8593..56d88b3ea6 100644 --- a/core/src/com/unciv/ui/popup/ToastPopup.kt +++ b/core/src/com/unciv/ui/popup/ToastPopup.kt @@ -1,5 +1,6 @@ package com.unciv.ui.popup +import com.badlogic.gdx.scenes.scene2d.Stage import com.unciv.ui.crashhandling.launchCrashHandling import com.unciv.ui.crashhandling.postCrashHandlingRunnable import com.unciv.ui.utils.BaseScreen @@ -10,7 +11,10 @@ import kotlinx.coroutines.delay * This is an unobtrusive popup which will close itself after a given amount of time. * Default time is two seconds (in milliseconds) */ -class ToastPopup (message: String, screen: BaseScreen, val time: Long = 2000) : Popup(screen){ +class ToastPopup (message: String, stage: Stage, val time: Long = 2000) : Popup(stage){ + + constructor(message: String, screen: BaseScreen, time: Long = 2000) : this(message, screen.stage, time) + init { //Make this popup unobtrusive setFillParent(false) @@ -20,7 +24,7 @@ class ToastPopup (message: String, screen: BaseScreen, val time: Long = 2000) : open() //move it to the top so its not in the middle of the screen //have to be done after open() because open() centers the popup - y = screen.stage.height - (height + 20f) + y = stage.height - (height + 20f) } private fun startTimer(){ diff --git a/core/src/com/unciv/ui/popup/YesNoPopup.kt b/core/src/com/unciv/ui/popup/YesNoPopup.kt index c0b85177af..057970aa6f 100644 --- a/core/src/com/unciv/ui/popup/YesNoPopup.kt +++ b/core/src/com/unciv/ui/popup/YesNoPopup.kt @@ -1,6 +1,7 @@ package com.unciv.ui.popup import com.badlogic.gdx.Gdx +import com.badlogic.gdx.scenes.scene2d.Stage import com.badlogic.gdx.utils.Align import com.unciv.Constants import com.unciv.UncivGame @@ -14,12 +15,19 @@ import com.unciv.ui.utils.extensions.toLabel * @param screen The parent screen - see [Popup.screen]. Optional, defaults to the current [WorldScreen][com.unciv.ui.worldscreen.WorldScreen] * @param restoreDefault A lambda to execute when "No" is chosen */ -open class YesNoPopup ( +open class YesNoPopup( question: String, - action: ()->Unit, - screen: BaseScreen = UncivGame.Current.worldScreen, - restoreDefault: ()->Unit = {} - ) : Popup(screen) { + stage: Stage, + restoreDefault: () -> Unit = {}, + action: () -> Unit +) : Popup(stage) { + + constructor( + question: String, + screen: BaseScreen, + restoreDefault: () -> Unit = {}, + action: () -> Unit + ) : this(question, screen.stage, restoreDefault, action) /** The [Label][com.badlogic.gdx.scenes.scene2d.ui.Label] created for parameter `question` for optional layout tweaking */ private val promptLabel = question.toLabel() @@ -34,13 +42,12 @@ open class YesNoPopup ( } /** Shortcut to open a [YesNoPopup] with the exit game question */ -class ExitGamePopup(screen: BaseScreen, force: Boolean = false) - : YesNoPopup( - question = "Do you want to exit the game?", - action = { Gdx.app.exit() }, - screen = screen, - restoreDefault = { screen.game.musicController.resume() } - ) { +class ExitGamePopup(screen: BaseScreen, force: Boolean = false) : YesNoPopup( + question = "Do you want to exit the game?", + screen = screen, + restoreDefault = { screen.game.musicController.resume() }, + action = { Gdx.app.exit() } +) { init { screen.game.musicController.pause() open(force) diff --git a/core/src/com/unciv/ui/saves/QuickSave.kt b/core/src/com/unciv/ui/saves/QuickSave.kt index 8b4e6ba60c..23d5d47d70 100644 --- a/core/src/com/unciv/ui/saves/QuickSave.kt +++ b/core/src/com/unciv/ui/saves/QuickSave.kt @@ -6,9 +6,11 @@ import com.unciv.UncivGame import com.unciv.logic.GameInfo import com.unciv.ui.crashhandling.launchCrashHandling import com.unciv.ui.crashhandling.postCrashHandlingRunnable +import com.unciv.ui.multiplayer.MultiplayerHelpers import com.unciv.ui.popup.Popup import com.unciv.ui.popup.ToastPopup import com.unciv.ui.worldscreen.WorldScreen +import com.unciv.utils.Log //todo reduce code duplication @@ -70,6 +72,7 @@ object QuickSave { outOfMemory() return@launchCrashHandling } catch (ex: Exception) { + Log.error("Could not autoload game", ex) postCrashHandlingRunnable { loadingPopup.close() ToastPopup("Cannot resume game!", screen) @@ -82,6 +85,13 @@ object QuickSave { screen.game.onlineMultiplayer.loadGame(savedGame) } catch (oom: OutOfMemoryError) { outOfMemory() + } catch (ex: Exception) { + val message = MultiplayerHelpers.getLoadExceptionMessage(ex) + Log.error("Could not autoload game", ex) + postCrashHandlingRunnable { + loadingPopup.close() + ToastPopup(message, screen) + } } } else { postCrashHandlingRunnable { /// ... and load it into the screen on main thread for GL context @@ -89,6 +99,10 @@ object QuickSave { screen.game.loadGame(savedGame) } catch (oom: OutOfMemoryError) { outOfMemory() + } catch (ex: Exception) { + Log.error("Could not autoload game", ex) + loadingPopup.close() + ToastPopup("Cannot resume game!", screen) } } } diff --git a/core/src/com/unciv/ui/saves/SaveGameScreen.kt b/core/src/com/unciv/ui/saves/SaveGameScreen.kt index 0c293da625..4eed922a3b 100644 --- a/core/src/com/unciv/ui/saves/SaveGameScreen.kt +++ b/core/src/com/unciv/ui/saves/SaveGameScreen.kt @@ -33,7 +33,7 @@ class SaveGameScreen(val gameInfo: GameInfo) : LoadOrSaveScreen("Current saves") rightSideButton.setText("Save game".tr()) val saveAction = { if (game.gameSaver.getSave(gameNameTextField.text).exists()) - YesNoPopup("Overwrite existing file?", { saveGame() }, this).open() + YesNoPopup("Overwrite existing file?", this) { saveGame() }.open() else saveGame() } rightSideButton.onClick(saveAction) diff --git a/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt b/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt index 7f286813db..17efcf1c90 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt @@ -172,7 +172,7 @@ class TileGroupIcons(val tileGroup: TileGroup) { val shouldDisplayResource = if (tileGroup.showEntireMap) showResourcesAndImprovements else showResourcesAndImprovements - && tileGroup.tileInfo.hasViewableResource(UncivGame.Current.worldScreen.viewingCiv) + && tileGroup.tileInfo.hasViewableResource(UncivGame.Current.worldScreen!!.viewingCiv) tileGroup.resourceImage!!.isVisible = shouldDisplayResource } } diff --git a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt index 8c78b05453..6acc62f931 100644 --- a/core/src/com/unciv/ui/trade/DiplomacyScreen.kt +++ b/core/src/com/unciv/ui/trade/DiplomacyScreen.kt @@ -73,7 +73,7 @@ class DiplomacyScreen( private val rightSideTable = Table() private val closeButton = Constants.close.toTextButton() - private fun isNotPlayersTurn() = !UncivGame.Current.worldScreen.canChangeState + private fun isNotPlayersTurn() = !UncivGame.Current.worldScreen!!.canChangeState init { onBackButtonClicked { UncivGame.Current.resetToWorldScreen() } @@ -205,7 +205,7 @@ class DiplomacyScreen( wrapper.addTooltip(name, 18f) wrapper.onClick { val pedia = CivilopediaScreen( - UncivGame.Current.gameInfo.ruleSet, + UncivGame.Current.gameInfo!!.ruleSet, this, link = "Resource/$name" ) @@ -389,11 +389,11 @@ class DiplomacyScreen( private fun getRevokeProtectionButton(otherCiv: CivilizationInfo): TextButton { val revokeProtectionButton = "Revoke Protection".toTextButton() revokeProtectionButton.onClick { - YesNoPopup("Revoke protection for [${otherCiv.civName}]?", { + YesNoPopup("Revoke protection for [${otherCiv.civName}]?", this) { otherCiv.removeProtectorCiv(viewingCiv) updateLeftSideTable(otherCiv) updateRightSide(otherCiv) - }, this).open() + }.open() } if (isNotPlayersTurn() || !otherCiv.otherCivCanWithdrawProtection(viewingCiv)) revokeProtectionButton.disable() return revokeProtectionButton @@ -402,11 +402,11 @@ class DiplomacyScreen( private fun getPledgeToProtectButton(otherCiv: CivilizationInfo): TextButton { val protectionButton = "Pledge to protect".toTextButton() protectionButton.onClick { - YesNoPopup("Declare Protection of [${otherCiv.civName}]?", { + YesNoPopup("Declare Protection of [${otherCiv.civName}]?", this) { otherCiv.addProtectorCiv(viewingCiv) updateLeftSideTable(otherCiv) updateRightSide(otherCiv) - }, this).open() + }.open() } if (isNotPlayersTurn() || !otherCiv.otherCivCanPledgeProtection(viewingCiv)) protectionButton.disable() return protectionButton @@ -418,7 +418,7 @@ class DiplomacyScreen( ): TextButton { val peaceButton = "Negotiate Peace".toTextButton() peaceButton.onClick { - YesNoPopup("Peace with [${otherCiv.civName}]?", { + YesNoPopup("Peace with [${otherCiv.civName}]?", this) { val tradeLogic = TradeLogic(viewingCiv, otherCiv) tradeLogic.currentTrade.ourOffers.add( TradeOffer( @@ -435,7 +435,7 @@ class DiplomacyScreen( tradeLogic.acceptTrade() updateLeftSideTable(otherCiv) updateRightSide(otherCiv) - }, this).open() + }.open() } val cityStatesAlly = otherCiv.getAllyCiv() val atWarWithItsAlly = viewingCiv.getKnownCivs() @@ -761,11 +761,11 @@ class DiplomacyScreen( ): TextButton { val denounceButton = "Denounce ([30] turns)".toTextButton() denounceButton.onClick { - YesNoPopup("Denounce [${otherCiv.civName}]?", { + YesNoPopup("Denounce [${otherCiv.civName}]?", this) { diplomacyManager.denounce() updateLeftSideTable(otherCiv) setRightSideFlavorText(otherCiv, "We will remember this.", "Very well.") - }, this).open() + }.open() } if (isNotPlayersTurn()) denounceButton.disable() return denounceButton @@ -956,12 +956,12 @@ class DiplomacyScreen( declareWarButton.setText(declareWarButton.text.toString() + " ($turnsToPeaceTreaty${Fonts.turn})") } declareWarButton.onClick { - YesNoPopup("Declare war on [${otherCiv.civName}]?", { + YesNoPopup("Declare war on [${otherCiv.civName}]?", this) { diplomacyManager.declareWar() setRightSideFlavorText(otherCiv, otherCiv.nation.attacked, "Very well.") updateLeftSideTable(otherCiv) UncivGame.Current.musicController.chooseTrack(otherCiv.civName, MusicMood.War, MusicTrackChooserFlags.setSpecific) - }, this).open() + }.open() } if (isNotPlayersTurn()) declareWarButton.disable() return declareWarButton @@ -996,8 +996,11 @@ class DiplomacyScreen( private fun getGoToOnMapButton(civilization: CivilizationInfo): TextButton { val goToOnMapButton = "Go to on map".toTextButton() goToOnMapButton.onClick { - UncivGame.Current.resetToWorldScreen() - UncivGame.Current.worldScreen.mapHolder.setCenterPosition(civilization.getCapital()!!.location, selectUnit = false) + val worldScreen = UncivGame.Current.worldScreen + if (worldScreen != null) { + UncivGame.Current.resetToWorldScreen() + worldScreen.mapHolder.setCenterPosition(civilization.getCapital()!!.location, selectUnit = false) + } } return goToOnMapButton } diff --git a/core/src/com/unciv/ui/trade/OffersListScroll.kt b/core/src/com/unciv/ui/trade/OffersListScroll.kt index ab1622847d..9d35996503 100644 --- a/core/src/com/unciv/ui/trade/OffersListScroll.kt +++ b/core/src/com/unciv/ui/trade/OffersListScroll.kt @@ -92,7 +92,7 @@ class OffersListScroll( Luxury_Resource, Strategic_Resource -> ImageGetter.getResourceImage(offer.name, 30f) WarDeclaration -> - ImageGetter.getNationIndicator(UncivGame.Current.gameInfo.ruleSet.nations[offer.name]!!, 30f) + ImageGetter.getNationIndicator(UncivGame.Current.gameInfo!!.ruleSet.nations[offer.name]!!, 30f) else -> null } val tradeButton = IconTextButton(tradeLabel, tradeIcon).apply { diff --git a/core/src/com/unciv/ui/utils/BaseScreen.kt b/core/src/com/unciv/ui/utils/BaseScreen.kt index 756fb46aba..2fe08eef80 100644 --- a/core/src/com/unciv/ui/utils/BaseScreen.kt +++ b/core/src/com/unciv/ui/utils/BaseScreen.kt @@ -7,16 +7,22 @@ import com.badlogic.gdx.graphics.GL20 import com.badlogic.gdx.graphics.g2d.BitmapFont import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.scenes.scene2d.Stage -import com.badlogic.gdx.scenes.scene2d.ui.* +import com.badlogic.gdx.scenes.scene2d.ui.CheckBox +import com.badlogic.gdx.scenes.scene2d.ui.Label +import com.badlogic.gdx.scenes.scene2d.ui.SelectBox +import com.badlogic.gdx.scenes.scene2d.ui.Skin +import com.badlogic.gdx.scenes.scene2d.ui.TextButton +import com.badlogic.gdx.scenes.scene2d.ui.TextField import com.badlogic.gdx.scenes.scene2d.utils.Drawable import com.badlogic.gdx.utils.viewport.ExtendViewport import com.unciv.UncivGame import com.unciv.models.Tutorial import com.unciv.ui.UncivStage import com.unciv.ui.images.ImageGetter +import com.unciv.ui.options.OptionsPopup import com.unciv.ui.popup.hasOpenPopups import com.unciv.ui.tutorials.TutorialController -import com.unciv.ui.options.OptionsPopup +import com.unciv.ui.utils.extensions.isNarrowerThan4to3 abstract class BaseScreen : Screen { @@ -122,7 +128,7 @@ abstract class BaseScreen : Screen { fun isCrampedPortrait() = isPortrait() && game.settings.resolution.split("x").map { it.toInt() }.last() <= 700 /** @return `true` if the screen is narrower than 4:3 landscape */ - fun isNarrowerThan4to3() = stage.viewport.screenHeight * 4 > stage.viewport.screenWidth * 3 + fun isNarrowerThan4to3() = stage.isNarrowerThan4to3() fun openOptionsPopup(startingPage: Int = OptionsPopup.defaultPage, onClose: () -> Unit = {}) { OptionsPopup(this, startingPage, onClose).open(force = true) diff --git a/core/src/com/unciv/ui/utils/TabbedPager.kt b/core/src/com/unciv/ui/utils/TabbedPager.kt index 5a0bfee711..32aefb1fa0 100644 --- a/core/src/com/unciv/ui/utils/TabbedPager.kt +++ b/core/src/com/unciv/ui/utils/TabbedPager.kt @@ -289,7 +289,7 @@ open class TabbedPager( //region Initialization init { - val screen = (if (UncivGame.isCurrentInitialized()) UncivGame.Current.screen else null) as? BaseScreen + val screen = (if (UncivGame.isCurrentInitialized()) UncivGame.Current.screen else null) val (screenWidth, screenHeight) = (screen?.stage?.run { width to height }) ?: (Float.MAX_VALUE to Float.MAX_VALUE) dimW = DimensionMeasurement.from(minimumWidth, maximumWidth, screenWidth) dimH = DimensionMeasurement.from(minimumHeight, maximumHeight, screenHeight) @@ -601,7 +601,7 @@ open class TabbedPager( if (!UncivGame.isCurrentInitialized() || askPasswordLock || deferredSecretPages.isEmpty()) return askPasswordLock = true // race condition: Popup closes _first_, then deferredSecretPages is emptied -> parent shows and calls us again - PassPopup(UncivGame.Current.screen as BaseScreen, { + PassPopup(UncivGame.Current.screen!!, { addDeferredSecrets() }, { deferredSecretPages.clear() diff --git a/core/src/com/unciv/ui/utils/extensions/Scene2dExtensions.kt b/core/src/com/unciv/ui/utils/extensions/Scene2dExtensions.kt index 2cd7c37d1b..b08328d46a 100644 --- a/core/src/com/unciv/ui/utils/extensions/Scene2dExtensions.kt +++ b/core/src/com/unciv/ui/utils/extensions/Scene2dExtensions.kt @@ -229,3 +229,6 @@ fun WidgetGroup.packIfNeeded(): WidgetGroup { if (needsLayout()) pack() return this } + +/** @return `true` if the screen is narrower than 4:3 landscape */ +fun Stage.isNarrowerThan4to3() = viewport.screenHeight * 4 > viewport.screenWidth * 3 diff --git a/core/src/com/unciv/ui/worldscreen/PlayerReadyScreen.kt b/core/src/com/unciv/ui/worldscreen/PlayerReadyScreen.kt index ef0c6b999a..05344fe566 100644 --- a/core/src/com/unciv/ui/worldscreen/PlayerReadyScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/PlayerReadyScreen.kt @@ -3,25 +3,24 @@ package com.unciv.ui.worldscreen import com.badlogic.gdx.scenes.scene2d.Touchable import com.badlogic.gdx.scenes.scene2d.ui.Table import com.unciv.Constants -import com.unciv.logic.GameInfo -import com.unciv.logic.civilization.CivilizationInfo import com.unciv.ui.crashhandling.postCrashHandlingRunnable import com.unciv.ui.images.ImageGetter import com.unciv.ui.utils.BaseScreen import com.unciv.ui.utils.extensions.onClick import com.unciv.ui.utils.extensions.toLabel -class PlayerReadyScreen(gameInfo: GameInfo, currentPlayerCiv: CivilizationInfo) : BaseScreen(){ +class PlayerReadyScreen(worldScreen: WorldScreen) : BaseScreen() { init { - val table= Table() - table.touchable= Touchable.enabled - table.background= ImageGetter.getBackground(currentPlayerCiv.nation.getOuterColor()) + val table = Table() + table.touchable = Touchable.enabled + val curCiv = worldScreen.viewingCiv + table.background = ImageGetter.getBackground(curCiv.nation.getOuterColor()) - table.add("[$currentPlayerCiv] ready?".toLabel(currentPlayerCiv.nation.getInnerColor(), Constants.headingFontSize)) + table.add("[$curCiv] ready?".toLabel(curCiv.nation.getInnerColor(), Constants.headingFontSize)) table.onClick { postCrashHandlingRunnable { // To avoid ANRs on Android when the creation of the worldscreen takes more than 500ms - game.resetToWorldScreen(WorldScreen(gameInfo, currentPlayerCiv)) + game.setScreen(worldScreen) } } table.setFillParent(true) diff --git a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt index 45f439fa60..526f3401cb 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt @@ -216,7 +216,7 @@ class WorldMapHolder( } private fun onTileRightClicked(unit: MapUnit, tile: TileInfo) { - if (UncivGame.Current.gameInfo.currentPlayerCiv.isSpectator()) { + if (UncivGame.Current.gameInfo!!.currentPlayerCiv.isSpectator()) { return } removeUnitActionOverlay() diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 56f66506d4..50f045b9bc 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -336,7 +336,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas try { debug("loadLatestMultiplayerState current game: gameId: %s, turn: %s, curCiv: %s", - game.worldScreen.gameInfo.gameId, game.worldScreen.gameInfo.turns, game.worldScreen.gameInfo.currentPlayer) + gameInfo.gameId, gameInfo.turns, gameInfo.currentPlayer) val latestGame = game.onlineMultiplayer.downloadGame(gameInfo.gameId) debug("loadLatestMultiplayerState downloaded game: gameId: %s, turn: %s, curCiv: %s", latestGame.gameId, latestGame.turns, latestGame.currentPlayer) @@ -345,8 +345,8 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas } postCrashHandlingRunnable { loadingGamePopup.close() - if (game.gameInfo.gameId == gameInfo.gameId) { // game could've been changed during download - createNewWorldScreen(latestGame) + if (game.gameInfo!!.gameId == gameInfo.gameId) { // game could've been changed during download + game.setScreen(createNewWorldScreen(latestGame)) } } } catch (ex: Throwable) { @@ -602,7 +602,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas } - private fun createNewWorldScreen(gameInfo: GameInfo, resize:Boolean=false) { + private fun createNewWorldScreen(gameInfo: GameInfo, resize:Boolean=false): WorldScreen { game.gameInfo = gameInfo val newWorldScreen = WorldScreen(gameInfo, gameInfo.getPlayerToViewAs()) @@ -621,7 +621,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas newWorldScreen.selectedCiv = gameInfo.getCivilization(selectedCiv.civName) newWorldScreen.fogOfWar = fogOfWar - game.resetToWorldScreen(newWorldScreen) + return newWorldScreen } fun nextTurn() { @@ -669,16 +669,15 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas // 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 postCrashHandlingRunnable { - + val newWorldScreen = createNewWorldScreen(gameInfoClone) if (gameInfoClone.currentPlayerCiv.civName != viewingCiv.civName - && !gameInfoClone.gameParameters.isOnlineMultiplayer) - game.setScreen(PlayerReadyScreen(gameInfoClone, gameInfoClone.getCurrentPlayerCivilization())) - else { - createNewWorldScreen(gameInfoClone) + && !gameInfoClone.gameParameters.isOnlineMultiplayer) { + game.setScreen(PlayerReadyScreen(newWorldScreen)) + } else { + game.setScreen(newWorldScreen) } if (shouldAutoSave) { - val newWorldScreen = this@WorldScreen.game.worldScreen newWorldScreen.waitingForAutosave = true newWorldScreen.shouldUpdate = true game.gameSaver.autoSave(gameInfoClone) { @@ -821,7 +820,7 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Bas nextTurn() } if (game.settings.confirmNextTurn) { - YesNoPopup("Confirm next turn", action, this).open() + YesNoPopup("Confirm next turn", this, action = action).open() } else { action() } diff --git a/core/src/com/unciv/ui/worldscreen/bottombar/TileInfoTable.kt b/core/src/com/unciv/ui/worldscreen/bottombar/TileInfoTable.kt index b7be42c06c..c80e5a8702 100644 --- a/core/src/com/unciv/ui/worldscreen/bottombar/TileInfoTable.kt +++ b/core/src/com/unciv/ui/worldscreen/bottombar/TileInfoTable.kt @@ -26,7 +26,7 @@ class TileInfoTable(private val viewingCiv :CivilizationInfo) : Table(BaseScreen if (tile != null && (UncivGame.Current.viewEntireMapForDebug || viewingCiv.exploredTiles.contains(tile.position)) ) { add(getStatsTable(tile)) add( MarkupRenderer.render(tile.toMarkup(viewingCiv), padding = 0f, iconDisplay = IconDisplay.None) { - UncivGame.Current.setScreen(CivilopediaScreen(viewingCiv.gameInfo.ruleSet, UncivGame.Current.worldScreen, link = it)) + UncivGame.Current.setScreen(CivilopediaScreen(viewingCiv.gameInfo.ruleSet, UncivGame.Current.worldScreen!!, link = it)) } ).pad(5f).row() if (UncivGame.Current.viewEntireMapForDebug) add(tile.position.run { "(${x.toInt()},${y.toInt()})" }.toLabel()).colspan(2).pad(5f) diff --git a/core/src/com/unciv/ui/worldscreen/minimap/MapOverlayToggleButton.kt b/core/src/com/unciv/ui/worldscreen/minimap/MapOverlayToggleButton.kt index b2f09e772c..7b85119398 100644 --- a/core/src/com/unciv/ui/worldscreen/minimap/MapOverlayToggleButton.kt +++ b/core/src/com/unciv/ui/worldscreen/minimap/MapOverlayToggleButton.kt @@ -42,7 +42,7 @@ class MapOverlayToggleButton( /** Toggle overlay. Called on click. */ fun toggle() { setter(!getter()) - UncivGame.Current.worldScreen.shouldUpdate = true + UncivGame.Current.worldScreen!!.shouldUpdate = true // Setting worldScreen.shouldUpdate implicitly causes this.update() to be called by the WorldScreen on the next update. } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt index fdde7e1943..7f8d7518de 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActions.kt @@ -127,7 +127,7 @@ object UnitActions { val disbandText = if (unit.currentTile.getOwner() == unit.civInfo) "Disband this unit for [${unit.baseUnit.getDisbandGold(unit.civInfo)}] gold?".tr() else "Do you really want to disband this unit?".tr() - YesNoPopup(disbandText, { unit.disband(); worldScreen.shouldUpdate = true }).open() + YesNoPopup(disbandText, UncivGame.Current.worldScreen!!) { unit.disband(); worldScreen.shouldUpdate = true }.open() } }.takeIf { unit.currentMovement > 0 }) } @@ -187,7 +187,7 @@ object UnitActions { if (tile.ruleset.tileImprovements.containsKey("City center")) tile.improvement = "City center" unit.destroy() - UncivGame.Current.worldScreen.shouldUpdate = true + UncivGame.Current.worldScreen!!.shouldUpdate = true } if (unit.civInfo.playerType == PlayerType.AI) @@ -204,7 +204,7 @@ object UnitActions { else { // ask if we would be breaking a promise val text = "Do you want to break your promise to [$leaders]?" - YesNoPopup(text, foundAction, UncivGame.Current.worldScreen).open(force = true) + YesNoPopup(text, UncivGame.Current.worldScreen!!, action = foundAction).open(force = true) } } ) @@ -275,7 +275,7 @@ object UnitActions { else actionList += UnitAction(type = UnitActionType.Pillage) { if (!worldScreen.hasOpenPopups()) { val pillageText = "Are you sure you want to pillage this [${unit.currentTile.improvement}]?" - YesNoPopup(pillageText, { (pillageAction.action)(); worldScreen.shouldUpdate = true }).open() + YesNoPopup(pillageText, UncivGame.Current.worldScreen!!) { (pillageAction.action)(); worldScreen.shouldUpdate = true }.open() } } } @@ -853,7 +853,7 @@ object UnitActions { unit.destroy() // City states dont get GPs else unit.gift(recipient) - UncivGame.Current.worldScreen.shouldUpdate = true + UncivGame.Current.worldScreen!!.shouldUpdate = true } return UnitAction(UnitActionType.GiftUnit, action = giftAction) diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt index 333f4d1004..16785fef6c 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt @@ -39,7 +39,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() { actionButton.pack() val action = { unitAction.action?.invoke() - UncivGame.Current.worldScreen.shouldUpdate = true + UncivGame.Current.worldScreen!!.shouldUpdate = true } if (unitAction.action == null) actionButton.disable() else { diff --git a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt index 966af89d0c..7c2282d2cf 100644 --- a/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt +++ b/desktop/src/com/unciv/app/desktop/DesktopLauncher.kt @@ -87,6 +87,7 @@ internal object DesktopLauncher { try { updateRpc(game) } catch (ex: Exception) { + debug("Exception while updating Discord Rich Presence", ex) } } } catch (ex: Throwable) { @@ -98,10 +99,15 @@ internal object DesktopLauncher { private fun updateRpc(game: UncivGame) { if (!game.isInitialized) return val presence = DiscordRichPresence() - val currentPlayerCiv = game.gameInfo.getCurrentPlayerCivilization() - presence.details = "${currentPlayerCiv.nation.leaderName} of ${currentPlayerCiv.nation.name}" presence.largeImageKey = "logo" // The actual image is uploaded to the discord app / applications webpage - presence.largeImageText = "Turn" + " " + currentPlayerCiv.gameInfo.turns + + val gameInfo = game.gameInfo + if (gameInfo != null) { + val currentPlayerCiv = gameInfo.getCurrentPlayerCivilization() + presence.details = "${currentPlayerCiv.nation.leaderName} of ${currentPlayerCiv.nation.name}" + presence.largeImageText = "Turn" + " " + currentPlayerCiv.gameInfo.turns + } + DiscordRPC.INSTANCE.Discord_UpdatePresence(presence) } }