Add options to use random number of players and city states. (#8394)

* Add option to use random number of players and city states.

* Fix spaces after =

* Update clone() and toString() of GameParameters

* Make sure at least one AI player when using random players.

* Fix typo in random players initial values.

* Fix for if user chooses random of 0 or 1 city states.

* Move random player and city state toggles to advanced. Move random city state choice to where it will not cache and reveal on next game. Change random city states to use min and max sliders that can be used inverted.

* Add random players min and max sliders.

* Change random players option to use min and max sliders that can be used inverted.
This commit is contained in:
Philip Keiter
2023-01-19 04:21:39 -06:00
committed by GitHub
parent 4267816880
commit 2ba79d85db
4 changed files with 144 additions and 5 deletions

View File

@ -360,6 +360,12 @@ Three Continents =
Four Corners = Four Corners =
Archipelago = Archipelago =
Inner Sea = Inner Sea =
Random number of Civilizations =
Min number of Civilizations =
Max number of Civilizations =
Random number of City-States =
Min number of City-States =
Max number of City-States =
Number of City-States = Number of City-States =
One City Challenge = One City Challenge =
No City Razing = No City Razing =

View File

@ -13,6 +13,7 @@ import com.unciv.logic.map.TileMap
import com.unciv.logic.map.mapgenerator.MapGenerator import com.unciv.logic.map.mapgenerator.MapGenerator
import com.unciv.models.metadata.GameParameters import com.unciv.models.metadata.GameParameters
import com.unciv.models.metadata.GameSetupInfo import com.unciv.models.metadata.GameSetupInfo
import com.unciv.models.metadata.Player
import com.unciv.models.ruleset.ModOptionsConstants import com.unciv.models.ruleset.ModOptionsConstants
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.RulesetCache
@ -236,6 +237,33 @@ object GameStarter {
val presetMajors = Stack<String>() val presetMajors = Stack<String>()
presetMajors.addAll(availableCivNames.filter { it in civNamesWithStartingLocations }) presetMajors.addAll(availableCivNames.filter { it in civNamesWithStartingLocations })
if (newGameParameters.randomNumberOfPlayers) {
// This swaps min and max if the user accidentally swapped min and max
val min = newGameParameters.minNumberOfPlayers.coerceAtMost(newGameParameters.maxNumberOfPlayers)
val max = newGameParameters.maxNumberOfPlayers.coerceAtLeast(newGameParameters.minNumberOfPlayers)
var playerCount = (min..max).random()
val humanPlayerCount = newGameParameters.players.filter {
it.playerType === PlayerType.Human
}.count()
val spectatorCount = newGameParameters.players.filter {
it.chosenCiv === Constants.spectator
}.count()
playerCount = playerCount.coerceAtLeast(humanPlayerCount + spectatorCount)
if (newGameParameters.players.size < playerCount) {
val neededPlayers = playerCount - newGameParameters.players.size
for (i in 1..neededPlayers) newGameParameters.players.add(Player())
} else if (newGameParameters.players.size > playerCount) {
val extraPlayers = newGameParameters.players.size - playerCount
val playersToRemove = newGameParameters.players.filter {
it.playerType === PlayerType.AI
}.shuffled().subList(0, extraPlayers)
newGameParameters.players.removeAll(playersToRemove)
}
}
for (player in newGameParameters.players.sortedBy { it.chosenCiv == Constants.random }) { for (player in newGameParameters.players.sortedBy { it.chosenCiv == Constants.random }) {
val nationName = when { val nationName = when {
player.chosenCiv != Constants.random -> player.chosenCiv player.chosenCiv != Constants.random -> player.chosenCiv
@ -263,9 +291,17 @@ object GameStarter {
.shuffled() .shuffled()
.sortedBy { it in civNamesWithStartingLocations } ) // pop() gets the last item, so sort ascending .sortedBy { it in civNamesWithStartingLocations } ) // pop() gets the last item, so sort ascending
val numberOfCityStates = if (newGameParameters.randomNumberOfCityStates) {
// This swaps min and max if the user accidentally swapped min and max
val min = newGameParameters.minNumberOfCityStates.coerceAtMost(newGameParameters.maxNumberOfCityStates)
val max = newGameParameters.maxNumberOfCityStates.coerceAtLeast(newGameParameters.minNumberOfCityStates)
(min..max).random()
} else {
newGameParameters.numberOfCityStates
}
var addedCityStates = 0 var addedCityStates = 0
// Keep trying to add city states until we reach the target number. // Keep trying to add city states until we reach the target number.
while (addedCityStates < newGameParameters.numberOfCityStates) { while (addedCityStates < numberOfCityStates) {
if (availableCityStatesNames.isEmpty()) // We ran out of city-states somehow if (availableCityStatesNames.isEmpty()) // We ran out of city-states somehow
break break

View File

@ -15,10 +15,16 @@ class GameParameters : IsPartOfGameInfoSerialization { // Default values are the
@Deprecated("Since 4.1.11") @Deprecated("Since 4.1.11")
var gameSpeed = "" var gameSpeed = ""
var randomNumberOfPlayers = false
var minNumberOfPlayers = 3
var maxNumberOfPlayers = 3
var players = ArrayList<Player>().apply { var players = ArrayList<Player>().apply {
add(Player().apply { playerType = PlayerType.Human }) add(Player().apply { playerType = PlayerType.Human })
for (i in 1..3) add(Player()) for (i in 1..3) add(Player())
} }
var randomNumberOfCityStates = false
var minNumberOfCityStates = 6
var maxNumberOfCityStates = 6
var numberOfCityStates = 6 var numberOfCityStates = 6
var noCityRazing = false var noCityRazing = false
@ -47,6 +53,12 @@ class GameParameters : IsPartOfGameInfoSerialization { // Default values are the
parameters.difficulty = difficulty parameters.difficulty = difficulty
parameters.speed = speed parameters.speed = speed
parameters.players = ArrayList(players) parameters.players = ArrayList(players)
parameters.randomNumberOfPlayers = randomNumberOfPlayers
parameters.minNumberOfPlayers = minNumberOfPlayers
parameters.maxNumberOfPlayers = maxNumberOfPlayers
parameters.randomNumberOfCityStates = randomNumberOfCityStates
parameters.minNumberOfCityStates = minNumberOfCityStates
parameters.maxNumberOfCityStates = maxNumberOfCityStates
parameters.numberOfCityStates = numberOfCityStates parameters.numberOfCityStates = numberOfCityStates
parameters.noBarbarians = noBarbarians parameters.noBarbarians = noBarbarians
parameters.ragingBarbarians = ragingBarbarians parameters.ragingBarbarians = ragingBarbarians
@ -67,7 +79,11 @@ class GameParameters : IsPartOfGameInfoSerialization { // Default values are the
yield("$difficulty $speed $startingEra") yield("$difficulty $speed $startingEra")
yield("${players.count { it.playerType == PlayerType.Human }} ${PlayerType.Human}") yield("${players.count { it.playerType == PlayerType.Human }} ${PlayerType.Human}")
yield("${players.count { it.playerType == PlayerType.AI }} ${PlayerType.AI}") yield("${players.count { it.playerType == PlayerType.AI }} ${PlayerType.AI}")
yield("$minNumberOfCityStates Min CS")
yield("$maxNumberOfCityStates Max CS")
yield("$numberOfCityStates CS") yield("$numberOfCityStates CS")
if (randomNumberOfPlayers) yield("Random number of Players")
if (randomNumberOfCityStates) yield("Random number of City-States")
if (isOnlineMultiplayer) yield("Online Multiplayer") if (isOnlineMultiplayer) yield("Online Multiplayer")
if (noBarbarians) yield("No barbs") if (noBarbarians) yield("No barbs")
if (ragingBarbarians) yield("Raging barbs") if (ragingBarbarians) yield("Raging barbs")

View File

@ -60,7 +60,16 @@ class GameOptionsTable(
val turnSlider = addMaxTurnsSlider() val turnSlider = addMaxTurnsSlider()
if (turnSlider != null) if (turnSlider != null)
add(turnSlider).padTop(10f).row() add(turnSlider).padTop(10f).row()
addCityStatesSlider() if (gameParameters.randomNumberOfPlayers) {
addMinPlayersSlider()
addMaxPlayersSlider()
}
if (gameParameters.randomNumberOfCityStates) {
addMinCityStatesSlider()
addMaxCityStatesSlider()
} else {
addCityStatesSlider()
}
}).colspan(2).fillX().row() }).colspan(2).fillX().row()
}).row() }).row()
addVictoryTypeCheckboxes() addVictoryTypeCheckboxes()
@ -82,6 +91,8 @@ class GameOptionsTable(
if (UncivGame.Current.settings.enableEspionageOption) if (UncivGame.Current.settings.enableEspionageOption)
it.addEnableEspionageCheckbox() it.addEnableEspionageCheckbox()
it.addNoStartBiasCheckbox() it.addNoStartBiasCheckbox()
it.addRandomPlayersCheckbox()
it.addRandomCityStatesCheckbox()
} }
add(expander).pad(10f).padTop(10f).growX().row() add(expander).pad(10f).padTop(10f).growX().row()
@ -141,6 +152,10 @@ class GameOptionsTable(
{ gameParameters.espionageEnabled = it } { gameParameters.espionageEnabled = it }
private fun numberOfPlayable() = ruleset.nations.values.count {
it.isMajorCiv()
}
private fun numberOfCityStates() = ruleset.nations.values.count { private fun numberOfCityStates() = ruleset.nations.values.count {
it.isCityState() it.isCityState()
&& !it.hasUnique(UniqueType.CityStateDeprecated) && !it.hasUnique(UniqueType.CityStateDeprecated)
@ -150,12 +165,78 @@ class GameOptionsTable(
addCheckbox("Disable starting bias", gameParameters.noStartBias) addCheckbox("Disable starting bias", gameParameters.noStartBias)
{ gameParameters.noStartBias = it } { gameParameters.noStartBias = it }
private fun Table.addRandomPlayersCheckbox() =
addCheckbox("Random number of Civilizations", gameParameters.randomNumberOfPlayers)
{
gameParameters.randomNumberOfPlayers = it
update()
}
private fun Table.addRandomCityStatesCheckbox() =
addCheckbox("Random number of City-States", gameParameters.randomNumberOfCityStates)
{
gameParameters.randomNumberOfCityStates = it
update()
}
private fun Table.addMinPlayersSlider() {
val playableAvailable = numberOfPlayable()
if (playableAvailable == 0) return
add("{Min number of Civilizations}:".toLabel()).left().expandX()
val slider = UncivSlider(2f, playableAvailable.toFloat(), 1f, initial = gameParameters.minNumberOfPlayers.toFloat()) {
gameParameters.minNumberOfPlayers = it.toInt()
}
slider.permanentTip = true
slider.isDisabled = locked
add(slider).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.permanentTip = true
slider.isDisabled = locked
add(slider).padTop(10f).row()
}
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.permanentTip = true
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.permanentTip = true
slider.isDisabled = locked
add(slider).padTop(10f).row()
}
private fun Table.addCityStatesSlider() { private fun Table.addCityStatesSlider() {
val maxCityStates = numberOfCityStates() val cityStatesAvailable = numberOfCityStates()
if (maxCityStates == 0) return if (cityStatesAvailable == 0) return
add("{Number of City-States}:".toLabel()).left().expandX() add("{Number of City-States}:".toLabel()).left().expandX()
val slider = UncivSlider(0f, maxCityStates.toFloat(), 1f, initial = gameParameters.numberOfCityStates.toFloat()) { val slider = UncivSlider(0f, cityStatesAvailable.toFloat(), 1f, initial = gameParameters.numberOfCityStates.toFloat()) {
gameParameters.numberOfCityStates = it.toInt() gameParameters.numberOfCityStates = it.toInt()
} }
slider.permanentTip = true slider.permanentTip = true