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:
Oskar Niesen 2024-04-23 15:59:02 -05:00 committed by GitHub
parent 6b2fe87887
commit a3d56845f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 197 additions and 103 deletions

View File

@ -37,6 +37,7 @@ import com.unciv.ui.screens.mainmenuscreen.MainMenuScreen
import com.unciv.ui.screens.savescreens.LoadGameScreen import com.unciv.ui.screens.savescreens.LoadGameScreen
import com.unciv.ui.screens.worldscreen.PlayerReadyScreen import com.unciv.ui.screens.worldscreen.PlayerReadyScreen
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
import com.unciv.ui.screens.worldscreen.unit.AutoPlay
import com.unciv.utils.Concurrency import com.unciv.utils.Concurrency
import com.unciv.utils.DebugUtils import com.unciv.utils.DebugUtils
import com.unciv.utils.Display import com.unciv.utils.Display
@ -173,8 +174,9 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
* Automatically runs on the appropriate thread. * Automatically runs on the appropriate thread.
* *
* Sets the returned `WorldScreen` as the only active screen. * 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 val prevGameInfo = gameInfo
gameInfo = newGameInfo gameInfo = newGameInfo
@ -204,7 +206,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
screenStack.clear() screenStack.clear()
worldScreen = null // This allows the GC to collect our old WorldScreen, otherwise we keep two WorldScreens in memory. 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 worldScreen = newWorldScreen
val moreThanOnePlayer = newGameInfo.civilizations.count { it.playerType == PlayerType.Human } > 1 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? { fun popScreen(): BaseScreen? {
if (screenStack.size == 1) { if (screenStack.size == 1) {
musicController.pause() musicController.pause()
settings.autoPlay.stopAutoPlay() worldScreen?.autoPlay?.stopAutoPlay()
ConfirmPopup( ConfirmPopup(
screen = screenStack.last(), screen = screenStack.last(),
question = "Do you want to exit the game?", question = "Do you want to exit the game?",

View File

@ -389,16 +389,17 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
// Automation done here // Automation done here
TurnManager(player).automateTurn() TurnManager(player).automateTurn()
val worldScreen = UncivGame.Current.worldScreen
// Do we need to break if player won? // Do we need to break if player won?
if (simulateUntilWin && player.victoryManager.hasWon()) { if (simulateUntilWin && player.victoryManager.hasWon()) {
simulateUntilWin = false simulateUntilWin = false
UncivGame.Current.settings.autoPlay.stopAutoPlay() worldScreen?.autoPlay?.stopAutoPlay()
break break
} }
// Do we need to stop AutoPlay? // Do we need to stop AutoPlay?
if (UncivGame.Current.settings.autoPlay.isAutoPlaying() && player.victoryManager.hasWon() && !oneMoreTurnMode) if (worldScreen != null && worldScreen.autoPlay.isAutoPlaying() && player.victoryManager.hasWon() && !oneMoreTurnMode)
UncivGame.Current.settings.autoPlay.stopAutoPlay() worldScreen.autoPlay.stopAutoPlay()
// Clean up // Clean up
TurnManager(player).endTurn(progressBar) TurnManager(player).endTurn(progressBar)

View File

@ -164,7 +164,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
&& city.getCenterTile().getTilesInDistance(5).none { it.militaryUnit?.civ == civInfo }) && city.getCenterTile().getTilesInDistance(5).none { it.militaryUnit?.civ == civInfo })
modifier = 5f // there's a settler just sitting here, doing nothing - BAD 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) modifier *= personality.scaledFocus(PersonalityValue.Military)
addChoice(relativeCostEffectiveness, militaryUnit, modifier) addChoice(relativeCostEffectiveness, militaryUnit, modifier)
} }

View File

@ -36,7 +36,7 @@ class RoadBetweenCitiesAutomation(val civInfo: Civilization, cachedForTurn: Int,
cloningSource?.bestRoadAvailable ?: cloningSource?.bestRoadAvailable ?:
//Player can choose not to auto-build roads & railroads. //Player can choose not to auto-build roads & railroads.
if (civInfo.isHuman() && (!UncivGame.Current.settings.autoBuildingRoads if (civInfo.isHuman() && (!UncivGame.Current.settings.autoBuildingRoads
|| UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())) || UncivGame.Current.settings.autoPlay.fullAutoPlayAI))
RoadStatus.None RoadStatus.None
else civInfo.tech.getBestRoadAvailable() else civInfo.tech.getBestRoadAvailable()

View File

@ -128,7 +128,7 @@ object UnitAutomation {
internal fun tryUpgradeUnit(unit: MapUnit): Boolean { internal fun tryUpgradeUnit(unit: MapUnit): Boolean {
if (unit.civ.isHuman() && (!UncivGame.Current.settings.automatedUnitsCanUpgrade 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) val upgradeUnits = getUnitsToUpgradeTo(unit)
if (upgradeUnits.none()) return false // for resource reasons, usually if (upgradeUnits.none()) return false // for resource reasons, usually

View File

@ -325,7 +325,7 @@ class WorkerAutomation(
.maxByOrNull { it.second }?.first .maxByOrNull { it.second }?.first
if (tile.improvement != null && civInfo.isHuman() && (!UncivGame.Current.settings.automatedWorkersReplaceImprovements 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 // Note that we might still want to build roads or remove fallout, so we can't exit the function immedietly
bestBuildableImprovement = null bestBuildableImprovement = null
} }

View File

@ -560,7 +560,7 @@ object Battle {
city.puppetCity(attackerCiv) 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. //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() 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 // we're not taking our former capital
attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered, city.id)) attackerCiv.popupAlerts.add(PopupAlert(AlertType.CityConquered, city.id))
} else automateCityConquer(attackerCiv, city) } else automateCityConquer(attackerCiv, city)

View File

@ -709,7 +709,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
val isCurrentPlayersTurn = city.civ.gameInfo.isUsersTurn() val isCurrentPlayersTurn = city.civ.gameInfo.isUsersTurn()
|| !city.civ.gameInfo.gameParameters.isOnlineMultiplayer || !city.civ.gameInfo.gameParameters.isOnlineMultiplayer
if ((isCurrentPlayersTurn && (UncivGame.Current.settings.autoAssignCityProduction 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) { || !city.civ.isHuman() || city.isPuppet) {
ConstructionAutomation(this).chooseNextConstruction() ConstructionAutomation(this).chooseNextConstruction()
} }

View File

@ -333,6 +333,11 @@ class Civilization : IsPartOfGameInfoSerialization {
if (firstCityIfNoCapital) cities.firstOrNull() else null if (firstCityIfNoCapital) cities.firstOrNull() else null
fun isHuman() = playerType == PlayerType.Human fun isHuman() = playerType == PlayerType.Human
fun isAI() = playerType == PlayerType.AI 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 isOneCityChallenger() = playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge
fun isCurrentPlayer() = gameInfo.currentPlayerCiv == this fun isCurrentPlayer() = gameInfo.currentPlayerCiv == this
@ -384,12 +389,11 @@ class Civilization : IsPartOfGameInfoSerialization {
} }
fun wantsToFocusOn(focus: Victory.Focus): Boolean { fun wantsToFocusOn(focus: Victory.Focus): Boolean {
return thingsToFocusOnForVictory.contains(focus) && return thingsToFocusOnForVictory.contains(focus) && isAIOrAutoPlaying()
(isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI())
} }
fun getPersonality(): Personality { 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 else Personality.neutralPersonality
} }

View File

@ -156,7 +156,7 @@ class DiplomacyFunctions(val civInfo: Civilization) {
if (diplomacyManager != null && (diplomacyManager.hasOpenBorders || diplomacyManager.diplomaticStatus == DiplomaticStatus.War)) if (diplomacyManager != null && (diplomacyManager.hasOpenBorders || diplomacyManager.diplomaticStatus == DiplomaticStatus.War))
return true return true
// Players can always pass through city-state tiles // 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 return false
} }

View File

@ -241,7 +241,6 @@ class OnlineMultiplayer {
} else if (onlinePreview != null && hasNewerGameState(preview, onlinePreview)) { } else if (onlinePreview != null && hasNewerGameState(preview, onlinePreview)) {
onlineGame.doManualUpdate(preview) onlineGame.doManualUpdate(preview)
} }
UncivGame.Current.settings.autoPlay.stopAutoPlay()
UncivGame.Current.loadGame(gameInfo) UncivGame.Current.loadGame(gameInfo)
} }

View File

@ -339,22 +339,6 @@ class GameSettings {
var autoPlayPolicies: Boolean = true var autoPlayPolicies: Boolean = true
var autoPlayReligion: Boolean = true var autoPlayReligion: Boolean = true
var autoPlayDiplomacy: 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 @Suppress("SuspiciousCallableReferenceInLambda") // By @Azzurite, safe as long as that warning below is followed

View File

@ -339,7 +339,7 @@ object UniqueTriggerActivation {
if (notification != null) if (notification != null)
civInfo.addNotification(notification, NotificationCategory.General) civInfo.addNotification(notification, NotificationCategory.General)
if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) { if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.fullAutoPlayAI) {
NextTurnAutomation.chooseGreatPerson(civInfo) NextTurnAutomation.chooseGreatPerson(civInfo)
} }
true true

View File

@ -76,7 +76,7 @@ object MayaCalendar {
val year = game.getYear() val year = game.getYear()
if (!isNewCycle(year, game.getYear(-1))) return if (!isNewCycle(year, game.getYear(-1))) return
civInfo.greatPeople.triggerMayanGreatPerson() civInfo.greatPeople.triggerMayanGreatPerson()
if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.isAutoPlayingAndFullAI()) if (civInfo.isAI() || UncivGame.Current.settings.autoPlay.fullAutoPlayAI)
NextTurnAutomation.chooseGreatPerson(civInfo) NextTurnAutomation.chooseGreatPerson(civInfo)
} }

View File

@ -1,13 +1,14 @@
package com.unciv.ui.popups.options package com.unciv.ui.popups.options
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.GUI
import com.unciv.models.metadata.GameSettings import com.unciv.models.metadata.GameSettings
import com.unciv.ui.components.widgets.UncivSlider import com.unciv.ui.components.widgets.UncivSlider
import com.unciv.ui.components.extensions.toLabel import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.worldscreen.WorldScreen
fun autoPlayTab( fun autoPlayTab(optionsPopup: OptionsPopup
optionsPopup: OptionsPopup
): Table = Table(BaseScreen.skin).apply { ): Table = Table(BaseScreen.skin).apply {
pad(10f) pad(10f)
defaults().pad(5f) defaults().pad(5f)
@ -56,7 +57,8 @@ fun autoPlayTab(
"Show AutoPlay button", "Show AutoPlay button",
settings.autoPlay.showAutoPlayButton, true settings.autoPlay.showAutoPlayButton, true
) { settings.autoPlay.showAutoPlayButton = it ) { settings.autoPlay.showAutoPlayButton = it
settings.autoPlay.stopAutoPlay() } GUI.getWorldScreenIfActive()?.autoPlay?.stopAutoPlay()
}
optionsPopup.addCheckbox( optionsPopup.addCheckbox(

View File

@ -135,7 +135,7 @@ fun debugTab(
val clipboardContentsString = Gdx.app.clipboard.contents.trim() val clipboardContentsString = Gdx.app.clipboard.contents.trim()
val loadedGame = UncivFiles.gameInfoFromString(clipboardContentsString) val loadedGame = UncivFiles.gameInfoFromString(clipboardContentsString)
loadedGame.gameParameters.isOnlineMultiplayer = false loadedGame.gameParameters.isOnlineMultiplayer = false
optionsPopup.game.loadGame(loadedGame, true) optionsPopup.game.loadGame(loadedGame, callFromLoadScreen = true)
optionsPopup.close() optionsPopup.close()
} catch (ex: Exception) { } catch (ex: Exception) {
ToastPopup(ex.message ?: ex::class.java.simpleName, optionsPopup.stageToShowOn).open(true) ToastPopup(ex.message ?: ex::class.java.simpleName, optionsPopup.stageToShowOn).open(true)

View File

@ -9,12 +9,13 @@ import com.unciv.ui.images.ImageGetter
import com.unciv.ui.components.extensions.isEnabled import com.unciv.ui.components.extensions.isEnabled
import com.unciv.ui.components.input.onClick import com.unciv.ui.components.input.onClick
import com.unciv.ui.components.input.onDoubleClick 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 private var theChosenOne: BaseUnit? = null
init { init {
UncivGame.Current.settings.autoPlay.stopAutoPlay() worldScreen.autoPlay.stopAutoPlay()
closeButton.isVisible = false closeButton.isVisible = false
rightSideButton.setText("Choose a free great person".tr()) rightSideButton.setText("Choose a free great person".tr())

View File

@ -29,6 +29,7 @@ import com.unciv.ui.popups.Popup
import com.unciv.ui.popups.ToastPopup import com.unciv.ui.popups.ToastPopup
import com.unciv.logic.github.Github import com.unciv.logic.github.Github
import com.unciv.logic.github.Github.folderNameToRepoName import com.unciv.logic.github.Github.folderNameToRepoName
import com.unciv.ui.popups.closeAllPopups
import com.unciv.utils.Concurrency import com.unciv.utils.Concurrency
import com.unciv.utils.Log import com.unciv.utils.Log
import com.unciv.utils.launchOnGLThread import com.unciv.utils.launchOnGLThread
@ -122,14 +123,13 @@ class LoadGameScreen : LoadOrSaveScreen() {
} }
private fun onLoadGame() { private fun onLoadGame() {
UncivGame.Current.settings.autoPlay.stopAutoPlay()
if (selectedSave == null) return if (selectedSave == null) return
val loadingPopup = LoadingPopup(this) val loadingPopup = LoadingPopup(this)
Concurrency.run(loadGame) { Concurrency.run(loadGame) {
try { try {
// This is what can lead to ANRs - reading the file and setting the transients, that's why this is in another thread // 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!!) val loadedGame = game.files.loadGameFromFile(selectedSave!!)
game.loadGame(loadedGame, true) game.loadGame(loadedGame, callFromLoadScreen = true)
} catch (notAPlayer: UncivShowableException) { } catch (notAPlayer: UncivShowableException) {
launchOnGLThread { launchOnGLThread {
val (message) = getLoadExceptionMessage(notAPlayer) val (message) = getLoadExceptionMessage(notAPlayer)
@ -155,7 +155,7 @@ class LoadGameScreen : LoadOrSaveScreen() {
try { try {
val clipboardContentsString = Gdx.app.clipboard.contents.trim() val clipboardContentsString = Gdx.app.clipboard.contents.trim()
val loadedGame = UncivFiles.gameInfoFromString(clipboardContentsString) val loadedGame = UncivFiles.gameInfoFromString(clipboardContentsString)
game.loadGame(loadedGame, true) game.loadGame(loadedGame, callFromLoadScreen = true)
} catch (ex: Exception) { } catch (ex: Exception) {
launchOnGLThread { handleLoadGameException(ex, "Could not load game from clipboard!") } launchOnGLThread { handleLoadGameException(ex, "Could not load game from clipboard!") }
} finally { } finally {
@ -181,7 +181,7 @@ class LoadGameScreen : LoadOrSaveScreen() {
Concurrency.run(Companion.loadFromCustomLocation) { Concurrency.run(Companion.loadFromCustomLocation) {
game.files.loadGameFromCustomLocation( game.files.loadGameFromCustomLocation(
{ {
Concurrency.run { game.loadGame(it, true) } Concurrency.run { game.loadGame(it, callFromLoadScreen = true) }
}, },
{ {
if (it !is PlatformSaverLoader.Cancelled) if (it !is PlatformSaverLoader.Cancelled)

View File

@ -35,7 +35,7 @@ object QuickSave {
} }
fun load(screen: WorldScreen) { fun load(screen: WorldScreen) {
UncivGame.Current.settings.autoPlay.stopAutoPlay() screen.autoPlay.stopAutoPlay()
val files = UncivGame.Current.files val files = UncivGame.Current.files
val toast = ToastPopup("Quickloading...", screen) val toast = ToastPopup("Quickloading...", screen)
Concurrency.run("QuickLoadGame") { Concurrency.run("QuickLoadGame") {
@ -58,7 +58,6 @@ object QuickSave {
} }
fun autoLoadGame(screen: MainMenuScreen) { fun autoLoadGame(screen: MainMenuScreen) {
UncivGame.Current.settings.autoPlay.stopAutoPlay()
val loadingPopup = LoadingPopup(screen) val loadingPopup = LoadingPopup(screen)
Concurrency.run("autoLoadGame") { Concurrency.run("autoLoadGame") {
// Load game from file to class on separate thread to avoid ANR... // Load game from file to class on separate thread to avoid ANR...

View File

@ -85,7 +85,7 @@ class VictoryScreen(
} }
init { init {
UncivGame.Current.settings.autoPlay.stopAutoPlay() worldScreen.autoPlay.stopAutoPlay()
//**************** Set up the tabs **************** //**************** Set up the tabs ****************
splitPane.setFirstWidget(tabs) splitPane.setFirstWidget(tabs)
val iconSize = Constants.headingFontSize.toFloat() val iconSize = Constants.headingFontSize.toFloat()
@ -161,7 +161,7 @@ class VictoryScreen(
displayWonOrLost("[$winningCiv] has won a [$victoryType] Victory!", victory.defeatString) displayWonOrLost("[$winningCiv] has won a [$victoryType] Victory!", victory.defeatString)
music.chooseTrack(playerCiv.civName, MusicMood.Defeat, EnumSet.of(MusicTrackChooserFlags.SuffixMustMatch)) music.chooseTrack(playerCiv.civName, MusicMood.Defeat, EnumSet.of(MusicTrackChooserFlags.SuffixMustMatch))
} }
UncivGame.Current.settings.autoPlay.stopAutoPlay() worldScreen.autoPlay.stopAutoPlay()
} }
private fun displayWonOrLost(vararg descriptions: String) { private fun displayWonOrLost(vararg descriptions: String) {

View File

@ -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.NextTurnProgress
import com.unciv.ui.screens.worldscreen.status.StatusButtons import com.unciv.ui.screens.worldscreen.status.StatusButtons
import com.unciv.ui.screens.worldscreen.topbar.WorldScreenTopBar 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.UnitTable
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsTable import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsTable
import com.unciv.utils.Concurrency import com.unciv.utils.Concurrency
@ -81,6 +82,7 @@ import kotlin.concurrent.timer
*/ */
class WorldScreen( class WorldScreen(
val gameInfo: GameInfo, val gameInfo: GameInfo,
val autoPlay: AutoPlay,
val viewingCiv: Civilization, val viewingCiv: Civilization,
restoreState: RestoreState? = null restoreState: RestoreState? = null
) : BaseScreen() { ) : BaseScreen() {
@ -130,6 +132,7 @@ class WorldScreen(
internal val undoHandler = UndoHandler(this) internal val undoHandler = UndoHandler(this)
init { init {
// notifications are right-aligned, they take up only as much space as necessary. // notifications are right-aligned, they take up only as much space as necessary.
notificationsScroll.width = stage.width / 2 notificationsScroll.width = stage.width / 2
@ -328,7 +331,7 @@ class WorldScreen(
launchOnGLThread { launchOnGLThread {
loadingGamePopup.close() loadingGamePopup.close()
} }
startNewScreenJob(latestGame) startNewScreenJob(latestGame, autoPlay)
} catch (ex: Throwable) { } catch (ex: Throwable) {
launchOnGLThread { launchOnGLThread {
val (message) = LoadGameScreen.getLoadExceptionMessage(ex, "Couldn't download the latest game state!") 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 the game has ended, lets stop AutoPlay
if (game.settings.autoPlay.isAutoPlaying() if (autoPlay.isAutoPlaying() && !gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.checkForVictory())) {
&& !gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.checkForVictory())) { autoPlay.stopAutoPlay()
game.settings.autoPlay.stopAutoPlay()
} }
if (!hasOpenPopups() && !game.settings.autoPlay.isAutoPlaying() && isPlayersTurn) { if (!hasOpenPopups() && !autoPlay.isAutoPlaying() && isPlayersTurn) {
when { when {
viewingCiv.shouldShowDiplomaticVotingResults() -> viewingCiv.shouldShowDiplomaticVotingResults() ->
UncivGame.Current.pushScreen(DiplomaticVoteResultScreen(gameInfo.diplomaticVictoryVotesCast, viewingCiv)) UncivGame.Current.pushScreen(DiplomaticVoteResultScreen(gameInfo.diplomaticVictoryVotesCast, viewingCiv))
!gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.checkForVictory()) -> !gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.checkForVictory()) ->
game.pushScreen(VictoryScreen(this)) game.pushScreen(VictoryScreen(this))
viewingCiv.greatPeople.freeGreatPeople > 0 -> viewingCiv.greatPeople.freeGreatPeople > 0 ->
game.pushScreen(GreatPersonPickerScreen(viewingCiv)) game.pushScreen(GreatPersonPickerScreen(this, viewingCiv))
viewingCiv.popupAlerts.any() -> AlertPopup(this, viewingCiv.popupAlerts.first()) viewingCiv.popupAlerts.any() -> AlertPopup(this, viewingCiv.popupAlerts.first())
viewingCiv.tradeRequests.isNotEmpty() -> { viewingCiv.tradeRequests.isNotEmpty() -> {
// In the meantime this became invalid, perhaps because we accepted previous trades // In the meantime this became invalid, perhaps because we accepted previous trades
@ -648,7 +650,7 @@ class WorldScreen(
progressBar.increment() progressBar.increment()
startNewScreenJob(gameInfoClone) startNewScreenJob(gameInfoClone, autoPlay)
} }
} }
@ -701,7 +703,7 @@ class WorldScreen(
} else { } else {
if (!game.settings.autoPlay.showAutoPlayButton) { if (!game.settings.autoPlay.showAutoPlayButton) {
statusButtons.autoPlayStatusButton = null 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 = timer("Resize", daemon = true, 500L, Long.MAX_VALUE) {
resizeDeferTimer?.cancel() resizeDeferTimer?.cancel()
resizeDeferTimer = null 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]. */ /** 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 { Concurrency.run {
val newWorldScreen = try { val newWorldScreen = try {
UncivGame.Current.loadGame(gameInfo) UncivGame.Current.loadGame(gameInfo, autoPlay)
} catch (notAPlayer: UncivShowableException) { } catch (notAPlayer: UncivShowableException) {
withGLContext { withGLContext {
val (message) = LoadGameScreen.getLoadExceptionMessage(notAPlayer) val (message) = LoadGameScreen.getLoadExceptionMessage(notAPlayer)

View File

@ -11,7 +11,7 @@ import com.unciv.ui.screens.worldscreen.WorldScreen
class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen, scrollable = Scrollability.All) { class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen, scrollable = Scrollability.All) {
init { init {
UncivGame.Current.settings.autoPlay.stopAutoPlay() worldScreen.autoPlay.stopAutoPlay()
defaults().fillX() defaults().fillX()
addButton("Main menu") { addButton("Main menu") {

View File

@ -3,13 +3,14 @@ package com.unciv.ui.screens.worldscreen.status
import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.Stage import com.badlogic.gdx.scenes.scene2d.Stage
import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.GUI
import com.unciv.logic.automation.civilization.NextTurnAutomation import com.unciv.logic.automation.civilization.NextTurnAutomation
import com.unciv.logic.automation.unit.UnitAutomation import com.unciv.logic.automation.unit.UnitAutomation
import com.unciv.logic.civilization.managers.TurnManager import com.unciv.logic.civilization.managers.TurnManager
import com.unciv.ui.components.input.KeyboardBinding import com.unciv.ui.components.input.KeyboardBinding
import com.unciv.ui.popups.AnimatedMenuPopup import com.unciv.ui.popups.AnimatedMenuPopup
import com.unciv.ui.screens.worldscreen.WorldScreen 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 * The "context" menu for the AutoPlay button
@ -20,18 +21,19 @@ class AutoPlayMenu(
private val nextTurnButton: NextTurnButton, private val nextTurnButton: NextTurnButton,
private val worldScreen: WorldScreen private val worldScreen: WorldScreen
) : AnimatedMenuPopup(stage, getActorTopRight(positionNextTo)) { ) : AnimatedMenuPopup(stage, getActorTopRight(positionNextTo)) {
private val settings = GUI.getSettings()
private val autoPlay: AutoPlay = worldScreen.autoPlay
init { init {
// We need to activate the end turn button again after the menu closes // We need to activate the end turn button again after the menu closes
afterCloseCallback = { worldScreen.shouldUpdate = true } afterCloseCallback = { worldScreen.shouldUpdate = true }
} }
override fun createContentTable(): Table { override fun createContentTable(): Table {
val table = super.createContentTable()!! val table = super.createContentTable()!!
// Using the same keyboard binding for bypassing this menu and the default option // Using the same keyboard binding for bypassing this menu and the default option
if (!worldScreen.gameInfo.gameParameters.isOnlineMultiplayer) 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 End Turn", KeyboardBinding.AutoPlayMenuEndTurn, ::autoPlayEndTurn)).row()
table.add(getButton("AutoPlay Military Once", KeyboardBinding.AutoPlayMenuMilitary, ::autoPlayMilitary)).row() table.add(getButton("AutoPlay Military Once", KeyboardBinding.AutoPlayMenuMilitary, ::autoPlayMilitary)).row()
table.add(getButton("AutoPlay Civilians Once", KeyboardBinding.AutoPlayMenuCivilians, ::autoPlayCivilian)).row() table.add(getButton("AutoPlay Civilians Once", KeyboardBinding.AutoPlayMenuCivilians, ::autoPlayCivilian)).row()
@ -41,33 +43,61 @@ class AutoPlayMenu(
} }
private fun autoPlayEndTurn() { private fun autoPlayEndTurn() {
TurnManager(worldScreen.viewingCiv).automateTurn() val endTurnFunction = {
worldScreen.nextTurn() 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() { private fun multiturnAutoPlay() {
settings.autoPlay.startAutoPlay() worldScreen.autoPlay.startMultiturnAutoPlay()
nextTurnButton.update() nextTurnButton.update()
} }
private fun autoPlayMilitary() { private fun autoPlayMilitary() {
val civInfo = worldScreen.viewingCiv val civInfo = worldScreen.viewingCiv
val isAtWar = civInfo.isAtWar() val autoPlayMilitaryFunction = {
val sortedUnits = civInfo.units.getCivUnits().filter { it.isMilitary() }.sortedBy { unit -> NextTurnAutomation.getUnitPriority(unit, isAtWar) } val isAtWar = civInfo.isAtWar()
for (unit in sortedUnits) UnitAutomation.automateUnitMoves(unit) 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) for (city in civInfo.cities) UnitAutomation.tryBombardEnemy(city)
worldScreen.shouldUpdate = true worldScreen.shouldUpdate = true
worldScreen.render(0f) }
if (civInfo.units.getCivUnitsSize() > 30) {
autoPlay.runAutoPlayJobInNewThread("AutoPlayMilitary", worldScreen, true, autoPlayMilitaryFunction)
} else {
autoPlay.autoPlayTurnInProgress = true
autoPlayMilitaryFunction()
autoPlay.autoPlayTurnInProgress = false
}
} }
private fun autoPlayCivilian() { private fun autoPlayCivilian() {
val civInfo = worldScreen.viewingCiv val civInfo = worldScreen.viewingCiv
val isAtWar = civInfo.isAtWar() val autoPlayCivilainFunction = {
val sortedUnits = civInfo.units.getCivUnits().filter { it.isCivilian() }.sortedBy { unit -> NextTurnAutomation.getUnitPriority(unit, isAtWar) } val isAtWar = civInfo.isAtWar()
for (unit in sortedUnits) UnitAutomation.automateUnitMoves(unit) val sortedUnits = civInfo.units.getCivUnits().filter { it.isCivilian() }
worldScreen.shouldUpdate = true .sortedBy { unit -> NextTurnAutomation.getUnitPriority(unit, isAtWar) }
worldScreen.render(0f) 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() { private fun autoPlayEconomy() {

View File

@ -23,17 +23,16 @@ class AutoPlayStatusButton(
init { init {
add(Stack(autoPlayImage)).pad(5f) add(Stack(autoPlayImage)).pad(5f)
val settings = GUI.getSettings()
onActivation(binding = KeyboardBinding.AutoPlayMenu) { onActivation(binding = KeyboardBinding.AutoPlayMenu) {
if (settings.autoPlay.isAutoPlaying()) if (worldScreen.autoPlay.isAutoPlaying())
settings.autoPlay.stopAutoPlay() worldScreen.autoPlay.stopAutoPlay()
else if (worldScreen.viewingCiv == worldScreen.gameInfo.currentPlayerCiv) else if (worldScreen.isPlayersTurn)
AutoPlayMenu(stage,this, nextTurnButton, worldScreen) AutoPlayMenu(stage,this, nextTurnButton, worldScreen)
} }
val directAutoPlay = { val directAutoPlay = {
if (!worldScreen.gameInfo.gameParameters.isOnlineMultiplayer if (!worldScreen.gameInfo.gameParameters.isOnlineMultiplayer
&& worldScreen.viewingCiv == worldScreen.gameInfo.currentPlayerCiv) { && worldScreen.viewingCiv == worldScreen.gameInfo.currentPlayerCiv) {
settings.autoPlay.startAutoPlay() worldScreen.autoPlay.startMultiturnAutoPlay()
nextTurnButton.update() nextTurnButton.update()
} }
} }
@ -48,9 +47,8 @@ class AutoPlayStatusButton(
} }
override fun dispose() { override fun dispose() {
val settings = GUI.getSettings() if (isPressed && worldScreen.autoPlay.isAutoPlaying()) {
if (isPressed && settings.autoPlay.isAutoPlaying()) { worldScreen.autoPlay.stopAutoPlay()
settings.autoPlay.stopAutoPlay()
} }
} }
} }

View File

@ -27,9 +27,9 @@ enum class NextTurnAction(protected val text: String, val color: Color) {
}, },
AutoPlay("AutoPlay", Color.WHITE) { AutoPlay("AutoPlay", Color.WHITE) {
override fun isChoice(worldScreen: WorldScreen) = override fun isChoice(worldScreen: WorldScreen) =
UncivGame.Current.settings.autoPlay.isAutoPlaying() worldScreen.autoPlay.isAutoPlaying()
override fun action(worldScreen: WorldScreen) = override fun action(worldScreen: WorldScreen) =
UncivGame.Current.settings.autoPlay.stopAutoPlay() worldScreen.autoPlay.stopAutoPlay()
}, },
Working(Constants.working, Color.GRAY) { Working(Constants.working, Color.GRAY) {
override fun isChoice(worldScreen: WorldScreen) = override fun isChoice(worldScreen: WorldScreen) =

View File

@ -14,6 +14,7 @@ import com.unciv.ui.images.IconTextButton
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.hasOpenPopups import com.unciv.ui.popups.hasOpenPopups
import com.unciv.ui.screens.worldscreen.WorldScreen import com.unciv.ui.screens.worldscreen.WorldScreen
import com.unciv.utils.Concurrency
class NextTurnButton( class NextTurnButton(
private val worldScreen: WorldScreen private val worldScreen: WorldScreen
@ -33,19 +34,17 @@ class NextTurnButton(
fun update() { fun update() {
nextTurnAction = getNextTurnAction(worldScreen) nextTurnAction = getNextTurnAction(worldScreen)
updateButton(nextTurnAction) updateButton(nextTurnAction)
val settings = GUI.getSettings() val autoPlay = worldScreen.autoPlay
if (!settings.autoPlay.autoPlayTurnInProgress && settings.autoPlay.isAutoPlaying() if (autoPlay.shouldContinueAutoPlaying() && worldScreen.isPlayersTurn
&& worldScreen.isPlayersTurn && !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning()) { && !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning()) {
settings.autoPlay.autoPlayTurnInProgress = true autoPlay.runAutoPlayJobInNewThread("MultiturnAutoPlay", worldScreen, false) {
if (!worldScreen.viewingCiv.isSpectator())
TurnManager(worldScreen.viewingCiv).automateTurn() TurnManager(worldScreen.viewingCiv).automateTurn()
worldScreen.nextTurn() worldScreen.nextTurn()
if (!settings.autoPlay.autoPlayUntilEnd) autoPlay.endTurnMultiturnAutoPlay()
settings.autoPlay.turnsToAutoPlay-- }
settings.autoPlay.autoPlayTurnInProgress = false
} }
isEnabled = nextTurnAction.getText (worldScreen) == "AutoPlay" isEnabled = nextTurnAction.getText (worldScreen) == "AutoPlay"
|| (!worldScreen.hasOpenPopups() && worldScreen.isPlayersTurn || (!worldScreen.hasOpenPopups() && worldScreen.isPlayersTurn
&& !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning()) && !worldScreen.waitingForAutosave && !worldScreen.isNextTurnUpdateRunning())
if (isEnabled) addTooltip(KeyboardBinding.NextTurn) else addTooltip("") if (isEnabled) addTooltip(KeyboardBinding.NextTurn) else addTooltip("")

View File

@ -98,7 +98,7 @@ class NextTurnProgress(
// On first update the button text is not yet updated. To stabilize geometry, do it now // On first update the button text is not yet updated. To stabilize geometry, do it now
if (progress == 0) nextTurnButton?.apply { if (progress == 0) nextTurnButton?.apply {
disable() disable()
if (UncivGame.Current.settings.autoPlay.isAutoPlaying()) if (GUI.getWorldScreenIfActive()?.autoPlay?.isAutoPlaying() == true)
updateButton(NextTurnAction.AutoPlay) updateButton(NextTurnAction.AutoPlay)
else updateButton(NextTurnAction.Working) else updateButton(NextTurnAction.Working)
barWidth = width - removeHorizontalPad - barWidth = width - removeHorizontalPad -

View 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
}

View File

@ -20,7 +20,7 @@ object UnitActionsPillage {
internal fun getPillageActions(unit: MapUnit, tile: Tile): Sequence<UnitAction> { internal fun getPillageActions(unit: MapUnit, tile: Tile): Sequence<UnitAction> {
val pillageAction = getPillageAction(unit, tile) val pillageAction = getPillageAction(unit, tile)
?: return emptySequence() ?: 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) return sequenceOf(pillageAction)
else return sequenceOf(UnitAction(UnitActionType.Pillage, 65f, pillageAction.title) { else return sequenceOf(UnitAction(UnitActionType.Pillage, 65f, pillageAction.title) {
val pillageText = "Are you sure you want to pillage this [${tile.getImprovementToPillageName()!!}]?" val pillageText = "Are you sure you want to pillage this [${tile.getImprovementToPillageName()!!}]?"