mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-08 23:08:35 +07:00
Moddable Ice generation (any impassable feature on Water possible) (#6329)
Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
@ -270,7 +270,8 @@
|
||||
"impassable": true,
|
||||
"overrideStats": true,
|
||||
"occursOn": ["Ocean", "Coast"],
|
||||
"uniques": ["[-1] to Fertility for Map Generation",
|
||||
"uniques": ["Occurs at temperature between [-1] and [-0.8] and humidity between [0] and [1]",
|
||||
"[-1] to Fertility for Map Generation",
|
||||
"Considered [Undesirable] when determining start locations"]
|
||||
},
|
||||
{
|
||||
|
@ -272,7 +272,8 @@
|
||||
"impassable": true,
|
||||
"overrideStats": true,
|
||||
"occursOn": ["Ocean", "Coast"],
|
||||
"uniques": ["[-1] to Fertility for Map Generation",
|
||||
"uniques": ["Occurs at temperature between [-1] and [-0.8] and humidity between [0] and [1]",
|
||||
"[-1] to Fertility for Map Generation",
|
||||
"Considered [Undesirable] when determining start locations"]
|
||||
},
|
||||
{
|
||||
|
@ -28,7 +28,6 @@ object Constants {
|
||||
const val jungle = "Jungle"
|
||||
const val ice = "Ice"
|
||||
val vegetation = arrayOf(forest, jungle)
|
||||
val sea = arrayOf(ocean, coast)
|
||||
|
||||
// Note the difference in case. **Not** interchangeable!
|
||||
/** The "Fresh water" terrain _unique_ */
|
||||
|
@ -10,6 +10,7 @@ import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.Terrain
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import kotlin.math.*
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import kotlin.random.Random
|
||||
@ -25,6 +26,27 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
private var randomness = MapGenerationRandomness()
|
||||
private val firstLandTerrain = ruleset.terrains.values.first { it.type==TerrainType.Land }
|
||||
|
||||
/** Associates [terrain] with a range of temperatures and a range of humidities (both open to closed) */
|
||||
private class TerrainOccursRange(
|
||||
val terrain: Terrain,
|
||||
val tempFrom: Float, val tempTo: Float,
|
||||
val humidFrom: Float, val humidTo: Float
|
||||
) {
|
||||
/** builds a [TerrainOccursRange] for [terrain] from a [unique] (type [UniqueType.TileGenerationConditions]) */
|
||||
constructor(terrain: Terrain, unique: Unique)
|
||||
: this(terrain,
|
||||
unique.params[0].toFloat(), unique.params[1].toFloat(),
|
||||
unique.params[2].toFloat(), unique.params[3].toFloat())
|
||||
/** checks if both [temperature] and [humidity] satisfy their ranges (>From, <=To) */
|
||||
// Yes this does implicit conversions Float/Double
|
||||
fun matches(temperature: Double, humidity: Double) =
|
||||
tempFrom < temperature && temperature <= tempTo &&
|
||||
humidFrom < humidity && humidity <= humidTo
|
||||
}
|
||||
private fun Terrain.getGenerationConditions() =
|
||||
getMatchingUniques(UniqueType.TileGenerationConditions)
|
||||
.map { unique -> TerrainOccursRange(this, unique) }
|
||||
|
||||
fun generateMap(mapParameters: MapParameters, civilizations: List<CivilizationInfo> = emptyList()): TileMap {
|
||||
val mapSize = mapParameters.mapSize
|
||||
val mapType = mapParameters.type
|
||||
@ -407,21 +429,11 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
|
||||
val scale = tileMap.mapParameters.tilesPerBiomeArea.toDouble()
|
||||
val temperatureExtremeness = tileMap.mapParameters.temperatureExtremeness
|
||||
|
||||
class TerrainOccursRange(
|
||||
val terrain: Terrain,
|
||||
val tempFrom: Float, val tempTo: Float,
|
||||
val humidFrom: Float, val humidTo: Float
|
||||
)
|
||||
|
||||
// List is OK here as it's only sequentially scanned
|
||||
val limitsMap: List<TerrainOccursRange> =
|
||||
ruleset.terrains.values.flatMap { terrain ->
|
||||
terrain.getMatchingUniques(UniqueType.TileGenerationConditions)
|
||||
.map { unique ->
|
||||
TerrainOccursRange(terrain,
|
||||
unique.params[0].toFloat(), unique.params[1].toFloat(),
|
||||
unique.params[2].toFloat(), unique.params[3].toFloat())
|
||||
}
|
||||
ruleset.terrains.values.flatMap {
|
||||
it.getGenerationConditions()
|
||||
}
|
||||
val noTerrainUniques = limitsMap.isEmpty()
|
||||
val elevationTerrains = arrayOf(Constants.mountain, Constants.hill)
|
||||
@ -453,10 +465,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
continue
|
||||
}
|
||||
|
||||
val matchingTerrain = limitsMap.firstOrNull {
|
||||
it.tempFrom < temperature && temperature <= it.tempTo
|
||||
&& it.humidFrom < humidity && humidity <= it.humidTo
|
||||
}
|
||||
val matchingTerrain = limitsMap.firstOrNull { it.matches(temperature, humidity) }
|
||||
|
||||
if (matchingTerrain != null) tile.baseTerrain = matchingTerrain.terrain.name
|
||||
else {
|
||||
@ -505,19 +514,43 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
* [MapParameters.temperatureExtremeness] as in [applyHumidityAndTemperature]
|
||||
*/
|
||||
private fun spawnIce(tileMap: TileMap) {
|
||||
if (!ruleset.terrains.containsKey(Constants.ice)) return // I can't think of how to make this nicely moddable
|
||||
val waterTerrain: Set<String> =
|
||||
ruleset.terrains.values.asSequence()
|
||||
.filter { it.type == TerrainType.Water }
|
||||
.map { it.name }.toSet()
|
||||
val iceEquivalents: List<TerrainOccursRange> =
|
||||
ruleset.terrains.values.asSequence()
|
||||
.filter { terrain ->
|
||||
terrain.type == TerrainType.TerrainFeature &&
|
||||
terrain.impassable &&
|
||||
terrain.occursOn.all { it in waterTerrain }
|
||||
}.flatMap { terrain ->
|
||||
val conditions = terrain.getGenerationConditions()
|
||||
if (conditions.any()) conditions
|
||||
else sequenceOf(TerrainOccursRange(terrain, -1f, -0.8f, 0f, 1f))
|
||||
}.toList()
|
||||
if (iceEquivalents.isEmpty()) return
|
||||
|
||||
tileMap.setTransients(ruleset)
|
||||
val temperatureSeed = randomness.RNG.nextInt().toDouble()
|
||||
for (tile in tileMap.values) {
|
||||
if (tile.baseTerrain !in Constants.sea || tile.terrainFeatures.isNotEmpty())
|
||||
if (tile.baseTerrain !in waterTerrain || tile.terrainFeatures.isNotEmpty())
|
||||
continue
|
||||
|
||||
val randomTemperature = randomness.getPerlinNoise(tile, temperatureSeed, scale = tileMap.mapParameters.tilesPerBiomeArea.toDouble(), nOctaves = 1)
|
||||
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.8f)
|
||||
tile.addTerrainFeature(Constants.ice)
|
||||
|
||||
val candidates = iceEquivalents
|
||||
.filter {
|
||||
it.matches(temperature, 1.0) &&
|
||||
tile.getLastTerrain().name in it.terrain.occursOn
|
||||
}.map { it.terrain.name }
|
||||
when (candidates.size) {
|
||||
1 -> tile.addTerrainFeature(candidates.first())
|
||||
!in 0..1 -> tile.addTerrainFeature(candidates.random(randomness.RNG))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,9 @@ Each terrain entry can have the following properties:
|
||||
| movementCost | Integer | Default 1 | base movement cost |
|
||||
| defenceBonus | Float | Default 0 | combat bonus for units being attacked here |
|
||||
| RGB | List Integer * 3 | Default 'Gold' | RGB color for 'Default' tileset display |
|
||||
| uniques | List | Default empty | List of effects, [see here](../Modders/Unique-parameter-types.md#terrain-uniques) |
|
||||
| uniques | List | Default empty | List of effects, [see here](../Modders/uniques.md#terrain-uniques) |
|
||||
| civilopediaText | List | Default empty | see [civilopediaText chapter](Miscellaneous-JSON-files.md#civilopedia-text) |
|
||||
|
||||
Note that many Natural Wonders have hardcoded routines for their placement and are recognized by name (e.g. Great Barrier Reef being more than one tile).
|
||||
|
||||
|
||||
## TileImprovements.json
|
||||
This file lists the improvements that can be constructed or created on a map tile by a unit (any unit having the appropriate unique).
|
||||
|
Reference in New Issue
Block a user