mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-22 22:00:24 +07:00
Add new map types (#10695)
* fractal map type * repeat create fractal until enough land * small changes and add map types Lakes and Small Continents * fix for removing land at edges of hexagonal maps * readability
This commit is contained in:
@ -145,7 +145,10 @@ object MapType : IsPartOfGameInfoSerialization {
|
|||||||
const val threeContinents = "Three Continents"
|
const val threeContinents = "Three Continents"
|
||||||
const val fourCorners = "Four Corners"
|
const val fourCorners = "Four Corners"
|
||||||
const val archipelago = "Archipelago"
|
const val archipelago = "Archipelago"
|
||||||
|
const val fractal = "Fractal"
|
||||||
const val innerSea = "Inner Sea"
|
const val innerSea = "Inner Sea"
|
||||||
|
const val lakes = "Lakes"
|
||||||
|
const val smallContinents = "Small Continents"
|
||||||
|
|
||||||
// All ocean tiles
|
// All ocean tiles
|
||||||
const val empty = "Empty"
|
const val empty = "Empty"
|
||||||
|
@ -39,7 +39,6 @@ class MapGenerationRandomness {
|
|||||||
return Perlin.noise3d(worldCoords.x.toDouble(), worldCoords.y.toDouble(), seed, nOctaves, persistence, lacunarity, scale)
|
return Perlin.noise3d(worldCoords.x.toDouble(), worldCoords.y.toDouble(), seed, nOctaves, persistence, lacunarity, scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun chooseSpreadOutLocations(number: Int, suitableTiles: List<Tile>, mapRadius: Int): ArrayList<Tile> {
|
fun chooseSpreadOutLocations(number: Int, suitableTiles: List<Tile>, mapRadius: Int): ArrayList<Tile> {
|
||||||
if (number <= 0) return ArrayList(0)
|
if (number <= 0) return ArrayList(0)
|
||||||
|
|
||||||
|
@ -51,6 +51,9 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
MapType.fourCorners -> createFourCorners(tileMap)
|
MapType.fourCorners -> createFourCorners(tileMap)
|
||||||
MapType.archipelago -> createArchipelago(tileMap)
|
MapType.archipelago -> createArchipelago(tileMap)
|
||||||
MapType.perlin -> createPerlin(tileMap)
|
MapType.perlin -> createPerlin(tileMap)
|
||||||
|
MapType.fractal -> createFractal(tileMap)
|
||||||
|
MapType.lakes -> createLakes(tileMap)
|
||||||
|
MapType.smallContinents -> createSmallContinents(tileMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tileMap.mapParameters.shape === MapShape.flatEarth) {
|
if (tileMap.mapParameters.shape === MapShape.flatEarth) {
|
||||||
@ -96,6 +99,48 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createFractal(tileMap: TileMap) {
|
||||||
|
do {
|
||||||
|
val elevationSeed = randomness.RNG.nextInt().toDouble()
|
||||||
|
for (tile in tileMap.values) {
|
||||||
|
val maxdim = max(tileMap.maxLatitude, tileMap.maxLongitude)
|
||||||
|
var ratio = maxdim / 32.0 // change scale depending on map size so that average number of continents stay the same
|
||||||
|
if (tileMap.mapParameters.shape === MapShape.hexagonal || tileMap.mapParameters.shape === MapShape.flatEarth) {
|
||||||
|
ratio *= 0.5 // In hexagonal type map for some reason it tends to make a single continent like pangaea if we don't diminish the scale
|
||||||
|
}
|
||||||
|
|
||||||
|
var elevation = randomness.getPerlinNoise(tile, elevationSeed, persistence=0.8, lacunarity=1.5, scale=ratio*30.0)
|
||||||
|
|
||||||
|
elevation += getOceanEdgesTransform(tile, tileMap)
|
||||||
|
|
||||||
|
spawnLandOrWater(tile, elevation)
|
||||||
|
}
|
||||||
|
waterThreshold -= 0.01
|
||||||
|
} while (tileMap.values.count { it.baseTerrain == waterTerrainName } > tileMap.values.size * 0.7f) // Over 70% water
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createLakes(tileMap: TileMap) {
|
||||||
|
val elevationSeed = randomness.RNG.nextInt().toDouble()
|
||||||
|
for (tile in tileMap.values) {
|
||||||
|
val elevation = 0.3 - getRidgedPerlinNoise(tile, elevationSeed, persistence=0.7, lacunarity=1.5)
|
||||||
|
|
||||||
|
spawnLandOrWater(tile, elevation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSmallContinents(tileMap: TileMap) {
|
||||||
|
val elevationSeed = randomness.RNG.nextInt().toDouble()
|
||||||
|
waterThreshold += 0.25
|
||||||
|
do {
|
||||||
|
for (tile in tileMap.values) {
|
||||||
|
var elevation = getRidgedPerlinNoise(tile, elevationSeed, scale = 22.0)
|
||||||
|
elevation += getOceanEdgesTransform(tile, tileMap)
|
||||||
|
spawnLandOrWater(tile, elevation)
|
||||||
|
}
|
||||||
|
waterThreshold -= 0.01
|
||||||
|
} while (tileMap.values.count { it.baseTerrain == waterTerrainName } > tileMap.values.size * 0.7f) // Over 70%
|
||||||
|
}
|
||||||
|
|
||||||
private fun createArchipelago(tileMap: TileMap) {
|
private fun createArchipelago(tileMap: TileMap) {
|
||||||
val elevationSeed = randomness.RNG.nextInt().toDouble()
|
val elevationSeed = randomness.RNG.nextInt().toDouble()
|
||||||
waterThreshold += 0.25
|
waterThreshold += 0.25
|
||||||
@ -115,9 +160,9 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
}
|
}
|
||||||
|
|
||||||
tileMap.assignContinents(TileMap.AssignContinentsMode.Reassign)
|
tileMap.assignContinents(TileMap.AssignContinentsMode.Reassign)
|
||||||
|
waterThreshold -= 0.01
|
||||||
} while (tileMap.continentSizes.values.count { it > 25 } != 1 // Multiple large continents
|
} while (tileMap.continentSizes.values.count { it > 25 } != 1 // Multiple large continents
|
||||||
|| tileMap.values.count { it.baseTerrain == waterTerrainName } > tileMap.values.size * 0.7f) // Over 70% water
|
|| tileMap.values.count { it.baseTerrain == waterTerrainName } > tileMap.values.size * 0.7f) // Over 70% water
|
||||||
|
|
||||||
tileMap.assignContinents(TileMap.AssignContinentsMode.Clear)
|
tileMap.assignContinents(TileMap.AssignContinentsMode.Clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,6 +357,56 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
return 1.0 - (5.0 * shouldBeWater*shouldBeWater + randomScale) / 3.0
|
return 1.0 - (5.0 * shouldBeWater*shouldBeWater + randomScale) / 3.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getOceanEdgesTransform(tile: Tile, tileMap: TileMap): Double {
|
||||||
|
// The idea is to reduce elevation at the border of the map, so that we have mostly ocean there.
|
||||||
|
val maxX = tileMap.maxLongitude
|
||||||
|
val maxY = tileMap.maxLatitude
|
||||||
|
val x = tile.longitude
|
||||||
|
val y = tile.latitude
|
||||||
|
|
||||||
|
var elevationOffset = 0.0
|
||||||
|
|
||||||
|
val xdistanceratio = abs(x) / maxX
|
||||||
|
val ydistanceratio = abs(y) / maxY
|
||||||
|
if (tileMap.mapParameters.shape === MapShape.hexagonal || tileMap.mapParameters.shape === MapShape.flatEarth) {
|
||||||
|
val startdropoffratio = 0.8 // distance from center at which we start decreasing elevation linearly
|
||||||
|
val xdrsquared = xdistanceratio * xdistanceratio
|
||||||
|
val ydrsquared = ydistanceratio * ydistanceratio
|
||||||
|
val distancefromcenter = sqrt(xdrsquared+ydrsquared)
|
||||||
|
var distanceoffset = 0.0
|
||||||
|
if (distancefromcenter > startdropoffratio) {
|
||||||
|
val dropoffdistance = distancefromcenter - startdropoffratio
|
||||||
|
val normalizationDivisor = 1.0 - startdropoffratio // for normalizing to [0;1] range
|
||||||
|
distanceoffset = dropoffdistance / normalizationDivisor
|
||||||
|
}
|
||||||
|
elevationOffset -= distanceoffset * 0.35
|
||||||
|
} else {
|
||||||
|
|
||||||
|
var xoffset = 0.0
|
||||||
|
|
||||||
|
val xstartdropoffratio = 0.8
|
||||||
|
if (xdistanceratio > xstartdropoffratio) {
|
||||||
|
val xdropoffdistance = xdistanceratio - xstartdropoffratio
|
||||||
|
val xnormalizationdivisor = 1.0 - xstartdropoffratio // for normalizing to [0;1] range
|
||||||
|
xoffset = xdropoffdistance / xnormalizationdivisor
|
||||||
|
}
|
||||||
|
|
||||||
|
var yoffset = 0.0
|
||||||
|
|
||||||
|
val ystartdropoffratio = 0.76 // we want to have enough space at the north and south for circumnavigation
|
||||||
|
if (ydistanceratio > ystartdropoffratio) {
|
||||||
|
val ydropoffdistance = ydistanceratio - ystartdropoffratio
|
||||||
|
val ynormalizationdivisor = 1.0 - ystartdropoffratio // for normalizing to [0;1] range
|
||||||
|
yoffset = ydropoffdistance / ynormalizationdivisor
|
||||||
|
}
|
||||||
|
// these factors were just found by trial and error to be adequate for having enough space while not reducing land too much.
|
||||||
|
elevationOffset -= xoffset * 0.33
|
||||||
|
elevationOffset -= yoffset * 0.35
|
||||||
|
}
|
||||||
|
|
||||||
|
return max(elevationOffset, -0.35)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates ridged perlin noise. As for parameters see [MapGenerationRandomness.getPerlinNoise]
|
* Generates ridged perlin noise. As for parameters see [MapGenerationRandomness.getPerlinNoise]
|
||||||
*/
|
*/
|
||||||
|
@ -139,6 +139,9 @@ class MapParametersTable(
|
|||||||
MapType.archipelago,
|
MapType.archipelago,
|
||||||
MapType.innerSea,
|
MapType.innerSea,
|
||||||
MapType.perlin,
|
MapType.perlin,
|
||||||
|
MapType.fractal,
|
||||||
|
MapType.lakes,
|
||||||
|
MapType.smallContinents,
|
||||||
if (forMapEditor && mapGeneratedMainType != MapGeneratedMainType.randomGenerated) MapType.empty else null
|
if (forMapEditor && mapGeneratedMainType != MapGeneratedMainType.randomGenerated) MapType.empty else null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user