Random Nations game starter - reactive UI (#9127)

* Random Nations game starter - reactive UI

* Random Nations game starter - reactive UI continued

* Random Nations game starter - reactive UI - patch
This commit is contained in:
SomeTroglodyte 2023-04-07 08:53:04 +02:00 committed by GitHub
parent 21510a8455
commit 36667d9d18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 396 additions and 295 deletions

View File

@ -344,12 +344,16 @@ object GameStarter {
for (player in chosenPlayers) {
val civ = Civilization(player.chosenCiv)
if (player.chosenCiv in usedMajorCivs) {
when (player.chosenCiv) {
Constants.spectator ->
civ.playerType = player.playerType
in usedMajorCivs -> {
for (tech in startingTechs)
civ.tech.techsResearched.add(tech.name) // can't be .addTechnology because the civInfo isn't assigned yet
civ.playerType = player.playerType
civ.playerId = player.playerId
} else {
}
else ->
if (!civ.cityStateFunctions.initCityState(ruleset, newGameParameters.startingEra, unusedMajorCivs))
continue
}

View File

@ -4,5 +4,6 @@ import com.unciv.logic.IsPartOfGameInfoSerialization
enum class PlayerType : IsPartOfGameInfoSerialization {
AI,
Human
Human;
fun toggle() = if (this == AI) Human else AI
}

View File

@ -1,4 +1,4 @@
package com.unciv.models.ruleset.nation
package com.unciv.models.ruleset.nation
import com.badlogic.gdx.graphics.Color
import com.unciv.Constants
@ -15,9 +15,9 @@ import com.unciv.ui.screens.civilopediascreen.CivilopediaScreen.Companion.showRe
import com.unciv.ui.screens.civilopediascreen.FormattedLine
import kotlin.math.pow
class Nation : RulesetObject() {
class Nation : RulesetObject() {
var leaderName = ""
fun getLeaderDisplayName() = if (isCityState) name
fun getLeaderDisplayName() = if (isCityState || isSpectator) name
else "[$leaderName] of [$name]"
val style = ""
@ -36,7 +36,6 @@ import kotlin.math.pow
lateinit var outerColor: List<Int>
var uniqueName = ""
override fun getUniqueTarget() = UniqueTarget.Nation
var uniqueText = ""
var innerColor: List<Int>? = null
var startBias = ArrayList<String>()
@ -52,6 +51,10 @@ import kotlin.math.pow
var favoredReligion: String? = null
var cities: ArrayList<String> = arrayListOf()
override fun getUniqueTarget() = UniqueTarget.Nation
@Transient
private lateinit var outerColorObject: Color
fun getOuterColor(): Color = outerColorObject
@ -84,8 +87,6 @@ import kotlin.math.pow
ignoreHillMovementCost = uniques.contains("Units ignore terrain costs when moving into any tile with Hills")
}
var cities: ArrayList<String> = arrayListOf()
override fun makeLink() = "Nation/$name"
override fun getSortGroup(ruleset: Ruleset) = when {
@ -305,12 +306,12 @@ import kotlin.math.pow
else -> uniques.contains(filter)
}
}
}
}
/** All defined by https://www.w3.org/TR/WCAG20/#relativeluminancedef */
fun getRelativeLuminance(color:Color):Double{
fun getRelativeChannelLuminance(channel:Float):Double =
/** All defined by https://www.w3.org/TR/WCAG20/#relativeluminancedef */
fun getRelativeLuminance(color: Color): Double {
fun getRelativeChannelLuminance(channel: Float): Double =
if (channel < 0.03928) channel / 12.92
else ((channel + 0.055) / 1.055).pow(2.4)
@ -319,13 +320,14 @@ import kotlin.math.pow
val B = getRelativeChannelLuminance(color.b)
return 0.2126 * R + 0.7152 * G + 0.0722 * B
}
}
/** https://www.w3.org/TR/WCAG20/#contrast-ratiodef */
fun getContrastRatio(color1:Color, color2:Color): Double { // ratio can range from 1 to 21
/** https://www.w3.org/TR/WCAG20/#contrast-ratiodef */
fun getContrastRatio(color1: Color, color2: Color): Double { // ratio can range from 1 to 21
val innerColorLuminance = getRelativeLuminance(color1)
val outerColorLuminance = getRelativeLuminance(color2)
return if (innerColorLuminance > outerColorLuminance) (innerColorLuminance + 0.05) / (outerColorLuminance + 0.05)
return if (innerColorLuminance > outerColorLuminance)
(innerColorLuminance + 0.05) / (outerColorLuminance + 0.05)
else (outerColorLuminance + 0.05) / (innerColorLuminance + 0.05)
}
}

View File

@ -48,15 +48,15 @@ private class RestorableTextButtonStyle(
val restoreStyle: ButtonStyle
) : TextButtonStyle(baseStyle)
/** Disable a [Button] by setting its [touchable][Button.touchable] and [color][Button.color] properties. */
/** Disable a [Button] by setting its [touchable][Button.touchable] and [style][Button.style] properties. */
fun Button.disable() {
touchable = Touchable.disabled
val oldStyle = style
if (oldStyle is RestorableTextButtonStyle) return
val disabledStyle = BaseScreen.skin.get("disabled", TextButtonStyle::class.java)
if (oldStyle !is RestorableTextButtonStyle)
style = RestorableTextButtonStyle(disabledStyle, oldStyle)
}
/** Enable a [Button] by setting its [touchable][Button.touchable] and [color][Button.color] properties. */
/** Enable a [Button] by setting its [touchable][Button.touchable] and [style][Button.style] properties. */
fun Button.enable() {
val oldStyle = style
if (oldStyle is RestorableTextButtonStyle) {
@ -64,7 +64,7 @@ fun Button.enable() {
}
touchable = Touchable.enabled
}
/** Enable or disable a [Button] by setting its [touchable][Button.touchable] and [color][Button.color] properties,
/** Enable or disable a [Button] by setting its [touchable][Button.touchable] and [style][Button.style] properties,
* or returns the corresponding state.
*
* Do not confuse with Gdx' builtin [isDisabled][Button.isDisabled] property,

View File

@ -299,7 +299,7 @@ class MapEditorEditStartsTab(
}
private fun allowedNations() = ruleset.nations.values.asSequence()
.filter { it.name !in disallowNations }
.filter { it.name !in disallowNations && !it.hasUnique(UniqueType.CityStateDeprecated) }
private fun getNations() = allowedNations()
.sortedWith(compareBy<Nation>{ it.isCityState }.thenBy(collator) { it.name.tr() })
.map { FormattedLine("[${it.name}] starting location", it.name, "Nation/${it.name}", size = 24) }

View File

@ -5,8 +5,11 @@ import com.badlogic.gdx.scenes.scene2d.Touchable
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
import com.unciv.UncivGame
import com.unciv.logic.civilization.PlayerType
import com.unciv.models.metadata.GameParameters
import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.ruleset.nation.Nation
import com.unciv.models.ruleset.unique.UniqueType
@ -31,11 +34,13 @@ import com.unciv.ui.popups.Popup
import com.unciv.ui.popups.ToastPopup
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.multiplayerscreens.MultiplayerHelpers
import kotlin.reflect.KMutableProperty0
class GameOptionsTable(
private val previousScreen: IPreviousScreen,
private val isPortrait: Boolean = false,
private val updatePlayerPickerTable: (desiredCiv:String)->Unit
private val updatePlayerPickerTable: (desiredCiv: String) -> Unit,
private val updatePlayerPickerRandomLabel: () -> Unit
) : Table(BaseScreen.skin) {
var gameParameters = previousScreen.gameSetupInfo.gameParameters
val ruleset = previousScreen.ruleset
@ -77,12 +82,10 @@ class GameOptionsTable(
if (turnSlider != null)
add(turnSlider).padTop(10f).row()
if (gameParameters.randomNumberOfPlayers) {
addMinPlayersSlider()
addMaxPlayersSlider()
addMinMaxPlayersSliders()
}
if (gameParameters.randomNumberOfCityStates) {
addMinCityStatesSlider()
addMaxCityStatesSlider()
addMinMaxCityStatesSliders()
} else {
addCityStatesSlider()
}
@ -204,7 +207,7 @@ class GameOptionsTable(
add(button)
}
private fun numberOfPlayable() = ruleset.nations.values.count {
private fun numberOfMajorCivs() = ruleset.nations.values.count {
it.isMajorCiv
}
@ -218,64 +221,101 @@ class GameOptionsTable(
private fun Table.addRandomPlayersCheckbox() =
addCheckbox("Random number of Civilizations", gameParameters.randomNumberOfPlayers)
{
gameParameters.randomNumberOfPlayers = it
{newRandomNumberOfPlayers ->
gameParameters.randomNumberOfPlayers = newRandomNumberOfPlayers
if (newRandomNumberOfPlayers) {
// remove all random AI from player picker
val newPlayers = gameParameters.players.asSequence()
.filterNot { it.playerType == PlayerType.AI && it.chosenCiv == Constants.random }
.toCollection(ArrayList(gameParameters.players.size))
if (newPlayers.size != gameParameters.players.size) {
gameParameters.players = newPlayers
updatePlayerPickerTable("")
}
} else {
// Fill up player picker with random AI until previously active min reached
val additionalRandom = gameParameters.minNumberOfPlayers - gameParameters.players.size
if (additionalRandom > 0) {
repeat(additionalRandom) {
gameParameters.players.add(Player(Constants.random))
}
updatePlayerPickerTable("")
}
}
update() // To see the new sliders
}
private fun Table.addRandomCityStatesCheckbox() =
addCheckbox("Random number of City-States", gameParameters.randomNumberOfCityStates)
{
gameParameters.randomNumberOfCityStates = it
gameParameters.run {
randomNumberOfCityStates = it
if (it) {
if (numberOfCityStates > maxNumberOfCityStates)
maxNumberOfCityStates = numberOfCityStates
if (numberOfCityStates < minNumberOfCityStates)
minNumberOfCityStates = numberOfCityStates
} else {
if (numberOfCityStates > maxNumberOfCityStates)
numberOfCityStates = maxNumberOfCityStates
if (numberOfCityStates < minNumberOfCityStates)
numberOfCityStates = minNumberOfCityStates
}
}
update() // To see the changed sliders
}
private fun Table.addMinPlayersSlider() {
val playableAvailable = numberOfPlayable()
if (playableAvailable == 0) return
private fun Table.addLinkedMinMaxSliders(
minValue: Int, maxValue: Int,
minText: String, maxText: String,
minField: KMutableProperty0<Int>,
maxField: KMutableProperty0<Int>,
onChangeCallback: (() -> Unit)? = null
) {
if (maxValue < minValue) return
add("{Min number of Civilizations}:".toLabel()).left().expandX()
val slider = UncivSlider(2f, playableAvailable.toFloat(), 1f, initial = gameParameters.minNumberOfPlayers.toFloat()) {
gameParameters.minNumberOfPlayers = it.toInt()
@Suppress("JoinDeclarationAndAssignment") // it's a forward declaration!
lateinit var maxSlider: UncivSlider // lateinit safe because the closure won't use it until the user operates a slider
val minSlider = UncivSlider(minValue.toFloat(), maxValue.toFloat(), 1f, initial = minField.get().toFloat()) {
val newMin = it.toInt()
minField.set(newMin)
if (newMin > maxSlider.value.toInt()) {
maxSlider.value = it
maxField.set(newMin)
}
slider.isDisabled = locked
add(slider).padTop(10f).row()
onChangeCallback?.invoke()
}
minSlider.isDisabled = locked
maxSlider = UncivSlider(minValue.toFloat(), maxValue.toFloat(), 1f, initial = maxField.get().toFloat()) {
val newMax = it.toInt()
maxField.set(newMax)
if (newMax < minSlider.value.toInt()) {
minSlider.value = it
minField.set(newMax)
}
onChangeCallback?.invoke()
}
maxSlider.isDisabled = locked
add(minText.toLabel()).left().expandX()
add(minSlider).padTop(10f).row()
add(maxText.toLabel()).left().expandX()
add(maxSlider).padTop(10f).row()
}
private fun Table.addMaxPlayersSlider() {
val playableAvailable = numberOfPlayable()
if (playableAvailable == 0) return
add("{Max number of Civilizations}:".toLabel()).left().expandX()
val slider = UncivSlider(2f, playableAvailable.toFloat(), 1f, initial = gameParameters.maxNumberOfPlayers.toFloat()) {
gameParameters.maxNumberOfPlayers = it.toInt()
}
slider.isDisabled = locked
add(slider).padTop(10f).row()
private fun Table.addMinMaxPlayersSliders() {
addLinkedMinMaxSliders(2, numberOfMajorCivs(),
"{Min number of Civilizations}:", "{Max number of Civilizations}:",
gameParameters::minNumberOfPlayers, gameParameters::maxNumberOfPlayers,
updatePlayerPickerRandomLabel
)
}
private fun Table.addMinCityStatesSlider() {
val cityStatesAvailable = numberOfCityStates()
if (cityStatesAvailable == 0) return
add("{Min number of City-States}:".toLabel()).left().expandX()
val slider = UncivSlider(0f, cityStatesAvailable.toFloat(), 1f, initial = gameParameters.minNumberOfCityStates.toFloat()) {
gameParameters.minNumberOfCityStates = it.toInt()
}
slider.isDisabled = locked
add(slider).padTop(10f).row()
}
private fun Table.addMaxCityStatesSlider() {
val cityStatesAvailable = numberOfCityStates()
if (cityStatesAvailable == 0) return
add("{Max number of City-States}:".toLabel()).left().expandX()
val slider = UncivSlider(0f, cityStatesAvailable.toFloat(), 1f, initial = gameParameters.maxNumberOfCityStates.toFloat()) {
gameParameters.maxNumberOfCityStates = it.toInt()
}
slider.isDisabled = locked
add(slider).padTop(10f).row()
private fun Table.addMinMaxCityStatesSliders() {
addLinkedMinMaxSliders( 0, numberOfCityStates(),
"{Min number of City-States}:", "{Max number of City-States}:",
gameParameters::minNumberOfCityStates, gameParameters::maxNumberOfCityStates
)
}
private fun Table.addCityStatesSlider() {

View File

@ -11,22 +11,15 @@ import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.GameStarter
import com.unciv.logic.IdChecker
import com.unciv.logic.files.MapSaver
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.files.MapSaver
import com.unciv.logic.map.MapGeneratedMainType
import com.unciv.logic.multiplayer.OnlineMultiplayer
import com.unciv.logic.multiplayer.storage.FileStorageRateLimitReached
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.translations.tr
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.screens.pickerscreens.PickerScreen
import com.unciv.ui.popups.ConfirmPopup
import com.unciv.ui.popups.Popup
import com.unciv.ui.popups.ToastPopup
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.components.ExpanderTab
import com.unciv.ui.screens.basescreen.RecreateOnResize
import com.unciv.ui.components.extensions.addSeparator
import com.unciv.ui.components.extensions.addSeparatorVertical
import com.unciv.ui.components.extensions.disable
@ -35,12 +28,19 @@ import com.unciv.ui.components.extensions.onClick
import com.unciv.ui.components.extensions.pad
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.ConfirmPopup
import com.unciv.ui.popups.Popup
import com.unciv.ui.popups.ToastPopup
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.ui.screens.basescreen.RecreateOnResize
import com.unciv.ui.screens.pickerscreens.PickerScreen
import com.unciv.utils.Log
import com.unciv.utils.concurrency.Concurrency
import com.unciv.utils.concurrency.launchOnGLThread
import kotlinx.coroutines.coroutineScope
import java.net.URL
import java.util.*
import java.util.UUID
import com.unciv.ui.components.AutoScrollPane as ScrollPane
class NewGameScreen(
@ -54,6 +54,8 @@ class NewGameScreen(
private val mapOptionsTable: MapOptionsTable
init {
val isPortrait = isNarrowerThan4to3()
updateRuleset() // must come before playerPickerTable so mod nations from fromSettings
// Has to be initialized before the mapOptionsTable, since the mapOptionsTable refers to it on init
@ -65,13 +67,17 @@ class NewGameScreen(
playerPickerTable = PlayerPickerTable(
this, gameSetupInfo.gameParameters,
if (isNarrowerThan4to3()) stage.width - 20f else 0f
if (isPortrait) stage.width - 20f else 0f
)
newGameOptionsTable = GameOptionsTable(
this, isPortrait,
updatePlayerPickerTable = { desiredCiv -> playerPickerTable.update(desiredCiv) },
updatePlayerPickerRandomLabel = { playerPickerTable.updateRandomNumberLabel() }
)
newGameOptionsTable = GameOptionsTable(this, isNarrowerThan4to3()) { desiredCiv: String -> playerPickerTable.update(desiredCiv) }
mapOptionsTable = MapOptionsTable(this)
setDefaultCloseAction()
if (isNarrowerThan4to3()) initPortrait()
if (isPortrait) initPortrait()
else initLandscape()
pickerPane.bottomTable.background = skinStrings.getUiBackground("NewGameScreen/BottomTable", tintColor = skinStrings.skinConfig.clearColor)
@ -94,7 +100,10 @@ class NewGameScreen(
rightSideButton.enable()
rightSideButton.setText("Start game!".tr())
rightSideButton.onClick {
rightSideButton.onClick(this::onStartGameClicked)
}
private fun onStartGameClicked() {
if (gameSetupInfo.gameParameters.isOnlineMultiplayer) {
if (!checkConnectionToMultiplayerServer()) {
val noInternetConnectionPopup = Popup(this)
@ -102,7 +111,7 @@ class NewGameScreen(
noInternetConnectionPopup.addGoodSizedLabel(label.tr()).row()
noInternetConnectionPopup.addCloseButton()
noInternetConnectionPopup.open()
return@onClick
return
}
for (player in gameSetupInfo.gameParameters.players.filter { it.playerType == PlayerType.Human }) {
@ -113,7 +122,7 @@ class NewGameScreen(
invalidPlayerIdPopup.addGoodSizedLabel("Invalid player ID!".tr()).row()
invalidPlayerIdPopup.addCloseButton()
invalidPlayerIdPopup.open()
return@onClick
return
}
}
@ -123,7 +132,7 @@ class NewGameScreen(
notAllowedToSpectate.addGoodSizedLabel("You are not allowed to spectate!".tr()).row()
notAllowedToSpectate.addCloseButton()
notAllowedToSpectate.open()
return@onClick
return
}
}
}
@ -138,7 +147,7 @@ class NewGameScreen(
noHumanPlayersPopup.addGoodSizedLabel("No human players selected!".tr()).row()
noHumanPlayersPopup.addCloseButton()
noHumanPlayersPopup.open()
return@onClick
return
}
if (gameSetupInfo.gameParameters.victoryTypes.isEmpty()) {
@ -146,7 +155,7 @@ class NewGameScreen(
noVictoryTypesPopup.addGoodSizedLabel("No victory conditions were selected!".tr()).row()
noVictoryTypesPopup.addCloseButton()
noVictoryTypesPopup.open()
return@onClick
return
}
Gdx.input.inputProcessor = null // remove input processing - nothing will be clicked!
@ -157,7 +166,7 @@ class NewGameScreen(
} catch (ex: Throwable) {
Gdx.input.inputProcessor = stage
ToastPopup("Could not load map!", this)
return@onClick
return
}
val rulesetIncompatibilities = map.getRulesetIncompatibility(ruleset)
@ -169,7 +178,7 @@ class NewGameScreen(
incompatibleMap.addCloseButton()
incompatibleMap.open()
Gdx.input.inputProcessor = stage
return@onClick
return
}
} else {
// Generated map - check for sensible dimensions and if exceeded correct them and notify user
@ -183,7 +192,7 @@ class NewGameScreen(
customMapHeight.text = mapSize.height.toString()
}
Gdx.input.inputProcessor = stage
return@onClick
return
}
}
@ -196,7 +205,6 @@ class NewGameScreen(
startNewGame()
}
}
}
private fun initLandscape() {
scrollPane.setScrollingDisabled(true,true)

View File

@ -11,6 +11,7 @@ import com.unciv.logic.IdChecker
import com.unciv.logic.civilization.PlayerType
import com.unciv.logic.multiplayer.FriendList
import com.unciv.models.metadata.GameParameters
import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.nation.Nation
@ -19,6 +20,7 @@ import com.unciv.ui.audio.MusicMood
import com.unciv.ui.audio.MusicTrackChooserFlags
import com.unciv.ui.components.KeyCharAndCode
import com.unciv.ui.components.UncivTextField
import com.unciv.ui.components.WrappableLabel
import com.unciv.ui.components.extensions.*
import com.unciv.ui.images.ImageGetter
import com.unciv.ui.popups.Popup
@ -44,12 +46,13 @@ class PlayerPickerTable(
blockWidth: Float = 0f
): Table() {
val playerListTable = Table()
val civBlocksWidth = if(blockWidth <= 10f) previousScreen.stage.width / 3 - 5f else blockWidth
val civBlocksWidth = if (blockWidth <= 10f) previousScreen.stage.width / 3 - 5f else blockWidth
private var randomNumberLabel: WrappableLabel? = null
/** Locks player table for editing, currently unused, was previously used for scenarios and could be useful in the future.*/
/** Locks player table for editing, currently unused, was previously used for scenarios and could be useful in the future. */
var locked = false
/** No random civilization is available, used during map editing.*/
/** No random civilization is available, potentially used in the future during map editing. */
var noRandom = false
private val friendList = FriendList()
@ -82,27 +85,47 @@ class PlayerPickerTable(
for (player in gameParameters.players) {
playerListTable.add(getPlayerTable(player)).width(civBlocksWidth).padBottom(20f).row()
}
val isRandomNumberOfPlayers = gameParameters.randomNumberOfPlayers
if (isRandomNumberOfPlayers) {
randomNumberLabel = WrappableLabel("", civBlocksWidth - 20f, Color.GOLD)
playerListTable.add(randomNumberLabel).fillX().pad(0f, 10f, 20f, 10f).row()
updateRandomNumberLabel()
}
if (!locked && gameParameters.players.size < gameBasics.nations.values.count { it.isMajorCiv }) {
val addPlayerButton = "+".toLabel(Color.BLACK, 30)
.apply { this.setAlignment(Align.center) }
.surroundWithCircle(50f)
.onClick {
var player = Player()
// no random mode - add first not spectator civ if still available
if (noRandom) {
val player = if (noRandom || isRandomNumberOfPlayers) {
val availableCiv = getAvailablePlayerCivs().firstOrNull()
if (availableCiv != null) player = Player(availableCiv.name)
// Spectators only Humans
else player = Player(Constants.spectator, PlayerType.Human)
}
if (availableCiv != null) Player(availableCiv.name)
// Spectators can only be Humans
else Player(Constants.spectator, PlayerType.Human)
} else Player() // normal: add random AI
gameParameters.players.add(player)
update()
}
playerListTable.add(addPlayerButton).pad(10f)
}
// enable start game when more than 1 active player
val moreThanOnePlayer = 1 < gameParameters.players.count { it.chosenCiv != Constants.spectator }
(previousScreen as? PickerScreen)?.setRightSideButtonEnabled(moreThanOnePlayer)
// enable start game when at least one human player and they're not alone
val humanPlayerCount = gameParameters.players.count { it.playerType == PlayerType.Human }
val isValid = humanPlayerCount >= 2 || humanPlayerCount >= 1 && isRandomNumberOfPlayers
(previousScreen as? PickerScreen)?.setRightSideButtonEnabled(isValid)
}
fun updateRandomNumberLabel() {
randomNumberLabel?.run {
val text = "These [${gameParameters.players.size}] players will be adjusted to [${gameParameters.minNumberOfPlayers}" +
"]-[${gameParameters.maxNumberOfPlayers}] actual players by adding random AI's or by randomly omitting AI's."
wrap = false
align(Align.center)
setText(text.tr())
wrap = true
}
}
/**
@ -145,29 +168,56 @@ class PlayerPickerTable(
playerTable.add(nationTable).left()
val playerTypeTextButton = player.playerType.name.toTextButton()
playerTypeTextButton.onClick {
if (player.playerType == PlayerType.AI)
player.playerType = PlayerType.Human
playerTable.add(playerTypeTextButton).width(100f).pad(5f).right()
fun updatePlayerTypeButtonEnabled() {
// This could be written much shorter with logical operators - I think this is readable
playerTypeTextButton.isEnabled = when {
// Can always change AI to Human
player.playerType == PlayerType.AI -> true
// we cannot change Spectator player to AI type, robots not allowed to spectate :(
else if (player.chosenCiv != Constants.spectator)
player.playerType = PlayerType.AI
player.chosenCiv == Constants.spectator -> false
// In randomNumberOfPlayers mode, don't let the user choose random AI's
gameParameters.randomNumberOfPlayers && player.chosenCiv == Constants.random -> false
else -> true
}
}
updatePlayerTypeButtonEnabled()
nationTable.onClick {
if (locked) return@onClick
val noRandom = noRandom ||
gameParameters.randomNumberOfPlayers && player.playerType == PlayerType.AI
popupNationPicker(player, noRandom)
updatePlayerTypeButtonEnabled()
}
playerTypeTextButton.onClick {
player.playerType = player.playerType.toggle()
update()
}
playerTable.add(playerTypeTextButton).width(100f).pad(5f).right()
if (!locked) {
playerTable.add("-".toLabel(Color.BLACK, 30).apply { this.setAlignment(Align.center) }
playerTable.add("-".toLabel(Color.BLACK, 30, Align.center)
.surroundWithCircle(40f)
.onClick {
gameParameters.players.remove(player)
update()
}).pad(5f).right().row()
}
if (gameParameters.isOnlineMultiplayer && player.playerType == PlayerType.Human) {
).pad(5f).right()
}
if (gameParameters.isOnlineMultiplayer && player.playerType == PlayerType.Human)
playerTable.addPlayerTableMultiplayerControls(player)
return playerTable
}
private fun Table.addPlayerTableMultiplayerControls(player: Player) {
row()
val playerIdTextField = UncivTextField.create("Please input Player ID!", player.playerId)
playerTable.add(playerIdTextField).colspan(2).fillX().pad(5f)
add(playerIdTextField).colspan(2).fillX().pad(5f)
val errorLabel = "".toLabel(Color.RED)
playerTable.add(errorLabel).pad(5f).row()
add(errorLabel).pad(5f).row()
fun onPlayerIdTextUpdated() {
try {
@ -179,22 +229,22 @@ class PlayerPickerTable(
}
}
onPlayerIdTextUpdated()
playerIdTextField.addListener { onPlayerIdTextUpdated(); true }
val currentUserId = UncivGame.Current.settings.multiplayer.userId
val setCurrentUserButton = "Set current user".toTextButton()
setCurrentUserButton.onClick {
playerIdTextField.text = currentUserId
onPlayerIdTextUpdated()
}
playerTable.add(setCurrentUserButton).colspan(3).fillX().pad(5f).row()
add(setCurrentUserButton).colspan(3).fillX().pad(5f).row()
val copyFromClipboardButton = "Player ID from clipboard".toTextButton()
copyFromClipboardButton.onClick {
playerIdTextField.text = Gdx.app.clipboard.contents
onPlayerIdTextUpdated()
}
playerTable.add(copyFromClipboardButton).right().colspan(3).fillX().pad(5f).row()
add(copyFromClipboardButton).right().colspan(3).fillX().pad(5f).row()
//check if friends list is empty before adding the select friend button
if (friendList.friendList.isNotEmpty()) {
@ -202,31 +252,25 @@ class PlayerPickerTable(
selectPlayerFromFriendsList.onClick {
popupFriendPicker(player)
}
playerTable.add(selectPlayerFromFriendsList).left().colspan(3).fillX().pad(5f)
add(selectPlayerFromFriendsList).left().colspan(3).fillX().pad(5f)
}
}
return playerTable
}
/**
* Creates clickable icon and nation name for some [Player]
* as a [Table]. Clicking creates [popupNationPicker] to choose new nation.
* Creates clickable icon and nation name for some [Player].
* @param player [Player] for which generated
* @return [Table] containing nation icon and name
*/
private fun getNationTable(player: Player): Table {
val nationTable = Table()
val nationImageName = previousScreen.ruleset.nations[player.chosenCiv]
val nationImage =
if (player.chosenCiv == Constants.random)
if (nationImageName == null)
ImageGetter.getRandomNationPortrait(40f)
else ImageGetter.getNationPortrait(previousScreen.ruleset.nations[player.chosenCiv]!!, 40f)
else ImageGetter.getNationPortrait(nationImageName, 40f)
nationTable.add(nationImage).pad(5f)
nationTable.add(player.chosenCiv.toLabel()).pad(5f)
nationTable.touchable = Touchable.enabled
nationTable.onClick {
if (!locked) popupNationPicker(player)
}
return nationTable
}
@ -247,8 +291,8 @@ class PlayerPickerTable(
* ruleset and other players nation choice.
* @param player current player
*/
private fun popupNationPicker(player: Player) {
NationPickerPopup(this, player).open()
private fun popupNationPicker(player: Player, noRandom: Boolean) {
NationPickerPopup(this, player, noRandom).open()
update()
}
@ -288,7 +332,7 @@ class FriendSelectionPopup(
screen: BaseScreen,
) : Popup(screen) {
val pickerPane = PickerPane()
private val pickerPane = PickerPane()
private var selectedFriendId: String? = null
init {
@ -327,7 +371,8 @@ class FriendSelectionPopup(
private class NationPickerPopup(
private val playerPicker: PlayerPickerTable,
private val player: Player
private val player: Player,
noRandom: Boolean
) : Popup(playerPicker.previousScreen as BaseScreen) {
companion object {
// These are used for the Close/OK buttons in the lower left/right corners:
@ -357,26 +402,23 @@ private class NationPickerPopup(
nationDetailsScroll.setOverscroll(false, false)
add(nationDetailsScroll).size(civBlocksWidth + 10f, partHeight) // Same here, see above
val randomNation = Nation().apply {
val nationSequence = sequence {
if (!noRandom) yield(Nation().apply {
name = Constants.random
innerColor = listOf(255, 255, 255)
outerColor = listOf(0, 0, 0)
setTransients()
}
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)
if (spectator != null && player.playerType != PlayerType.AI) // only humans can spectate, sorry robots
yield(spectator)
} + playerPicker.getAvailablePlayerCivs(player.chosenCiv)
.sortedWith(compareBy(UncivGame.Current.settings.getCollatorFromLocale()) { it.name.tr() })
val nations = nationSequence.toCollection(ArrayList<Nation>(previousScreen.ruleset.nations.size))
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
@ -386,6 +428,10 @@ private class NationPickerPopup(
nationTable.onClick {
setNationDetails(nation)
}
nationTable.onDoubleClick {
selectedNation = nation
returnSelected()
}
if (player.chosenCiv == nation.name)
setNationDetails(nation)
}