mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-27 08:09:21 +07:00
Fix startBias regional assignments (#9171)
This commit is contained in:
@ -21,6 +21,8 @@ import com.unciv.models.stats.Stat
|
|||||||
import com.unciv.models.translations.equalsPlaceholderText
|
import com.unciv.models.translations.equalsPlaceholderText
|
||||||
import com.unciv.models.translations.getPlaceholderParameters
|
import com.unciv.models.translations.getPlaceholderParameters
|
||||||
import com.unciv.ui.components.extensions.randomWeighted
|
import com.unciv.ui.components.extensions.randomWeighted
|
||||||
|
import com.unciv.utils.Log
|
||||||
|
import com.unciv.utils.Tag
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -210,6 +212,9 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
return Pair(splitOffRegion, regionToSplit)
|
return Pair(splitOffRegion, regionToSplit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Buckets for startBias to region assignments, used only in [assignRegions]. [PositiveFallback] is only for logging. */
|
||||||
|
private enum class BiasTypes { Coastal, Positive, Negative, Random, PositiveFallback }
|
||||||
|
|
||||||
fun assignRegions(tileMap: TileMap, civilizations: List<Civilization>, gameParameters: GameParameters) {
|
fun assignRegions(tileMap: TileMap, civilizations: List<Civilization>, gameParameters: GameParameters) {
|
||||||
if (civilizations.isEmpty()) return
|
if (civilizations.isEmpty()) return
|
||||||
|
|
||||||
@ -256,28 +261,43 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
normalizeStart(tileMap[region.startPosition!!], tileMap, minorCiv = false)
|
normalizeStart(tileMap[region.startPosition!!], tileMap, minorCiv = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val coastBiasCivs = civilizations.filter { ruleset.nations[it.civName]!!.startBias.contains("Coast") }
|
val civBiases = civilizations.associateWith { ruleset.nations[it.civName]!!.startBias }
|
||||||
val negativeBiasCivs = civilizations.filter { ruleset.nations[it.civName]!!.startBias.any { bias -> bias.equalsPlaceholderText("Avoid []") } }
|
// This ensures each civ can only be in one of the buckets
|
||||||
.sortedByDescending { ruleset.nations[it.civName]!!.startBias.size } // Civs with more complex avoids go first
|
val civsByBiasType = civBiases.entries.groupBy(
|
||||||
val randomCivs = civilizations.filter { ruleset.nations[it.civName]!!.startBias.isEmpty() }.toMutableList() // We might fill this up as we go
|
keySelector = {
|
||||||
// The rest are positive bias
|
(_, startBias) ->
|
||||||
val positiveBiasCivs = civilizations.filterNot { it in coastBiasCivs || it in negativeBiasCivs || it in randomCivs }
|
when {
|
||||||
.sortedBy { ruleset.nations[it.civName]!!.startBias.size } // civs with only one desired region go first
|
gameParameters.noStartBias -> BiasTypes.Random
|
||||||
val positiveBiasFallbackCivs = ArrayList<Civilization>() // Civs who couldn't get their desired region at first pass
|
startBias.any { bias -> bias.equalsPlaceholderText("Avoid []") } -> BiasTypes.Negative
|
||||||
|
"Coast" in startBias -> BiasTypes.Coastal
|
||||||
|
startBias.isNotEmpty() -> BiasTypes.Positive
|
||||||
|
else -> BiasTypes.Random
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valueTransform = { (civ, _) -> civ }
|
||||||
|
)
|
||||||
|
|
||||||
|
val coastBiasCivs = civsByBiasType[BiasTypes.Coastal]
|
||||||
|
?: emptyList()
|
||||||
|
val positiveBiasCivs = civsByBiasType[BiasTypes.Positive]
|
||||||
|
?.sortedBy { civBiases[it]?.size } // civs with only one desired region go first
|
||||||
|
?: emptyList()
|
||||||
|
val negativeBiasCivs = civsByBiasType[BiasTypes.Negative]
|
||||||
|
?.sortedByDescending { civBiases[it]?.size } // Civs with more complex avoids go first
|
||||||
|
?: emptyList()
|
||||||
|
val randomCivs = civsByBiasType[BiasTypes.Random]
|
||||||
|
?.toMutableList() // We might fill this up as we go
|
||||||
|
?: mutableListOf()
|
||||||
|
val positiveBiasFallbackCivs = mutableListOf<Civilization>() // Civs who couldn't get their desired region at first pass
|
||||||
val unpickedRegions = regions.toMutableList()
|
val unpickedRegions = regions.toMutableList()
|
||||||
|
|
||||||
// First assign coast bias civs
|
// First assign coast bias civs
|
||||||
for (civ in coastBiasCivs) {
|
for (civ in coastBiasCivs) {
|
||||||
// If noStartBias is enabled consider these to be randomCivs
|
|
||||||
if (gameParameters.noStartBias) {
|
|
||||||
randomCivs.addAll(coastBiasCivs)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find a coastal start, preferably a really coastal one
|
// Try to find a coastal start, preferably a really coastal one
|
||||||
var startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].isCoastalTile() }
|
var startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].isCoastalTile() }
|
||||||
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
||||||
if (startRegion != null) {
|
if (startRegion != null) {
|
||||||
|
logAssignRegion(true, BiasTypes.Coastal, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
continue
|
continue
|
||||||
@ -286,6 +306,7 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].neighbors.any { neighbor -> neighbor.getBaseTerrain().hasUnique(UniqueType.FreshWater) } }
|
startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].neighbors.any { neighbor -> neighbor.getBaseTerrain().hasUnique(UniqueType.FreshWater) } }
|
||||||
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
||||||
if (startRegion != null) {
|
if (startRegion != null) {
|
||||||
|
logAssignRegion(true, BiasTypes.Coastal, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
continue
|
continue
|
||||||
@ -294,6 +315,7 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].isAdjacentToRiver() }
|
startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].isAdjacentToRiver() }
|
||||||
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
||||||
if (startRegion != null) {
|
if (startRegion != null) {
|
||||||
|
logAssignRegion(true, BiasTypes.Coastal, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
continue
|
continue
|
||||||
@ -302,72 +324,85 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].neighbors.any { neighbor -> neighbor.isAdjacentToRiver() } }
|
startRegion = unpickedRegions.filter { tileMap[it.startPosition!!].neighbors.any { neighbor -> neighbor.isAdjacentToRiver() } }
|
||||||
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
.maxByOrNull { it.terrainCounts["Coastal"] ?: 0 }
|
||||||
if (startRegion != null) {
|
if (startRegion != null) {
|
||||||
|
logAssignRegion(true, BiasTypes.Coastal, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Else pick a random region at the end
|
// Else pick a random region at the end
|
||||||
|
logAssignRegion(false, BiasTypes.Coastal, civ)
|
||||||
randomCivs.add(civ)
|
randomCivs.add(civ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next do positive bias civs
|
// Next do positive bias civs
|
||||||
for (civ in positiveBiasCivs) {
|
for (civ in positiveBiasCivs) {
|
||||||
// If noStartBias is enabled consider these to be randomCivs
|
|
||||||
if (gameParameters.noStartBias) {
|
|
||||||
randomCivs.addAll(positiveBiasCivs)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find a start that matches any of the desired regions, ideally with lots of desired terrain
|
// Try to find a start that matches any of the desired regions, ideally with lots of desired terrain
|
||||||
val preferred = ruleset.nations[civ.civName]!!.startBias
|
val preferred = civBiases[civ]!!
|
||||||
val startRegion = unpickedRegions.filter { it.type in preferred }
|
val startRegion = unpickedRegions.filter { it.type in preferred }
|
||||||
.maxByOrNull { it.terrainCounts.filterKeys { terrain -> terrain in preferred }.values.sum() }
|
.maxByOrNull { it.terrainCounts.filterKeys { terrain -> terrain in preferred }.values.sum() }
|
||||||
if (startRegion != null) {
|
if (startRegion != null) {
|
||||||
|
logAssignRegion(true, BiasTypes.Positive, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
continue
|
continue
|
||||||
} else if (ruleset.nations[civ.civName]!!.startBias.size == 1) { // Civs with a single bias (only) get to look for a fallback region
|
} else if (preferred.size == 1) { // Civs with a single bias (only) get to look for a fallback region
|
||||||
positiveBiasFallbackCivs.add(civ)
|
positiveBiasFallbackCivs.add(civ)
|
||||||
} else { // Others get random starts
|
} else { // Others get random starts
|
||||||
|
logAssignRegion(false, BiasTypes.Positive, civ)
|
||||||
randomCivs.add(civ)
|
randomCivs.add(civ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a second pass for fallback civs, choosing the region most similar to the desired type
|
// Do a second pass for fallback civs, choosing the region most similar to the desired type
|
||||||
for (civ in positiveBiasFallbackCivs) {
|
for (civ in positiveBiasFallbackCivs) {
|
||||||
val startRegion = getFallbackRegion(ruleset.nations[civ.civName]!!.startBias.first(), unpickedRegions)
|
val startRegion = getFallbackRegion(civBiases[civ]!!.first(), unpickedRegions)
|
||||||
|
logAssignRegion(true, BiasTypes.PositiveFallback, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next do negative bias ones (ie "Avoid []")
|
// Next do negative bias ones (ie "Avoid []")
|
||||||
for (civ in negativeBiasCivs) {
|
for (civ in negativeBiasCivs) {
|
||||||
// If noStartBias is enabled consider these to be randomCivs
|
val (avoidBias, preferred) = civBiases[civ]!!
|
||||||
if (gameParameters.noStartBias) {
|
.partition { bias -> bias.equalsPlaceholderText("Avoid []") }
|
||||||
randomCivs.addAll(negativeBiasCivs)
|
val avoided = avoidBias.map { it.getPlaceholderParameters()[0] }
|
||||||
break
|
// Try to find a region not of the avoided types, secondary sort by
|
||||||
}
|
// least number of undesired terrains (weighed double) / most number of desired terrains
|
||||||
|
|
||||||
val avoided = ruleset.nations[civ.civName]!!.startBias.map { it.getPlaceholderParameters()[0] }
|
|
||||||
// Try to find a region not of the avoided types, secondary sort by least number of undesired terrains
|
|
||||||
val startRegion = unpickedRegions.filterNot { it.type in avoided }
|
val startRegion = unpickedRegions.filterNot { it.type in avoided }
|
||||||
.minByOrNull { it.terrainCounts.filterKeys { terrain -> terrain in avoided }.values.sum() }
|
.minByOrNull {
|
||||||
|
2 * it.terrainCounts.filterKeys { terrain -> terrain in avoided }.values.sum()
|
||||||
|
- it.terrainCounts.filterKeys { terrain -> terrain in preferred }.values.sum()
|
||||||
|
}
|
||||||
if (startRegion != null) {
|
if (startRegion != null) {
|
||||||
|
logAssignRegion(true, BiasTypes.Negative, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
continue
|
continue
|
||||||
} else
|
} else {
|
||||||
|
logAssignRegion(false, BiasTypes.Negative, civ)
|
||||||
randomCivs.add(civ) // else pick a random region at the end
|
randomCivs.add(civ) // else pick a random region at the end
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally assign the remaining civs randomly
|
// Finally assign the remaining civs randomly
|
||||||
for (civ in randomCivs) {
|
for (civ in randomCivs) {
|
||||||
|
// throws if regions.size < civilizations.size or if the assigning mismatched - leads to popup on newgame screen
|
||||||
val startRegion = unpickedRegions.random()
|
val startRegion = unpickedRegions.random()
|
||||||
|
logAssignRegion(true, BiasTypes.Random, civ, startRegion)
|
||||||
assignCivToRegion(civ, startRegion)
|
assignCivToRegion(civ, startRegion)
|
||||||
unpickedRegions.remove(startRegion)
|
unpickedRegions.remove(startRegion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun logAssignRegion(success: Boolean, startBiasType: BiasTypes, civ: Civilization, region: Region? = null) {
|
||||||
|
if (Log.backend.isRelease()) return
|
||||||
|
|
||||||
|
val logCiv = { civ.civName + " " + ruleset.nations[civ.civName]!!.startBias.joinToString(",", "(", ")") }
|
||||||
|
val msg = if (success) "(%s): %s to %s"
|
||||||
|
else "no region (%s) found for %s"
|
||||||
|
Log.debug(Tag("assignRegions"), msg, startBiasType, logCiv, region)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getRegionPriority(terrain: Terrain?): Int? {
|
private fun getRegionPriority(terrain: Terrain?): Int? {
|
||||||
if (terrain == null) // ie "hybrid"
|
if (terrain == null) // ie "hybrid"
|
||||||
return 99999 // a big number
|
return 99999 // a big number
|
||||||
@ -381,9 +416,9 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
terrain.getMatchingUniques(UniqueType.RegionRequirePercentTwoTypes).first().params[3].toInt()
|
terrain.getMatchingUniques(UniqueType.RegionRequirePercentTwoTypes).first().params[3].toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignCivToRegion(civInfo: Civilization, region: Region) {
|
private fun assignCivToRegion(civ: Civilization, region: Region) {
|
||||||
val tile = region.tileMap[region.startPosition!!]
|
val tile = region.tileMap[region.startPosition!!]
|
||||||
region.tileMap.addStartingLocation(civInfo.civName, tile)
|
region.tileMap.addStartingLocation(civ.civName, tile)
|
||||||
|
|
||||||
// Place impacts to keep city states etc at appropriate distance
|
// Place impacts to keep city states etc at appropriate distance
|
||||||
placeImpact(ImpactType.MinorCiv,tile, 6)
|
placeImpact(ImpactType.MinorCiv,tile, 6)
|
||||||
@ -1743,4 +1778,6 @@ class Region (val tileMap: TileMap, val rect: Rectangle, val continentID: Int =
|
|||||||
|
|
||||||
/** Returns number terrains with [name] */
|
/** Returns number terrains with [name] */
|
||||||
fun getTerrainAmount(name: String) = terrainCounts[name] ?: 0
|
fun getTerrainAmount(name: String) = terrainCounts[name] ?: 0
|
||||||
|
|
||||||
|
override fun toString() = "Region($type, ${tiles.size} tiles, ${terrainCounts.entries.joinToString { "${it.value} ${it.key}" }})"
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ object Log {
|
|||||||
*/
|
*/
|
||||||
val disableLogsFrom = (
|
val disableLogsFrom = (
|
||||||
System.getProperty("noLog")
|
System.getProperty("noLog")
|
||||||
?: "Battle,Music,Sounds,Translations,WorkerAutomation"
|
?: "Battle,Music,Sounds,Translations,WorkerAutomation,assignRegions"
|
||||||
).split(',').filterNot { it.isEmpty() }.toMutableSet()
|
).split(',').filterNot { it.isEmpty() }.toMutableSet()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,31 +63,38 @@ Each building can have the following attributes:
|
|||||||
|
|
||||||
This file contains all the nations and city states, including Barbarians and Spectator.
|
This file contains all the nations and city states, including Barbarians and Spectator.
|
||||||
|
|
||||||
| Attribute | Type | Optional | Notes |
|
| Attribute | Type | Optional | Notes |
|
||||||
| --------- | ---- | -------- | ----- |
|
|----------------------|------------|------------------|------------------------------------------------------------------------------------------------------------------|
|
||||||
| name | String | Required | |
|
| name | String | Required | |
|
||||||
| leaderName | String | Default empty | Omit only for city states! If you want LeaderPortraits, the image file names must match exactly, including case. |
|
| leaderName | String | Default empty | Omit only for city states! If you want LeaderPortraits, the image file names must match exactly, including case. |
|
||||||
| style | String | Default empty | Modifier appended to pixel unit image names |
|
| style | String | Default empty | Modifier appended to pixel unit image names |
|
||||||
| adjective | String | Default empty | Currently unused |
|
| adjective | String | Default empty | Currently unused |
|
||||||
| cityStateType | Enum | Default absent | Distinguishes Major Civilizations from City States (Cultured, Maritime, Mercantile, Militaristic) |
|
| cityStateType | Enum | Default absent | Distinguishes Major Civilizations from City States (Cultured, Maritime, Mercantile, Militaristic) |
|
||||||
| startBias | List | Default empty | Zero or more of: terrainFilter or "Avoid [terrainFilter]". Two or more will be logically "and"-ed, and if the filters result in no choices, the entire attribute is ignored (e.g. `"startBias": ["Snow","Tundra"]` will _never_ work). |
|
| startBias | List | Default empty | Zero or more of: terrainFilter or "Avoid [terrainFilter]". [^S] |
|
||||||
| preferredVictoryType | Enum | Default Neutral | Neutral, Cultural, Diplomatic, Domination or Scientific |
|
| preferredVictoryType | Enum | Default Neutral | Neutral, Cultural, Diplomatic, Domination or Scientific |
|
||||||
| startIntroPart1 | String | Default empty | Introductory blurb shown to Player on game start... |
|
| startIntroPart1 | String | Default empty | Introductory blurb shown to Player on game start... |
|
||||||
| startIntroPart2 | String | Default empty | ... second paragraph. ***NO*** "TBD"!!! Leave empty to skip that alert. |
|
| startIntroPart2 | String | Default empty | ... second paragraph. ***NO*** "TBD"!!! Leave empty to skip that alert. |
|
||||||
| declaringWar | String | Default empty | another greeting |
|
| declaringWar | String | Default empty | another greeting |
|
||||||
| attacked | String | Default empty | another greeting |
|
| attacked | String | Default empty | another greeting |
|
||||||
| defeated | String | Default empty | another greeting |
|
| defeated | String | Default empty | another greeting |
|
||||||
| introduction | String | Default empty | another greeting |
|
| introduction | String | Default empty | another greeting |
|
||||||
| neutralHello | String | Default empty | another greeting |
|
| neutralHello | String | Default empty | another greeting |
|
||||||
| hateHello | String | Default empty | another greeting |
|
| hateHello | String | Default empty | another greeting |
|
||||||
| tradeRequest | String | Default empty | another greeting |
|
| tradeRequest | String | Default empty | another greeting |
|
||||||
| innerColor | 3x Integer | Default black | R, G, B for outer ring of nation icon |
|
| innerColor | 3x Integer | Default black | R, G, B for outer ring of nation icon |
|
||||||
| outerColor | 3x Integer | Required | R, G, B for inner circle of nation icon |
|
| outerColor | 3x Integer | Required | R, G, B for inner circle of nation icon |
|
||||||
| uniqueName | String | Default empty | Decorative name for the special characteristic of this Nation |
|
| uniqueName | String | Default empty | Decorative name for the special characteristic of this Nation |
|
||||||
| uniqueText | String | Default empty | Replacement text for "uniques". If empty, uniques are listed individually. |
|
| uniqueText | String | Default empty | Replacement text for "uniques". If empty, uniques are listed individually. |
|
||||||
| uniques | List | Default empty | Properties of the civilization - see [here](../Modders/Unique-parameters.md#general-uniques) |
|
| uniques | List | Default empty | Properties of the civilization - see [here](../Modders/Unique-parameters.md#general-uniques) |
|
||||||
| cities | List | Default empty | City names used sequentially for newly founded cities. |
|
| cities | List | Default empty | City names used sequentially for newly founded cities. |
|
||||||
| civilopediaText | List | Default empty | see [civilopediaText chapter](Miscellaneous-JSON-files.md#civilopedia-text) |
|
| civilopediaText | List | Default empty | see [civilopediaText chapter](Miscellaneous-JSON-files.md#civilopedia-text) |
|
||||||
|
|
||||||
|
[^S]: A "Coast" preference (_unless_ combined with "Avoid") is translated to a complex test for coastal land tiles, tiles next to Lakes, river tiles or near-river tiles, and such civs are processed first. Other startBias entries are ignored in that case.
|
||||||
|
Other positive (no "Avoid") startBias are processed next. Multiple positive preferences are treated equally, but get no "fallback".
|
||||||
|
Single positive startBias can get a "fallback" region if there is no (or no more) region with that primary type: any leftover region with as much of the specified terrain as possible will do.
|
||||||
|
Multiple "Avoid" entries are treated equally (and reduce chance for success - if no region is left avoiding _all_ specified types that civ gets a random one).
|
||||||
|
When combining preferred terrain with "Avoid", the latter takes precedence, and preferred terrain only has minor weight when choosing between regions that are not of a type to avoid.
|
||||||
|
These notes are **only** valid when playing on generated maps, loaded maps from map editor get no "regions" and startBias is processed differently (but you can expect single-entry startBias to work best).
|
||||||
|
|
||||||
## Policies.json
|
## Policies.json
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user