mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-23 13:10:54 +07:00
New game and custom/mod maps UI update and sync fixes (#11423)
* Offer custom map mod/ruleset and name as master/child SelectBoxes * Let map Preview class (partial parse) include starting locations * Implement button to use map-selected nations * Show a LoadingImage while the maps are still loading * Fix merge errors * Usability improvements * More out-of-sync fixes and improvements * Template
This commit is contained in:
parent
c037776674
commit
b8706c1330
@ -349,6 +349,7 @@ Four Corners =
|
||||
Archipelago =
|
||||
Inner Sea =
|
||||
Perlin =
|
||||
Select players from starting locations =
|
||||
Random number of Civilizations =
|
||||
Min number of Civilizations =
|
||||
Max number of Civilizations =
|
||||
|
@ -40,22 +40,20 @@ object MapSaver {
|
||||
|
||||
private fun mapFromJson(json: String): TileMap = json().fromJson(TileMap::class.java, json)
|
||||
|
||||
/** Class to parse only the parameters out of a map file */
|
||||
private class TileMapPreview {
|
||||
val mapParameters = MapParameters()
|
||||
}
|
||||
|
||||
fun loadMapParameters(mapFile: FileHandle): MapParameters {
|
||||
return mapParametersFromSavedString(mapFile.readString())
|
||||
return loadMapPreview(mapFile).mapParameters
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
fun mapParametersFromSavedString(mapString: String): MapParameters {
|
||||
fun loadMapPreview(mapFile: FileHandle): TileMap.Preview {
|
||||
return mapPreviewFromSavedString(mapFile.readString())
|
||||
}
|
||||
|
||||
private fun mapPreviewFromSavedString(mapString: String): TileMap.Preview {
|
||||
val unzippedJson = try {
|
||||
Gzip.unzip(mapString.trim())
|
||||
} catch (_: Exception) {
|
||||
mapString
|
||||
}
|
||||
return json().fromJson(TileMapPreview::class.java, unzippedJson).mapParameters
|
||||
return json().fromJson(TileMap.Preview::class.java, unzippedJson)
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
|
||||
/**
|
||||
* creates a hexagonal map of given radius (filled with grassland)
|
||||
*
|
||||
* To help you visualize how UnCiv hexagonal cooridinate system works, here's a small example:
|
||||
* To help you visualize how UnCiv hexagonal coordinate system works, here's a small example:
|
||||
*
|
||||
* _____ _____ _____
|
||||
* / \ / \ / \
|
||||
@ -119,10 +119,10 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
|
||||
* / 1 ,-2 \_____/ 0,-1 \_____/ -1,0 \_____/ -2,1 \
|
||||
* \ / \ / \ / \ /
|
||||
* \_____/ 0,-2 \_____/ -1,-1 \_____/ -2,0 \_____/
|
||||
* / \ / \ / \ /
|
||||
* / 0,-3 \_____/ -1,-2 \_____/ -2,-1 \_____/
|
||||
* \ / \ / \ /
|
||||
* \_____/ \_____/ \_____/
|
||||
* / \ / \ / \ / \
|
||||
* / 0,-3 \_____/ -1,-2 \_____/ -2,-1 \_____/ -3,0 \
|
||||
* \ / \ / \ / \ /
|
||||
* \_____/ \_____/ \_____/ \_____/
|
||||
*
|
||||
*
|
||||
* The rules are simple if you think about your X and Y axis as diagonal w.r.t. a standard carthesian plane. As such:
|
||||
@ -132,8 +132,9 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
|
||||
* moving "up-right" and "down-left": moving along Y axis
|
||||
* moving "up-left" and "down-right": moving along X axis
|
||||
*
|
||||
* Tip: you can always use the in-game map editor if you have any doubt
|
||||
* */
|
||||
* Tip: you can always use the in-game map editor if you have any doubt,
|
||||
* and the "secret" options can turn on coordinate display on the main map.
|
||||
*/
|
||||
constructor(radius: Int, ruleset: Ruleset, worldWrap: Boolean = false)
|
||||
: this (HexMath.getNumberOfTilesInHexagon(radius)) {
|
||||
startingLocations.clear()
|
||||
@ -749,4 +750,11 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
/** Class to parse only the parameters and starting locations out of a map file */
|
||||
class Preview {
|
||||
val mapParameters = MapParameters()
|
||||
private val startingLocations = arrayListOf<StartingLocation>()
|
||||
fun getDeclaredNations() = startingLocations.asSequence().map { it.nation }.distinct()
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,11 @@ fun <T> Iterable<T>.toGdxArray(): Array<T> {
|
||||
for (it in this) arr.add(it)
|
||||
return arr
|
||||
}
|
||||
fun <T> Sequence<T>.toGdxArray(): Array<T> {
|
||||
val arr = Array<T>()
|
||||
for (it in this) arr.add(it)
|
||||
return arr
|
||||
}
|
||||
|
||||
/** [yield][SequenceScope.yield]s [element] if it's not null */
|
||||
suspend fun <T> SequenceScope<T>.yieldIfNotNull(element: T?) {
|
||||
|
@ -132,6 +132,8 @@ class LoadingImage(
|
||||
if (animated) hideAnimated(onComplete)
|
||||
else hideDelayed(onComplete)
|
||||
|
||||
fun isShowing() = loadingIcon.isVisible && actions.isEmpty
|
||||
|
||||
//region Hiding helpers
|
||||
private fun hideAnimated(onComplete: (() -> Unit)?) {
|
||||
actions.clear()
|
||||
|
@ -42,9 +42,9 @@ class GameOptionsTable(
|
||||
private val updatePlayerPickerTable: (desiredCiv: String) -> Unit,
|
||||
private val updatePlayerPickerRandomLabel: () -> Unit
|
||||
) : Table(BaseScreen.skin) {
|
||||
var gameParameters = previousScreen.gameSetupInfo.gameParameters
|
||||
var ruleset = previousScreen.ruleset
|
||||
var locked = false
|
||||
private var gameParameters = previousScreen.gameSetupInfo.gameParameters
|
||||
private var ruleset = previousScreen.ruleset
|
||||
internal var locked = false
|
||||
|
||||
private var baseRulesetHash = gameParameters.baseRuleset.hashCode()
|
||||
|
||||
@ -56,7 +56,7 @@ class GameOptionsTable(
|
||||
*
|
||||
* The second reason this is public: [NewGameScreen] accesses [ModCheckboxTable.savedModcheckResult] for display.
|
||||
*/
|
||||
val modCheckboxes = getModCheckboxes(isPortrait = isPortrait)
|
||||
internal val modCheckboxes = getModCheckboxes(isPortrait = isPortrait)
|
||||
|
||||
// Remember this so we can unselect it when the pool dialog returns an empty pool
|
||||
private var randomNationsPoolCheckbox: CheckBox? = null
|
||||
@ -74,12 +74,12 @@ class GameOptionsTable(
|
||||
clear()
|
||||
|
||||
// Mods may have changed (e.g. custom map selection)
|
||||
modCheckboxes.updateSelection()
|
||||
val newBaseRulesetHash = gameParameters.baseRuleset.hashCode()
|
||||
if (newBaseRulesetHash != baseRulesetHash) {
|
||||
baseRulesetHash = newBaseRulesetHash
|
||||
modCheckboxes.setBaseRuleset(gameParameters.baseRuleset)
|
||||
}
|
||||
modCheckboxes.updateSelection()
|
||||
|
||||
add(Table().apply {
|
||||
defaults().pad(5f)
|
||||
@ -442,6 +442,7 @@ class GameOptionsTable(
|
||||
fun updateRuleset(ruleset: Ruleset) {
|
||||
this.ruleset = ruleset
|
||||
gameParameters.acceptedModCheckErrors = ""
|
||||
modCheckboxes.updateSelection()
|
||||
modCheckboxes.setBaseRuleset(gameParameters.baseRuleset)
|
||||
}
|
||||
|
||||
@ -493,6 +494,11 @@ class GameOptionsTable(
|
||||
|
||||
updatePlayerPickerTable(desiredCiv)
|
||||
}
|
||||
|
||||
fun changeGameParameters(newGameParameters: GameParameters) {
|
||||
gameParameters = newGameParameters
|
||||
modCheckboxes.changeGameParameters(newGameParameters)
|
||||
}
|
||||
}
|
||||
|
||||
private class RandomNationPickerPopup(
|
||||
|
@ -1,57 +1,98 @@
|
||||
package com.unciv.ui.screens.newgamescreen
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor
|
||||
import com.badlogic.gdx.scenes.scene2d.Group
|
||||
import com.badlogic.gdx.scenes.scene2d.actions.Actions
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Cell
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Container
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.files.MapSaver
|
||||
import com.unciv.logic.map.MapParameters
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.metadata.Player
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.nation.Nation
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.extensions.disable
|
||||
import com.unciv.ui.components.extensions.enable
|
||||
import com.unciv.ui.components.extensions.pad
|
||||
import com.unciv.ui.components.extensions.toGdxArray
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.extensions.toTextButton
|
||||
import com.unciv.ui.components.input.onActivation
|
||||
import com.unciv.ui.components.input.onChange
|
||||
import com.unciv.ui.components.widgets.LoadingImage
|
||||
import com.unciv.ui.popups.AnimatedMenuPopup
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.screens.victoryscreen.LoadMapPreview
|
||||
import com.unciv.utils.Concurrency
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.isActive
|
||||
import com.badlogic.gdx.utils.Array as GdxArray
|
||||
|
||||
class MapFileSelectTable(
|
||||
private val newGameScreen: NewGameScreen,
|
||||
private val mapParameters: MapParameters
|
||||
) : Table() {
|
||||
|
||||
private val mapCategorySelectBox = SelectBox<String>(BaseScreen.skin)
|
||||
private val mapFileSelectBox = SelectBox<MapWrapper>(BaseScreen.skin)
|
||||
private val loadingIcon = LoadingImage(30f, LoadingImage.Style(loadingColor = Color.SCARLET))
|
||||
private val useNationsFromMapButton = "Select players from starting locations".toTextButton(AnimatedMenuPopup.SmallButtonStyle())
|
||||
private val useNationsButtonCell: Cell<Actor?>
|
||||
private var mapNations = emptyList<Nation>()
|
||||
private val miniMapWrapper = Container<Group?>()
|
||||
private var mapPreviewJob: Job? = null
|
||||
private var preselectedName = mapParameters.name
|
||||
|
||||
// The SelectBox auto displays the text a object.toString(), which on the FileHandle itself includes the folder path.
|
||||
// So we wrap it in another object with a custom toString()
|
||||
private class MapWrapper(val fileHandle: FileHandle, val mapParameters: MapParameters) {
|
||||
override fun toString(): String = mapParameters.baseRuleset + " | " + fileHandle.name()
|
||||
private class MapWrapper(val fileHandle: FileHandle, val mapPreview: TileMap.Preview) {
|
||||
override fun toString(): String = fileHandle.name()
|
||||
fun getCategoryName(): String = fileHandle.parent().parent().name()
|
||||
.ifEmpty { mapPreview.mapParameters.baseRuleset }
|
||||
}
|
||||
private val mapWrappers = ArrayList<MapWrapper>()
|
||||
|
||||
private val columnWidth = newGameScreen.getColumnWidth()
|
||||
|
||||
private val collator = UncivGame.Current.settings.getCollatorFromLocale()
|
||||
|
||||
init {
|
||||
defaults().pad(5f, 10f) // Must stay same as in MapParametersTable
|
||||
val mapFileLabel = "{Map file}:".toLabel()
|
||||
add(mapFileLabel).left()
|
||||
add(mapFileSelectBox)
|
||||
add(Table().apply {
|
||||
defaults().pad(5f, 10f) // Must stay same as in MapParametersTable
|
||||
val mapCategoryLabel = "{Map Mod}:".toLabel()
|
||||
val mapFileLabel = "{Map file}:".toLabel()
|
||||
// because SOME people gotta give the hugest names to their maps
|
||||
.width(columnWidth - 40f - mapFileLabel.prefWidth)
|
||||
.right().row()
|
||||
val selectBoxWidth = columnWidth - 80f -
|
||||
mapFileLabel.prefWidth.coerceAtLeast(mapCategoryLabel.prefWidth)
|
||||
add(mapCategoryLabel).left()
|
||||
add(mapCategorySelectBox).width(selectBoxWidth).right().row()
|
||||
add(mapFileLabel).left()
|
||||
add(mapFileSelectBox).width(selectBoxWidth).right().row()
|
||||
}).growX()
|
||||
|
||||
add(loadingIcon).padRight(5f).padLeft(0f).row()
|
||||
|
||||
useNationsButtonCell = add().pad(0f)
|
||||
row()
|
||||
|
||||
add(miniMapWrapper)
|
||||
.pad(15f)
|
||||
.maxWidth(columnWidth - 20f)
|
||||
.colspan(2).center().row()
|
||||
|
||||
mapFileSelectBox.onChange { onSelectBoxChange() }
|
||||
mapCategorySelectBox.onChange { onCategorySelectBoxChange() }
|
||||
mapFileSelectBox.onChange { onFileSelectBoxChange() }
|
||||
useNationsFromMapButton.onActivation { onUseNationsFromMap() }
|
||||
|
||||
addMapWrappersAsync()
|
||||
}
|
||||
@ -66,21 +107,58 @@ class MapFileSelectTable(
|
||||
}.sortedByDescending { it.lastModified() }
|
||||
|
||||
private fun addMapWrappersAsync() {
|
||||
val mapFilesSequence = getMapFilesSequence()
|
||||
val mapFilesFlow = getMapFilesSequence().asFlow().map {
|
||||
MapWrapper(it, MapSaver.loadMapPreview(it))
|
||||
}
|
||||
|
||||
loadingIcon.show()
|
||||
Concurrency.run {
|
||||
for (mapFile in mapFilesSequence) {
|
||||
val mapParameters = try {
|
||||
MapSaver.loadMapParameters(mapFile)
|
||||
} catch (_: Exception) {
|
||||
continue
|
||||
mapFilesFlow
|
||||
.onEach {
|
||||
mapWrappers.add(it)
|
||||
Concurrency.runOnGLThread {
|
||||
addAsyncEntryToSelectBoxes(it)
|
||||
}
|
||||
}
|
||||
mapWrappers.add(MapWrapper(mapFile, mapParameters))
|
||||
.catch {}
|
||||
.collect()
|
||||
Concurrency.runOnGLThread {
|
||||
loadingIcon.hide {
|
||||
loadingIcon.remove()
|
||||
}
|
||||
onCategorySelectBoxChange() // re-sort lower SelectBox
|
||||
}
|
||||
Concurrency.runOnGLThread { fillMapFileSelectBox() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun addAsyncEntryToSelectBoxes(mapWrapper: MapWrapper) {
|
||||
// Take the mod name where the map is stored, or if it's not a mod map, the base ruleset it's saved for
|
||||
val categoryName = mapWrapper.getCategoryName()
|
||||
|
||||
if (!mapCategorySelectBox.items.contains(categoryName, false)) {
|
||||
val sortToTop = newGameScreen.gameSetupInfo.gameParameters.baseRuleset
|
||||
val select = if (mapCategorySelectBox.selection.isEmpty) categoryName
|
||||
else mapCategorySelectBox.selected
|
||||
// keep Ruleset SelectBox sorted while async is running - few entries
|
||||
val newItems = (mapCategorySelectBox.items.asSequence() + categoryName)
|
||||
.sortedWith(
|
||||
compareBy<String?> { it != sortToTop }
|
||||
.thenBy(collator) { it }
|
||||
).toGdxArray()
|
||||
mapCategorySelectBox.selection.setProgrammaticChangeEvents(false)
|
||||
mapCategorySelectBox.items = newItems
|
||||
mapCategorySelectBox.selected = select
|
||||
mapCategorySelectBox.selection.setProgrammaticChangeEvents(true)
|
||||
}
|
||||
|
||||
if (mapCategorySelectBox.selected != categoryName) return
|
||||
|
||||
// .. but add the maps themselves as they come
|
||||
mapFileSelectBox.selection.setProgrammaticChangeEvents(false)
|
||||
mapFileSelectBox.items.add(mapWrapper)
|
||||
mapFileSelectBox.items = mapFileSelectBox.items // Call setItems so SelectBox sees the change
|
||||
mapFileSelectBox.selection.setProgrammaticChangeEvents(true)
|
||||
}
|
||||
|
||||
private val firstMap: FileHandle? by lazy {
|
||||
getMapFilesSequence().firstOrNull {
|
||||
@ -93,36 +171,42 @@ class MapFileSelectTable(
|
||||
}
|
||||
}
|
||||
|
||||
private fun FileHandle.isRecentlyModified() = lastModified() > System.currentTimeMillis() - 900000 // 900s = quarter hour
|
||||
fun isNotEmpty() = firstMap != null
|
||||
fun recentlySavedMapExists() = firstMap!=null && firstMap!!.lastModified() > System.currentTimeMillis() - 900000
|
||||
fun recentlySavedMapExists() = firstMap != null && firstMap!!.isRecentlyModified()
|
||||
|
||||
private fun fillMapFileSelectBox() {
|
||||
if (!mapFileSelectBox.items.isEmpty) return
|
||||
fun activateCustomMaps() {
|
||||
if (loadingIcon.isShowing()) return // Default map selection will be handled when background loading finishes
|
||||
preselectedName = mapParameters.name
|
||||
onFileSelectBoxChange()
|
||||
}
|
||||
|
||||
val mapFiles = GdxArray<MapWrapper>()
|
||||
mapWrappers
|
||||
.sortedWith(compareBy(UncivGame.Current.settings.getCollatorFromLocale()) { it.toString() })
|
||||
.sortedByDescending { it.mapParameters.baseRuleset == newGameScreen.gameSetupInfo.gameParameters.baseRuleset }
|
||||
.forEach { mapFiles.add(it) }
|
||||
private fun onCategorySelectBoxChange() {
|
||||
val selectedRuleset: String? = mapCategorySelectBox.selected
|
||||
val mapFiles = mapWrappers.asSequence()
|
||||
.filter { it.getCategoryName() == selectedRuleset }
|
||||
.sortedWith(compareBy(collator) { it.toString() })
|
||||
.toGdxArray()
|
||||
fun getPreselect(): MapWrapper? {
|
||||
if (mapFiles.isEmpty) return null
|
||||
val recent = mapFiles.asSequence()
|
||||
.filter { it.fileHandle.isRecentlyModified() }
|
||||
.maxByOrNull { it.fileHandle.lastModified() }
|
||||
val oldestTimestamp = mapFiles.minOfOrNull { it.fileHandle.lastModified() } ?: 0L
|
||||
// Do not use most recent if all maps in the category have the same time within a tenth of a second (like a mod unzip does)
|
||||
if (recent != null && (recent.fileHandle.lastModified() - oldestTimestamp) > 100 || mapFiles.size == 1)
|
||||
return recent
|
||||
val named = mapFiles.firstOrNull { it.fileHandle.name() == preselectedName }
|
||||
if (named != null)
|
||||
return named
|
||||
return mapFiles.first()
|
||||
}
|
||||
val selectedItem = getPreselect()
|
||||
|
||||
// Pre-select: a) map saved within last 15min or b) map named in mapParameters or c) alphabetically first
|
||||
// This is a kludge - the better way would be to have a "play this map now" menu button in the editor
|
||||
// (which would ideally not even require a save file - which makes implementation non-trivial)
|
||||
val selectedItem =
|
||||
mapFiles.maxByOrNull { it.fileHandle.lastModified() }
|
||||
?.takeIf { it.fileHandle.lastModified() > System.currentTimeMillis() - 900000 }
|
||||
?: mapFiles.firstOrNull { it.fileHandle.name() == mapParameters.name }
|
||||
?: mapFiles.firstOrNull()
|
||||
?: return
|
||||
|
||||
// since mapFileSelectBox.selection.setProgrammaticChangeEvents() defaults to true, this would
|
||||
// kill the mapParameters.name we would like to look for when determining what to pre-select -
|
||||
// so do it ***after*** getting selectedItem - but control programmaticChangeEvents anyway
|
||||
// to avoid the overhead of doing a full updateRuleset + updateTables + startMapPreview
|
||||
// avoid the overhead of doing a full updateRuleset + updateTables + startMapPreview
|
||||
// (all expensive) for something that will be overthrown momentarily
|
||||
mapFileSelectBox.selection.setProgrammaticChangeEvents(false)
|
||||
mapFileSelectBox.items = mapFiles
|
||||
|
||||
// Now, we want this ON because the event removes map selections which are pulling mods
|
||||
// that trip updateRuleset - so that code should still be active for the pre-selection
|
||||
mapFileSelectBox.selection.setProgrammaticChangeEvents(true)
|
||||
@ -131,17 +215,38 @@ class MapFileSelectTable(
|
||||
// Do NOT try to put back the "bad" preselection!
|
||||
}
|
||||
|
||||
fun onSelectBoxChange() {
|
||||
private fun onFileSelectBoxChange() {
|
||||
cancelBackgroundJobs()
|
||||
if (mapFileSelectBox.selection.isEmpty) return
|
||||
val mapFile = mapFileSelectBox.selected.fileHandle
|
||||
mapParameters.name = mapFile.name()
|
||||
newGameScreen.gameSetupInfo.mapFile = mapFile
|
||||
val mapMods = mapFileSelectBox.selected.mapParameters.mods.partition { RulesetCache[it]?.modOptions?.isBaseRuleset == true }
|
||||
val selection = mapFileSelectBox.selected
|
||||
|
||||
val mapMods = selection.mapPreview.mapParameters.mods
|
||||
.partition { RulesetCache[it]?.modOptions?.isBaseRuleset == true }
|
||||
newGameScreen.gameSetupInfo.gameParameters.mods = LinkedHashSet(mapMods.second)
|
||||
newGameScreen.gameSetupInfo.gameParameters.baseRuleset = mapMods.first.firstOrNull()
|
||||
?: mapFileSelectBox.selected.mapParameters.baseRuleset
|
||||
?: selection.mapPreview.mapParameters.baseRuleset
|
||||
val success = newGameScreen.tryUpdateRuleset(updateUI = true)
|
||||
|
||||
mapNations = if (success)
|
||||
selection.mapPreview.getDeclaredNations()
|
||||
.mapNotNull { newGameScreen.ruleset.nations[it] }
|
||||
.filter { it.isMajorCiv }
|
||||
.toList()
|
||||
else emptyList()
|
||||
|
||||
if (mapNations.isEmpty()) {
|
||||
useNationsButtonCell.setActor(null)
|
||||
useNationsButtonCell.height(0f).pad(0f)
|
||||
} else {
|
||||
useNationsFromMapButton.enable()
|
||||
useNationsButtonCell.setActor(useNationsFromMapButton)
|
||||
useNationsButtonCell.height(useNationsFromMapButton.prefHeight).padLeft(5f).padTop(10f)
|
||||
}
|
||||
|
||||
val mapFile = selection.fileHandle
|
||||
mapParameters.name = mapFile.name()
|
||||
newGameScreen.gameSetupInfo.mapFile = mapFile
|
||||
|
||||
newGameScreen.updateTables()
|
||||
hideMiniMap()
|
||||
if (success) {
|
||||
@ -153,7 +258,7 @@ class MapFileSelectTable(
|
||||
items.removeIndex(mapFileSelectBox.selectedIndex)
|
||||
// Changing the array itself is not enough, SelectBox gets out of sync, need to call setItems()
|
||||
mapFileSelectBox.items = items
|
||||
// Note - this will have triggered a nested onSelectBoxChange()!
|
||||
// Note - this will have triggered a nested onFileSelectBoxChange()!
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,4 +316,19 @@ class MapFileSelectTable(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun onUseNationsFromMap() {
|
||||
useNationsFromMapButton.disable()
|
||||
val players = newGameScreen.playerPickerTable.gameParameters.players
|
||||
players.clear()
|
||||
val pickForHuman = mapNations.random().name
|
||||
mapNations.asSequence()
|
||||
.map { it.name to it.name.tr(hideIcons = true) } // Sort by translation but keep untranslated name
|
||||
.sortedWith(
|
||||
compareBy<Pair<String, String>>{ it.first != pickForHuman }
|
||||
.thenBy(collator) { it.second }
|
||||
).map { Player(it.first, if (it.first == pickForHuman) PlayerType.Human else PlayerType.AI) }
|
||||
.toCollection(players)
|
||||
newGameScreen.playerPickerTable.update()
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ class MapOptionsTable(private val newGameScreen: NewGameScreen, isReset: Boolean
|
||||
MapGeneratedMainType.custom -> {
|
||||
mapParameters.type = MapGeneratedMainType.custom
|
||||
mapTypeSpecificTable.add(savedMapOptionsTable)
|
||||
savedMapOptionsTable.onSelectBoxChange()
|
||||
savedMapOptionsTable.activateCustomMaps()
|
||||
newGameScreen.unlockTables()
|
||||
}
|
||||
MapGeneratedMainType.generated -> {
|
||||
|
@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener
|
||||
import com.unciv.models.metadata.GameParameters
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.validation.ModCompatibility
|
||||
@ -18,14 +19,14 @@ import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
* A widget containing one expander for extension mods.
|
||||
* Manages compatibility checks, warns or prevents incompatibilities.
|
||||
*
|
||||
* @param mods In/out set of active mods, modified in place
|
||||
* @param mods **Reference**: In/out set of active mods, modified in place: If this needs to change, call [changeGameParameters]
|
||||
* @param initialBaseRuleset The selected base Ruleset, only for running mod checks against. Use [setBaseRuleset] to change on the fly.
|
||||
* @param screen Parent screen, used only to show [ToastPopup]s
|
||||
* @param isPortrait Used only for minor layout tweaks, arrangement is always vertical
|
||||
* @param onUpdate Callback, parameter is the mod name, called after any checks that may prevent mod selection succeed.
|
||||
*/
|
||||
class ModCheckboxTable(
|
||||
private val mods: LinkedHashSet<String>,
|
||||
private var mods: LinkedHashSet<String>,
|
||||
initialBaseRuleset: String,
|
||||
private val screen: BaseScreen,
|
||||
isPortrait: Boolean = false,
|
||||
@ -217,4 +218,8 @@ class ModCheckboxTable(
|
||||
.filter { it.widget.isChecked }
|
||||
.map { it.mod }
|
||||
.asIterable()
|
||||
|
||||
fun changeGameParameters(newGameParameters: GameParameters) {
|
||||
mods = newGameParameters.mods
|
||||
}
|
||||
}
|
||||
|
@ -41,9 +41,9 @@ import com.unciv.ui.screens.pickerscreens.PickerScreen
|
||||
import com.unciv.utils.Concurrency
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.launchOnGLThread
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import java.net.URL
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlin.math.floor
|
||||
import com.unciv.ui.components.widgets.AutoScrollPane as ScrollPane
|
||||
|
||||
@ -55,7 +55,7 @@ class NewGameScreen(
|
||||
override var gameSetupInfo = defaultGameSetupInfo ?: GameSetupInfo.fromSettings()
|
||||
override val ruleset = Ruleset() // updateRuleset will clear and add
|
||||
private val newGameOptionsTable: GameOptionsTable
|
||||
private val playerPickerTable: PlayerPickerTable
|
||||
internal val playerPickerTable: PlayerPickerTable
|
||||
private val mapOptionsTable: MapOptionsTable
|
||||
|
||||
init {
|
||||
@ -406,7 +406,7 @@ class NewGameScreen(
|
||||
fun updateTables() {
|
||||
playerPickerTable.gameParameters = gameSetupInfo.gameParameters
|
||||
playerPickerTable.update()
|
||||
newGameOptionsTable.gameParameters = gameSetupInfo.gameParameters
|
||||
newGameOptionsTable.changeGameParameters(gameSetupInfo.gameParameters)
|
||||
newGameOptionsTable.update()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user