mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-24 10:01:39 +07:00
AutoPlayEndTurn can run on a different thread (#11329)
* AutoPlay now builds military units more * AutoPlayEndTurn now launches in a new thread if there are more than 30 units/cities * Moved AutoPlay to WorldScreen and added isAIOrAutoPlaying() to Civilization * Fixed AI not wanting to pass through city-state tiles * Added black space to the end of AutoPlay * Partially fixed some NextTurnButton AutoPlay Behaviour * AutoPlay now persists across next turn WorldScreens * Made player's turn using AutoPlay run on a different thread * Remove the extra isAutoPlaying variable * AutoPlay class now manages all AutoPlay threads * Made AutoPlayMilitary and AutoPlayCivilian both able to run on a new thread. * Added more comments to AutoPlay * Maybe finally fixed the problems?
This commit is contained in:
parent
6b2fe87887
commit
a3d56845f9
@ -37,6 +37,7 @@ import com.unciv.ui.screens.mainmenuscreen.MainMenuScreen
|
||||
import com.unciv.ui.screens.savescreens.LoadGameScreen
|
||||
import com.unciv.ui.screens.worldscreen.PlayerReadyScreen
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
import com.unciv.ui.screens.worldscreen.unit.AutoPlay
|
||||
import com.unciv.utils.Concurrency
|
||||
import com.unciv.utils.DebugUtils
|
||||
import com.unciv.utils.Display
|
||||
@ -173,8 +174,9 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
* Automatically runs on the appropriate thread.
|
||||
*
|
||||
* Sets the returned `WorldScreen` as the only active screen.
|
||||
* @param autoPlay pass in the old WorldScreen AutoPlay to retain the state throughout turns. Otherwise leave it is the default.
|
||||
*/
|
||||
suspend fun loadGame(newGameInfo: GameInfo, callFromLoadScreen: Boolean = false): WorldScreen = withThreadPoolContext toplevel@{
|
||||
suspend fun loadGame(newGameInfo: GameInfo, autoPlay: AutoPlay = AutoPlay(settings.autoPlay), callFromLoadScreen: Boolean = false): WorldScreen = withThreadPoolContext toplevel@{
|
||||
val prevGameInfo = gameInfo
|
||||
gameInfo = newGameInfo
|
||||
|
||||
@ -204,7 +206,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
screenStack.clear()
|
||||
|
||||
worldScreen = null // This allows the GC to collect our old WorldScreen, otherwise we keep two WorldScreens in memory.
|
||||
val newWorldScreen = WorldScreen(newGameInfo, newGameInfo.getPlayerToViewAs(), worldScreenRestoreState)
|
||||
val newWorldScreen = WorldScreen(newGameInfo, autoPlay, newGameInfo.getPlayerToViewAs(), worldScreenRestoreState)
|
||||
worldScreen = newWorldScreen
|
||||
|
||||
val moreThanOnePlayer = newGameInfo.civilizations.count { it.playerType == PlayerType.Human } > 1
|
||||
@ -290,7 +292,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
|
||||
fun popScreen(): BaseScreen? {
|
||||
if (screenStack.size == 1) {
|
||||
musicController.pause()
|
||||
settings.autoPlay.stopAutoPlay()
|
||||
worldScreen?.autoPlay?.stopAutoPlay()
|
||||
ConfirmPopup(
|
||||
screen = screenStack.last(),
|
||||
question = "Do you want to exit the game?",
|
||||
|
@ -389,16 +389,17 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
||||
// Automation done here
|
||||
TurnManager(player).automateTurn()
|
||||
|
||||
val worldScreen = UncivGame.Current.worldScreen
|
||||
// Do we need to break if player won?
|
||||
if (simulateUntilWin && player.victoryManager.hasWon()) {
|
||||
simulateUntilWin = false
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
worldScreen?.autoPlay?.stopAutoPlay()
|
||||
break
|
||||
}
|
||||
|
||||
// Do we need to stop AutoPlay?
|
||||
if (UncivGame.Current.settings.autoPlay.isAutoPlaying() && player.victoryManager.hasWon() && !oneMoreTurnMode)
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
if (worldScreen != null && worldScreen.autoPlay.isAutoPlaying() && player.victoryManager.hasWon() && !oneMoreTurnMode)
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
|
||||
// Clean up
|
||||
TurnManager(player).endTurn(progressBar)
|
||||
|
@ -164,7 +164,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
|
||||
&& city.getCenterTile().getTilesInDistance(5).none { it.militaryUnit?.civ == civInfo })
|
||||
modifier = 5f // there's a settler just sitting here, doing nothing - BAD
|
||||
|
||||
if (civInfo.playerType == PlayerType.Human) modifier /= 2 // Players prefer to make their own unit choices usually
|
||||
if (!civInfo.isAIOrAutoPlaying()) modifier /= 2 // Players prefer to make their own unit choices usually
|
||||
modifier *= personality.scaledFocus(PersonalityValue.Military)
|
||||
addChoice(relativeCostEffectiveness, militaryUnit, modifier)
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class RoadBetweenCitiesAutomation(val civInfo: Civilization, cachedForTurn: Int,
|
||||
cloningSource?.bestRoadAvailable ?:
|
||||
//Player can choose not to auto-build roads & railroads.
|
||||
if (civInfo.isHuman() && (!UncivGame.Current.settings.autoBuildingRoads
|
||||
|| UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()))
|
||||
|| UncivGame.Current.settings.autoPlay.fullAutoPlayAI))
|
||||
RoadStatus.None
|
||||
else civInfo.tech.getBestRoadAvailable()
|
||||
|
||||
|
@ -128,7 +128,7 @@ object UnitAutomation {
|
||||
|
||||
internal fun tryUpgradeUnit(unit: MapUnit): Boolean {
|
||||
if (unit.civ.isHuman() && (!UncivGame.Current.settings.automatedUnitsCanUpgrade
|
||||
|| UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())) return false
|
||||
|| UncivGame.Current.settings.autoPlay.fullAutoPlayAI)) return false
|
||||
|
||||
val upgradeUnits = getUnitsToUpgradeTo(unit)
|
||||
if (upgradeUnits.none()) return false // for resource reasons, usually
|
||||
|
@ -325,7 +325,7 @@ class WorkerAutomation(
|
||||
.maxByOrNull { it.second }?.first
|
||||
|
||||
if (tile.improvement != null && civInfo.isHuman() && (!UncivGame.Current.settings.automatedWorkersReplaceImprovements
|
||||
|| UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())) {
|
||||
|| UncivGame.Current.settings.autoPlay.fullAutoPlayAI)) {
|
||||
// Note that we might still want to build roads or remove fallout, so we can't exit the function immedietly
|
||||
bestBuildableImprovement = null
|
||||
}
|
||||
|
@ -560,7 +560,7 @@ object Battle {
|
||||
city.puppetCity(attackerCiv)
|
||||
//Although in Civ5 Venice is unable to re-annex their capital, that seems a bit silly. No check for May not annex cities here.
|
||||
city.annexCity()
|
||||
} else if (attackerCiv.isHuman() && !(UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())) {
|
||||
} else if (attackerCiv.isHuman() && !(UncivGame.Current.settings.autoPlay.fullAutoPlayAI)) {
|
||||
// we're not taking our former capital
|
||||
attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered, city.id))
|
||||
} else automateCityConquer(attackerCiv, city)
|
||||
|
@ -709,7 +709,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
val isCurrentPlayersTurn = city.civ.gameInfo.isUsersTurn()
|
||||
|| !city.civ.gameInfo.gameParameters.isOnlineMultiplayer
|
||||
if ((isCurrentPlayersTurn && (UncivGame.Current.settings.autoAssignCityProduction
|
||||
|| UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())) // only automate if the active human player has the setting to automate production
|
||||
|| UncivGame.Current.settings.autoPlay.fullAutoPlayAI)) // only automate if the active human player has the setting to automate production
|
||||
|| !city.civ.isHuman() || city.isPuppet) {
|
||||
ConstructionAutomation(this).chooseNextConstruction()
|
||||
}
|
||||
|
@ -333,6 +333,11 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
if (firstCityIfNoCapital) cities.firstOrNull() else null
|
||||
fun isHuman() = playerType == PlayerType.Human
|
||||
fun isAI() = playerType == PlayerType.AI
|
||||
fun isAIOrAutoPlaying(): Boolean {
|
||||
if (playerType == PlayerType.AI) return true
|
||||
val worldScreen = UncivGame.Current.worldScreen ?: return false
|
||||
return worldScreen.viewingCiv == this && worldScreen.autoPlay.isAutoPlaying()
|
||||
}
|
||||
fun isOneCityChallenger() = playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge
|
||||
|
||||
fun isCurrentPlayer() = gameInfo.currentPlayerCiv == this
|
||||
@ -384,12 +389,11 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
|
||||
fun wantsToFocusOn(focus: Victory.Focus): Boolean {
|
||||
return thingsToFocusOnForVictory.contains(focus) &&
|
||||
(isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())
|
||||
return thingsToFocusOnForVictory.contains(focus) && isAIOrAutoPlaying()
|
||||
}
|
||||
|
||||
fun getPersonality(): Personality {
|
||||
return if (isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) gameInfo.ruleset.personalities[nation.personality] ?: Personality.neutralPersonality
|
||||
return if (isAIOrAutoPlaying()) gameInfo.ruleset.personalities[nation.personality] ?: Personality.neutralPersonality
|
||||
else Personality.neutralPersonality
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ class DiplomacyFunctions(val civInfo: Civilization) {
|
||||
if (diplomacyManager != null && (diplomacyManager.hasOpenBorders || diplomacyManager.diplomaticStatus == DiplomaticStatus.War))
|
||||
return true
|
||||
// Players can always pass through city-state tiles
|
||||
if ((civInfo.isHuman() && !UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) && otherCiv.isCityState()) return true
|
||||
if (!civInfo.isAIOrAutoPlaying() && otherCiv.isCityState()) return true
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,6 @@ class OnlineMultiplayer {
|
||||
} else if (onlinePreview != null && hasNewerGameState(preview, onlinePreview)) {
|
||||
onlineGame.doManualUpdate(preview)
|
||||
}
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
UncivGame.Current.loadGame(gameInfo)
|
||||
}
|
||||
|
||||
|
@ -339,22 +339,6 @@ class GameSettings {
|
||||
var autoPlayPolicies: Boolean = true
|
||||
var autoPlayReligion: Boolean = true
|
||||
var autoPlayDiplomacy: Boolean = true
|
||||
|
||||
var turnsToAutoPlay: Int = 0
|
||||
var autoPlayTurnInProgress: Boolean = false
|
||||
|
||||
fun startAutoPlay() {
|
||||
turnsToAutoPlay = autoPlayMaxTurns
|
||||
}
|
||||
|
||||
fun stopAutoPlay() {
|
||||
turnsToAutoPlay = 0
|
||||
autoPlayTurnInProgress = false
|
||||
}
|
||||
|
||||
fun isAutoPlaying(): Boolean = turnsToAutoPlay > 0
|
||||
|
||||
fun isAutoPlayingAndFullAI(): Boolean = isAutoPlaying() && fullAutoPlayAI
|
||||
}
|
||||
|
||||
@Suppress("SuspiciousCallableReferenceInLambda") // By @Azzurite, safe as long as that warning below is followed
|
||||
|
@ -339,7 +339,7 @@ object UniqueTriggerActivation {
|
||||
if (notification != null)
|
||||
civInfo.addNotification(notification, NotificationCategory.General)
|
||||
|
||||
if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) {
|
||||
if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.fullAutoPlayAI) {
|
||||
NextTurnAutomation.chooseGreatPerson(civInfo)
|
||||
}
|
||||
true
|
||||
|
@ -76,7 +76,7 @@ object MayaCalendar {
|
||||
val year = game.getYear()
|
||||
if (!isNewCycle(year, game.getYear(-1))) return
|
||||
civInfo.greatPeople.triggerMayanGreatPerson()
|
||||
if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())
|
||||
if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.fullAutoPlayAI)
|
||||
NextTurnAutomation.chooseGreatPerson(civInfo)
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
package com.unciv.ui.popups.options
|
||||
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.GUI
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.ui.components.widgets.UncivSlider
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
|
||||
fun autoPlayTab(
|
||||
optionsPopup: OptionsPopup
|
||||
fun autoPlayTab(optionsPopup: OptionsPopup
|
||||
): Table = Table(BaseScreen.skin).apply {
|
||||
pad(10f)
|
||||
defaults().pad(5f)
|
||||
@ -56,7 +57,8 @@ fun autoPlayTab(
|
||||
"Show AutoPlay button",
|
||||
settings.autoPlay.showAutoPlayButton, true
|
||||
) { settings.autoPlay.showAutoPlayButton = it
|
||||
settings.autoPlay.stopAutoPlay() }
|
||||
GUI.getWorldScreenIfActive()?.autoPlay?.stopAutoPlay()
|
||||
}
|
||||
|
||||
|
||||
optionsPopup.addCheckbox(
|
||||
|
@ -135,7 +135,7 @@ fun debugTab(
|
||||
val clipboardContentsString = Gdx.app.clipboard.contents.trim()
|
||||
val loadedGame = UncivFiles.gameInfoFromString(clipboardContentsString)
|
||||
loadedGame.gameParameters.isOnlineMultiplayer = false
|
||||
optionsPopup.game.loadGame(loadedGame, true)
|
||||
optionsPopup.game.loadGame(loadedGame, callFromLoadScreen = true)
|
||||
optionsPopup.close()
|
||||
} catch (ex: Exception) {
|
||||
ToastPopup(ex.message ?: ex::class.java.simpleName, optionsPopup.stageToShowOn).open(true)
|
||||
|
@ -9,12 +9,13 @@ import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.components.extensions.isEnabled
|
||||
import com.unciv.ui.components.input.onClick
|
||||
import com.unciv.ui.components.input.onDoubleClick
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
|
||||
class GreatPersonPickerScreen(val civInfo: Civilization) : PickerScreen() {
|
||||
class GreatPersonPickerScreen(val worldScreen: WorldScreen, val civInfo: Civilization) : PickerScreen() {
|
||||
private var theChosenOne: BaseUnit? = null
|
||||
|
||||
init {
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
closeButton.isVisible = false
|
||||
rightSideButton.setText("Choose a free great person".tr())
|
||||
|
||||
|
@ -29,6 +29,7 @@ import com.unciv.ui.popups.Popup
|
||||
import com.unciv.ui.popups.ToastPopup
|
||||
import com.unciv.logic.github.Github
|
||||
import com.unciv.logic.github.Github.folderNameToRepoName
|
||||
import com.unciv.ui.popups.closeAllPopups
|
||||
import com.unciv.utils.Concurrency
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.launchOnGLThread
|
||||
@ -122,14 +123,13 @@ class LoadGameScreen : LoadOrSaveScreen() {
|
||||
}
|
||||
|
||||
private fun onLoadGame() {
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
if (selectedSave == null) return
|
||||
val loadingPopup = LoadingPopup(this)
|
||||
Concurrency.run(loadGame) {
|
||||
try {
|
||||
// This is what can lead to ANRs - reading the file and setting the transients, that's why this is in another thread
|
||||
val loadedGame = game.files.loadGameFromFile(selectedSave!!)
|
||||
game.loadGame(loadedGame, true)
|
||||
game.loadGame(loadedGame, callFromLoadScreen = true)
|
||||
} catch (notAPlayer: UncivShowableException) {
|
||||
launchOnGLThread {
|
||||
val (message) = getLoadExceptionMessage(notAPlayer)
|
||||
@ -155,7 +155,7 @@ class LoadGameScreen : LoadOrSaveScreen() {
|
||||
try {
|
||||
val clipboardContentsString = Gdx.app.clipboard.contents.trim()
|
||||
val loadedGame = UncivFiles.gameInfoFromString(clipboardContentsString)
|
||||
game.loadGame(loadedGame, true)
|
||||
game.loadGame(loadedGame, callFromLoadScreen = true)
|
||||
} catch (ex: Exception) {
|
||||
launchOnGLThread { handleLoadGameException(ex, "Could not load game from clipboard!") }
|
||||
} finally {
|
||||
@ -181,7 +181,7 @@ class LoadGameScreen : LoadOrSaveScreen() {
|
||||
Concurrency.run(Companion.loadFromCustomLocation) {
|
||||
game.files.loadGameFromCustomLocation(
|
||||
{
|
||||
Concurrency.run { game.loadGame(it, true) }
|
||||
Concurrency.run { game.loadGame(it, callFromLoadScreen = true) }
|
||||
},
|
||||
{
|
||||
if (it !is PlatformSaverLoader.Cancelled)
|
||||
|
@ -35,7 +35,7 @@ object QuickSave {
|
||||
}
|
||||
|
||||
fun load(screen: WorldScreen) {
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
screen.autoPlay.stopAutoPlay()
|
||||
val files = UncivGame.Current.files
|
||||
val toast = ToastPopup("Quickloading...", screen)
|
||||
Concurrency.run("QuickLoadGame") {
|
||||
@ -58,7 +58,6 @@ object QuickSave {
|
||||
}
|
||||
|
||||
fun autoLoadGame(screen: MainMenuScreen) {
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
val loadingPopup = LoadingPopup(screen)
|
||||
Concurrency.run("autoLoadGame") {
|
||||
// Load game from file to class on separate thread to avoid ANR...
|
||||
|
@ -85,7 +85,7 @@ class VictoryScreen(
|
||||
}
|
||||
|
||||
init {
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
//**************** Set up the tabs ****************
|
||||
splitPane.setFirstWidget(tabs)
|
||||
val iconSize = Constants.headingFontSize.toFloat()
|
||||
@ -161,7 +161,7 @@ class VictoryScreen(
|
||||
displayWonOrLost("[$winningCiv] has won a [$victoryType] Victory!", victory.defeatString)
|
||||
music.chooseTrack(playerCiv.civName, MusicMood.Defeat, EnumSet.of(MusicTrackChooserFlags.SuffixMustMatch))
|
||||
}
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
}
|
||||
|
||||
private fun displayWonOrLost(vararg descriptions: String) {
|
||||
|
@ -59,6 +59,7 @@ import com.unciv.ui.screens.worldscreen.status.NextTurnButton
|
||||
import com.unciv.ui.screens.worldscreen.status.NextTurnProgress
|
||||
import com.unciv.ui.screens.worldscreen.status.StatusButtons
|
||||
import com.unciv.ui.screens.worldscreen.topbar.WorldScreenTopBar
|
||||
import com.unciv.ui.screens.worldscreen.unit.AutoPlay
|
||||
import com.unciv.ui.screens.worldscreen.unit.UnitTable
|
||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsTable
|
||||
import com.unciv.utils.Concurrency
|
||||
@ -81,6 +82,7 @@ import kotlin.concurrent.timer
|
||||
*/
|
||||
class WorldScreen(
|
||||
val gameInfo: GameInfo,
|
||||
val autoPlay: AutoPlay,
|
||||
val viewingCiv: Civilization,
|
||||
restoreState: RestoreState? = null
|
||||
) : BaseScreen() {
|
||||
@ -130,6 +132,7 @@ class WorldScreen(
|
||||
|
||||
internal val undoHandler = UndoHandler(this)
|
||||
|
||||
|
||||
init {
|
||||
// notifications are right-aligned, they take up only as much space as necessary.
|
||||
notificationsScroll.width = stage.width / 2
|
||||
@ -328,7 +331,7 @@ class WorldScreen(
|
||||
launchOnGLThread {
|
||||
loadingGamePopup.close()
|
||||
}
|
||||
startNewScreenJob(latestGame)
|
||||
startNewScreenJob(latestGame, autoPlay)
|
||||
} catch (ex: Throwable) {
|
||||
launchOnGLThread {
|
||||
val (message) = LoadGameScreen.getLoadExceptionMessage(ex, "Couldn't download the latest game state!")
|
||||
@ -406,19 +409,18 @@ class WorldScreen(
|
||||
}
|
||||
|
||||
// If the game has ended, lets stop AutoPlay
|
||||
if (game.settings.autoPlay.isAutoPlaying()
|
||||
&& !gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.checkForVictory())) {
|
||||
game.settings.autoPlay.stopAutoPlay()
|
||||
if (autoPlay.isAutoPlaying() && !gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.checkForVictory())) {
|
||||
autoPlay.stopAutoPlay()
|
||||
}
|
||||
|
||||
if (!hasOpenPopups() && !game.settings.autoPlay.isAutoPlaying() && isPlayersTurn) {
|
||||
if (!hasOpenPopups() && !autoPlay.isAutoPlaying() && isPlayersTurn) {
|
||||
when {
|
||||
viewingCiv.shouldShowDiplomaticVotingResults() ->
|
||||
UncivGame.Current.pushScreen(DiplomaticVoteResultScreen(gameInfo.diplomaticVictoryVotesCast, viewingCiv))
|
||||
!gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.checkForVictory()) ->
|
||||
game.pushScreen(VictoryScreen(this))
|
||||
viewingCiv.greatPeople.freeGreatPeople > 0 ->
|
||||
game.pushScreen(GreatPersonPickerScreen(viewingCiv))
|
||||
game.pushScreen(GreatPersonPickerScreen(this, viewingCiv))
|
||||
viewingCiv.popupAlerts.any() -> AlertPopup(this, viewingCiv.popupAlerts.first())
|
||||
viewingCiv.tradeRequests.isNotEmpty() -> {
|
||||
// In the meantime this became invalid, perhaps because we accepted previous trades
|
||||
@ -648,7 +650,7 @@ class WorldScreen(
|
||||
|
||||
progressBar.increment()
|
||||
|
||||
startNewScreenJob(gameInfoClone)
|
||||
startNewScreenJob(gameInfoClone, autoPlay)
|
||||
}
|
||||
}
|
||||
|
||||
@ -701,7 +703,7 @@ class WorldScreen(
|
||||
} else {
|
||||
if (!game.settings.autoPlay.showAutoPlayButton) {
|
||||
statusButtons.autoPlayStatusButton = null
|
||||
game.settings.autoPlay.stopAutoPlay()
|
||||
autoPlay.stopAutoPlay()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -725,7 +727,7 @@ class WorldScreen(
|
||||
resizeDeferTimer = timer("Resize", daemon = true, 500L, Long.MAX_VALUE) {
|
||||
resizeDeferTimer?.cancel()
|
||||
resizeDeferTimer = null
|
||||
startNewScreenJob(gameInfo, true) // start over
|
||||
startNewScreenJob(gameInfo, autoPlay, true) // start over
|
||||
}
|
||||
}
|
||||
|
||||
@ -805,10 +807,10 @@ class WorldScreen(
|
||||
}
|
||||
|
||||
/** This exists so that no reference to the current world screen remains, so the old world screen can get garbage collected during [UncivGame.loadGame]. */
|
||||
private fun startNewScreenJob(gameInfo: GameInfo, autosaveDisabled: Boolean = false) {
|
||||
private fun startNewScreenJob(gameInfo: GameInfo, autoPlay: AutoPlay, autosaveDisabled: Boolean = false) {
|
||||
Concurrency.run {
|
||||
val newWorldScreen = try {
|
||||
UncivGame.Current.loadGame(gameInfo)
|
||||
UncivGame.Current.loadGame(gameInfo, autoPlay)
|
||||
} catch (notAPlayer: UncivShowableException) {
|
||||
withGLContext {
|
||||
val (message) = LoadGameScreen.getLoadExceptionMessage(notAPlayer)
|
||||
|
@ -11,7 +11,7 @@ import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
|
||||
class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen, scrollable = Scrollability.All) {
|
||||
init {
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
defaults().fillX()
|
||||
|
||||
addButton("Main menu") {
|
||||
|
@ -3,13 +3,14 @@ package com.unciv.ui.screens.worldscreen.status
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.Stage
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.GUI
|
||||
import com.unciv.logic.automation.civilization.NextTurnAutomation
|
||||
import com.unciv.logic.automation.unit.UnitAutomation
|
||||
import com.unciv.logic.civilization.managers.TurnManager
|
||||
import com.unciv.ui.components.input.KeyboardBinding
|
||||
import com.unciv.ui.popups.AnimatedMenuPopup
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
import com.unciv.ui.screens.worldscreen.unit.AutoPlay
|
||||
import com.unciv.utils.Concurrency
|
||||
|
||||
/**
|
||||
* The "context" menu for the AutoPlay button
|
||||
@ -20,18 +21,19 @@ class AutoPlayMenu(
|
||||
private val nextTurnButton: NextTurnButton,
|
||||
private val worldScreen: WorldScreen
|
||||
) : AnimatedMenuPopup(stage, getActorTopRight(positionNextTo)) {
|
||||
private val settings = GUI.getSettings()
|
||||
|
||||
private val autoPlay: AutoPlay = worldScreen.autoPlay
|
||||
|
||||
init {
|
||||
// We need to activate the end turn button again after the menu closes
|
||||
afterCloseCallback = { worldScreen.shouldUpdate = true }
|
||||
}
|
||||
|
||||
|
||||
override fun createContentTable(): Table {
|
||||
val table = super.createContentTable()!!
|
||||
// Using the same keyboard binding for bypassing this menu and the default option
|
||||
if (!worldScreen.gameInfo.gameParameters.isOnlineMultiplayer)
|
||||
table.add(getButton("Start AutoPlay", KeyboardBinding.AutoPlay, ::autoPlay)).row()
|
||||
table.add(getButton("Start AutoPlay", KeyboardBinding.AutoPlay, ::multiturnAutoPlay)).row()
|
||||
table.add(getButton("AutoPlay End Turn", KeyboardBinding.AutoPlayMenuEndTurn, ::autoPlayEndTurn)).row()
|
||||
table.add(getButton("AutoPlay Military Once", KeyboardBinding.AutoPlayMenuMilitary, ::autoPlayMilitary)).row()
|
||||
table.add(getButton("AutoPlay Civilians Once", KeyboardBinding.AutoPlayMenuCivilians, ::autoPlayCivilian)).row()
|
||||
@ -41,33 +43,61 @@ class AutoPlayMenu(
|
||||
}
|
||||
|
||||
private fun autoPlayEndTurn() {
|
||||
TurnManager(worldScreen.viewingCiv).automateTurn()
|
||||
worldScreen.nextTurn()
|
||||
val endTurnFunction = {
|
||||
nextTurnButton.update()
|
||||
TurnManager(worldScreen.viewingCiv).automateTurn()
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
worldScreen.nextTurn()
|
||||
}
|
||||
|
||||
if (worldScreen.viewingCiv.units.getCivUnitsSize() + worldScreen.viewingCiv.cities.size >= 30) {
|
||||
autoPlay.runAutoPlayJobInNewThread("AutoPlayEndTurn", worldScreen, false, endTurnFunction)
|
||||
} else {
|
||||
autoPlay.autoPlayTurnInProgress = true
|
||||
endTurnFunction()
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoPlay() {
|
||||
settings.autoPlay.startAutoPlay()
|
||||
private fun multiturnAutoPlay() {
|
||||
worldScreen.autoPlay.startMultiturnAutoPlay()
|
||||
nextTurnButton.update()
|
||||
}
|
||||
|
||||
private fun autoPlayMilitary() {
|
||||
val civInfo = worldScreen.viewingCiv
|
||||
val isAtWar = civInfo.isAtWar()
|
||||
val sortedUnits = civInfo.units.getCivUnits().filter { it.isMilitary() }.sortedBy { unit -> NextTurnAutomation.getUnitPriority(unit, isAtWar) }
|
||||
for (unit in sortedUnits) UnitAutomation.automateUnitMoves(unit)
|
||||
val autoPlayMilitaryFunction = {
|
||||
val isAtWar = civInfo.isAtWar()
|
||||
val sortedUnits = civInfo.units.getCivUnits().filter { it.isMilitary() }.sortedBy { unit -> NextTurnAutomation.getUnitPriority(unit, isAtWar) }
|
||||
for (unit in sortedUnits) UnitAutomation.automateUnitMoves(unit)
|
||||
|
||||
for (city in civInfo.cities) UnitAutomation.tryBombardEnemy(city)
|
||||
worldScreen.shouldUpdate = true
|
||||
worldScreen.render(0f)
|
||||
for (city in civInfo.cities) UnitAutomation.tryBombardEnemy(city)
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
if (civInfo.units.getCivUnitsSize() > 30) {
|
||||
autoPlay.runAutoPlayJobInNewThread("AutoPlayMilitary", worldScreen, true, autoPlayMilitaryFunction)
|
||||
} else {
|
||||
autoPlay.autoPlayTurnInProgress = true
|
||||
autoPlayMilitaryFunction()
|
||||
autoPlay.autoPlayTurnInProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoPlayCivilian() {
|
||||
val civInfo = worldScreen.viewingCiv
|
||||
val isAtWar = civInfo.isAtWar()
|
||||
val sortedUnits = civInfo.units.getCivUnits().filter { it.isCivilian() }.sortedBy { unit -> NextTurnAutomation.getUnitPriority(unit, isAtWar) }
|
||||
for (unit in sortedUnits) UnitAutomation.automateUnitMoves(unit)
|
||||
worldScreen.shouldUpdate = true
|
||||
worldScreen.render(0f)
|
||||
val autoPlayCivilainFunction = {
|
||||
val isAtWar = civInfo.isAtWar()
|
||||
val sortedUnits = civInfo.units.getCivUnits().filter { it.isCivilian() }
|
||||
.sortedBy { unit -> NextTurnAutomation.getUnitPriority(unit, isAtWar) }
|
||||
for (unit in sortedUnits) UnitAutomation.automateUnitMoves(unit)
|
||||
worldScreen.shouldUpdate = true
|
||||
}
|
||||
if (civInfo.units.getCivUnitsSize() > 50) {
|
||||
autoPlay.runAutoPlayJobInNewThread("AutoPlayCivilian", worldScreen, true, autoPlayCivilainFunction)
|
||||
} else {
|
||||
autoPlay.autoPlayTurnInProgress = true
|
||||
autoPlayCivilainFunction()
|
||||
autoPlay.autoPlayTurnInProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoPlayEconomy() {
|
||||
|
@ -23,17 +23,16 @@ class AutoPlayStatusButton(
|
||||
|
||||
init {
|
||||
add(Stack(autoPlayImage)).pad(5f)
|
||||
val settings = GUI.getSettings()
|
||||
onActivation(binding = KeyboardBinding.AutoPlayMenu) {
|
||||
if (settings.autoPlay.isAutoPlaying())
|
||||
settings.autoPlay.stopAutoPlay()
|
||||
else if (worldScreen.viewingCiv == worldScreen.gameInfo.currentPlayerCiv)
|
||||
if (worldScreen.autoPlay.isAutoPlaying())
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
else if (worldScreen.isPlayersTurn)
|
||||
AutoPlayMenu(stage,this, nextTurnButton, worldScreen)
|
||||
}
|
||||
val directAutoPlay = {
|
||||
if (!worldScreen.gameInfo.gameParameters.isOnlineMultiplayer
|
||||
&& worldScreen.viewingCiv == worldScreen.gameInfo.currentPlayerCiv) {
|
||||
settings.autoPlay.startAutoPlay()
|
||||
worldScreen.autoPlay.startMultiturnAutoPlay()
|
||||
nextTurnButton.update()
|
||||
}
|
||||
}
|
||||
@ -48,9 +47,8 @@ class AutoPlayStatusButton(
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
val settings = GUI.getSettings()
|
||||
if (isPressed && settings.autoPlay.isAutoPlaying()) {
|
||||
settings.autoPlay.stopAutoPlay()
|
||||
if (isPressed && worldScreen.autoPlay.isAutoPlaying()) {
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,9 @@ enum class NextTurnAction(protected val text: String, val color: Color) {
|
||||
},
|
||||
AutoPlay("AutoPlay", Color.WHITE) {
|
||||
override fun isChoice(worldScreen: WorldScreen) =
|
||||
UncivGame.Current.settings.autoPlay.isAutoPlaying()
|
||||
worldScreen.autoPlay.isAutoPlaying()
|
||||
override fun action(worldScreen: WorldScreen) =
|
||||
UncivGame.Current.settings.autoPlay.stopAutoPlay()
|
||||
worldScreen.autoPlay.stopAutoPlay()
|
||||
},
|
||||
Working(Constants.working, Color.GRAY) {
|
||||
override fun isChoice(worldScreen: WorldScreen) =
|
||||
|
@ -14,6 +14,7 @@ import com.unciv.ui.images.IconTextButton
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.popups.hasOpenPopups
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
import com.unciv.utils.Concurrency
|
||||
|
||||
class NextTurnButton(
|
||||
private val worldScreen: WorldScreen
|
||||
@ -33,19 +34,17 @@ class NextTurnButton(
|
||||
fun update() {
|
||||
nextTurnAction = getNextTurnAction(worldScreen)
|
||||
updateButton(nextTurnAction)
|
||||
val settings = GUI.getSettings()
|
||||
if (!settings.autoPlay.autoPlayTurnInProgress && settings.autoPlay.isAutoPlaying()
|
||||
&& worldScreen.isPlayersTurn && !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning()) {
|
||||
settings.autoPlay.autoPlayTurnInProgress = true
|
||||
if (!worldScreen.viewingCiv.isSpectator())
|
||||
val autoPlay = worldScreen.autoPlay
|
||||
if (autoPlay.shouldContinueAutoPlaying() && worldScreen.isPlayersTurn
|
||||
&& !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning()) {
|
||||
autoPlay.runAutoPlayJobInNewThread("MultiturnAutoPlay", worldScreen, false) {
|
||||
TurnManager(worldScreen.viewingCiv).automateTurn()
|
||||
worldScreen.nextTurn()
|
||||
if (!settings.autoPlay.autoPlayUntilEnd)
|
||||
settings.autoPlay.turnsToAutoPlay--
|
||||
settings.autoPlay.autoPlayTurnInProgress = false
|
||||
worldScreen.nextTurn()
|
||||
autoPlay.endTurnMultiturnAutoPlay()
|
||||
}
|
||||
}
|
||||
|
||||
isEnabled = nextTurnAction.getText (worldScreen) == "AutoPlay"
|
||||
|
||||
isEnabled = nextTurnAction.getText (worldScreen) == "AutoPlay"
|
||||
|| (!worldScreen.hasOpenPopups() && worldScreen.isPlayersTurn
|
||||
&& !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning())
|
||||
if (isEnabled) addTooltip(KeyboardBinding.NextTurn) else addTooltip("")
|
||||
|
@ -98,7 +98,7 @@ class NextTurnProgress(
|
||||
// On first update the button text is not yet updated. To stabilize geometry, do it now
|
||||
if (progress == 0) nextTurnButton?.apply {
|
||||
disable()
|
||||
if (UncivGame.Current.settings.autoPlay.isAutoPlaying())
|
||||
if (GUI.getWorldScreenIfActive()?.autoPlay?.isAutoPlaying() == true)
|
||||
updateButton(NextTurnAction.AutoPlay)
|
||||
else updateButton(NextTurnAction.Working)
|
||||
barWidth = width - removeHorizontalPad -
|
||||
|
73
core/src/com/unciv/ui/screens/worldscreen/unit/AutoPlay.kt
Normal file
73
core/src/com/unciv/ui/screens/worldscreen/unit/AutoPlay.kt
Normal file
@ -0,0 +1,73 @@
|
||||
package com.unciv.ui.screens.worldscreen.unit
|
||||
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
import com.unciv.utils.Concurrency
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
class AutoPlay(private var autoPlaySettings: GameSettings.GameSettingsAutoPlay) {
|
||||
/**
|
||||
* How many turns we should multiturn AutoPlay for.
|
||||
* In the case that [autoPlaySettings].autoPlayUntilEnd is true, the value should not be decremented after each turn.
|
||||
*/
|
||||
var turnsToAutoPlay: Int = 0
|
||||
|
||||
/**
|
||||
* Determines whether or not we are currently processing the viewing player's turn.
|
||||
* This can be on the main thread or on a different thread.
|
||||
*/
|
||||
var autoPlayTurnInProgress: Boolean = false
|
||||
var autoPlayJob: Job? = null
|
||||
|
||||
fun startMultiturnAutoPlay() {
|
||||
autoPlayTurnInProgress = false
|
||||
turnsToAutoPlay = autoPlaySettings.autoPlayMaxTurns
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the end of the user's turn being AutoPlayed.
|
||||
* Only decrements [turnsToAutoPlay] if [autoPlaySettings].autoPlayUntilEnd is false.
|
||||
*/
|
||||
fun endTurnMultiturnAutoPlay() {
|
||||
if (!autoPlaySettings.autoPlayUntilEnd && turnsToAutoPlay > 0)
|
||||
turnsToAutoPlay--
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops multiturn AutoPlay and sets [autoPlayTurnInProgress] to false
|
||||
*/
|
||||
fun stopAutoPlay() {
|
||||
turnsToAutoPlay = 0
|
||||
autoPlayTurnInProgress = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the provided job on a new thread if there isn't already an AutoPlay thread running.
|
||||
* Will set autoPlayTurnInProgress to true for the duration of the job.
|
||||
*
|
||||
* @param setPlayerTurnAfterEnd keep this as the default (true) if it will still be the viewing player's turn after the job is finished.
|
||||
* Set it to false if the turn will end.
|
||||
* @throws IllegalStateException if an AutoPlay job is currently running as this is called.
|
||||
*/
|
||||
fun runAutoPlayJobInNewThread(jobName: String, worldScreen: WorldScreen, setPlayerTurnAfterEnd: Boolean = true, job: () -> Unit) {
|
||||
if (autoPlayTurnInProgress) throw IllegalStateException("Trying to start an AutoPlay job while a job is currently running")
|
||||
autoPlayTurnInProgress = true
|
||||
worldScreen.isPlayersTurn = false
|
||||
autoPlayJob = Concurrency.runOnNonDaemonThreadPool(jobName) {
|
||||
job()
|
||||
autoPlayTurnInProgress = false
|
||||
if (setPlayerTurnAfterEnd)
|
||||
worldScreen.isPlayersTurn = true
|
||||
}
|
||||
}
|
||||
|
||||
fun isAutoPlaying(): Boolean = turnsToAutoPlay > 0 || autoPlayTurnInProgress
|
||||
|
||||
fun fullAutoPlayAI(): Boolean = isAutoPlaying() && autoPlaySettings.fullAutoPlayAI
|
||||
|
||||
/**
|
||||
* @return true if we should play at least 1 more turn and we are not currenlty processing any AutoPlay
|
||||
*/
|
||||
fun shouldContinueAutoPlaying(): Boolean = !autoPlayTurnInProgress && turnsToAutoPlay > 0
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ object UnitActionsPillage {
|
||||
internal fun getPillageActions(unit: MapUnit, tile: Tile): Sequence<UnitAction> {
|
||||
val pillageAction = getPillageAction(unit, tile)
|
||||
?: return emptySequence()
|
||||
if (pillageAction.action == null || unit.civ.isAI() || (unit.civ.isHuman() && UncivGame.Current.settings.autoPlay.isAutoPlaying()))
|
||||
if (pillageAction.action == null || unit.civ.isAIOrAutoPlaying())
|
||||
return sequenceOf(pillageAction)
|
||||
else return sequenceOf(UnitAction(UnitActionType.Pillage, 65f, pillageAction.title) {
|
||||
val pillageText = "Are you sure you want to pillage this [${tile.getImprovementToPillageName()!!}]?"
|
||||
|
Loading…
Reference in New Issue
Block a user