Added experimental scenarios!

This commit is contained in:
Yair Morgenstern 2023-12-23 23:36:14 +02:00
parent 8e2b443427
commit c3d592393e
9 changed files with 116 additions and 11 deletions

View File

@ -826,6 +826,7 @@ Default Font =
Max zoom out =
Enable Easter Eggs =
Enable Scenarios (experimental) =
Enlarge selected notifications =
Generate translation files =
@ -1835,6 +1836,7 @@ Date ↓ =
Stars ↓ =
Status ↓ =
Scenarios =
# Uniques that are relevant to more than one type of game object

View File

@ -89,7 +89,7 @@ class Civilization : IsPartOfGameInfoSerialization {
@Transient
val units = UnitManager(this)
@Transient
var threatManager = ThreatManager(this)
@ -321,7 +321,9 @@ class Civilization : IsPartOfGameInfoSerialization {
* city-states to contain the barbarians. Therefore, [getKnownCivs] will **not** list the barbarians
* for major civs, but **will** do so for city-states after some gameplay.
*/
fun getKnownCivs() = diplomacy.values.asSequence().map { it.otherCiv() }.filter { !it.isDefeated() }
fun getKnownCivs() = diplomacy.values.asSequence().map { it.otherCiv() }
.filter { !it.isDefeated() && !it.isSpectator() }
fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName)
fun knows(otherCiv: Civilization) = knows(otherCiv.civName)

View File

@ -15,9 +15,12 @@ import com.unciv.logic.GameInfoPreview
import com.unciv.logic.GameInfoSerializationVersion
import com.unciv.logic.HasGameInfoSerializationVersion
import com.unciv.logic.UncivShowableException
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.civilization.managers.TurnManager
import com.unciv.models.metadata.GameSettings
import com.unciv.models.metadata.doMigrations
import com.unciv.models.metadata.isMigrationNecessary
import com.unciv.models.ruleset.RulesetCache
import com.unciv.ui.screens.savescreens.Gzip
import com.unciv.utils.Concurrency
import com.unciv.utils.Log
@ -309,6 +312,31 @@ class UncivFiles(
getGeneralSettingsFile().writeString(json().toJson(gameSettings), false, Charsets.UTF_8.name())
}
val scenarioFolder = "scenarios"
fun getScenarioFiles() = sequence {
for (mod in RulesetCache.values) {
val modFolder = mod.folderLocation ?: continue
val scenarioFolder = modFolder.child(scenarioFolder)
if (scenarioFolder.exists())
for (file in scenarioFolder.list())
yield(Pair(file, mod))
}
}
fun loadScenario(gameFile: FileHandle): GameInfo {
val game = loadGameFromFile(gameFile)
game.civilizations.removeAll { it.isSpectator() }
if (game.civilizations.none { it.isHuman() })
game.civilizations.first { it.isMajorCiv() }.playerType = PlayerType.Human
game.currentPlayerCiv = game.civilizations.first { it.playerType == PlayerType.Human }
game.currentPlayer = game.currentPlayerCiv.civName
TurnManager(game.currentPlayerCiv).startTurn()
return game
}
companion object {
var saveZipped = false

View File

@ -91,7 +91,7 @@ class GameSettings {
var androidHideSystemUi = true
var multiplayer = GameSettingsMultiplayer()
var autoPlay = GameSettingsAutoPlay()
var enableEspionageOption = false
@ -336,21 +336,21 @@ 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
}

View File

@ -26,6 +26,7 @@ enum class KeyboardBinding(
Multiplayer(Category.MainMenu), // Name disambiguation maybe soon, not yet necessary
MapEditor(Category.MainMenu, "Map editor", KeyCharAndCode('E')),
ModManager(Category.MainMenu, "Mods", KeyCharAndCode('D')),
Scenarios(Category.MainMenu, "Scenarios", KeyCharAndCode('S')),
MainMenuOptions(Category.MainMenu, "Options", KeyCharAndCode('O')), // Separate binding from World where it's Ctrl-O default
// Worldscreen

View File

@ -17,8 +17,8 @@ import com.unciv.Constants
import com.unciv.GUI
import com.unciv.UncivGame
import com.unciv.models.metadata.GameSettings
import com.unciv.models.metadata.ModCategories
import com.unciv.models.metadata.GameSettings.ScreenSize
import com.unciv.models.metadata.ModCategories
import com.unciv.models.translations.TranslationFileWriter
import com.unciv.models.translations.tr
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
@ -42,11 +42,11 @@ import com.unciv.utils.Concurrency
import com.unciv.utils.Display
import com.unciv.utils.ScreenOrientation
import com.unciv.utils.launchOnGLThread
import java.util.UUID
import java.util.zip.Deflater
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.UUID
import java.util.zip.Deflater
fun advancedTab(
optionsPopup: OptionsPopup,

View File

@ -167,6 +167,12 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
{ game.pushScreen(ModManagementScreen()) }
column2.add(modsTable).row()
if (game.files.getScenarioFiles().any()){
val scenarioTable = getMenuButton("Scenarios", "OtherIcons/Mods", KeyboardBinding.Scenarios)
{ game.pushScreen(ScenarioScreen()) }
column2.add(scenarioTable).row()
}
val optionsTable = getMenuButton("Options", "OtherIcons/Options", KeyboardBinding.MainMenuOptions)
{ openOptionsPopup() }
optionsTable.onLongPress { openOptionsPopup(withDebug = true) }
@ -352,3 +358,5 @@ class MainMenuScreen: BaseScreen(), RecreateOnResize {
// We contain a map...
override fun getShortcutDispatcherVetoer() = KeyShortcutDispatcherVeto.createTileGroupMapDispatcherVetoer()
}

View File

@ -0,0 +1,44 @@
package com.unciv.ui.screens.mainmenuscreen
import com.badlogic.gdx.files.FileHandle
import com.unciv.models.translations.tr
import com.unciv.ui.components.extensions.enable
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.components.input.onClick
import com.unciv.ui.screens.pickerscreens.PickerScreen
import com.unciv.utils.Concurrency
class ScenarioScreen: PickerScreen() {
private var scenarioToLoad: FileHandle? = null
init {
val scenarioFiles = game.files.getScenarioFiles()
rightSideButton.setText("Choose scenario")
Concurrency.run {
for ((file, mod) in scenarioFiles) {
try {
val scenarioPreview = game.files.loadGamePreviewFromFile(file)
Concurrency.runOnGLThread {
topTable.add(file.name().toTextButton().onClick {
descriptionLabel.setText("Mod: [${mod.name}]".tr())
scenarioToLoad = file
rightSideButton.setText(file.name())
rightSideButton.enable()
})
}
} catch (ex: Exception) { } // invalid, couldn't even load preview, probably invalid json
}
}
rightSideButton.onClick {
if (scenarioToLoad != null)
Concurrency.run {
val scenario = game.files.loadScenario(scenarioToLoad!!)
game.loadGame(scenario)
}
}
setDefaultCloseAction()
}
}

20
docs/Modders/Scenarios.md Normal file
View File

@ -0,0 +1,20 @@
# Scenarios
Scenarios are specific game states, set up so a player has a specific experience.
These can range from just having cities and units in specific places, to having full-blown custom rulesets to support them.
When creating a mod, we differentiate the *ruleset* from the *scenario* - the scenario is just a specific game state, or in other words - a saved game.
To create a scenario:
- Create a new game with the players you want, AND a spectator
- Enter the game as the spectator, and edit the save using the console
- Save the game, copy the game save file to a "scenarios" folder in your mod
## Console
To open the console, click the "`" button on your keyboard.
To see available commands, click enter. This works for subcommands as well (e.g. when you entered `tile`)