Update ModConstants and start its documentation (#6309)

* Update ModConstants

* Update ModConstants - revert Ice, tweak formula

* Update ModConstants - comment on predefined MapSizes
This commit is contained in:
SomeTroglodyte 2022-03-13 21:43:23 +01:00 committed by GitHub
parent 88ba48d7f1
commit e4b0afb7e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 20 deletions

View File

@ -8,6 +8,14 @@ import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.ruleset.RulesetCache
/* Predefined Map Sizes - ours are a little lighter than the original values. For reference those are:
Civ5Duel(40,24,17),
Civ5Tiny(56,36,25),
Civ5Small(66,42,30),
Civ5Medium(80,52,37),
Civ5Large(104,64,47),
Civ5Huge(128,80,58),
*/
enum class MapSize(val radius: Int, val width: Int, val height: Int) {
Tiny(10, 23, 15),
Small(15, 33, 21),

View File

@ -148,6 +148,8 @@ class MapGenerator(val ruleset: Ruleset) {
val tilesInArea = ArrayList<TileInfo>()
val tilesToCheck = ArrayList<TileInfo>()
val maxLakeSize = ruleset.modOptions.constants.maxLakeSize
while (waterTiles.isNotEmpty()) {
val initialWaterTile = waterTiles.random(randomness.RNG)
tilesInArea += initialWaterTile
@ -166,7 +168,7 @@ class MapGenerator(val ruleset: Ruleset) {
tilesToCheck -= tileWeAreChecking
}
if (tilesInArea.size <= 10) {
if (tilesInArea.size <= maxLakeSize) {
for (tile in tilesInArea) {
tile.baseTerrain = Constants.lakes
tile.setTransients()
@ -194,8 +196,10 @@ class MapGenerator(val ruleset: Ruleset) {
if (map.mapParameters.noRuins || ruinsEquivalents.isEmpty() )
return
val suitableTiles = map.values.filter { it.isLand && !it.isImpassible() }
val locations = randomness.chooseSpreadOutLocations(suitableTiles.size / 50,
suitableTiles, map.mapParameters.mapSize.radius)
val locations = randomness.chooseSpreadOutLocations(
(suitableTiles.size * ruleset.modOptions.constants.ancientRuinCountMultiplier).roundToInt(),
suitableTiles,
map.mapParameters.mapSize.radius)
for (tile in locations)
tile.improvement = ruinsEquivalents.keys.random()
}
@ -512,7 +516,7 @@ class MapGenerator(val ruleset: Ruleset) {
val latitudeTemperature = 1.0 - 2.0 * abs(tile.latitude) / tileMap.maxLatitude
var temperature = ((latitudeTemperature + randomTemperature) / 2.0)
temperature = abs(temperature).pow(1.0 - tileMap.mapParameters.temperatureExtremeness) * temperature.sign
if (temperature < -0.8)
if (temperature < -0.8f)
tile.addTerrainFeature(Constants.ice)
}
}

View File

@ -10,6 +10,7 @@ import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueType
import kotlin.math.abs
import kotlin.math.round
import kotlin.math.roundToInt
class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGenerationRandomness) {
@ -25,8 +26,10 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
if (tileMap.mapParameters.noNaturalWonders)
return
val mapRadius = tileMap.mapParameters.mapSize.radius
// number of Natural Wonders scales linearly with mapRadius as #wonders = mapRadius * 0.13133208 - 0.56128831
val numberToSpawn = round(mapRadius * 0.13133208f - 0.56128831f).toInt()
// number of Natural Wonders scales linearly with mapRadius
val numberToSpawn = ruleset.modOptions.constants.run {
mapRadius * naturalWonderCountMultiplier + naturalWonderCountAddedConstant
}.roundToInt()
val spawned = mutableListOf<Terrain>()
val allNaturalWonders = ruleset.terrains.values

View File

@ -5,21 +5,20 @@ import com.unciv.Constants
import com.unciv.logic.map.TileInfo
import com.unciv.logic.map.TileMap
import com.unciv.models.ruleset.Ruleset
import kotlin.math.roundToInt
class RiverGenerator(
private val tileMap: TileMap,
private val randomness: MapGenerationRandomness,
private val ruleset: Ruleset
ruleset: Ruleset
) {
companion object{
const val MAP_TILES_PER_RIVER = 100
const val MIN_RIVER_LENGTH = 5
const val MAX_RIVER_LENGTH = 666 // Do not set < max map radius
}
private val riverCountMultiplier = ruleset.modOptions.constants.riverCountMultiplier
private val minRiverLength = ruleset.modOptions.constants.minRiverLength
private val maxRiverLength = ruleset.modOptions.constants.maxRiverLength
fun spawnRivers() {
if (tileMap.values.none { it.isWater }) return
val numberOfRivers = tileMap.values.count { it.isLand } / MAP_TILES_PER_RIVER
val numberOfRivers = (tileMap.values.count { it.isLand } * riverCountMultiplier).roundToInt()
var optionalTiles = tileMap.values.asSequence()
.filter { it.baseTerrain == Constants.mountain && it.isFarEnoughFromWater() }
@ -37,14 +36,14 @@ class RiverGenerator(
}
private fun TileInfo.isFarEnoughFromWater(): Boolean {
for (distance in 1 until MIN_RIVER_LENGTH) {
for (distance in 1 until minRiverLength) {
if (getTilesAtDistance(distance).any { it.isWater }) return false
}
return true
}
private fun getClosestWaterTile(tile: TileInfo): TileInfo {
for (distance in 1..MAX_RIVER_LENGTH) {
for (distance in 1..maxRiverLength) {
val waterTiles = tile.getTilesAtDistance(distance).filter { it.isWater }
if (waterTiles.any())
return waterTiles.toList().random(randomness.RNG)
@ -59,7 +58,7 @@ class RiverGenerator(
var riverCoordinate = RiverCoordinate(initialPosition.position,
RiverCoordinate.BottomRightOrLeft.values().random(randomness.RNG))
for (step in 1..MAX_RIVER_LENGTH) { // Arbitrary max on river length, otherwise this will go in circles - rarely
for (step in 1..maxRiverLength) { // Arbitrary max on river length, otherwise this will go in circles - rarely
if (riverCoordinate.getAdjacentTiles(tileMap).any { it.isWater }) return
val possibleCoordinates = riverCoordinate.getAdjacentPositions(tileMap)
if (possibleCoordinates.none()) return // end of the line

View File

@ -25,11 +25,29 @@ class ModConstants {
// unitSupplyBase and unitSupplyPerCity can be found in difficulties.json
// unitSupplyBase, unitSupplyPerCity and unitSupplyPerPopulation can also be increased through uniques
val unitSupplyPerPopulation = 0.5
// The minimal distance that must be between any two cities, not counting the tiles cities are on
// The number is the amount of tiles between two cities, not counting the tiles the cities are on.
// e.g. "C__C", where "C" is a tile with a city and "_" is a tile without a city, has a distance of 2.
// First constant is for cities on the same landmass, the second is for cities on different continents.
val minimalCityDistance = 3
val minimalCityDistanceOnDifferentContinents = 2
}
// NaturalWonderGenerator uses these to determine the number of Natural Wonders to spawn for a given map size.
// With these values, radius * mul + add gives a 1-2-3-4-5 progression for Unciv predefined map sizes and a 2-3-4-5-6-7 progression for the original Civ5 map sizes.
// 0.124 = (Civ5.Huge.getHexagonalRadiusForArea(w*h) - Civ5.Duel.getHexagonalRadiusForArea(w*h)) / 5 (if you do not round in the radius function)
// The other constant is empiric to avoid an ugly jump in the progression.
val naturalWonderCountMultiplier = 0.124f
val naturalWonderCountAddedConstant = 0.1f
// MapGenerator.spreadAncientRuins: number of ruins = suitable tile count * this
val ancientRuinCountMultiplier = 0.02f
// MapGenerator.spawnIce: spawn Ice where T < this, with T calculated from temperatureExtremeness, latitude and perlin noise.
val spawnIceBelowTemperature = -0.8f
// MapGenerator.spawnLakesAndCoasts: Water bodies up to this tile count become Lakes
val maxLakeSize = 10
// RiverGenerator: river frequency and length bounds
val riverCountMultiplier = 0.01f
val minRiverLength = 5
val maxRiverLength = 666 // Do not set < max map radius
}

View File

@ -48,7 +48,7 @@ Each era can have the following attributes:
| unitBaseBuyCost | Integer (≥0) | defaults to 200 | Base cost of buying units with Faith, Food, Science or Culture when no other cost is provided |
| startingSettlerCount | Integer (≥0) | defaults to 1 | Amount of settler units that should be spawned when starting a game in this era |
| startingSettlerUnit | String | defaults to "Settler" | Name of the unit that should be used for the previous field. Must be in [Units.json](Unit-related-JSON-files.md#unitsjson) |
| startingWokerCount | Integer (≥0) | defaults to 0 | Amount of worker units that should be spawned when starting a game in this era |
| startingWorkerCount | Integer (≥0) | defaults to 0 | Amount of worker units that should be spawned when starting a game in this era |
| startingWorkerUnit | String | defaults to "Worker" | Name of the unit that should be used for the previous field. Must be in [Units.json](Unit-related-JSON-files.md#unitsjson) |
| startingMilitaryUnitCount | Integer (≥0) | defaults to 1 | Amount of military units that should be spawned when starting a game in this era |
| startingMilitaryUnit | String | defaults to "Warrior" | Name of the unit that should be used for the previous field. Must be in [Units.json](Unit-related-JSON-files.md#unitsjson)|
@ -69,7 +69,7 @@ The file can have the following attributes, including the values Unciv sets (no
| Attribute | Type | Defaults | Notes |
|-----------|------|-----------|-------|
| isBaseRuleset | Boolean | false | Differentiates mods that change the vanilla ruleset or replace it |
| maxXPfromBarbarians | Integer | 30 | ...as the name says... |
| maxXPfromBarbarians | Integer | 30 | *Deprecated*, see [constants](#ModConstants) |
| uniques | List | empty | Mod-wide specials, [see here](../Modders/Unique-parameter-types.md#modoptions-uniques) |
| techsToRemove | List | empty | List of [Technologies](Civilization-related-JSON-files.md#techsjson) to remove (isBaseRuleset=false only) |
| buildingsToRemove | List | empty | List of [Buildings or Wonders](Civilization-related-JSON-files.md#buildingsjson) to remove (isBaseRuleset=false only) |
@ -79,6 +79,53 @@ The file can have the following attributes, including the values Unciv sets (no
| modUrl | String | empty | Set automatically after download - URL of repository |
| author | String | empty | Set automatically after download - Owner of repository |
| modSize | Integer | empty | Set automatically after download - kB in entire repository, not sum of default branch files |
| constants | Object | empty | see [constants](#ModOptions.constants) |
### ModConstants
Stored in ModOptions.constants, this is a collection of constants used internally in Unciv.
| Attribute | Type | Defaults | Notes |
|-----------|------|-----------|-------|
| maxXPfromBarbarians | Int | 30 | [^A] |
| cityStrengthBase| Float | 8.0 | [^B] |
| cityStrengthPerPop| Float | 0.4 | [^B] |
| cityStrengthFromTechsMultiplier| Float | 5.5 | [^B] |
| cityStrengthFromTechsExponent| Float | 2.8 | [^B] |
| cityStrengthFromTechsFullMultiplier| Float | 1.0 | [^B] |
| cityStrengthFromGarrison| Float | 0.2 | [^B] |
| unitSupplyPerPopulation| Float | 0.5 | [^C] |
| minimalCityDistance| Int | 3 | [^D] |
| minimalCityDistanceOnDifferentContinents| Int | 2 | [^D] |
| naturalWonderCountMultiplier| Float | 0.124 | [^E] |
| naturalWonderCountAddedConstant| Float | 0.1 | [^E] |
| ancientRuinCountMultiplier| Float | 0.02 | [^F] |
| maxLakeSize| Int | 10 | [^H] |
| riverCountMultiplier| Float | 0.01 | [^I] |
| minRiverLength| Int | 5 | [^I] |
| maxRiverLength| Int | 666 | [^I] |
[^A]: Max amount of experience that can be gained from combat with barbarians
[^B]: Formula for city Strength:
Strength = baseStrength + strengthPerPop + strengthFromTiles +
((%techs * multiplier) ^ exponent) * fullMultiplier +
(garrisonBonus * garrisonUnitStrength * garrisonUnitHealth/100) +
defensiveBuildingStrength
where %techs is the percentage of techs in the tech tree that are complete
If no techs exist in this ruleset, %techs = 0.5 (=50%)
[^C]: Formula for Unit Supply:
Supply = unitSupplyBase (difficulties.json)
unitSupplyPerCity * amountOfCities + (difficulties.json)
unitSupplyPerPopulation * amountOfPopulationInAllCities
unitSupplyBase and unitSupplyPerCity can be found in difficulties.json
unitSupplyBase, unitSupplyPerCity and unitSupplyPerPopulation can also be increased through uniques
[^D]: The minimal distance that must be between any two cities, not counting the tiles cities are on
The number is the amount of tiles between two cities, not counting the tiles the cities are on.
e.g. "C__C", where "C" is a tile with a city and "_" is a tile without a city, has a distance of 2.
First constant is for cities on the same landmass, the second is for cities on different continents.
[^E]: NaturalWonderGenerator uses these to determine the number of Natural Wonders to spawn for a given map size. The number scales linearly with map radius: #wonders = radius * naturalWonderCountMultiplier + naturalWonderCountAddedConstant. The defaults effectively mean Tiny - 1, Small - 2, Medium - 3, Large - 4, Huge - 5, Custom radius >=109 - all G&K wonders.
[^F]: MapGenerator.spreadAncientRuins: number of ruins = suitable tile count * this
[^H]: MapGenerator.spawnLakesAndCoasts: Water bodies up to this tile count become Lakes
[^I]: RiverGenerator: river frequency and length bounds
## Civilopedia text