Fix NW placement

This commit is contained in:
SimonCeder 2022-07-30 00:07:48 +02:00
parent 64e79ffa0d
commit a6e61c120b
3 changed files with 63 additions and 11 deletions

View File

@ -139,18 +139,22 @@ class MapGenerator(val ruleset: Ruleset) {
runAndMeasure("assignRegions") {
regions.assignRegions(map, civilizations.filter { ruleset.nations[it.civName]!!.isMajorCiv() }, gameParameters)
}
// Natural wonders need to go before most resources since there is a minimum distance
runAndMeasure("NaturalWonderGenerator") {
NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
}
runAndMeasure("placeResourcesAndMinorCivs") {
regions.placeResourcesAndMinorCivs(map, civilizations.filter { ruleset.nations[it.civName]!!.isCityState() })
}
} else {
runAndMeasure("NaturalWonderGenerator") {
NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
}
// Fallback spread resources function - used when generating maps in map editor
runAndMeasure("spreadResources") {
spreadResources(map)
}
}
runAndMeasure("NaturalWonderGenerator") {
NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
}
runAndMeasure("spreadAncientRuins") {
spreadAncientRuins(map)
}
@ -288,7 +292,9 @@ class MapGenerator(val ruleset: Ruleset) {
private fun spreadStrategicResources(tileMap: TileMap, mapRadius: Int) {
val strategicResources = ruleset.tileResources.values.filter { it.resourceType == ResourceType.Strategic }
// passable land tiles (no mountains, no wonders) without resources yet
val candidateTiles = tileMap.values.filter { it.resource == null && !it.isImpassible() }
// can't be next to NW
val candidateTiles = tileMap.values.filter { it.resource == null && !it.isImpassible()
&& it.neighbors.none { neighbor -> neighbor.isNaturalWonder() }}
val totalNumberOfResources = candidateTiles.count { it.isLand } * tileMap.mapParameters.resourceRichness
val resourcesPerType = (totalNumberOfResources/strategicResources.size).toInt()
for (resource in strategicResources) {
@ -314,7 +320,8 @@ class MapGenerator(val ruleset: Ruleset) {
val suitableTiles = tileMap.values
.filterNot { it.baseTerrain == Constants.snow && it.isHill() }
.filter { it.resource == null && resourcesOfType.any { r -> r.terrainsCanBeFoundOn.contains(it.getLastTerrain().name) } }
.filter { it.resource == null && it.neighbors.none { neighbor -> neighbor.isNaturalWonder() }
&& resourcesOfType.any { r -> r.terrainsCanBeFoundOn.contains(it.getLastTerrain().name) } }
val numberOfResources = tileMap.values.count { it.isLand && !it.isImpassible() } *
tileMap.mapParameters.resourceRichness
val locations = randomness.chooseSpreadOutLocations(numberOfResources.toInt(), suitableTiles, mapRadius)

View File

@ -829,12 +829,23 @@ class MapRegions (val ruleset: Ruleset){
}
fun placeResourcesAndMinorCivs(tileMap: TileMap, minorCivs: List<CivilizationInfo>) {
placeNaturalWonderImpacts(tileMap)
assignLuxuries()
placeMinorCivs(tileMap, minorCivs)
placeLuxuries(tileMap)
placeStrategicAndBonuses(tileMap)
}
/** Places impacts from NWs that have been generated just prior to this step. */
private fun placeNaturalWonderImpacts(tileMap: TileMap) {
for (tile in tileMap.values.filter { it.isNaturalWonder() }) {
placeImpact(ImpactType.Bonus, tile, 1)
placeImpact(ImpactType.Strategic, tile, 1)
placeImpact(ImpactType.Luxury, tile, 1)
placeImpact(ImpactType.MinorCiv, tile, 1)
}
}
/** Assigns a luxury to each region. No luxury can be assigned to too many regions.
* Some luxuries are earmarked for city states. The rest are randomly distributed or
* don't occur att all in the map */

View File

@ -18,9 +18,14 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
.filter { it.type == TerrainType.TerrainFeature }
.map { it.name }.toSet()
private val blockedTiles = HashSet<TileInfo>()
/*
https://gaming.stackexchange.com/questions/95095/do-natural-wonders-spawn-more-closely-to-city-states/96479
https://www.reddit.com/r/civ/comments/1jae5j/information_on_the_occurrence_of_natural_wonders/
Above all, look in assignstartingplots.lua! The wonders are always attempted to be placed in order of
which has the least amount of candidate tiles. There is a minimum distance between wonders equal
to the map height / 5.
*/
fun spawnNaturalWonders(tileMap: TileMap) {
if (tileMap.mapParameters.noNaturalWonders)
@ -31,31 +36,58 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
mapRadius * naturalWonderCountMultiplier + naturalWonderCountAddedConstant
}.roundToInt()
val spawned = mutableListOf<Terrain>()
val chosenWonders = mutableListOf<Terrain>()
val wonderCandidateTiles = mutableMapOf<Terrain, Collection<TileInfo>>()
val allNaturalWonders = ruleset.terrains.values
.filter { it.type == TerrainType.NaturalWonder }.toMutableList()
val spawned = mutableListOf<Terrain>()
while (allNaturalWonders.isNotEmpty() && spawned.size < numberToSpawn) {
while (allNaturalWonders.isNotEmpty() && chosenWonders.size < numberToSpawn) {
val totalWeight = allNaturalWonders.sumOf { it.weight }.toFloat()
val random = randomness.RNG.nextDouble()
var sum = 0f
for (wonder in allNaturalWonders) {
sum += wonder.weight / totalWeight
if (random <= sum) {
if (spawnSpecificWonder(tileMap, wonder))
spawned.add(wonder)
chosenWonders.add(wonder)
allNaturalWonders.remove(wonder)
break
}
}
}
// First attempt to spawn the chosen wonders in order of least candidate tiles
chosenWonders.forEach {
wonderCandidateTiles[it] = getCandidateTilesForWonder(tileMap, it)
}
chosenWonders.sortBy { wonderCandidateTiles[it]!!.size }
for (wonder in chosenWonders) {
if (trySpawnOnSuitableLocation(wonderCandidateTiles[wonder]!!.filter { it !in blockedTiles }.toList(), wonder))
spawned.add(wonder)
}
// If some wonders were not able to be spawned we will pull a wonder from the fallback list
if (spawned.size < numberToSpawn) {
// Now we have to do some more calculations. Unfortunately we have to calculate candidate tiles for everyone.
allNaturalWonders.forEach {
wonderCandidateTiles[it] = getCandidateTilesForWonder(tileMap, it)
}
allNaturalWonders.sortBy { wonderCandidateTiles[it]!!.size }
for (wonder in allNaturalWonders) {
if (trySpawnOnSuitableLocation(wonderCandidateTiles[wonder]!!.filter { it !in blockedTiles }
.toList(), wonder))
spawned.add(wonder)
if (spawned.size >= numberToSpawn)
break
}
}
debug("Natural Wonders for this game: %s", spawned)
}
private fun Unique.getIntParam(index: Int) = params[index].toInt()
private fun spawnSpecificWonder(tileMap: TileMap, naturalWonder: Terrain): Boolean {
private fun getCandidateTilesForWonder(tileMap: TileMap, naturalWonder: Terrain): Collection<TileInfo> {
val continentsRelevant = naturalWonder.hasUnique(UniqueType.NaturalWonderLargerLandmass) ||
naturalWonder.hasUnique(UniqueType.NaturalWonderSmallerLandmass)
val sortedContinents = if (continentsRelevant)
@ -98,7 +130,7 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
}
}
return trySpawnOnSuitableLocation(suitableLocations, naturalWonder)
return suitableLocations
}
private fun trySpawnOnSuitableLocation(suitableLocations: List<TileInfo>, wonder: Terrain): Boolean {
@ -138,6 +170,8 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
clearTile(it)
it.naturalWonder = wonder.name
it.baseTerrain = wonder.turnsInto!!
// Add all tiles within a certain distance to a blacklist so NW:s don't cluster
blockedTiles.addAll(it.getTilesInDistance(it.tileMap.mapParameters.mapSize.height / 5))
}
if (convertNeighborsTo != null) {
for (tile in location.neighbors) {