mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-26 13:27:22 -04:00
Current map generation is extremely dependant on specific terrains existing in the ruleset. (#6067)
* Current map generation is extremely dependant on specific terrains existing in the ruleset. This attempts to eliminate those dependencies. All changes indicate areas where a crash occured before the change. I still encounter problems in generateRegions when trying to generate a map with no water tiles - @SimorCedar I think the splitRegion doesn't like the fact that there are land tiles on edges of the map? * Split 'equal fertility' regions as close to the center as possible Also, don't crash if no luxury resources are defined
This commit is contained in:
parent
a237e7bf82
commit
461385fff6
@ -793,7 +793,7 @@ open class TileInfo {
|
|||||||
// Uninitialized tilemap - when you're displaying a tile in the civilopedia or map editor
|
// Uninitialized tilemap - when you're displaying a tile in the civilopedia or map editor
|
||||||
if (::tileMap.isInitialized) convertHillToTerrainFeature()
|
if (::tileMap.isInitialized) convertHillToTerrainFeature()
|
||||||
if (!ruleset.terrains.containsKey(baseTerrain))
|
if (!ruleset.terrains.containsKey(baseTerrain))
|
||||||
throw Exception()
|
throw Exception("Terrain $baseTerrain does not exist in ruleset!")
|
||||||
baseTerrainObject = ruleset.terrains[baseTerrain]!!
|
baseTerrainObject = ruleset.terrains[baseTerrain]!!
|
||||||
isWater = getBaseTerrain().type == TerrainType.Water
|
isWater = getBaseTerrain().type == TerrainType.Water
|
||||||
isLand = getBaseTerrain().type == TerrainType.Land
|
isLand = getBaseTerrain().type == TerrainType.Land
|
||||||
|
@ -9,6 +9,7 @@ import com.unciv.logic.civilization.CivilizationInfo
|
|||||||
import com.unciv.models.metadata.Player
|
import com.unciv.models.metadata.Player
|
||||||
import com.unciv.models.ruleset.Nation
|
import com.unciv.models.ruleset.Nation
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
|
import com.unciv.models.ruleset.tile.TerrainType
|
||||||
import com.unciv.models.ruleset.unique.UniqueType
|
import com.unciv.models.ruleset.unique.UniqueType
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
@ -94,8 +95,10 @@ class TileMap {
|
|||||||
/** creates a hexagonal map of given radius (filled with grassland) */
|
/** creates a hexagonal map of given radius (filled with grassland) */
|
||||||
constructor(radius: Int, ruleset: Ruleset, worldWrap: Boolean = false) {
|
constructor(radius: Int, ruleset: Ruleset, worldWrap: Boolean = false) {
|
||||||
startingLocations.clear()
|
startingLocations.clear()
|
||||||
|
val firstAvailableLandTerrain = ruleset.terrains.values.firstOrNull { it.type==TerrainType.Land }
|
||||||
|
?: throw Exception("Cannot create map - no land terrains found!")
|
||||||
for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius, worldWrap))
|
for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius, worldWrap))
|
||||||
tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland })
|
tileList.add(TileInfo().apply { position = vector; baseTerrain = firstAvailableLandTerrain.name })
|
||||||
setTransients(ruleset)
|
setTransients(ruleset)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +208,11 @@ class TileMap {
|
|||||||
/** @return all tiles within [rectangle], respecting world edges and wrap.
|
/** @return all tiles within [rectangle], respecting world edges and wrap.
|
||||||
* If using even Q coordinates the rectangle will be "straight" ie parallel with rectangular map edges. */
|
* If using even Q coordinates the rectangle will be "straight" ie parallel with rectangular map edges. */
|
||||||
fun getTilesInRectangle(rectangle: Rectangle, evenQ: Boolean = false): Sequence<TileInfo> =
|
fun getTilesInRectangle(rectangle: Rectangle, evenQ: Boolean = false): Sequence<TileInfo> =
|
||||||
if (rectangle.width <= 0 || rectangle.height <= 0)
|
if (rectangle.width <= 0 || rectangle.height <= 0) {
|
||||||
sequenceOf(get(rectangle.x.toInt(), rectangle.y.toInt()))
|
val tile = getIfTileExistsOrNull(rectangle.x.toInt(), rectangle.y.toInt())
|
||||||
|
if (tile == null) sequenceOf()
|
||||||
|
else sequenceOf(tile)
|
||||||
|
}
|
||||||
else
|
else
|
||||||
sequence {
|
sequence {
|
||||||
for (x in 0 until rectangle.width.toInt()) {
|
for (x in 0 until rectangle.width.toInt()) {
|
||||||
|
@ -400,15 +400,16 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
|
|
||||||
// Old, static map generation rules - necessary for existing base ruleset mods to continue to function
|
// Old, static map generation rules - necessary for existing base ruleset mods to continue to function
|
||||||
if (noTerrainUniques) {
|
if (noTerrainUniques) {
|
||||||
tile.baseTerrain = when {
|
val autoTerrain = when {
|
||||||
temperature < -0.4 -> if (humidity < 0.5) Constants.snow else Constants.tundra
|
temperature < -0.4 -> if (humidity < 0.5) Constants.snow else Constants.tundra
|
||||||
temperature < 0.8 -> if (humidity < 0.5) Constants.plains else Constants.grassland
|
temperature < 0.8 -> if (humidity < 0.5) Constants.plains else Constants.grassland
|
||||||
temperature <= 1.0 -> if (humidity < 0.7) Constants.desert else Constants.plains
|
temperature <= 1.0 -> if (humidity < 0.7) Constants.desert else Constants.plains
|
||||||
else -> {
|
else -> {
|
||||||
println("applyHumidityAndTemperature: Invalid temperature $temperature")
|
println("applyHumidityAndTemperature: Invalid temperature $temperature")
|
||||||
Constants.grassland
|
Constants.grassland
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ruleset.terrains.containsKey(autoTerrain)) tile.baseTerrain = autoTerrain
|
||||||
tile.setTerrainTransients()
|
tile.setTerrainTransients()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -432,7 +433,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
*/
|
*/
|
||||||
private fun spawnVegetation(tileMap: TileMap) {
|
private fun spawnVegetation(tileMap: TileMap) {
|
||||||
val vegetationSeed = randomness.RNG.nextInt().toDouble()
|
val vegetationSeed = randomness.RNG.nextInt().toDouble()
|
||||||
val candidateTerrains = Constants.vegetation.flatMap{ ruleset.terrains[it]!!.occursOn }
|
val candidateTerrains = Constants.vegetation.mapNotNull { ruleset.terrains[it] }.flatMap{ it.occursOn }
|
||||||
//Checking it.baseTerrain in candidateTerrains to make sure forest does not spawn on desert hill
|
//Checking it.baseTerrain in candidateTerrains to make sure forest does not spawn on desert hill
|
||||||
for (tile in tileMap.values.asSequence().filter { it.baseTerrain in candidateTerrains
|
for (tile in tileMap.values.asSequence().filter { it.baseTerrain in candidateTerrains
|
||||||
&& it.getLastTerrain().name in candidateTerrains }) {
|
&& it.getLastTerrain().name in candidateTerrains }) {
|
||||||
|
@ -154,8 +154,11 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
var bestSplitPoint = 1 // will be the size of the split-off region
|
var bestSplitPoint = 1 // will be the size of the split-off region
|
||||||
var closestFertility = 0
|
var closestFertility = 0
|
||||||
var cumulativeFertility = 0
|
var cumulativeFertility = 0
|
||||||
val pointsToTry = if (widerThanTall) 1..regionToSplit.rect.width.toInt()
|
|
||||||
else 1..regionToSplit.rect.height.toInt()
|
val highestPointToTry = if (widerThanTall) regionToSplit.rect.width.toInt()
|
||||||
|
else regionToSplit.rect.height.toInt()
|
||||||
|
val pointsToTry = 1..highestPointToTry
|
||||||
|
val halfwayPoint = highestPointToTry/2
|
||||||
|
|
||||||
for (splitPoint in pointsToTry) {
|
for (splitPoint in pointsToTry) {
|
||||||
val nextRect = if (widerThanTall)
|
val nextRect = if (widerThanTall)
|
||||||
@ -175,7 +178,11 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
nextRect.sumOf { if (it.getContinent() == splitOffRegion.continentID) it.getTileFertility(true) else 0 }
|
nextRect.sumOf { if (it.getContinent() == splitOffRegion.continentID) it.getTileFertility(true) else 0 }
|
||||||
|
|
||||||
// Better than last try?
|
// Better than last try?
|
||||||
if (abs(cumulativeFertility - targetFertility) <= abs(closestFertility - targetFertility)) {
|
val bestSplitPointFertilityDeltaFromTarget = abs(closestFertility - targetFertility)
|
||||||
|
val currentSplitPointFertilityDeltaFromTarget = abs(cumulativeFertility - targetFertility)
|
||||||
|
if (currentSplitPointFertilityDeltaFromTarget < bestSplitPointFertilityDeltaFromTarget
|
||||||
|
|| (currentSplitPointFertilityDeltaFromTarget == bestSplitPointFertilityDeltaFromTarget // same fertility split but better 'amount of tiles' split
|
||||||
|
&& abs(halfwayPoint- splitPoint) < abs(halfwayPoint- bestSplitPoint) )) { // current split point is closer to the halfway point
|
||||||
bestSplitPoint = splitPoint
|
bestSplitPoint = splitPoint
|
||||||
closestFertility = cumulativeFertility
|
closestFertility = cumulativeFertility
|
||||||
}
|
}
|
||||||
@ -1137,7 +1144,7 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
targetLuxuries++
|
targetLuxuries++
|
||||||
}
|
}
|
||||||
|
|
||||||
val luxuryToPlace = ruleset.tileResources[region.luxury]!!
|
val luxuryToPlace = ruleset.tileResources[region.luxury] ?: continue
|
||||||
// First check 2 inner rings
|
// First check 2 inner rings
|
||||||
val firstPass = tileMap[region.startPosition!!].getTilesInDistanceRange(1..2)
|
val firstPass = tileMap[region.startPosition!!].getTilesInDistanceRange(1..2)
|
||||||
.shuffled().sortedBy { it.getTileFertility(false) } // Check bad tiles first
|
.shuffled().sortedBy { it.getTileFertility(false) } // Check bad tiles first
|
||||||
@ -1184,7 +1191,7 @@ class MapRegions (val ruleset: Ruleset){
|
|||||||
}
|
}
|
||||||
regionTargetNumber = max(1, regionTargetNumber)
|
regionTargetNumber = max(1, regionTargetNumber)
|
||||||
for (region in regions) {
|
for (region in regions) {
|
||||||
val resource = ruleset.tileResources[region.luxury]!!
|
val resource = ruleset.tileResources[region.luxury] ?: continue
|
||||||
if (isWaterOnlyResource(resource))
|
if (isWaterOnlyResource(resource))
|
||||||
tryAddingResourceToTiles(resource, regionTargetNumber,
|
tryAddingResourceToTiles(resource, regionTargetNumber,
|
||||||
tileMap.getTilesInRectangle(region.rect).filter { it.isWater && it.neighbors.any { neighbor -> neighbor.getContinent() == region.continentID } }.shuffled(),
|
tileMap.getTilesInRectangle(region.rect).filter { it.isWater && it.neighbors.any { neighbor -> neighbor.getContinent() == region.continentID } }.shuffled(),
|
||||||
|
@ -16,17 +16,21 @@ class RiverGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun spawnRivers() {
|
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 } / MAP_TILES_PER_RIVER
|
||||||
|
|
||||||
var optionalTiles = tileMap.values.asSequence()
|
var optionalTiles = tileMap.values.asSequence()
|
||||||
.filter { it.baseTerrain == Constants.mountain && it.isFarEnoughFromWater() }.toMutableList()
|
.filter { it.baseTerrain == Constants.mountain && it.isFarEnoughFromWater() }
|
||||||
|
.toMutableList()
|
||||||
if (optionalTiles.size < numberOfRivers)
|
if (optionalTiles.size < numberOfRivers)
|
||||||
optionalTiles.addAll(tileMap.values.filter { it.isHill() && it.isFarEnoughFromWater() })
|
optionalTiles.addAll(tileMap.values.filter { it.isHill() && it.isFarEnoughFromWater() })
|
||||||
if (optionalTiles.size < numberOfRivers)
|
if (optionalTiles.size < numberOfRivers)
|
||||||
optionalTiles = tileMap.values.filter { it.isLand && it.isFarEnoughFromWater() }.toMutableList()
|
optionalTiles =
|
||||||
|
tileMap.values.filter { it.isLand && it.isFarEnoughFromWater() }.toMutableList()
|
||||||
|
|
||||||
val mapRadius = tileMap.mapParameters.mapSize.radius
|
val mapRadius = tileMap.mapParameters.mapSize.radius
|
||||||
val riverStarts = randomness.chooseSpreadOutLocations(numberOfRivers, optionalTiles, mapRadius)
|
val riverStarts =
|
||||||
|
randomness.chooseSpreadOutLocations(numberOfRivers, optionalTiles, mapRadius)
|
||||||
for (tile in riverStarts) spawnRiver(tile)
|
for (tile in riverStarts) spawnRiver(tile)
|
||||||
|
|
||||||
for (tile in tileMap.values) {
|
for (tile in tileMap.values) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user