mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-25 22:59:12 +07:00
Newgame screen overhaul for portrait mode (#4522)
* Newgame screen scrolls nicer, mods sorted, base/ext mod clearer, unified separators * Newgame-Mods-UI - sort Nations
This commit is contained in:
parent
7a42ab3e99
commit
f9bef4a917
@ -268,8 +268,7 @@ Radius =
|
||||
# Requires translation!
|
||||
Enable Religion =
|
||||
|
||||
Show advanced settings = Mostrar configurações avançadas
|
||||
Hide advanced settings = Esconder configurações avançadas
|
||||
Advanced Settings = Configurações Avançadas
|
||||
# Requires translation!
|
||||
RNG Seed =
|
||||
Map Height = Altura do Mapa
|
||||
|
@ -255,8 +255,7 @@ Radius =
|
||||
# Requires translation!
|
||||
Enable Religion =
|
||||
|
||||
Show advanced settings = Afficher les paramètres avancés
|
||||
Hide advanced settings = Cacher les paramètres avancés
|
||||
Advanced Settings = Paramètres Avancés
|
||||
RNG Seed = Graine du générateur aléatoire
|
||||
Map Height = Altitude moyenne
|
||||
Temperature extremeness = Amplitude thermique
|
||||
|
@ -130,7 +130,7 @@ You refused to stop settling cities near us = Ihr habt euch geweigert, auf Stadt
|
||||
Your arrogant demands are in bad taste = Eure arroganten Forderungen sind geschmacklos.
|
||||
Your use of nuclear weapons is disgusting! = Euer Einsatz von Atomwaffen ist ekelhaft!
|
||||
You have stolen our lands! = Ihr habt unser Land geraubt!
|
||||
You gave us units! = Ihr habt uns Einheiten geliefert!
|
||||
You gave us units! = Ihr habt uns Einheiten geschenkt!
|
||||
|
||||
Demands = Forderungen
|
||||
Please don't settle new cities near us. = Bitte gründet keine neuen Städte in unserer Nähe.
|
||||
@ -238,23 +238,18 @@ Cultural = Kulturell
|
||||
Map Shape = Kartenform
|
||||
Hexagonal = Sechseckig
|
||||
Rectangular = Rechteckig
|
||||
# Requires translation!
|
||||
Height =
|
||||
# Requires translation!
|
||||
Width =
|
||||
# Requires translation!
|
||||
Radius =
|
||||
# Requires translation!
|
||||
Enable Religion =
|
||||
Height = Höhe
|
||||
Width = Breite
|
||||
Radius = Radius
|
||||
Enable Religion = Religion aktivieren
|
||||
|
||||
Show advanced settings = Zeige erweiterte Einstellungen
|
||||
Hide advanced settings = Verstecke erweiterte Einstellungen
|
||||
Advanced Settings = Fortgeschrittene Einstellungen
|
||||
RNG Seed = Seed
|
||||
Map Height = Erhebungen
|
||||
Temperature extremeness = Temperaturextreme
|
||||
Resource richness = Ressourcenreichtum
|
||||
Vegetation richness = Vegetationsreichtum
|
||||
Rare features richness = Reichtum an seltenen Merkmalen
|
||||
Rare features richness = Außergewöhnliches Gelände
|
||||
Max Coast extension = Maximale Küstenausdehnung
|
||||
Biome areas extension = Biombereichausdehnung
|
||||
Water level = Wasser-Niveau
|
||||
@ -564,8 +559,7 @@ Pillage = Plündern
|
||||
Are you sure you want to pillage this [improvement]? = Bist du sicher, dass du die Feldverbesserung [improvement] plündern willst?
|
||||
Create [improvement] = Erzeuge [improvement]
|
||||
Start Golden Age = Goldenes Zeitalter starten
|
||||
# Requires translation!
|
||||
Show more =
|
||||
Show more = Weitere Befehle
|
||||
Yes = Ja
|
||||
No = Nein
|
||||
Acquire = Übernehmen
|
||||
@ -577,8 +571,7 @@ Gold = Gold
|
||||
Happiness = Zufriedenheit
|
||||
Culture = Kultur
|
||||
Science = Wissenschaft
|
||||
# Requires translation!
|
||||
Faith =
|
||||
Faith = Glaube
|
||||
|
||||
Crop Yield = Ernteertrag
|
||||
Territory = Territorium
|
||||
@ -746,8 +739,7 @@ Luxury resource = Luxus-ressource
|
||||
Strategic resource = Strategische Ressource
|
||||
Fresh water = Frischwasser
|
||||
non-fresh water = nicht frisches Wasser
|
||||
# Requires translation!
|
||||
Natural Wonder =
|
||||
Natural Wonder = Naturwunder
|
||||
|
||||
# improvementFilters
|
||||
|
||||
@ -1299,8 +1291,7 @@ Colosseum = Kolosseum
|
||||
'Regard your soldiers as your children, and they will follow you into the deepest valleys; look on them as your own beloved sons, and they will stand by you even unto death.' - Sun Tzu = 'Behandle Deine Soldaten als seien sie Deine Kinder und sie werden Dir in die tiefsten Täler folgen; betrachte sie als Deine geliebten Söhne und sie werden an Deiner Seite stehen, sogar bis zum Tode.' - Sun Tzu
|
||||
Terracotta Army = Terrakotta-Armee
|
||||
|
||||
# Requires translation!
|
||||
Temple =
|
||||
Temple = Tempel
|
||||
|
||||
National College = Nationale Hochschule
|
||||
|
||||
|
@ -247,8 +247,7 @@ Radius =
|
||||
# Requires translation!
|
||||
Enable Religion =
|
||||
|
||||
Show advanced settings = Avanzate
|
||||
Hide advanced settings = Nascondi avanzate
|
||||
Advanced settings = Avanzate
|
||||
RNG Seed = Seme RNG
|
||||
Map Height = Altezza mappa
|
||||
Temperature extremeness = Estremità temperatura
|
||||
|
@ -247,8 +247,7 @@ Radius =
|
||||
# Requires translation!
|
||||
Enable Religion =
|
||||
|
||||
Show advanced settings = Mostrar Opciones Avanzadas
|
||||
Hide advanced settings = Ocultar Opciones Avanzadas
|
||||
Advanced Settings = Opciones Avanzadas
|
||||
RNG Seed = Semilla RNG
|
||||
Map Height = Elevación del Mapa
|
||||
Temperature extremeness = Temperatura Extrema
|
||||
|
@ -244,8 +244,7 @@ Width =
|
||||
Radius =
|
||||
Enable Religion =
|
||||
|
||||
Show advanced settings =
|
||||
Hide advanced settings =
|
||||
Advanced Settings =
|
||||
RNG Seed =
|
||||
Map Height =
|
||||
Temperature extremeness =
|
||||
|
@ -192,8 +192,8 @@ class UncivGame(parameters: UncivGameParameters) : Game() {
|
||||
autoSaveThread.join()
|
||||
} else
|
||||
GameSaver.autoSaveSingleThreaded(gameInfo) // NO new thread
|
||||
settings.save()
|
||||
}
|
||||
settings.save()
|
||||
|
||||
threadList.filter { it !== Thread.currentThread() && it.name != "DestroyJavaVM"}.forEach {
|
||||
println (" Thread ${it.name} still running in UncivGame.dispose().")
|
||||
|
@ -1,22 +1,18 @@
|
||||
package com.unciv.ui.newgamescreen
|
||||
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Array
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.models.metadata.BaseRuleset
|
||||
import com.unciv.models.metadata.GameSpeed
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.VictoryType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.CameraStageBaseScreen
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.utils.onChange
|
||||
import com.unciv.ui.utils.toLabel
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPickerTable:(desiredCiv:String)->Unit)
|
||||
: Table(CameraStageBaseScreen.skin) {
|
||||
class GameOptionsTable(
|
||||
val previousScreen: IPreviousScreen,
|
||||
val withoutMods: Boolean = false,
|
||||
val updatePlayerPickerTable:(desiredCiv:String)->Unit
|
||||
) : Table(CameraStageBaseScreen.skin) {
|
||||
var gameParameters = previousScreen.gameSetupInfo.gameParameters
|
||||
val ruleset = previousScreen.ruleset
|
||||
var locked = false
|
||||
@ -34,36 +30,38 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick
|
||||
top()
|
||||
defaults().pad(5f)
|
||||
|
||||
add("Game Options".toLabel(fontSize = 24)).padTop(0f).padBottom(20f).colspan(2).row()
|
||||
add(Table().apply {
|
||||
defaults().pad(5f)
|
||||
//addBaseRulesetSelectBox()
|
||||
addBaseRulesetSelectBox()
|
||||
addDifficultySelectBox()
|
||||
addGameSpeedSelectBox()
|
||||
addEraSelectBox()
|
||||
}).colspan(2).row()
|
||||
addCityStatesSelectBox()
|
||||
// align left and right edges with other SelectBoxes but allow independent dropdown width
|
||||
add(Table().apply {
|
||||
addCityStatesSlider()
|
||||
}).colspan(2).fillX().row()
|
||||
}).row()
|
||||
addVictoryTypeCheckboxes()
|
||||
|
||||
val checkboxTable = Table().apply { defaults().pad(5f) }
|
||||
val checkboxTable = Table().apply { defaults().left().pad(2.5f) }
|
||||
checkboxTable.addBarbariansCheckbox()
|
||||
checkboxTable.addOneCityChallengeCheckbox()
|
||||
checkboxTable.addNuclearWeaponsCheckbox()
|
||||
checkboxTable.addIsOnlineMultiplayerCheckbox()
|
||||
if (UncivGame.Current.settings.showExperimentalReligion)
|
||||
checkboxTable.addReligionCheckbox()
|
||||
checkboxTable.addModCheckboxes()
|
||||
add(checkboxTable).colspan(2).row()
|
||||
add(checkboxTable).center().row()
|
||||
|
||||
if (!withoutMods)
|
||||
add(getModCheckboxes()).row()
|
||||
|
||||
pack()
|
||||
}
|
||||
|
||||
private fun Table.addCheckbox(text: String, initialState: Boolean, lockable: Boolean = true, onChange: (newValue: Boolean) -> Unit) {
|
||||
val checkbox = CheckBox(text.tr(), CameraStageBaseScreen.skin)
|
||||
checkbox.isChecked = initialState
|
||||
val checkbox = text.toCheckBox(initialState) { onChange(it) }
|
||||
checkbox.isDisabled = lockable && locked
|
||||
checkbox.onChange { onChange(checkbox.isChecked) }
|
||||
add(checkbox).colspan(2).left().row()
|
||||
add(checkbox).colspan(2).row()
|
||||
}
|
||||
|
||||
private fun Table.addBarbariansCheckbox() =
|
||||
@ -90,25 +88,20 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick
|
||||
addCheckbox("Enable Religion", gameParameters.religionEnabled)
|
||||
{ gameParameters.religionEnabled = it }
|
||||
|
||||
private fun addCityStatesSelectBox() {
|
||||
add("{Number of City-States}:".toLabel())
|
||||
val cityStatesSelectBox = SelectBox<Int>(CameraStageBaseScreen.skin)
|
||||
|
||||
private fun Table.addCityStatesSlider() {
|
||||
val numberOfCityStates = ruleset.nations.filter { it.value.isCityState() }.size
|
||||
if (numberOfCityStates == 0) return
|
||||
|
||||
val cityStatesArray = Array<Int>(numberOfCityStates + 1)
|
||||
(0..numberOfCityStates).forEach { cityStatesArray.add(it) }
|
||||
|
||||
cityStatesSelectBox.items = cityStatesArray
|
||||
cityStatesSelectBox.selected = gameParameters.numberOfCityStates
|
||||
add(cityStatesSelectBox).width(50f).row()
|
||||
cityStatesSelectBox.isDisabled = locked
|
||||
cityStatesSelectBox.onChange {
|
||||
gameParameters.numberOfCityStates = cityStatesSelectBox.selected
|
||||
add("{Number of City-States}:".toLabel()).left().expandX()
|
||||
val slider = UncivSlider(0f,numberOfCityStates.toFloat(),1f) {
|
||||
gameParameters.numberOfCityStates = it.toInt()
|
||||
}
|
||||
slider.value = gameParameters.numberOfCityStates.toFloat()
|
||||
slider.isDisabled = locked
|
||||
add(slider).row()
|
||||
}
|
||||
|
||||
fun Table.addSelectBox(text: String, values: Collection<String>, initialState: String, onChange: (newValue: String) -> Unit) {
|
||||
private fun Table.addSelectBox(text: String, values: Collection<String>, initialState: String, onChange: (newValue: String) -> Unit) {
|
||||
add(text.toLabel()).left()
|
||||
val selectBox = TranslatedSelectBox(values, initialState, CameraStageBaseScreen.skin)
|
||||
selectBox.isDisabled = locked
|
||||
@ -123,6 +116,7 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick
|
||||
}
|
||||
|
||||
private fun Table.addBaseRulesetSelectBox() {
|
||||
if (BaseRuleset.values().size < 2) return
|
||||
addSelectBox("{Base Ruleset}:", BaseRuleset.values().map { it.fullName }, gameParameters.baseRuleset.fullName)
|
||||
{
|
||||
gameParameters.baseRuleset = BaseRuleset.values().first { br -> br.fullName == it }
|
||||
@ -152,18 +146,16 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick
|
||||
val victoryConditionsTable = Table().apply { defaults().pad(5f) }
|
||||
for (victoryType in VictoryType.values()) {
|
||||
if (victoryType == VictoryType.Neutral) continue
|
||||
val victoryCheckbox = CheckBox(victoryType.name.tr(), CameraStageBaseScreen.skin)
|
||||
victoryCheckbox.name = victoryType.name
|
||||
victoryCheckbox.isChecked = gameParameters.victoryTypes.contains(victoryType)
|
||||
victoryCheckbox.isDisabled = locked
|
||||
victoryCheckbox.onChange {
|
||||
val victoryCheckbox = victoryType.name.toCheckBox(gameParameters.victoryTypes.contains(victoryType)) {
|
||||
// If the checkbox is checked, adds the victoryTypes else remove it
|
||||
if (victoryCheckbox.isChecked) {
|
||||
if (it) {
|
||||
gameParameters.victoryTypes.add(victoryType)
|
||||
} else {
|
||||
gameParameters.victoryTypes.remove(victoryType)
|
||||
}
|
||||
}
|
||||
victoryCheckbox.name = victoryType.name
|
||||
victoryCheckbox.isDisabled = locked
|
||||
victoryConditionsTable.add(victoryCheckbox).left()
|
||||
if (++i % 2 == 0) victoryConditionsTable.row()
|
||||
}
|
||||
@ -180,8 +172,8 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick
|
||||
ImageGetter.setNewRuleset(ruleset)
|
||||
}
|
||||
|
||||
fun Table.addModCheckboxes() {
|
||||
val table = ModCheckboxTable(gameParameters.mods, previousScreen as CameraStageBaseScreen) {
|
||||
fun getModCheckboxes(isPortrait: Boolean = false): Table {
|
||||
return ModCheckboxTable(gameParameters.mods, previousScreen as CameraStageBaseScreen, isPortrait) {
|
||||
UncivGame.Current.translations.translationActiveMods = gameParameters.mods
|
||||
reloadRuleset()
|
||||
update()
|
||||
@ -196,7 +188,6 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick
|
||||
|
||||
updatePlayerPickerTable(desiredCiv)
|
||||
}
|
||||
add(table).row()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,9 +12,6 @@ interface IPreviousScreen {
|
||||
var stage: Stage
|
||||
val ruleset: Ruleset
|
||||
|
||||
/**
|
||||
* Method added for compatibility with [PlayerPickerTable] which addresses
|
||||
* [setRightSideButtonEnabled] method of previous screen
|
||||
*/
|
||||
fun setRightSideButtonEnabled(boolean: Boolean)
|
||||
// Having `fun setRightSideButtonEnabled(boolean: Boolean)` part of this interface gives a warning:
|
||||
// "Names of the parameter #1 conflict in the following members of supertypes: 'public abstract fun setRightSideButtonEnabled(boolean: Boolean): Unit defined in com.unciv.ui.newgamescreen.IPreviousScreen, public final fun setRightSideButtonEnabled(bool: Boolean): Unit defined in com.unciv.ui.pickerscreens.PickerScreen'. This may cause problems when calling this function with named arguments."
|
||||
}
|
@ -13,13 +13,14 @@ import com.unciv.ui.utils.Popup
|
||||
import com.unciv.ui.utils.onChange
|
||||
import com.unciv.ui.utils.toLabel
|
||||
|
||||
class MapOptionsTable(val newGameScreen: NewGameScreen): Table() {
|
||||
class MapOptionsTable(private val newGameScreen: NewGameScreen): Table() {
|
||||
|
||||
val mapParameters = newGameScreen.gameSetupInfo.mapParameters
|
||||
private val mapParameters = newGameScreen.gameSetupInfo.mapParameters
|
||||
private var mapTypeSpecificTable = Table()
|
||||
val generatedMapOptionsTable = MapParametersTable(mapParameters)
|
||||
private val savedMapOptionsTable = Table()
|
||||
lateinit var mapTypeSelectBox: TranslatedSelectBox
|
||||
private val mapFileSelectBox = createMapFileSelectBox()
|
||||
|
||||
private val mapFilesSequence = sequence<FileHandleWrapper> {
|
||||
yieldAll(MapSaver.getMaps().asSequence().map { FileHandleWrapper(it) })
|
||||
@ -30,16 +31,13 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() {
|
||||
}
|
||||
}
|
||||
|
||||
val mapFileSelectBox = createMapFileSelectBox()
|
||||
|
||||
init {
|
||||
defaults().pad(5f)
|
||||
add("Map Options".toLabel(fontSize = 24)).top().padBottom(20f).colspan(2).row()
|
||||
//defaults().pad(5f) - each nested table having the same can give 'stairs' effects,
|
||||
// better control directly. Besides, the first Labels/Buttons should have 10f to look nice
|
||||
addMapTypeSelection()
|
||||
}
|
||||
|
||||
private fun addMapTypeSelection() {
|
||||
add("{Map Type}:".toLabel())
|
||||
val mapTypes = arrayListOf("Generated")
|
||||
if (mapFilesSequence.any()) mapTypes.add(MapType.custom)
|
||||
mapTypeSelectBox = TranslatedSelectBox(mapTypes, "Generated", CameraStageBaseScreen.skin)
|
||||
@ -47,8 +45,10 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() {
|
||||
savedMapOptionsTable.defaults().pad(5f)
|
||||
savedMapOptionsTable.add("{Map file}:".toLabel()).left()
|
||||
// because SOME people gotta give the hugest names to their maps
|
||||
savedMapOptionsTable.add(mapFileSelectBox).maxWidth(newGameScreen.stage.width / 2)
|
||||
.right().row()
|
||||
val columnWidth = newGameScreen.stage.width / (if (newGameScreen.isNarrowerThan4to3()) 1 else 3)
|
||||
savedMapOptionsTable.add(mapFileSelectBox)
|
||||
.maxWidth((columnWidth - 120f).coerceAtLeast(120f))
|
||||
.right().row()
|
||||
|
||||
|
||||
fun updateOnMapTypeChange() {
|
||||
@ -74,8 +74,11 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() {
|
||||
|
||||
mapTypeSelectBox.onChange { updateOnMapTypeChange() }
|
||||
|
||||
add(mapTypeSelectBox).row()
|
||||
add(mapTypeSpecificTable).colspan(2).row()
|
||||
val mapTypeSelectWrapper = Table() // wrap to center-align Label and SelectBox easier
|
||||
mapTypeSelectWrapper.add("{Map Type}:".toLabel()).left().expandX()
|
||||
mapTypeSelectWrapper.add(mapTypeSelectBox).right()
|
||||
add(mapTypeSelectWrapper).pad(10f).fillX().row()
|
||||
add(mapTypeSpecificTable).row()
|
||||
}
|
||||
|
||||
private fun createMapFileSelectBox(): SelectBox<FileHandleWrapper> {
|
||||
|
@ -8,7 +8,6 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextField.TextFieldFilter.DigitsOnlyFi
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.map.*
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
/** Table for editing [mapParameters]
|
||||
@ -17,31 +16,38 @@ import com.unciv.ui.utils.*
|
||||
*
|
||||
* @param isEmptyMapAllowed whether the [MapType.empty] option should be present. Is used by the Map Editor, but should **never** be used with the New Game
|
||||
* */
|
||||
class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed: Boolean = false):
|
||||
Table() {
|
||||
class MapParametersTable(
|
||||
private val mapParameters: MapParameters,
|
||||
private val isEmptyMapAllowed: Boolean = false
|
||||
) : Table() {
|
||||
// These are accessed fom outside the class to read _and_ write values,
|
||||
// namely from MapOptionsTable, NewMapScreen and NewGameScreen
|
||||
lateinit var mapTypeSelectBox: TranslatedSelectBox
|
||||
lateinit var worldSizeSelectBox: TranslatedSelectBox
|
||||
private var customWorldSizeTable = Table ()
|
||||
private var hexagonalSizeTable = Table()
|
||||
private var rectangularSizeTable = Table()
|
||||
lateinit var noRuinsCheckbox: CheckBox
|
||||
lateinit var noNaturalWondersCheckbox: CheckBox
|
||||
lateinit var worldWrapCheckbox: CheckBox
|
||||
lateinit var customMapSizeRadius: TextField
|
||||
lateinit var customMapWidth: TextField
|
||||
lateinit var customMapHeight: TextField
|
||||
|
||||
private lateinit var worldSizeSelectBox: TranslatedSelectBox
|
||||
private var customWorldSizeTable = Table ()
|
||||
private var hexagonalSizeTable = Table()
|
||||
private var rectangularSizeTable = Table()
|
||||
private lateinit var noRuinsCheckbox: CheckBox
|
||||
private lateinit var noNaturalWondersCheckbox: CheckBox
|
||||
private lateinit var worldWrapCheckbox: CheckBox
|
||||
|
||||
// Keep references (in the key) and settings value getters (in the value) of the 'advanced' sliders
|
||||
// in a HashMap for reuse later - in the reset to defaults button. Better here as field than as closure.
|
||||
// A HashMap indexed on a Widget is problematic, as it does not define its own hashCode and equals
|
||||
// overrides nor is a Widget a data class. Seems to work anyway.
|
||||
private val advancedSliders = HashMap<UncivSlider, ()->Float>()
|
||||
|
||||
init {
|
||||
skin = CameraStageBaseScreen.skin
|
||||
defaults().pad(5f)
|
||||
defaults().pad(5f, 10f)
|
||||
addMapShapeSelectBox()
|
||||
addMapTypeSelectBox()
|
||||
addWorldSizeTable()
|
||||
addNoRuinsCheckbox()
|
||||
addNoNaturalWondersCheckbox()
|
||||
if (UncivGame.Current.settings.showExperimentalWorldWrap)
|
||||
addWorldWrapCheckbox()
|
||||
addWrappedCheckBoxes()
|
||||
addAdvancedSettings()
|
||||
}
|
||||
|
||||
@ -62,7 +68,7 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
||||
}
|
||||
|
||||
private fun addMapTypeSelectBox() {
|
||||
|
||||
// MapType is not an enum so we can't simply enumerate. //todo: make it so!
|
||||
val mapTypes = listOfNotNull(
|
||||
MapType.default,
|
||||
MapType.pangaea,
|
||||
@ -154,61 +160,49 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
||||
mapParameters.mapSize = MapSizeNew(worldSizeSelectBox.selected.value)
|
||||
}
|
||||
|
||||
private fun addNoRuinsCheckbox() {
|
||||
noRuinsCheckbox = CheckBox("No Ancient Ruins".tr(), skin)
|
||||
noRuinsCheckbox.isChecked = mapParameters.noRuins
|
||||
noRuinsCheckbox.onChange { mapParameters.noRuins = noRuinsCheckbox.isChecked }
|
||||
add(noRuinsCheckbox).colspan(2).left().row()
|
||||
private fun Table.addNoRuinsCheckbox() {
|
||||
noRuinsCheckbox = "No Ancient Ruins".toCheckBox(mapParameters.noRuins) {
|
||||
mapParameters.noRuins = it
|
||||
}
|
||||
add(noRuinsCheckbox).row()
|
||||
}
|
||||
|
||||
private fun addNoNaturalWondersCheckbox() {
|
||||
noNaturalWondersCheckbox = CheckBox("No Natural Wonders".tr(), skin)
|
||||
noNaturalWondersCheckbox.isChecked = mapParameters.noNaturalWonders
|
||||
noNaturalWondersCheckbox.onChange {
|
||||
mapParameters.noNaturalWonders = noNaturalWondersCheckbox.isChecked
|
||||
private fun Table.addNoNaturalWondersCheckbox() {
|
||||
noNaturalWondersCheckbox = "No Natural Wonders".toCheckBox(mapParameters.noNaturalWonders) {
|
||||
mapParameters.noNaturalWonders = it
|
||||
}
|
||||
add(noNaturalWondersCheckbox).colspan(2).left().row()
|
||||
add(noNaturalWondersCheckbox).row()
|
||||
}
|
||||
|
||||
private fun addWorldWrapCheckbox() {
|
||||
worldWrapCheckbox = CheckBox("World Wrap".tr(), skin)
|
||||
worldWrapCheckbox.isChecked = mapParameters.worldWrap
|
||||
worldWrapCheckbox.onChange {
|
||||
mapParameters.worldWrap = worldWrapCheckbox.isChecked
|
||||
private fun Table.addWorldWrapCheckbox() {
|
||||
worldWrapCheckbox = "World Wrap".toCheckBox(mapParameters.worldWrap) {
|
||||
mapParameters.worldWrap = it
|
||||
}
|
||||
add(worldWrapCheckbox).colspan(2).left().row()
|
||||
add("World wrap maps are very memory intensive - creating large world wrap maps on Android can lead to crashes!"
|
||||
add(worldWrapCheckbox).row()
|
||||
}
|
||||
|
||||
private fun addWrappedCheckBoxes() {
|
||||
val showWorldWrap = UncivGame.Current.settings.showExperimentalWorldWrap
|
||||
add(Table(skin).apply {
|
||||
defaults().left().pad(2.5f)
|
||||
addNoRuinsCheckbox()
|
||||
addNoNaturalWondersCheckbox()
|
||||
if (showWorldWrap) addWorldWrapCheckbox()
|
||||
}).colspan(2).center().row()
|
||||
if (showWorldWrap)
|
||||
add("World wrap maps are very memory intensive - creating large world wrap maps on Android can lead to crashes!"
|
||||
.toLabel(fontSize = 14).apply { wrap=true }).colspan(2).fillX().row()
|
||||
}
|
||||
|
||||
private fun addAdvancedSettings() {
|
||||
|
||||
val advancedSettingsTable = getAdvancedSettingsTable()
|
||||
|
||||
val button = "Show advanced settings".toTextButton()
|
||||
|
||||
add(button).colspan(2).row()
|
||||
val advancedSettingsCell = add(Table()).colspan(2)
|
||||
row()
|
||||
|
||||
button.onClick {
|
||||
advancedSettingsTable.isVisible = !advancedSettingsTable.isVisible
|
||||
|
||||
if (advancedSettingsTable.isVisible) {
|
||||
button.setText("Hide advanced settings".tr())
|
||||
advancedSettingsCell.setActor(advancedSettingsTable)
|
||||
} else {
|
||||
button.setText("Show advanced settings".tr())
|
||||
advancedSettingsCell.setActor(Table())
|
||||
}
|
||||
val expander = ExpanderTab("Advanced Settings", startsOutOpened = false) {
|
||||
addAdvancedControls(it)
|
||||
}
|
||||
|
||||
add(expander).pad(0f).padTop(10f).colspan(2).growX().row()
|
||||
}
|
||||
|
||||
private fun getAdvancedSettingsTable(): Table {
|
||||
|
||||
val advancedSettingsTable = Table()
|
||||
.apply {isVisible = false; defaults().pad(5f)}
|
||||
private fun addAdvancedControls(table: Table) {
|
||||
table.defaults().pad(5f)
|
||||
|
||||
val seedTextField = TextField(mapParameters.seed.toString(), skin)
|
||||
seedTextField.textFieldFilter = DigitsOnlyFilter()
|
||||
@ -222,17 +216,15 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
||||
}
|
||||
}
|
||||
|
||||
advancedSettingsTable.add("RNG Seed".toLabel()).left()
|
||||
advancedSettingsTable.add(seedTextField).fillX().row()
|
||||
|
||||
val sliders = HashMap<UncivSlider, ()->Float>()
|
||||
table.add("RNG Seed".toLabel()).left()
|
||||
table.add(seedTextField).fillX().padBottom(10f).row()
|
||||
|
||||
fun addSlider(text: String, getValue:()->Float, min:Float, max:Float, onChange: (value:Float)->Unit): UncivSlider {
|
||||
val slider = UncivSlider(min, max, (max - min) / 20, onChange = onChange)
|
||||
slider.value = getValue()
|
||||
advancedSettingsTable.add(text.toLabel()).left()
|
||||
advancedSettingsTable.add(slider).fillX().row()
|
||||
sliders[slider] = getValue
|
||||
table.add(text.toLabel()).left()
|
||||
table.add(slider).fillX().row()
|
||||
advancedSliders[slider] = getValue
|
||||
return slider
|
||||
}
|
||||
|
||||
@ -264,10 +256,9 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
||||
resetToDefaultButton.onClick {
|
||||
mapParameters.resetAdvancedSettings()
|
||||
seedTextField.text = mapParameters.seed.toString()
|
||||
for (entry in sliders)
|
||||
for (entry in advancedSliders)
|
||||
entry.key.value = entry.value()
|
||||
}
|
||||
advancedSettingsTable.add(resetToDefaultButton).colspan(2).row()
|
||||
return advancedSettingsTable
|
||||
table.add(resetToDefaultButton).colspan(2).padTop(10f).row()
|
||||
}
|
||||
}
|
||||
|
@ -1,89 +1,99 @@
|
||||
package com.unciv.ui.newgamescreen
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.Ruleset.CheckModLinksResult
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.utils.CameraStageBaseScreen
|
||||
import com.unciv.ui.utils.ToastPopup
|
||||
import com.unciv.ui.utils.onChange
|
||||
import com.unciv.ui.utils.toLabel
|
||||
import com.unciv.ui.utils.*
|
||||
|
||||
class ModCheckboxTable(
|
||||
private val mods:LinkedHashSet<String>,
|
||||
private val screen: CameraStageBaseScreen,
|
||||
isPortrait: Boolean = false,
|
||||
onUpdate: (String) -> Unit
|
||||
): Table(){
|
||||
private val modRulesets = RulesetCache.values.filter { it.name != "" }
|
||||
|
||||
class ModCheckboxTable(val mods:LinkedHashSet<String>, val screen: CameraStageBaseScreen, onUpdate: (String) -> Unit): Table(){
|
||||
init {
|
||||
val modRulesets = RulesetCache.values.filter { it.name != "" }
|
||||
|
||||
val baseRulesetCheckboxes = ArrayList<CheckBox>()
|
||||
val extentionRulesetModButtons = ArrayList<CheckBox>()
|
||||
val extensionRulesetModButtons = ArrayList<CheckBox>()
|
||||
|
||||
for (mod in modRulesets) {
|
||||
val checkBox = CheckBox(mod.name.tr(), CameraStageBaseScreen.skin)
|
||||
if (mod.name in mods) checkBox.isChecked = true
|
||||
for (mod in modRulesets.sortedBy { it.name }) {
|
||||
val checkBox = mod.name.toCheckBox(mod.name in mods)
|
||||
checkBox.onChange {
|
||||
if (checkBox.isChecked) {
|
||||
val modLinkErrors = mod.checkModLinks()
|
||||
if (modLinkErrors.isNotOK()) {
|
||||
ToastPopup("The mod you selected is incorrectly defined!\n\n$modLinkErrors", screen)
|
||||
if (modLinkErrors.isError()) {
|
||||
checkBox.isChecked = false
|
||||
return@onChange
|
||||
}
|
||||
}
|
||||
|
||||
val previousMods = mods.toList()
|
||||
|
||||
if (mod.modOptions.isBaseRuleset)
|
||||
for (oldBaseRuleset in previousMods) // so we don't get concurrent modification exceptions
|
||||
if (modRulesets.firstOrNull { it.name == oldBaseRuleset }?.modOptions?.isBaseRuleset == true)
|
||||
mods.remove(oldBaseRuleset)
|
||||
mods.add(mod.name)
|
||||
|
||||
var complexModLinkCheck = CheckModLinksResult()
|
||||
try {
|
||||
val newRuleset = RulesetCache.getComplexRuleset(mods)
|
||||
newRuleset.modOptions.isBaseRuleset = true // This is so the checkModLinks finds all connections
|
||||
complexModLinkCheck = newRuleset.checkModLinks()
|
||||
} catch (ex: Exception) {
|
||||
// This happens if a building is dependent on a tech not in the base ruleset
|
||||
// because newRuleset.updateBuildingCosts() in getComplexRuleset() throws an error
|
||||
complexModLinkCheck = CheckModLinksResult(Ruleset.CheckModLinksStatus.Error, ex.localizedMessage)
|
||||
}
|
||||
|
||||
if (complexModLinkCheck.isError()) {
|
||||
ToastPopup("{The mod you selected is incompatible with the defined ruleset!}\n\n{$complexModLinkCheck}", screen)
|
||||
checkBox.isChecked = false
|
||||
mods.clear()
|
||||
mods.addAll(previousMods)
|
||||
return@onChange
|
||||
}
|
||||
|
||||
} else {
|
||||
mods.remove(mod.name)
|
||||
if (checkBoxChanged(checkBox, mod)) {
|
||||
//todo: persist ExpanderTab states here
|
||||
onUpdate(mod.name)
|
||||
}
|
||||
|
||||
onUpdate(mod.name)
|
||||
|
||||
}
|
||||
if (mod.modOptions.isBaseRuleset) baseRulesetCheckboxes.add(checkBox)
|
||||
else extentionRulesetModButtons.add(checkBox)
|
||||
else extensionRulesetModButtons.add(checkBox)
|
||||
}
|
||||
|
||||
val padTop = if (isPortrait) 0f else 16f
|
||||
|
||||
if (baseRulesetCheckboxes.any()) {
|
||||
add("Base ruleset mods:".toLabel(fontSize = 24)).padTop(16f).colspan(2).row()
|
||||
val modCheckboxTable = Table().apply { defaults().pad(5f) }
|
||||
for (checkbox in baseRulesetCheckboxes) modCheckboxTable.add(checkbox).row()
|
||||
add(modCheckboxTable).colspan(2).row()
|
||||
add(ExpanderTab("Base ruleset mods:") {
|
||||
it.defaults().pad(5f,0f)
|
||||
for (checkbox in baseRulesetCheckboxes) it.add(checkbox).row()
|
||||
}).padTop(padTop).growX().row()
|
||||
}
|
||||
|
||||
if (isPortrait && baseRulesetCheckboxes.any() && extensionRulesetModButtons.any())
|
||||
addSeparator(Color.DARK_GRAY, height = 1f)
|
||||
|
||||
if (extentionRulesetModButtons.any()) {
|
||||
add("Extension mods:".toLabel(fontSize = 24)).padTop(16f).colspan(2).row()
|
||||
val modCheckboxTable = Table().apply { defaults().pad(5f) }
|
||||
for (checkbox in extentionRulesetModButtons) modCheckboxTable.add(checkbox).row()
|
||||
add(modCheckboxTable).colspan(2).row()
|
||||
if (extensionRulesetModButtons.any()) {
|
||||
add(ExpanderTab("Extension mods:") {
|
||||
it.defaults().pad(5f,0f)
|
||||
for (checkbox in extensionRulesetModButtons) it.add(checkbox).row()
|
||||
}).padTop(padTop).growX().row()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkBoxChanged(checkBox: CheckBox, mod: Ruleset): Boolean {
|
||||
if (checkBox.isChecked) {
|
||||
val modLinkErrors = mod.checkModLinks()
|
||||
if (modLinkErrors.isNotOK()) {
|
||||
ToastPopup("The mod you selected is incorrectly defined!\n\n$modLinkErrors", screen)
|
||||
if (modLinkErrors.isError()) {
|
||||
checkBox.isChecked = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val previousMods = mods.toList()
|
||||
|
||||
if (mod.modOptions.isBaseRuleset)
|
||||
for (oldBaseRuleset in previousMods) // so we don't get concurrent modification exceptions
|
||||
if (modRulesets.firstOrNull { it.name == oldBaseRuleset }?.modOptions?.isBaseRuleset == true)
|
||||
mods.remove(oldBaseRuleset)
|
||||
mods.add(mod.name)
|
||||
|
||||
var complexModLinkCheck: CheckModLinksResult
|
||||
try {
|
||||
val newRuleset = RulesetCache.getComplexRuleset(mods)
|
||||
newRuleset.modOptions.isBaseRuleset = true // This is so the checkModLinks finds all connections
|
||||
complexModLinkCheck = newRuleset.checkModLinks()
|
||||
} catch (ex: Exception) {
|
||||
// This happens if a building is dependent on a tech not in the base ruleset
|
||||
// because newRuleset.updateBuildingCosts() in getComplexRuleset() throws an error
|
||||
complexModLinkCheck = CheckModLinksResult(Ruleset.CheckModLinksStatus.Error, ex.localizedMessage)
|
||||
}
|
||||
|
||||
if (complexModLinkCheck.isError()) {
|
||||
ToastPopup("{The mod you selected is incompatible with the defined ruleset!}\n\n{$complexModLinkCheck}", screen)
|
||||
checkBox.isChecked = false
|
||||
mods.clear()
|
||||
mods.addAll(previousMods)
|
||||
return false
|
||||
}
|
||||
|
||||
} else {
|
||||
mods.remove(mod.name)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ class NationTable(val nation: Nation, width: Float, minHeight: Float, ruleset: R
|
||||
val titleText = if (ruleset == null || nation.name== Constants.random || nation.name==Constants.spectator)
|
||||
nation.name else nation.getLeaderDisplayName()
|
||||
val leaderDisplayLabel = titleText.toLabel(nation.getInnerColor(), 24)
|
||||
val leaderDisplayNameMaxWidth = internalWidth - 80 // for the nation indicator
|
||||
val leaderDisplayNameMaxWidth = internalWidth - 90f // 70 for the nation indicator + 20 extra
|
||||
if (leaderDisplayLabel.width > leaderDisplayNameMaxWidth) { // for instance Polish has really long [x] of [y] translations
|
||||
leaderDisplayLabel.wrap = true
|
||||
titleTable.add(leaderDisplayLabel).width(leaderDisplayNameMaxWidth)
|
||||
|
@ -2,6 +2,7 @@ package com.unciv.ui.newgamescreen
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.files.FileHandle
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.SelectBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Skin
|
||||
import com.badlogic.gdx.utils.Array
|
||||
@ -37,29 +38,27 @@ class GameSetupInfo(var gameId:String, var gameParameters: GameParameters, var m
|
||||
}
|
||||
}
|
||||
|
||||
class NewGameScreen(private val previousScreen: CameraStageBaseScreen, _gameSetupInfo: GameSetupInfo?=null): IPreviousScreen, PickerScreen(disableScroll = true) {
|
||||
class NewGameScreen(
|
||||
private val previousScreen: CameraStageBaseScreen,
|
||||
_gameSetupInfo: GameSetupInfo? = null
|
||||
): IPreviousScreen, PickerScreen() {
|
||||
|
||||
override val gameSetupInfo = _gameSetupInfo ?: GameSetupInfo()
|
||||
override var ruleset = RulesetCache.getComplexRuleset(gameSetupInfo.gameParameters.mods) // needs to be set because the GameOptionsTable etc. depend on this
|
||||
var newGameOptionsTable = GameOptionsTable(this) { desiredCiv: String -> playerPickerTable.update(desiredCiv) }
|
||||
private val newGameOptionsTable = GameOptionsTable(this, isNarrowerThan4to3()) { desiredCiv: String -> playerPickerTable.update(desiredCiv) }
|
||||
|
||||
// Has to be defined before the mapOptionsTable, since the mapOptionsTable refers to it on init
|
||||
private var playerPickerTable = PlayerPickerTable(this, gameSetupInfo.gameParameters)
|
||||
private var mapOptionsTable = MapOptionsTable(this)
|
||||
|
||||
private val playerPickerTable = PlayerPickerTable(
|
||||
this, gameSetupInfo.gameParameters,
|
||||
if (isNarrowerThan4to3()) stage.width - 20f else 0f
|
||||
)
|
||||
private val mapOptionsTable = MapOptionsTable(this)
|
||||
|
||||
init {
|
||||
setDefaultCloseAction(previousScreen)
|
||||
|
||||
topTable.add(ScrollPane(newGameOptionsTable).apply { setOverscroll(false, false) })
|
||||
.width(stage.width / 3).padTop(20f).top()
|
||||
topTable.addSeparatorVertical()
|
||||
topTable.add(ScrollPane(mapOptionsTable).apply { setOverscroll(false, false) })
|
||||
.width(stage.width / 3).padTop(20f).top()
|
||||
topTable.addSeparatorVertical()
|
||||
topTable.add(ScrollPane(playerPickerTable)
|
||||
.apply { setOverscroll(false, false) }
|
||||
.apply { setScrollingDisabled(true, false) })
|
||||
.width(stage.width / 3).padTop(20f).top()
|
||||
if (isNarrowerThan4to3()) initPortrait()
|
||||
else initLandscape()
|
||||
|
||||
updateRuleset()
|
||||
|
||||
@ -90,7 +89,6 @@ class NewGameScreen(private val previousScreen: CameraStageBaseScreen, _gameSetu
|
||||
|
||||
Gdx.input.inputProcessor = null // remove input processing - nothing will be clicked!
|
||||
|
||||
|
||||
if (mapOptionsTable.mapTypeSelectBox.selected.value == MapType.custom){
|
||||
val map = MapSaver.loadMap(gameSetupInfo.mapFile!!)
|
||||
val rulesetIncompatibilities = HashSet<String>()
|
||||
@ -136,16 +134,61 @@ class NewGameScreen(private val previousScreen: CameraStageBaseScreen, _gameSetu
|
||||
}
|
||||
}
|
||||
|
||||
private fun initLandscape() {
|
||||
scrollPane.setScrollingDisabled(true,true)
|
||||
|
||||
topTable.add("Game Options".toLabel(fontSize = 24)).pad(20f, 0f)
|
||||
topTable.addSeparatorVertical(Color.BLACK, 1f)
|
||||
topTable.add("Map Options".toLabel(fontSize = 24)).pad(20f,0f)
|
||||
topTable.addSeparatorVertical(Color.BLACK, 1f)
|
||||
topTable.add("Civilizations".toLabel(fontSize = 24)).pad(20f,0f)
|
||||
topTable.addSeparator(Color.CLEAR, height = 1f)
|
||||
|
||||
topTable.add(ScrollPane(newGameOptionsTable)
|
||||
.apply { setOverscroll(false, false) })
|
||||
.width(stage.width / 3).top()
|
||||
topTable.addSeparatorVertical(Color.CLEAR, 1f)
|
||||
topTable.add(ScrollPane(mapOptionsTable)
|
||||
.apply { setOverscroll(false, false) })
|
||||
.width(stage.width / 3).top()
|
||||
topTable.addSeparatorVertical(Color.CLEAR, 1f)
|
||||
topTable.add(playerPickerTable) // No ScrollPane, PlayerPickerTable has its own
|
||||
.width(stage.width / 3).top()
|
||||
}
|
||||
|
||||
private fun initPortrait() {
|
||||
scrollPane.setScrollingDisabled(false,false)
|
||||
|
||||
topTable.add(ExpanderTab("Game Options") {
|
||||
it.add(newGameOptionsTable).row()
|
||||
}).expandX().fillX().row()
|
||||
topTable.addSeparator(Color.DARK_GRAY, height = 1f)
|
||||
|
||||
topTable.add(newGameOptionsTable.getModCheckboxes(isPortrait = true)).expandX().fillX().row()
|
||||
topTable.addSeparator(Color.DARK_GRAY, height = 1f)
|
||||
|
||||
topTable.add(ExpanderTab("Map Options") {
|
||||
it.add(mapOptionsTable).row()
|
||||
}).expandX().fillX().row()
|
||||
topTable.addSeparator(Color.DARK_GRAY, height = 1f)
|
||||
|
||||
(playerPickerTable.playerListTable.parent as ScrollPane).setScrollingDisabled(true,true)
|
||||
topTable.add(ExpanderTab("Civilizations") {
|
||||
it.add(playerPickerTable).row()
|
||||
}).expandX().fillX().row()
|
||||
}
|
||||
|
||||
private fun newGameThread() {
|
||||
try {
|
||||
newGame = GameStarter.startNewGame(gameSetupInfo)
|
||||
} catch (exception: Exception) {
|
||||
Gdx.app.postRunnable {
|
||||
val cantMakeThatMapPopup = Popup(this)
|
||||
cantMakeThatMapPopup.addGoodSizedLabel("It looks like we can't make a map with the parameters you requested!".tr()).row()
|
||||
cantMakeThatMapPopup.addGoodSizedLabel("Maybe you put too many players into too small a map?".tr()).row()
|
||||
cantMakeThatMapPopup.addCloseButton()
|
||||
cantMakeThatMapPopup.open()
|
||||
Popup(this).apply {
|
||||
addGoodSizedLabel("It looks like we can't make a map with the parameters you requested!".tr()).row()
|
||||
addGoodSizedLabel("Maybe you put too many players into too small a map?".tr()).row()
|
||||
addCloseButton()
|
||||
open()
|
||||
}
|
||||
Gdx.input.inputProcessor = stage
|
||||
rightSideButton.enable()
|
||||
rightSideButton.setText("Start game!".tr())
|
||||
@ -168,10 +211,11 @@ class NewGameScreen(private val previousScreen: CameraStageBaseScreen, _gameSetu
|
||||
GameSaver.saveGame(newGame!!, newGame!!.gameId, true)
|
||||
} catch (ex: Exception) {
|
||||
Gdx.app.postRunnable {
|
||||
val cantUploadNewGamePopup = Popup(this)
|
||||
cantUploadNewGamePopup.addGoodSizedLabel("Could not upload game!")
|
||||
cantUploadNewGamePopup.addCloseButton()
|
||||
cantUploadNewGamePopup.open()
|
||||
Popup(this).apply {
|
||||
addGoodSizedLabel("Could not upload game!")
|
||||
addCloseButton()
|
||||
open()
|
||||
}
|
||||
}
|
||||
newGame = null
|
||||
}
|
||||
|
@ -18,24 +18,30 @@ import com.unciv.models.ruleset.Nation
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.mapeditor.GameParametersScreen
|
||||
import com.unciv.ui.pickerscreens.PickerScreen
|
||||
import com.unciv.ui.utils.*
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* This [Table] is used to pick or edit players information for new game creation.
|
||||
* Could be inserted to [NewGameScreen], [GameParametersScreen] or any other [Screen]
|
||||
* Could be inserted to [NewGameScreen], [GameParametersScreen] or any other [Screen][CameraStageBaseScreen]
|
||||
* which provides [GameSetupInfo] and [Ruleset].
|
||||
* Upon player changes updates property [gameParameters]. Also updates available nations when mod changes.
|
||||
* In case it is used in map editor, as a part of [GameParametersScreen], additionally tries to
|
||||
* update units/starting location on the [previousScreen] when player deleted or
|
||||
* switched nation.
|
||||
* @param [previousScreen] [Screen] where player table is inserted, should provide [GameSetupInfo] as property,
|
||||
* updated when player added/deleted/changed
|
||||
* @param [gameParameters] contains info about number of players.
|
||||
* @param previousScreen A [Screen][CameraStageBaseScreen] where the player table is inserted, should provide [GameSetupInfo] as property, updated when a player is added/deleted/changed
|
||||
* @param gameParameters contains info about number of players.
|
||||
* @param blockWidth sets a width for the Civ "blocks". If too small a third of the stage is used.
|
||||
*/
|
||||
class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters: GameParameters): Table() {
|
||||
class PlayerPickerTable(
|
||||
val previousScreen: IPreviousScreen,
|
||||
var gameParameters: GameParameters,
|
||||
blockWidth: Float = 0f
|
||||
): Table() {
|
||||
val playerListTable = Table()
|
||||
val civBlocksWidth = previousScreen.stage.width / 3
|
||||
val civBlocksWidth = if(blockWidth <= 10f) previousScreen.stage.width / 3 - 5f else blockWidth
|
||||
|
||||
/** Locks player table for editing, currently unused, was previously used for scenarios and could be useful in the future.*/
|
||||
var locked = false
|
||||
@ -48,7 +54,6 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
||||
player.playerId = "" // This is to stop people from getting other users' IDs and cheating with them in multiplayer games
|
||||
|
||||
top()
|
||||
add("Civilizations".toLabel(fontSize = 24)).padBottom(20f).row()
|
||||
add(ScrollPane(playerListTable).apply { setOverscroll(false, false) }).width(civBlocksWidth)
|
||||
update()
|
||||
}
|
||||
@ -78,7 +83,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
||||
var player = Player()
|
||||
// no random mode - add first not spectator civ if still available
|
||||
if (noRandom) {
|
||||
val availableCiv = getAvailablePlayerCivs().firstOrNull { !it.isSpectator() }
|
||||
val availableCiv = getAvailablePlayerCivs().firstOrNull()
|
||||
if (availableCiv != null) player = Player(availableCiv.name)
|
||||
// Spectators only Humans
|
||||
else player = Player(Constants.spectator).apply { playerType = PlayerType.Human }
|
||||
@ -88,8 +93,9 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
||||
}
|
||||
playerListTable.add(addPlayerButton).pad(10f)
|
||||
}
|
||||
// can enable start game when more than 1 active player
|
||||
previousScreen.setRightSideButtonEnabled(gameParameters.players.count { it.chosenCiv != Constants.spectator } > 1)
|
||||
// enable start game when more than 1 active player
|
||||
val moreThanOnePlayer = 1 < gameParameters.players.count { it.chosenCiv != Constants.spectator }
|
||||
(previousScreen as? PickerScreen)?.setRightSideButtonEnabled(moreThanOnePlayer)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,8 +134,8 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
||||
val nationTable = getNationTable(player)
|
||||
playerTable.add(nationTable).left()
|
||||
|
||||
val playerTypeTextbutton = player.playerType.name.toTextButton()
|
||||
playerTypeTextbutton.onClick {
|
||||
val playerTypeTextButton = player.playerType.name.toTextButton()
|
||||
playerTypeTextButton.onClick {
|
||||
if (player.playerType == PlayerType.AI)
|
||||
player.playerType = PlayerType.Human
|
||||
// we cannot change Spectator player to AI type, robots not allowed to spectate :(
|
||||
@ -137,7 +143,7 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
||||
player.playerType = PlayerType.AI
|
||||
update()
|
||||
}
|
||||
playerTable.add(playerTypeTextbutton).width(100f).pad(5f).right()
|
||||
playerTable.add(playerTypeTextButton).width(100f).pad(5f).right()
|
||||
if (!locked) {
|
||||
playerTable.add("-".toLabel(Color.BLACK, 30).apply { this.setAlignment(Align.center) }
|
||||
.surroundWithCircle(40f)
|
||||
@ -149,34 +155,34 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
||||
}
|
||||
if (gameParameters.isOnlineMultiplayer && player.playerType == PlayerType.Human) {
|
||||
|
||||
val playerIdTextfield = TextField(player.playerId, CameraStageBaseScreen.skin)
|
||||
playerIdTextfield.messageText = "Please input Player ID!".tr()
|
||||
playerTable.add(playerIdTextfield).colspan(2).fillX().pad(5f)
|
||||
val playerIdTextField = TextField(player.playerId, CameraStageBaseScreen.skin)
|
||||
playerIdTextField.messageText = "Please input Player ID!".tr()
|
||||
playerTable.add(playerIdTextField).colspan(2).fillX().pad(5f)
|
||||
val errorLabel = "✘".toLabel(Color.RED)
|
||||
playerTable.add(errorLabel).pad(5f).row()
|
||||
|
||||
fun onPlayerIdTextUpdated() {
|
||||
try {
|
||||
UUID.fromString(IdChecker.checkAndReturnPlayerUuid(playerIdTextfield.text))
|
||||
player.playerId = playerIdTextfield.text.trim()
|
||||
UUID.fromString(IdChecker.checkAndReturnPlayerUuid(playerIdTextField.text))
|
||||
player.playerId = playerIdTextField.text.trim()
|
||||
errorLabel.apply { setText("✔");setFontColor(Color.GREEN) }
|
||||
} catch (ex: Exception) {
|
||||
errorLabel.apply { setText("✘");setFontColor(Color.RED) }
|
||||
}
|
||||
}
|
||||
|
||||
playerIdTextfield.addListener { onPlayerIdTextUpdated(); true }
|
||||
playerIdTextField.addListener { onPlayerIdTextUpdated(); true }
|
||||
val currentUserId = UncivGame.Current.settings.userId
|
||||
val setCurrentUserButton = "Set current user".toTextButton()
|
||||
setCurrentUserButton.onClick {
|
||||
playerIdTextfield.text = currentUserId
|
||||
playerIdTextField.text = currentUserId
|
||||
onPlayerIdTextUpdated()
|
||||
}
|
||||
playerTable.add(setCurrentUserButton).colspan(3).fillX().pad(5f).row()
|
||||
|
||||
val copyFromClipboardButton = "Player ID from clipboard".toTextButton()
|
||||
copyFromClipboardButton.onClick {
|
||||
playerIdTextfield.text = Gdx.app.clipboard.contents
|
||||
playerIdTextField.text = Gdx.app.clipboard.contents
|
||||
onPlayerIdTextUpdated()
|
||||
}
|
||||
playerTable.add(copyFromClipboardButton).colspan(3).fillX().pad(5f)
|
||||
@ -214,74 +220,116 @@ class PlayerPickerTable(val previousScreen: IPreviousScreen, var gameParameters:
|
||||
* @param player current player
|
||||
*/
|
||||
private fun popupNationPicker(player: Player) {
|
||||
val nationsPopup = Popup(previousScreen as CameraStageBaseScreen)
|
||||
val nationListTable = Table()
|
||||
|
||||
val ruleset = previousScreen.ruleset
|
||||
val height = previousScreen.stage.height * 0.8f
|
||||
nationsPopup.add(ScrollPane(nationListTable).apply { setOverscroll(false, false) })
|
||||
.size(civBlocksWidth + 10, height) // +10, because the nation table has a 5f pad, for a total of +10f
|
||||
val nationDetailsTable = Table()
|
||||
nationsPopup.add(ScrollPane(nationDetailsTable).apply { setOverscroll(false, false) })
|
||||
.size(civBlocksWidth + 10, height) // Same here, see above
|
||||
|
||||
val randomNation = Nation().apply { name = "Random"; innerColor = listOf(255, 255, 255); outerColor = listOf(0, 0, 0); setTransients() }
|
||||
val nations = ArrayList<Nation>()
|
||||
if (!noRandom) nations += randomNation
|
||||
nations += getAvailablePlayerCivs()
|
||||
for (nation in nations) {
|
||||
if (player.chosenCiv == nation.name)
|
||||
continue
|
||||
// only humans can spectate, sorry robots
|
||||
if (player.playerType == PlayerType.AI && nation.isSpectator())
|
||||
continue
|
||||
val nationTable = NationTable(nation, civBlocksWidth, 0f) // no need for min height
|
||||
nationListTable.add(nationTable).row()//.width(civBlocksWidth).row()
|
||||
nationTable.onClick {
|
||||
nationDetailsTable.clear()
|
||||
|
||||
val nationUniqueLabel = nation.getUniqueString(ruleset).toLabel(nation.getInnerColor())
|
||||
nationUniqueLabel.wrap = true
|
||||
nationDetailsTable.add(NationTable(nation, civBlocksWidth, height, ruleset))
|
||||
nationDetailsTable.onClick {
|
||||
if (previousScreen is GameParametersScreen)
|
||||
previousScreen.mapEditorScreen.tileMap.switchPlayersNation(player, nation)
|
||||
player.chosenCiv = nation.name
|
||||
nationsPopup.close()
|
||||
update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nationsPopup.pack()
|
||||
|
||||
val closeImage = ImageGetter.getImage("OtherIcons/Close")
|
||||
closeImage.setSize(30f, 30f)
|
||||
val closeImageHolder = Group() // This is to add it some more clickable space, to make it easier to click on the phone
|
||||
closeImageHolder.setSize(50f, 50f)
|
||||
closeImage.center(closeImageHolder)
|
||||
closeImageHolder.addActor(closeImage)
|
||||
closeImageHolder.onClick { nationsPopup.close() }
|
||||
closeImageHolder.setPosition(0f, nationsPopup.height, Align.topLeft)
|
||||
nationsPopup.addActor(closeImageHolder)
|
||||
|
||||
nationsPopup.open()
|
||||
NationPickerPopup(this, player).open()
|
||||
update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of available civilization for all players, according
|
||||
* to current ruleset, with exeption of city states nations and barbarians
|
||||
* @return [ArrayList] of available [Nation]s
|
||||
* Returns a list of available civilization for all players, according
|
||||
* to current ruleset, with exception of city states nations, spectator and barbarians.
|
||||
*
|
||||
* Skips nations already chosen by a player, unless parameter [dontSkipNation] says to keep a
|
||||
* specific one. That is used so the picker can be used to inspect and confirm the current selection.
|
||||
*
|
||||
* @return [Sequence] of available [Nation]s
|
||||
*/
|
||||
private fun getAvailablePlayerCivs(): ArrayList<Nation> {
|
||||
val nations = ArrayList<Nation>()
|
||||
for (nation in previousScreen.ruleset.nations.values
|
||||
.filter { it.isMajorCiv() || it.isSpectator() }) {
|
||||
if (gameParameters.players.any { it.chosenCiv == nation.name })
|
||||
continue
|
||||
nations.add(nation)
|
||||
internal fun getAvailablePlayerCivs(dontSkipNation: String? = null) =
|
||||
previousScreen.ruleset.nations.values.asSequence()
|
||||
.filter { it.isMajorCiv() }
|
||||
.filter { it.name == dontSkipNation || gameParameters.players.none { player -> player.chosenCiv == it.name } }
|
||||
|
||||
}
|
||||
|
||||
private class NationPickerPopup(
|
||||
private val playerPicker: PlayerPickerTable,
|
||||
private val player: Player
|
||||
) : Popup(playerPicker.previousScreen as CameraStageBaseScreen) {
|
||||
private val previousScreen = playerPicker.previousScreen
|
||||
private val ruleset = previousScreen.ruleset
|
||||
// This Popup's body has two halves of same size, either side by side or arranged vertically
|
||||
// depending on screen proportions - determine height for one of those
|
||||
private val partHeight = screen.stage.height * (if (screen.isNarrowerThan4to3()) 0.45f else 0.8f)
|
||||
private val civBlocksWidth = playerPicker.civBlocksWidth
|
||||
private val nationListTable = Table()
|
||||
private val nationListScroll = ScrollPane(nationListTable)
|
||||
private val nationDetailsTable = Table()
|
||||
|
||||
init {
|
||||
nationListScroll.setOverscroll(false, false)
|
||||
add(nationListScroll).size( civBlocksWidth + 10f, partHeight )
|
||||
// +10, because the nation table has a 5f pad, for a total of +10f
|
||||
if (screen.isNarrowerThan4to3()) row()
|
||||
add(ScrollPane(nationDetailsTable).apply { setOverscroll(false, false) })
|
||||
.size(civBlocksWidth + 10f, partHeight) // Same here, see above
|
||||
|
||||
val randomNation = Nation().apply {
|
||||
name = "Random"
|
||||
innerColor = listOf(255, 255, 255)
|
||||
outerColor = listOf(0, 0, 0)
|
||||
setTransients()
|
||||
}
|
||||
return nations
|
||||
val nations = ArrayList<Nation>()
|
||||
if (!playerPicker.noRandom) nations += randomNation
|
||||
val spectator = previousScreen.ruleset.nations[Constants.spectator]
|
||||
if (spectator != null) nations += spectator
|
||||
|
||||
nations += playerPicker.getAvailablePlayerCivs(player.chosenCiv)
|
||||
.sortedWith(compareBy(Collator.getInstance(), { it.name.tr() }))
|
||||
|
||||
var nationListScrollY = 0f
|
||||
var currentY = 0f
|
||||
for (nation in nations) {
|
||||
// only humans can spectate, sorry robots
|
||||
if (player.playerType == PlayerType.AI && nation.isSpectator())
|
||||
continue
|
||||
if (player.chosenCiv == nation.name)
|
||||
nationListScrollY = currentY
|
||||
val nationTable = NationTable(nation, civBlocksWidth, 0f) // no need for min height
|
||||
val cell = nationListTable.add(nationTable)
|
||||
currentY += cell.padBottom + cell.prefHeight + cell.padTop
|
||||
cell.row()
|
||||
nationTable.onClick {
|
||||
setNationDetails(nation)
|
||||
}
|
||||
if (player.chosenCiv == nation.name)
|
||||
setNationDetails(nation)
|
||||
}
|
||||
|
||||
nationListScroll.layout()
|
||||
pack()
|
||||
if (nationListScrollY > 0f) {
|
||||
// center the selected nation vertically, getRowHeight safe because nationListScrollY > 0f ensures at least 1 row
|
||||
nationListScrollY -= (nationListScroll.height - nationListTable.getRowHeight(0)) / 2
|
||||
nationListScroll.scrollY = nationListScrollY.coerceIn(0f, nationListScroll.maxY)
|
||||
}
|
||||
|
||||
val closeImage = ImageGetter.getImage("OtherIcons/Close")
|
||||
closeImage.setSize(30f, 30f)
|
||||
val closeImageHolder =
|
||||
Group() // This is to add it some more clickable space, to make it easier to click on the phone
|
||||
closeImageHolder.setSize(50f, 50f)
|
||||
closeImage.center(closeImageHolder)
|
||||
closeImageHolder.addActor(closeImage)
|
||||
closeImageHolder.onClick { close() }
|
||||
closeImageHolder.setPosition(0f, height, Align.topLeft)
|
||||
addActor(closeImageHolder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setNationDetails(nation: Nation) {
|
||||
nationDetailsTable.clear()
|
||||
|
||||
// val nationUniqueLabel = nation.getUniqueString(ruleset).toLabel(nation.getInnerColor())
|
||||
// nationUniqueLabel.wrap = true
|
||||
nationDetailsTable.add(NationTable(nation, civBlocksWidth, partHeight, ruleset))
|
||||
nationDetailsTable.onClick {
|
||||
if (previousScreen is GameParametersScreen)
|
||||
previousScreen.mapEditorScreen.tileMap.switchPlayersNation(
|
||||
player,
|
||||
nation
|
||||
)
|
||||
player.chosenCiv = nation.name
|
||||
close()
|
||||
playerPicker.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,20 +6,21 @@ import com.unciv.UncivGame
|
||||
import com.unciv.ui.utils.*
|
||||
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
|
||||
|
||||
open class PickerScreen(val disableScroll: Boolean = false) : CameraStageBaseScreen() {
|
||||
open class PickerScreen(disableScroll: Boolean = false) : CameraStageBaseScreen() {
|
||||
|
||||
internal var closeButton: TextButton = Constants.close.toTextButton()
|
||||
protected var descriptionLabel: Label
|
||||
protected var rightSideGroup = VerticalGroup()
|
||||
private var rightSideGroup = VerticalGroup()
|
||||
protected var rightSideButton: TextButton
|
||||
protected var screenSplit = 0.85f
|
||||
private val screenSplit = 0.85f
|
||||
private val maxBottomTableHeight = 150f // about 7 lines of normal text
|
||||
|
||||
/**
|
||||
* The table displaying the choices from which to pick (usually).
|
||||
* Also the element which most of the screen realestate is devoted to displaying.
|
||||
*/
|
||||
protected var topTable: Table
|
||||
var bottomTable:Table = Table()
|
||||
protected var bottomTable:Table = Table()
|
||||
internal var splitPane: SplitPane
|
||||
protected var scrollPane: ScrollPane
|
||||
|
||||
@ -36,16 +37,16 @@ open class PickerScreen(val disableScroll: Boolean = false) : CameraStageBaseScr
|
||||
rightSideGroup.addActor(rightSideButton)
|
||||
|
||||
bottomTable.add(rightSideGroup).pad(10f).right()
|
||||
bottomTable.height = stage.height * (1 - screenSplit)
|
||||
bottomTable.height = (stage.height * (1 - screenSplit)).coerceAtMost(maxBottomTableHeight)
|
||||
|
||||
topTable = Table()
|
||||
scrollPane = ScrollPane(topTable)
|
||||
|
||||
scrollPane.setScrollingDisabled(disableScroll, disableScroll)
|
||||
scrollPane.setSize(stage.width, stage.height * screenSplit)
|
||||
scrollPane.setSize(stage.width, stage.height - bottomTable.height)
|
||||
|
||||
splitPane = SplitPane(scrollPane, bottomTable, true, skin)
|
||||
splitPane.splitAmount = screenSplit
|
||||
splitPane.splitAmount = scrollPane.height / stage.height
|
||||
splitPane.setFillParent(true)
|
||||
stage.addActor(splitPane)
|
||||
}
|
||||
|
@ -101,9 +101,13 @@ open class CameraStageBaseScreen : Screen {
|
||||
keyPressDispatcher[KeyCharAndCode.BACK] = action
|
||||
}
|
||||
|
||||
/** @return `true` if the screen is higher than it is wide */
|
||||
fun isPortrait() = stage.viewport.screenHeight > stage.viewport.screenWidth
|
||||
/** @return `true` if the screen is higher than it is wide _and_ resolution is at most 1050x700 */
|
||||
fun isCrampedPortrait() = isPortrait() &&
|
||||
game.settings.resolution.split("x").map { it.toInt() }.last() <= 700
|
||||
/** @return `true` if the screen is narrower than 4:3 landscape */
|
||||
fun isNarrowerThan4to3() = stage.viewport.screenHeight * 4 > stage.viewport.screenWidth * 3
|
||||
|
||||
fun openOptionsPopup() {
|
||||
val limitOrientationsHelper = game.limitOrientationsHelper
|
||||
|
@ -71,6 +71,9 @@ class UncivSlider (
|
||||
}
|
||||
val isDragging: Boolean
|
||||
get() = slider.isDragging
|
||||
var isDisabled: Boolean
|
||||
get() = slider.isDisabled
|
||||
set(value) { slider.isDisabled = value }
|
||||
|
||||
// Value tip format
|
||||
var tipFormat = "%.1f"
|
||||
|
Loading…
Reference in New Issue
Block a user