mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-25 22:18:50 +07: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
|
||||
if (::tileMap.isInitialized) convertHillToTerrainFeature()
|
||||
if (!ruleset.terrains.containsKey(baseTerrain))
|
||||
throw Exception()
|
||||
throw Exception("Terrain $baseTerrain does not exist in ruleset!")
|
||||
baseTerrainObject = ruleset.terrains[baseTerrain]!!
|
||||
isWater = getBaseTerrain().type == TerrainType.Water
|
||||
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.ruleset.Nation
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import kotlin.math.abs
|
||||
|
||||
@ -94,8 +95,10 @@ class TileMap {
|
||||
/** creates a hexagonal map of given radius (filled with grassland) */
|
||||
constructor(radius: Int, ruleset: Ruleset, worldWrap: Boolean = false) {
|
||||
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))
|
||||
tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland })
|
||||
tileList.add(TileInfo().apply { position = vector; baseTerrain = firstAvailableLandTerrain.name })
|
||||
setTransients(ruleset)
|
||||
}
|
||||
|
||||
@ -205,8 +208,11 @@ class TileMap {
|
||||
/** @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. */
|
||||
fun getTilesInRectangle(rectangle: Rectangle, evenQ: Boolean = false): Sequence<TileInfo> =
|
||||
if (rectangle.width <= 0 || rectangle.height <= 0)
|
||||
sequenceOf(get(rectangle.x.toInt(), rectangle.y.toInt()))
|
||||
if (rectangle.width <= 0 || rectangle.height <= 0) {
|
||||
val tile = getIfTileExistsOrNull(rectangle.x.toInt(), rectangle.y.toInt())
|
||||
if (tile == null) sequenceOf()
|
||||
else sequenceOf(tile)
|
||||
}
|
||||
else
|
||||
sequence {
|
||||
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
|
||||
if (noTerrainUniques) {
|
||||
tile.baseTerrain = when {
|
||||
temperature < -0.4 -> if (humidity < 0.5) Constants.snow else Constants.tundra
|
||||
temperature < 0.8 -> if (humidity < 0.5) Constants.plains else Constants.grassland
|
||||
val autoTerrain = when {
|
||||
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 <= 1.0 -> if (humidity < 0.7) Constants.desert else Constants.plains
|
||||
else -> {
|
||||
println("applyHumidityAndTemperature: Invalid temperature $temperature")
|
||||
Constants.grassland
|
||||
}
|
||||
}
|
||||
if (ruleset.terrains.containsKey(autoTerrain)) tile.baseTerrain = autoTerrain
|
||||
tile.setTerrainTransients()
|
||||
continue
|
||||
}
|
||||
@ -432,7 +433,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
*/
|
||||
private fun spawnVegetation(tileMap: TileMap) {
|
||||
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
|
||||
for (tile in tileMap.values.asSequence().filter { it.baseTerrain 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 closestFertility = 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) {
|
||||
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 }
|
||||
|
||||
// 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
|
||||
closestFertility = cumulativeFertility
|
||||
}
|
||||
@ -1137,7 +1144,7 @@ class MapRegions (val ruleset: Ruleset){
|
||||
targetLuxuries++
|
||||
}
|
||||
|
||||
val luxuryToPlace = ruleset.tileResources[region.luxury]!!
|
||||
val luxuryToPlace = ruleset.tileResources[region.luxury] ?: continue
|
||||
// First check 2 inner rings
|
||||
val firstPass = tileMap[region.startPosition!!].getTilesInDistanceRange(1..2)
|
||||
.shuffled().sortedBy { it.getTileFertility(false) } // Check bad tiles first
|
||||
@ -1184,7 +1191,7 @@ class MapRegions (val ruleset: Ruleset){
|
||||
}
|
||||
regionTargetNumber = max(1, regionTargetNumber)
|
||||
for (region in regions) {
|
||||
val resource = ruleset.tileResources[region.luxury]!!
|
||||
val resource = ruleset.tileResources[region.luxury] ?: continue
|
||||
if (isWaterOnlyResource(resource))
|
||||
tryAddingResourceToTiles(resource, regionTargetNumber,
|
||||
tileMap.getTilesInRectangle(region.rect).filter { it.isWater && it.neighbors.any { neighbor -> neighbor.getContinent() == region.continentID } }.shuffled(),
|
||||
|
@ -16,17 +16,21 @@ class RiverGenerator(
|
||||
}
|
||||
|
||||
fun spawnRivers() {
|
||||
if (tileMap.values.none { it.isWater }) return
|
||||
val numberOfRivers = tileMap.values.count { it.isLand } / MAP_TILES_PER_RIVER
|
||||
|
||||
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)
|
||||
optionalTiles.addAll(tileMap.values.filter { it.isHill() && it.isFarEnoughFromWater() })
|
||||
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 riverStarts = randomness.chooseSpreadOutLocations(numberOfRivers, optionalTiles, mapRadius)
|
||||
val riverStarts =
|
||||
randomness.chooseSpreadOutLocations(numberOfRivers, optionalTiles, mapRadius)
|
||||
for (tile in riverStarts) spawnRiver(tile)
|
||||
|
||||
for (tile in tileMap.values) {
|
||||
|
Loading…
Reference in New Issue
Block a user