mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-15 10:18:26 +07:00
Flat Earth Hexagonal (#8140)
* Add new map shape: Flat Earth Hexagonal. * Make flat earth logic easier to read. * Make flat earth set temperature based on tile distance from center. * Make flat earth waste less space on the ice ring. * Add variety to the flat earth ice ring. * Use baseTerrain for snow on flat earth edge instead of addTerrainFeature. * Ensure flat earth center tiles and edge tiles are ocean not coast. * Give flat earth ice ring some random juts and dips. * Make 3rd continent smaller when flat earth. * Enable more continent positions for three continents when flat earth. * Add parens around or statement. * Refactor flat earth temperature code into functions. * Invert some flat earth if statements to reduce nesting. * Allow coast near flat earth ice walls. * Make flat earth ice wall generation more efficient. * Stop adding ice to flat earth mountains. * Move flat earth ice wall generation to its own function. * Improve flat earth water placement comments. * Move flat earth extra water generation to its own function. * Move flat earth center ice spawn to its own function. * Move flat earth edge ice spawn to its own function. * Minor efficiency tweak. * Add Flat Earth Hexagonal to template.
This commit is contained in:
@ -377,6 +377,7 @@ Time =
|
|||||||
|
|
||||||
Map Shape =
|
Map Shape =
|
||||||
Hexagonal =
|
Hexagonal =
|
||||||
|
Flat Earth Hexagonal =
|
||||||
Rectangular =
|
Rectangular =
|
||||||
Height =
|
Height =
|
||||||
Width =
|
Width =
|
||||||
|
@ -124,6 +124,7 @@ class MapSizeNew : IsPartOfGameInfoSerialization {
|
|||||||
|
|
||||||
object MapShape : IsPartOfGameInfoSerialization {
|
object MapShape : IsPartOfGameInfoSerialization {
|
||||||
const val hexagonal = "Hexagonal"
|
const val hexagonal = "Hexagonal"
|
||||||
|
const val flatEarth = "Flat Earth Hexagonal"
|
||||||
const val rectangular = "Rectangular"
|
const val rectangular = "Rectangular"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,12 +230,12 @@ class MapParameters : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getArea() = when {
|
fun getArea() = when {
|
||||||
shape == MapShape.hexagonal -> getNumberOfTilesInHexagon(mapSize.radius)
|
shape == MapShape.hexagonal || shape == MapShape.flatEarth -> getNumberOfTilesInHexagon(mapSize.radius)
|
||||||
worldWrap && mapSize.width % 2 != 0 -> (mapSize.width - 1) * mapSize.height
|
worldWrap && mapSize.width % 2 != 0 -> (mapSize.width - 1) * mapSize.height
|
||||||
else -> mapSize.width * mapSize.height
|
else -> mapSize.width * mapSize.height
|
||||||
}
|
}
|
||||||
fun displayMapDimensions() = mapSize.run {
|
fun displayMapDimensions() = mapSize.run {
|
||||||
(if (shape == MapShape.hexagonal) "R$radius" else "${width}x$height") +
|
(if (shape == MapShape.hexagonal || shape == MapShape.flatEarth) "R$radius" else "${width}x$height") +
|
||||||
(if (worldWrap) "w" else "")
|
(if (worldWrap) "w" else "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +267,7 @@ class MapParameters : IsPartOfGameInfoSerialization {
|
|||||||
}.joinToString("")
|
}.joinToString("")
|
||||||
|
|
||||||
fun numberOfTiles() =
|
fun numberOfTiles() =
|
||||||
if (shape == MapShape.hexagonal) {
|
if (shape == MapShape.hexagonal || shape == MapShape.flatEarth) {
|
||||||
1 + 3 * mapSize.radius * (mapSize.radius - 1)
|
1 + 3 * mapSize.radius * (mapSize.radius - 1)
|
||||||
} else {
|
} else {
|
||||||
mapSize.width * mapSize.height
|
mapSize.width * mapSize.height
|
||||||
|
@ -26,6 +26,7 @@ import kotlin.math.max
|
|||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.math.sign
|
import kotlin.math.sign
|
||||||
|
import kotlin.math.sqrt
|
||||||
import kotlin.math.ulp
|
import kotlin.math.ulp
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -35,6 +36,14 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
private const val consoleTimings = false
|
private const val consoleTimings = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val landTerrainName =
|
||||||
|
MapLandmassGenerator.getInitializationTerrain(ruleset, TerrainType.Land)
|
||||||
|
private val waterTerrainName: String = try {
|
||||||
|
MapLandmassGenerator.getInitializationTerrain(ruleset, TerrainType.Water)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
landTerrainName
|
||||||
|
}
|
||||||
|
|
||||||
private var randomness = MapGenerationRandomness()
|
private var randomness = MapGenerationRandomness()
|
||||||
private val firstLandTerrain = ruleset.terrains.values.first { it.type==TerrainType.Land }
|
private val firstLandTerrain = ruleset.terrains.values.first { it.type==TerrainType.Land }
|
||||||
|
|
||||||
@ -503,9 +512,19 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
val humidityRandom = randomness.getPerlinNoise(tile, humiditySeed, scale = scale, nOctaves = 1)
|
val humidityRandom = randomness.getPerlinNoise(tile, humiditySeed, scale = scale, nOctaves = 1)
|
||||||
val humidity = ((humidityRandom + 1.0) / 2.0 + humidityShift).coerceIn(0.0..1.0)
|
val humidity = ((humidityRandom + 1.0) / 2.0 + humidityShift).coerceIn(0.0..1.0)
|
||||||
|
|
||||||
val randomTemperature = randomness.getPerlinNoise(tile, temperatureSeed, scale = scale, nOctaves = 1)
|
val expectedTemperature = if (tileMap.mapParameters.shape === MapShape.flatEarth) {
|
||||||
|
// Flat Earth uses radius because North is center of map
|
||||||
|
val radius = getTileRadius(tile, tileMap)
|
||||||
|
val radiusTemperature = getTemperatureAtRadius(radius)
|
||||||
|
radiusTemperature
|
||||||
|
} else {
|
||||||
|
// Globe Earth uses latitude because North is top of map
|
||||||
val latitudeTemperature = 1.0 - 2.0 * abs(tile.latitude) / tileMap.maxLatitude
|
val latitudeTemperature = 1.0 - 2.0 * abs(tile.latitude) / tileMap.maxLatitude
|
||||||
var temperature = (5.0 * latitudeTemperature + randomTemperature) / 6.0
|
latitudeTemperature
|
||||||
|
}
|
||||||
|
|
||||||
|
val randomTemperature = randomness.getPerlinNoise(tile, temperatureSeed, scale = scale, nOctaves = 1)
|
||||||
|
var temperature = (5.0 * expectedTemperature + randomTemperature) / 6.0
|
||||||
temperature = abs(temperature).pow(1.0 - temperatureExtremeness) * temperature.sign
|
temperature = abs(temperature).pow(1.0 - temperatureExtremeness) * temperature.sign
|
||||||
temperature = (temperature + temperatureShift).coerceIn(-1.0..1.0)
|
temperature = (temperature + temperatureShift).coerceIn(-1.0..1.0)
|
||||||
|
|
||||||
@ -538,6 +557,65 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getTileRadius(tile: TileInfo, tileMap: TileMap): Float {
|
||||||
|
val latitudeRatio = abs(tile.latitude) / tileMap.maxLatitude
|
||||||
|
val longitudeRatio = abs(tile.longitude) / tileMap.maxLongitude
|
||||||
|
return sqrt(latitudeRatio.pow(2) + longitudeRatio.pow(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTemperatureAtRadius(radius: Float): Double {
|
||||||
|
/*
|
||||||
|
Radius is in the range of 0.0 to 1.0
|
||||||
|
Temperature is in the range of -1.0 to 1.0
|
||||||
|
|
||||||
|
Radius of 0.0 (arctic) is -1.0 (cold)
|
||||||
|
Radius of 0.25 (mid North) is 0.0 (temperate)
|
||||||
|
Radius of 0.5 (equator) is 1.0 (hot)
|
||||||
|
Radius of 0.75 (mid South) is 0.0 (temperate)
|
||||||
|
Radius of 1.0 (antarctic) is -1.0 (cold)
|
||||||
|
|
||||||
|
Scale the radius range to the temperature range
|
||||||
|
*/
|
||||||
|
return when {
|
||||||
|
/*
|
||||||
|
North Zone
|
||||||
|
Starts cold at arctic and gets hotter as it goes South to equator
|
||||||
|
x1 is set to 0.05 instead of 0.0 to offset the ice in the center of the map
|
||||||
|
*/
|
||||||
|
radius < 0.5 -> scaleToRange(0.05, 0.5, -1.0, 1.0, radius)
|
||||||
|
|
||||||
|
/*
|
||||||
|
South Zone
|
||||||
|
Starts hot at equator and gets colder as it goes South to antarctic
|
||||||
|
x2 is set to 0.95 instead of 1.0 to offset the ice on the edges of the map
|
||||||
|
*/
|
||||||
|
radius > 0.5 -> scaleToRange(0.5, 0.95, 1.0, -1.0, radius)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Equator
|
||||||
|
Always hot
|
||||||
|
radius == 0.5
|
||||||
|
*/
|
||||||
|
else -> 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @x1 start of the original range
|
||||||
|
* @x2 end of the original range
|
||||||
|
* @y1 start of the new range
|
||||||
|
* @y2 end of the new range
|
||||||
|
* @value value to be scaled from the original range to the new range
|
||||||
|
*
|
||||||
|
* @returns value in new scale
|
||||||
|
* special thanks to @letstalkaboutdune for the math
|
||||||
|
*/
|
||||||
|
private fun scaleToRange(x1: Double, x2: Double, y1: Double, y2: Double, value: Float): Double {
|
||||||
|
val gain = (y2 - y1) / (x2 - x1)
|
||||||
|
val offset = y2 - (gain * x2)
|
||||||
|
return (gain * value) + offset
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [MapParameters.vegetationRichness] is the threshold for vegetation spawn
|
* [MapParameters.vegetationRichness] is the threshold for vegetation spawn
|
||||||
*/
|
*/
|
||||||
@ -596,6 +674,11 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
-1f, ruleset.modOptions.constants.spawnIceBelowTemperature,
|
-1f, ruleset.modOptions.constants.spawnIceBelowTemperature,
|
||||||
0f, 1f))
|
0f, 1f))
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
|
if (tileMap.mapParameters.shape === MapShape.flatEarth) {
|
||||||
|
spawnFlatEarthIceWalls(tileMap, iceEquivalents)
|
||||||
|
}
|
||||||
|
|
||||||
if (iceEquivalents.isEmpty()) return
|
if (iceEquivalents.isEmpty()) return
|
||||||
|
|
||||||
tileMap.setTransients(ruleset)
|
tileMap.setTransients(ruleset)
|
||||||
@ -621,6 +704,145 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun spawnFlatEarthIceWalls(tileMap: TileMap, iceEquivalents: List<TerrainOccursRange>) {
|
||||||
|
val iceCandidates = iceEquivalents.filter {
|
||||||
|
it.matches(-1.0, 1.0)
|
||||||
|
}.map {
|
||||||
|
it.terrain.name
|
||||||
|
}
|
||||||
|
val iceTerrainName =
|
||||||
|
when (iceCandidates.size) {
|
||||||
|
1 -> iceCandidates.first()
|
||||||
|
!in 0..1 -> iceCandidates.random(randomness.RNG)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val snowCandidates = ruleset.terrains.values.asSequence().filter {
|
||||||
|
it.type == TerrainType.Land
|
||||||
|
}.flatMap { terrain ->
|
||||||
|
val conditions = terrain.getGenerationConditions()
|
||||||
|
if (conditions.any()) conditions
|
||||||
|
else sequenceOf(
|
||||||
|
TerrainOccursRange(
|
||||||
|
terrain,
|
||||||
|
-1f, ruleset.modOptions.constants.spawnIceBelowTemperature,
|
||||||
|
0f, 1f
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}.toList().filter {
|
||||||
|
it.matches(-1.0, 1.0)
|
||||||
|
}.map {
|
||||||
|
it.terrain.name
|
||||||
|
}
|
||||||
|
val snowTerrainName =
|
||||||
|
when (snowCandidates.size) {
|
||||||
|
1 -> snowCandidates.first()
|
||||||
|
!in 0..1 -> snowCandidates.random(randomness.RNG)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val mountainTerrainName =
|
||||||
|
ruleset.terrains.values.firstOrNull { it.hasUnique(UniqueType.OccursInChains) }?.name
|
||||||
|
|
||||||
|
val bestArcticTileName = when {
|
||||||
|
iceTerrainName != null -> iceTerrainName
|
||||||
|
snowTerrainName != null -> snowTerrainName
|
||||||
|
mountainTerrainName != null -> mountainTerrainName
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val arcticTileNameList =
|
||||||
|
arrayOf(iceTerrainName, snowTerrainName, mountainTerrainName).filterNotNull()
|
||||||
|
|
||||||
|
// Skip the tile loop if nothing can be done in it
|
||||||
|
if (bestArcticTileName == null && arcticTileNameList.isEmpty()) return
|
||||||
|
|
||||||
|
// Flat Earth needs a 1 tile wide perimeter of ice/mountain/snow and a 2 radius cluster of ice in the center.
|
||||||
|
for (tile in tileMap.values) {
|
||||||
|
val isCenterTile = tile.latitude == 0f && tile.longitude == 0f
|
||||||
|
val isEdgeTile = tile.neighbors.count() < 6
|
||||||
|
|
||||||
|
// Make center tiles ice or snow or mountain depending on availability
|
||||||
|
if (isCenterTile && bestArcticTileName != null) {
|
||||||
|
spawnFlatEarthCenterIceWall(tile, bestArcticTileName, iceTerrainName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make edge tiles randomly ice or snow or mountain if available
|
||||||
|
if (isEdgeTile && arcticTileNameList.isNotEmpty()) {
|
||||||
|
spawnFlatEarthEdgeIceWall(tile, arcticTileNameList, iceTerrainName, mountainTerrainName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun spawnFlatEarthCenterIceWall(tile: TileInfo, bestArcticTileName: String, iceTerrainName: String?) {
|
||||||
|
// Spawn ice on center tile
|
||||||
|
if (bestArcticTileName == iceTerrainName) {
|
||||||
|
tile.baseTerrain = waterTerrainName
|
||||||
|
tile.addTerrainFeature(iceTerrainName)
|
||||||
|
} else {
|
||||||
|
tile.baseTerrain = bestArcticTileName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn circle of ice around center tile
|
||||||
|
for (neighbor in tile.neighbors) {
|
||||||
|
if (bestArcticTileName == iceTerrainName) {
|
||||||
|
neighbor.baseTerrain = waterTerrainName
|
||||||
|
neighbor.addTerrainFeature(iceTerrainName)
|
||||||
|
} else {
|
||||||
|
neighbor.baseTerrain = bestArcticTileName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn partial circle of ice around circle of ice
|
||||||
|
for (neighbor2 in neighbor.neighbors) {
|
||||||
|
if (randomness.RNG.nextDouble() < 0.75) {
|
||||||
|
// Do nothing most of the time at random.
|
||||||
|
} else if (bestArcticTileName == iceTerrainName) {
|
||||||
|
neighbor2.baseTerrain = waterTerrainName
|
||||||
|
neighbor2.addTerrainFeature(iceTerrainName)
|
||||||
|
} else {
|
||||||
|
neighbor2.baseTerrain = bestArcticTileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun spawnFlatEarthEdgeIceWall(tile: TileInfo, arcticTileNameList: List<String>, iceTerrainName: String?, mountainTerrainName: String?) {
|
||||||
|
// Select one of the arctic tiles at random
|
||||||
|
val arcticTileName = when (arcticTileNameList.size) {
|
||||||
|
1 -> arcticTileNameList.first()
|
||||||
|
else -> arcticTileNameList.random(randomness.RNG)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn arctic tiles on edge tile
|
||||||
|
if (arcticTileName == iceTerrainName) {
|
||||||
|
tile.baseTerrain = waterTerrainName
|
||||||
|
tile.addTerrainFeature(iceTerrainName)
|
||||||
|
} else if (iceTerrainName != null && arcticTileName != mountainTerrainName) {
|
||||||
|
tile.baseTerrain = arcticTileName
|
||||||
|
tile.addTerrainFeature(iceTerrainName)
|
||||||
|
} else {
|
||||||
|
tile.baseTerrain = arcticTileName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn partial circle of arctic tiles next to the edge
|
||||||
|
for (neighbor in tile.neighbors) {
|
||||||
|
val neighborIsEdgeTile = neighbor.neighbors.count() < 6
|
||||||
|
if (neighborIsEdgeTile) {
|
||||||
|
// Do not redo edge tile. It is already done.
|
||||||
|
} else if (randomness.RNG.nextDouble() < 0.75) {
|
||||||
|
// Do nothing most of the time at random.
|
||||||
|
} else if (arcticTileName == iceTerrainName) {
|
||||||
|
neighbor.baseTerrain = waterTerrainName
|
||||||
|
neighbor.addTerrainFeature(iceTerrainName)
|
||||||
|
} else if (iceTerrainName != null && arcticTileName != mountainTerrainName) {
|
||||||
|
neighbor.baseTerrain = arcticTileName
|
||||||
|
neighbor.addTerrainFeature(iceTerrainName)
|
||||||
|
} else {
|
||||||
|
neighbor.baseTerrain = arcticTileName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapGenerationRandomness {
|
class MapGenerationRandomness {
|
||||||
|
@ -48,6 +48,36 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
MapType.archipelago -> createArchipelago(tileMap)
|
MapType.archipelago -> createArchipelago(tileMap)
|
||||||
MapType.default -> createPerlin(tileMap)
|
MapType.default -> createPerlin(tileMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tileMap.mapParameters.shape === MapShape.flatEarth) {
|
||||||
|
generateFlatEarthExtraWater(tileMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateFlatEarthExtraWater(tileMap: TileMap) {
|
||||||
|
for (tile in tileMap.values) {
|
||||||
|
val isCenterTile = tile.latitude == 0f && tile.longitude == 0f
|
||||||
|
val isEdgeTile = tile.neighbors.count() < 6
|
||||||
|
|
||||||
|
if (!isCenterTile && !isEdgeTile) continue
|
||||||
|
|
||||||
|
/*
|
||||||
|
Flat Earth needs a 3 tile wide water perimeter and a 4 tile radius water center.
|
||||||
|
This helps map generators to not place important things there which would be destroyed
|
||||||
|
when the ice walls are placed there.
|
||||||
|
*/
|
||||||
|
tile.baseTerrain = waterTerrainName
|
||||||
|
for (neighbor in tile.neighbors) {
|
||||||
|
neighbor.baseTerrain = waterTerrainName
|
||||||
|
for (neighbor2 in neighbor.neighbors) {
|
||||||
|
neighbor2.baseTerrain = waterTerrainName
|
||||||
|
if (!isCenterTile) continue
|
||||||
|
for (neighbor3 in neighbor2.neighbors) {
|
||||||
|
neighbor3.baseTerrain = waterTerrainName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun spawnLandOrWater(tile: TileInfo, elevation: Double) {
|
private fun spawnLandOrWater(tile: TileInfo, elevation: Double) {
|
||||||
@ -109,7 +139,7 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
|
|
||||||
private fun createTwoContinents(tileMap: TileMap) {
|
private fun createTwoContinents(tileMap: TileMap) {
|
||||||
val isLatitude =
|
val isLatitude =
|
||||||
if (tileMap.mapParameters.shape === MapShape.hexagonal) randomness.RNG.nextDouble() > 0.5f
|
if (tileMap.mapParameters.shape === MapShape.hexagonal || tileMap.mapParameters.shape === MapShape.flatEarth) randomness.RNG.nextDouble() > 0.5f
|
||||||
else if (tileMap.mapParameters.mapSize.height > tileMap.mapParameters.mapSize.width) true
|
else if (tileMap.mapParameters.mapSize.height > tileMap.mapParameters.mapSize.width) true
|
||||||
else if (tileMap.mapParameters.mapSize.width > tileMap.mapParameters.mapSize.height) false
|
else if (tileMap.mapParameters.mapSize.width > tileMap.mapParameters.mapSize.height) false
|
||||||
else randomness.RNG.nextDouble() > 0.5f
|
else randomness.RNG.nextDouble() > 0.5f
|
||||||
@ -123,12 +153,14 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createThreeContinents(tileMap: TileMap) {
|
private fun createThreeContinents(tileMap: TileMap) {
|
||||||
val isNorth = randomness.RNG.nextDouble() < 0.5f
|
val isNorth = randomness.RNG.nextDouble() < 0.5
|
||||||
|
// On flat earth maps we can randomly do East or West instead of North or South
|
||||||
|
val isEastWest = tileMap.mapParameters.shape === MapShape.flatEarth && randomness.RNG.nextDouble() > 0.5
|
||||||
|
|
||||||
val elevationSeed = randomness.RNG.nextInt().toDouble()
|
val elevationSeed = randomness.RNG.nextInt().toDouble()
|
||||||
for (tile in tileMap.values) {
|
for (tile in tileMap.values) {
|
||||||
var elevation = randomness.getPerlinNoise(tile, elevationSeed)
|
var elevation = randomness.getPerlinNoise(tile, elevationSeed)
|
||||||
elevation = (elevation + getThreeContinentsTransform(tile, tileMap, isNorth)) / 2.0
|
elevation = (elevation + getThreeContinentsTransform(tile, tileMap, isNorth, isEastWest)) / 2.0
|
||||||
spawnLandOrWater(tile, elevation)
|
spawnLandOrWater(tile, elevation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,16 +212,25 @@ class MapLandmassGenerator(val ruleset: Ruleset, val randomness: MapGenerationRa
|
|||||||
return min(0.2, -1.0 + (5.0 * factor.pow(0.6f) + randomScale) / 3.0)
|
return min(0.2, -1.0 + (5.0 * factor.pow(0.6f) + randomScale) / 3.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getThreeContinentsTransform(tileInfo: TileInfo, tileMap: TileMap, isNorth: Boolean): Double {
|
private fun getThreeContinentsTransform(tileInfo: TileInfo, tileMap: TileMap, isNorth: Boolean, isEastWest: Boolean): Double {
|
||||||
// The idea here is to create a water area separating the two four areas.
|
// The idea here is to create a water area separating the two four areas.
|
||||||
// So what we do it create a line of water in the middle - where longitude is close to 0.
|
// So what we do it create a line of water in the middle - where longitude is close to 0.
|
||||||
val randomScale = randomness.RNG.nextDouble()
|
val randomScale = randomness.RNG.nextDouble()
|
||||||
var longitudeFactor = abs(tileInfo.longitude) / tileMap.maxLongitude
|
var longitudeFactor = abs(tileInfo.longitude) / tileMap.maxLongitude
|
||||||
var latitudeFactor = abs(tileInfo.latitude) / tileMap.maxLatitude
|
var latitudeFactor = abs(tileInfo.latitude) / tileMap.maxLatitude
|
||||||
|
|
||||||
|
// 3rd continent should use only half the map width, or if flat earth, only a third
|
||||||
|
val sizeReductionFactor = if (tileMap.mapParameters.shape === MapShape.flatEarth) 3f else 2f
|
||||||
|
|
||||||
// We then pick one side to be merged into one centered continent instead of two cornered.
|
// We then pick one side to be merged into one centered continent instead of two cornered.
|
||||||
|
if (isEastWest) {
|
||||||
|
// In EastWest mode North represents West
|
||||||
|
if (isNorth && tileInfo.longitude < 0 || !isNorth && tileInfo.longitude > 0)
|
||||||
|
latitudeFactor = max(0f, tileMap.maxLatitude - abs(tileInfo.latitude * sizeReductionFactor)) / tileMap.maxLatitude
|
||||||
|
} else {
|
||||||
if (isNorth && tileInfo.latitude < 0 || !isNorth && tileInfo.latitude > 0)
|
if (isNorth && tileInfo.latitude < 0 || !isNorth && tileInfo.latitude > 0)
|
||||||
longitudeFactor = max(0f, tileMap.maxLongitude - abs(tileInfo.longitude * 2f)) / tileMap.maxLongitude
|
longitudeFactor = max(0f, tileMap.maxLongitude - abs(tileInfo.longitude * sizeReductionFactor)) / tileMap.maxLongitude
|
||||||
|
}
|
||||||
|
|
||||||
// If this is a world wrap, we want it to be separated on both sides -
|
// If this is a world wrap, we want it to be separated on both sides -
|
||||||
// so we make the actual strip of water thinner, but we put it both in the middle of the map and on the edges of the map
|
// so we make the actual strip of water thinner, but we put it both in the middle of the map and on the edges of the map
|
||||||
|
@ -76,7 +76,7 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
val totalLand = tileMap.continentSizes.values.sum().toFloat()
|
val totalLand = tileMap.continentSizes.values.sum().toFloat()
|
||||||
val largestContinent = tileMap.continentSizes.values.maxOf { it }.toFloat()
|
val largestContinent = tileMap.continentSizes.values.maxOf { it }.toFloat()
|
||||||
|
|
||||||
val radius = if (tileMap.mapParameters.shape == MapShape.hexagonal)
|
val radius = if (tileMap.mapParameters.shape == MapShape.hexagonal || tileMap.mapParameters.shape == MapShape.flatEarth)
|
||||||
tileMap.mapParameters.mapSize.radius.toFloat()
|
tileMap.mapParameters.mapSize.radius.toFloat()
|
||||||
else
|
else
|
||||||
(max(tileMap.mapParameters.mapSize.width / 2, tileMap.mapParameters.mapSize.height / 2)).toFloat()
|
(max(tileMap.mapParameters.mapSize.width / 2, tileMap.mapParameters.mapSize.height / 2)).toFloat()
|
||||||
|
@ -230,7 +230,7 @@ class MapEditorScreen(map: TileMap? = null): BaseScreen(), RecreateOnResize {
|
|||||||
ToastPopup(message, this@MapEditorScreen, 4000L )
|
ToastPopup(message, this@MapEditorScreen, 4000L )
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.shape == MapShape.hexagonal) {
|
if (params.shape == MapShape.hexagonal || params.shape == MapShape.flatEarth) {
|
||||||
params.mapSize = MapSizeNew(HexMath.getHexagonalRadiusForArea(areaFromTiles).toInt())
|
params.mapSize = MapSizeNew(HexMath.getHexagonalRadiusForArea(areaFromTiles).toInt())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ class MapParametersTable(
|
|||||||
private fun addMapShapeSelectBox() {
|
private fun addMapShapeSelectBox() {
|
||||||
val mapShapes = listOfNotNull(
|
val mapShapes = listOfNotNull(
|
||||||
MapShape.hexagonal,
|
MapShape.hexagonal,
|
||||||
|
MapShape.flatEarth,
|
||||||
MapShape.rectangular
|
MapShape.rectangular
|
||||||
)
|
)
|
||||||
val mapShapeSelectBox =
|
val mapShapeSelectBox =
|
||||||
@ -176,7 +177,7 @@ class MapParametersTable(
|
|||||||
private fun updateWorldSizeTable() {
|
private fun updateWorldSizeTable() {
|
||||||
customWorldSizeTable.clear()
|
customWorldSizeTable.clear()
|
||||||
|
|
||||||
if (mapParameters.shape == MapShape.hexagonal && worldSizeSelectBox.selected.value == MapSize.custom)
|
if ((mapParameters.shape == MapShape.hexagonal || mapParameters.shape == MapShape.flatEarth) && worldSizeSelectBox.selected.value == MapSize.custom)
|
||||||
customWorldSizeTable.add(hexagonalSizeTable).grow().row()
|
customWorldSizeTable.add(hexagonalSizeTable).grow().row()
|
||||||
else if (mapParameters.shape == MapShape.rectangular && worldSizeSelectBox.selected.value == MapSize.custom)
|
else if (mapParameters.shape == MapShape.rectangular && worldSizeSelectBox.selected.value == MapSize.custom)
|
||||||
customWorldSizeTable.add(rectangularSizeTable).grow().row()
|
customWorldSizeTable.add(rectangularSizeTable).grow().row()
|
||||||
|
Reference in New Issue
Block a user