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:
SomeTroglodyte 2021-07-24 20:43:12 +02:00 committed by GitHub
parent 7a42ab3e99
commit f9bef4a917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 419 additions and 341 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -244,8 +244,7 @@ Width =
Radius =
Enable Religion =
Show advanced settings =
Hide advanced settings =
Advanced Settings =
RNG Seed =
Map Height =
Temperature extremeness =

View File

@ -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().")

View File

@ -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()
}
}

View File

@ -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."
}

View File

@ -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> {

View File

@ -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()
}
}

View File

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

View File

@ -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)

View File

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

View File

@ -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()
}
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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"