mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 06:16:37 -04:00
Rework game start again (continents) (#5335)
Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
parent
5f9bcd0d74
commit
d3868dae62
@ -3,21 +3,18 @@ package com.unciv.logic
|
|||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.civilization.*
|
import com.unciv.logic.civilization.*
|
||||||
import com.unciv.logic.map.BFS
|
|
||||||
import com.unciv.logic.map.TileInfo
|
import com.unciv.logic.map.TileInfo
|
||||||
import com.unciv.logic.map.TileMap
|
import com.unciv.logic.map.TileMap
|
||||||
import com.unciv.logic.map.mapgenerator.MapGenerator
|
import com.unciv.logic.map.mapgenerator.MapGenerator
|
||||||
import com.unciv.models.metadata.GameParameters
|
import com.unciv.models.metadata.GameParameters
|
||||||
import com.unciv.models.metadata.GameSetupInfo
|
import com.unciv.models.metadata.GameSetupInfo
|
||||||
import com.unciv.models.ruleset.Era
|
|
||||||
import com.unciv.models.ruleset.ModOptionsConstants
|
import com.unciv.models.ruleset.ModOptionsConstants
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.RulesetCache
|
import com.unciv.models.ruleset.RulesetCache
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
import com.unciv.models.ruleset.tile.ResourceType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
import kotlin.math.max
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
object GameStarter {
|
object GameStarter {
|
||||||
// temporary instrumentation while tuning/debugging
|
// temporary instrumentation while tuning/debugging
|
||||||
@ -86,7 +83,7 @@ object GameStarter {
|
|||||||
|
|
||||||
if (tileMap.continentSizes.isEmpty()) // Probably saved map without continent data
|
if (tileMap.continentSizes.isEmpty()) // Probably saved map without continent data
|
||||||
runAndMeasure("assignContinents") {
|
runAndMeasure("assignContinents") {
|
||||||
mapGen.assignContinents(tileMap)
|
tileMap.assignContinents()
|
||||||
}
|
}
|
||||||
|
|
||||||
runAndMeasure("addCivStartingUnits") {
|
runAndMeasure("addCivStartingUnits") {
|
||||||
@ -244,23 +241,21 @@ object GameStarter {
|
|||||||
for (tile in tileMap.values) {
|
for (tile in tileMap.values) {
|
||||||
startScores[tile] = tile.getTileStartScore()
|
startScores[tile] = tile.getTileStartScore()
|
||||||
}
|
}
|
||||||
|
val allCivs = gameInfo.civilizations.filter { !it.isBarbarian() }
|
||||||
|
val landTilesInBigEnoughGroup = getCandidateLand(allCivs.size, tileMap, startScores)
|
||||||
|
|
||||||
// First we get start locations for the major civs, on the second pass the city states (without predetermined starts) can squeeze in wherever
|
// First we get start locations for the major civs, on the second pass the city states (without predetermined starts) can squeeze in wherever
|
||||||
// I hear copying code is good
|
|
||||||
val civNamesWithStartingLocations = tileMap.startingLocationsByNation.keys
|
val civNamesWithStartingLocations = tileMap.startingLocationsByNation.keys
|
||||||
val bestCivs = gameInfo.civilizations.filter { !it.isBarbarian() && (!it.isCityState() || it.civName in civNamesWithStartingLocations) }
|
val bestCivs = allCivs.filter { !it.isCityState() || it.civName in civNamesWithStartingLocations }
|
||||||
val bestLocations = getStartingLocations(bestCivs, tileMap, startScores)
|
val bestLocations = getStartingLocations(bestCivs, tileMap, landTilesInBigEnoughGroup, startScores)
|
||||||
for ((civ, tile) in bestLocations) {
|
for ((civ, tile) in bestLocations) {
|
||||||
if (civ.civName in civNamesWithStartingLocations) // Already have explicit starting locations
|
// A nation can have multiple marked starting locations, of which the first pass may have chosen one
|
||||||
continue
|
tileMap.removeStartingLocations(civ.civName)
|
||||||
|
|
||||||
// Mark the best start locations so we remember them for the second pass
|
// Mark the best start locations so we remember them for the second pass
|
||||||
tileMap.addStartingLocation(civ.civName, tile)
|
tileMap.addStartingLocation(civ.civName, tile)
|
||||||
}
|
}
|
||||||
|
|
||||||
val startingLocations = getStartingLocations(
|
val startingLocations = getStartingLocations(allCivs, tileMap, landTilesInBigEnoughGroup, startScores)
|
||||||
gameInfo.civilizations.filter { !it.isBarbarian() },
|
|
||||||
tileMap, startScores)
|
|
||||||
|
|
||||||
val settlerLikeUnits = ruleSet.units.filter {
|
val settlerLikeUnits = ruleSet.units.filter {
|
||||||
it.value.uniqueObjects.any { unique -> unique.placeholderText == Constants.settlerUnique }
|
it.value.uniqueObjects.any { unique -> unique.placeholderText == Constants.settlerUnique }
|
||||||
@ -349,70 +344,66 @@ object GameStarter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getCandidateLand(
|
||||||
|
civCount: Int,
|
||||||
|
tileMap: TileMap,
|
||||||
|
startScores: HashMap<TileInfo, Float>
|
||||||
|
): Map<TileInfo, Float> {
|
||||||
|
if (tileMap.continentSizes.isEmpty()) tileMap.assignContinents()
|
||||||
|
|
||||||
private fun getStartingLocations(civs: List<CivilizationInfo>, tileMap: TileMap, startScores: HashMap<TileInfo, Float>): HashMap<CivilizationInfo, TileInfo> {
|
// We want to distribute starting locations fairly, and thus not place anybody on a small island
|
||||||
val landTilesInBigEnoughGroup = tileMap.landTilesInBigEnoughGroup
|
// - unless necessary. Old code would only consider landmasses >= 20 tiles.
|
||||||
if (landTilesInBigEnoughGroup.isEmpty()) {
|
// Instead, take continents until >=75% total area or everybody can get their own island
|
||||||
// Worst case - a pre-made map with continent data. This means we didn't re-run assignContinents,
|
val orderedContinents = tileMap.continentSizes.asSequence().sortedByDescending { it.value }.toList()
|
||||||
// so we don't have a cached landTilesInBigEnoughGroup. So we need to do it the hard way.
|
val totalArea = tileMap.continentSizes.values.sum()
|
||||||
var landTiles = tileMap.values
|
var candidateArea = 0
|
||||||
// Games starting on snow might as well start over...
|
val candidateContinents = HashSet<Int>()
|
||||||
.filter { it.isLand && !it.isImpassible() && it.baseTerrain != Constants.snow }
|
for ((index, continentSize) in orderedContinents.withIndex()) {
|
||||||
while (landTiles.any()) {
|
candidateArea += continentSize.value
|
||||||
val bfs = BFS(landTiles.random()) { it.isLand && !it.isImpassible() }
|
candidateContinents.add(continentSize.key)
|
||||||
bfs.stepToEnd()
|
if (candidateArea * 4 >= totalArea * 3) break
|
||||||
val tilesInGroup = bfs.getReachedTiles()
|
if (index >= civCount) break
|
||||||
landTiles = landTiles.filter { it !in tilesInGroup }
|
|
||||||
if (tilesInGroup.size > 20) // is this a good number? I dunno, but it's easy enough to change later on
|
|
||||||
landTilesInBigEnoughGroup.addAll(tilesInGroup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return startScores.filter { it.key.getContinent() in candidateContinents }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getStartingLocations(
|
||||||
|
civs: List<CivilizationInfo>,
|
||||||
|
tileMap: TileMap,
|
||||||
|
landTilesInBigEnoughGroup: Map<TileInfo, Float>,
|
||||||
|
startScores: HashMap<TileInfo, Float>
|
||||||
|
): HashMap<CivilizationInfo, TileInfo> {
|
||||||
|
|
||||||
val civsOrderedByAvailableLocations = civs.shuffled() // Order should be random since it determines who gets best start
|
val civsOrderedByAvailableLocations = civs.shuffled() // Order should be random since it determines who gets best start
|
||||||
.sortedBy { civ ->
|
.sortedBy { civ ->
|
||||||
when {
|
when {
|
||||||
civ.civName in tileMap.startingLocationsByNation -> 1 // harshest requirements
|
civ.civName in tileMap.startingLocationsByNation -> 1 // harshest requirements
|
||||||
civ.nation.startBias.contains("Tundra") -> 2 // Tundra starts are hard to find, so let's do them first
|
civ.nation.startBias.any { it in tileMap.naturalWonders } -> 2
|
||||||
civ.nation.startBias.isNotEmpty() -> 3 // less harsh
|
civ.nation.startBias.contains("Tundra") -> 3 // Tundra starts are hard to find, so let's do them first
|
||||||
else -> 4 // no requirements
|
civ.nation.startBias.isNotEmpty() -> 4 // less harsh
|
||||||
|
else -> 5 // no requirements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (minimumDistanceBetweenStartingLocations in tileMap.tileMatrix.size / 4 downTo 0) {
|
for (minimumDistanceBetweenStartingLocations in tileMap.tileMatrix.size / 6 downTo 0) {
|
||||||
val freeTiles = landTilesInBigEnoughGroup
|
val freeTiles = landTilesInBigEnoughGroup.asSequence()
|
||||||
.filter {
|
.filter {
|
||||||
HexMath.getDistanceFromEdge(it.position, tileMap.mapParameters) >=
|
HexMath.getDistanceFromEdge(it.key.position, tileMap.mapParameters) >=
|
||||||
(minimumDistanceBetweenStartingLocations * 2) /3
|
(minimumDistanceBetweenStartingLocations * 2) / 3
|
||||||
}.toMutableList()
|
}.sortedBy { it.value }
|
||||||
|
.map { it.key }
|
||||||
|
.toMutableList()
|
||||||
|
|
||||||
val startingLocations = HashMap<CivilizationInfo, TileInfo>()
|
val startingLocations = HashMap<CivilizationInfo, TileInfo>()
|
||||||
for (civ in civsOrderedByAvailableLocations) {
|
for (civ in civsOrderedByAvailableLocations) {
|
||||||
var startingLocation: TileInfo
|
val distanceToNext = minimumDistanceBetweenStartingLocations /
|
||||||
val presetStartingLocation = tileMap.startingLocationsByNation[civ.civName]?.randomOrNull() // in case map editor is extended to allow alternate starting locations for a nation
|
(if (civ.isCityState()) 2 else 1) // We allow city states to squeeze in tighter
|
||||||
var distanceToNext = minimumDistanceBetweenStartingLocations
|
val presetStartingLocation = tileMap.startingLocationsByNation[civ.civName]?.randomOrNull()
|
||||||
|
val startingLocation = if (presetStartingLocation != null) presetStartingLocation
|
||||||
if (presetStartingLocation != null) startingLocation = presetStartingLocation
|
|
||||||
else {
|
else {
|
||||||
if (freeTiles.isEmpty()) break // we failed to get all the starting tiles with this minimum distance
|
if (freeTiles.isEmpty()) break // we failed to get all the starting tiles with this minimum distance
|
||||||
if (civ.isCityState())
|
getOneStartingLocation(civ, tileMap, freeTiles, startScores)
|
||||||
distanceToNext = minimumDistanceBetweenStartingLocations / 2 // We allow random city states to squeeze in tighter
|
|
||||||
|
|
||||||
freeTiles.sortBy { startScores[it] }
|
|
||||||
|
|
||||||
var preferredTiles = freeTiles.toList()
|
|
||||||
|
|
||||||
for (startBias in civ.nation.startBias) {
|
|
||||||
preferredTiles = when {
|
|
||||||
startBias.startsWith("Avoid [") -> {
|
|
||||||
val tileToAvoid = startBias.removePrefix("Avoid [").removeSuffix("]")
|
|
||||||
preferredTiles.filter { !it.matchesTerrainFilter(tileToAvoid) }
|
|
||||||
}
|
|
||||||
startBias == Constants.coast -> preferredTiles.filter { it.isCoastalTile() }
|
|
||||||
else -> preferredTiles.filter { it.matchesTerrainFilter(startBias) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
startingLocation = if (preferredTiles.isNotEmpty()) preferredTiles.last() else freeTiles.last()
|
|
||||||
}
|
}
|
||||||
startingLocations[civ] = startingLocation
|
startingLocations[civ] = startingLocation
|
||||||
freeTiles.removeAll(tileMap.getTilesInDistance(startingLocation.position, distanceToNext))
|
freeTiles.removeAll(tileMap.getTilesInDistance(startingLocation.position, distanceToNext))
|
||||||
@ -424,6 +415,36 @@ object GameStarter {
|
|||||||
throw Exception("Didn't manage to get starting tiles even with distance of 1?")
|
throw Exception("Didn't manage to get starting tiles even with distance of 1?")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getOneStartingLocation(
|
||||||
|
civ: CivilizationInfo,
|
||||||
|
tileMap: TileMap,
|
||||||
|
freeTiles: MutableList<TileInfo>,
|
||||||
|
startScores: HashMap<TileInfo, Float>
|
||||||
|
): TileInfo {
|
||||||
|
if (civ.nation.startBias.any { it in tileMap.naturalWonders }) {
|
||||||
|
// startPref wants Natural wonder neighbor: Rare and very likely to be outside getDistanceFromEdge
|
||||||
|
val wonderNeighbor = tileMap.values.asSequence()
|
||||||
|
.filter { it.isNaturalWonder() && it.naturalWonder!! in civ.nation.startBias }
|
||||||
|
.sortedByDescending { startScores[it] }
|
||||||
|
.firstOrNull()
|
||||||
|
if (wonderNeighbor != null) return wonderNeighbor
|
||||||
|
}
|
||||||
|
|
||||||
|
var preferredTiles = freeTiles.toList()
|
||||||
|
for (startBias in civ.nation.startBias) {
|
||||||
|
preferredTiles = when {
|
||||||
|
startBias.startsWith("Avoid [") -> {
|
||||||
|
val tileToAvoid = startBias.removePrefix("Avoid [").removeSuffix("]")
|
||||||
|
preferredTiles.filter { !it.matchesTerrainFilter(tileToAvoid) }
|
||||||
|
}
|
||||||
|
startBias == Constants.coast -> preferredTiles.filter { it.isCoastalTile() }
|
||||||
|
startBias in tileMap.naturalWonders -> preferredTiles // passthrough: already failed
|
||||||
|
else -> preferredTiles.filter { it.matchesTerrainFilter(startBias) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return preferredTiles.lastOrNull() ?: freeTiles.last()
|
||||||
|
}
|
||||||
|
|
||||||
private fun addConsolationPrize(gameInfo: GameInfo, spawn: TileInfo, points: Int) {
|
private fun addConsolationPrize(gameInfo: GameInfo, spawn: TileInfo, points: Int) {
|
||||||
val relevantTiles = spawn.getTilesInDistanceRange(1..2).shuffled()
|
val relevantTiles = spawn.getTilesInDistanceRange(1..2).shuffled()
|
||||||
var addedPoints = 0
|
var addedPoints = 0
|
||||||
|
@ -2,7 +2,6 @@ package com.unciv.logic.map
|
|||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2
|
import com.badlogic.gdx.math.Vector2
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.UncivGame
|
|
||||||
import com.unciv.logic.GameInfo
|
import com.unciv.logic.GameInfo
|
||||||
import com.unciv.logic.HexMath
|
import com.unciv.logic.HexMath
|
||||||
import com.unciv.logic.civilization.CivilizationInfo
|
import com.unciv.logic.civilization.CivilizationInfo
|
||||||
@ -79,9 +78,6 @@ class TileMap {
|
|||||||
@Transient
|
@Transient
|
||||||
val startingLocationsByNation = HashMap<String,HashSet<TileInfo>>()
|
val startingLocationsByNation = HashMap<String,HashSet<TileInfo>>()
|
||||||
|
|
||||||
@Transient
|
|
||||||
val landTilesInBigEnoughGroup = ArrayList<TileInfo>() // cached at map gen
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region Constructors
|
//region Constructors
|
||||||
|
|
||||||
@ -546,11 +542,42 @@ class TileMap {
|
|||||||
// we do not clean up an empty startingLocationsByNation[nationName] set - not worth it
|
// we do not clean up an empty startingLocationsByNation[nationName] set - not worth it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Removes all starting positions for [nationName], maintaining the transients */
|
||||||
|
fun removeStartingLocations(nationName: String) {
|
||||||
|
if (startingLocationsByNation[nationName] == null) return
|
||||||
|
for (tile in startingLocationsByNation[nationName]!!) {
|
||||||
|
startingLocations.remove(StartingLocation(tile.position, nationName))
|
||||||
|
}
|
||||||
|
startingLocationsByNation[nationName]!!.clear()
|
||||||
|
}
|
||||||
|
|
||||||
/** Clears starting positions, e.g. after GameStarter is done with them. Does not clear the pseudo-improvements. */
|
/** Clears starting positions, e.g. after GameStarter is done with them. Does not clear the pseudo-improvements. */
|
||||||
fun clearStartingLocations() {
|
fun clearStartingLocations() {
|
||||||
startingLocations.clear()
|
startingLocations.clear()
|
||||||
startingLocationsByNation.clear()
|
startingLocationsByNation.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set a continent id for each tile, so we can quickly see which tiles are connected.
|
||||||
|
* Can also be called on saved maps.
|
||||||
|
* @throws Exception when any land tile already has a continent ID
|
||||||
|
*/
|
||||||
|
fun assignContinents() {
|
||||||
|
var landTiles = values.filter { it.isLand && !it.isImpassible() }
|
||||||
|
var currentContinent = 0
|
||||||
|
|
||||||
|
while (landTiles.any()) {
|
||||||
|
val bfs = BFS(landTiles.random()) { it.isLand && !it.isImpassible() }
|
||||||
|
bfs.stepToEnd()
|
||||||
|
bfs.getReachedTiles().forEach {
|
||||||
|
it.setContinent(currentContinent)
|
||||||
|
}
|
||||||
|
val continent = bfs.getReachedTiles()
|
||||||
|
continentSizes[currentContinent] = continent.size
|
||||||
|
|
||||||
|
currentContinent++
|
||||||
|
landTiles = landTiles.filter { it !in continent }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
spawnIce(map)
|
spawnIce(map)
|
||||||
}
|
}
|
||||||
runAndMeasure("assignContinents") {
|
runAndMeasure("assignContinents") {
|
||||||
assignContinents(map)
|
map.assignContinents()
|
||||||
}
|
}
|
||||||
runAndMeasure("NaturalWonderGenerator") {
|
runAndMeasure("NaturalWonderGenerator") {
|
||||||
NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
|
NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
|
||||||
@ -463,32 +463,6 @@ class MapGenerator(val ruleset: Ruleset) {
|
|||||||
tile.terrainFeatures.add(Constants.ice)
|
tile.terrainFeatures.add(Constants.ice)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set a continent id for each tile, so we can quickly see which tiles are connected.
|
|
||||||
* Can also be called on saved maps.
|
|
||||||
* @throws Exception when any land tile already has a continent ID
|
|
||||||
*/
|
|
||||||
fun assignContinents(tileMap: TileMap) {
|
|
||||||
var landTiles = tileMap.values
|
|
||||||
.filter { it.isLand && !it.isImpassible()}
|
|
||||||
var currentContinent = 0
|
|
||||||
|
|
||||||
while (landTiles.any()) {
|
|
||||||
val bfs = BFS(landTiles.random()) { it.isLand && !it.isImpassible() }
|
|
||||||
bfs.stepToEnd()
|
|
||||||
bfs.getReachedTiles().forEach {
|
|
||||||
it.setContinent(currentContinent)
|
|
||||||
}
|
|
||||||
val continent = bfs.getReachedTiles()
|
|
||||||
tileMap.continentSizes[currentContinent] = continent.size
|
|
||||||
if (continent.size > 20) {
|
|
||||||
tileMap.landTilesInBigEnoughGroup.addAll(continent)
|
|
||||||
}
|
|
||||||
|
|
||||||
currentContinent++
|
|
||||||
landTiles = landTiles.filter { it !in continent }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MapGenerationRandomness {
|
class MapGenerationRandomness {
|
||||||
|
@ -54,6 +54,15 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
|
|||||||
private fun Unique.getIntParam(index: Int) = params[index].toInt()
|
private fun Unique.getIntParam(index: Int) = params[index].toInt()
|
||||||
|
|
||||||
private fun spawnSpecificWonder(tileMap: TileMap, wonder: Terrain): Boolean {
|
private fun spawnSpecificWonder(tileMap: TileMap, wonder: Terrain): Boolean {
|
||||||
|
val continentsRelevant = wonder.hasUnique(UniqueType.NaturalWonderLargerLandmass) ||
|
||||||
|
wonder.hasUnique(UniqueType.NaturalWonderSmallerLandmass)
|
||||||
|
val sortedContinents = if (continentsRelevant)
|
||||||
|
tileMap.continentSizes.asSequence()
|
||||||
|
.sortedByDescending { it.value }
|
||||||
|
.map { it.key }
|
||||||
|
.toList()
|
||||||
|
else listOf()
|
||||||
|
|
||||||
val suitableLocations = tileMap.values.filter { tile->
|
val suitableLocations = tileMap.values.filter { tile->
|
||||||
tile.resource == null &&
|
tile.resource == null &&
|
||||||
wonder.occursOn.contains(tile.getLastTerrain().name) &&
|
wonder.occursOn.contains(tile.getLastTerrain().name) &&
|
||||||
@ -71,13 +80,12 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
|
|||||||
}
|
}
|
||||||
count in unique.getIntParam(0)..unique.getIntParam(1)
|
count in unique.getIntParam(0)..unique.getIntParam(1)
|
||||||
}
|
}
|
||||||
UniqueType.NaturalWonderLandmass -> {
|
UniqueType.NaturalWonderSmallerLandmass -> {
|
||||||
val sortedContinents = tileMap.continentSizes.asSequence()
|
|
||||||
.sortedByDescending { it.value }
|
|
||||||
.map { it.key }
|
|
||||||
.toList()
|
|
||||||
tile.getContinent() !in sortedContinents.take(unique.getIntParam(0))
|
tile.getContinent() !in sortedContinents.take(unique.getIntParam(0))
|
||||||
}
|
}
|
||||||
|
UniqueType.NaturalWonderLargerLandmass -> {
|
||||||
|
tile.getContinent() in sortedContinents.take(unique.getIntParam(0))
|
||||||
|
}
|
||||||
UniqueType.NaturalWonderLatitude -> {
|
UniqueType.NaturalWonderLatitude -> {
|
||||||
val lower = tileMap.maxLatitude * unique.getIntParam(0) * 0.01f
|
val lower = tileMap.maxLatitude * unique.getIntParam(0) * 0.01f
|
||||||
val upper = tileMap.maxLatitude * unique.getIntParam(1) * 0.01f
|
val upper = tileMap.maxLatitude * unique.getIntParam(1) * 0.01f
|
||||||
@ -168,7 +176,9 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration
|
|||||||
private fun TileInfo.matchesWonderFilter(filter: String) = when (filter) {
|
private fun TileInfo.matchesWonderFilter(filter: String) = when (filter) {
|
||||||
"Elevated" -> baseTerrain == Constants.mountain || isHill()
|
"Elevated" -> baseTerrain == Constants.mountain || isHill()
|
||||||
"Water" -> isWater
|
"Water" -> isWater
|
||||||
|
"Land" -> isLand
|
||||||
"Hill" -> isHill()
|
"Hill" -> isHill()
|
||||||
|
naturalWonder -> true
|
||||||
in allTerrainFeatures -> getLastTerrain().name == filter
|
in allTerrainFeatures -> getLastTerrain().name == filter
|
||||||
else -> baseTerrain == filter
|
else -> baseTerrain == filter
|
||||||
}
|
}
|
||||||
|
@ -92,14 +92,11 @@ enum class UniqueParameterType(val parameterName:String) {
|
|||||||
},
|
},
|
||||||
/** Used by NaturalWonderGenerator, only tests base terrain or a feature */
|
/** Used by NaturalWonderGenerator, only tests base terrain or a feature */
|
||||||
SimpleTerrain("simpleTerrain") {
|
SimpleTerrain("simpleTerrain") {
|
||||||
|
private val knownValues = setOf("Elevated", "Water", "Land")
|
||||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||||
UniqueType.UniqueComplianceErrorSeverity? {
|
UniqueType.UniqueComplianceErrorSeverity? {
|
||||||
if (parameterText == "Elevated") return null
|
if (parameterText in knownValues) return null
|
||||||
if (ruleset.terrains.values.any {
|
if (ruleset.terrains.containsKey(parameterText)) return null
|
||||||
it.name == parameterText &&
|
|
||||||
(it.type.isBaseTerrain || it.type == TerrainType.TerrainFeature)
|
|
||||||
})
|
|
||||||
return null
|
|
||||||
return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -103,14 +103,16 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
|
|||||||
CityStateMilitaryUnits("Provides military units every ≈[amount] turns", UniqueTarget.CityState), // No conditional support as of yet
|
CityStateMilitaryUnits("Provides military units every ≈[amount] turns", UniqueTarget.CityState), // No conditional support as of yet
|
||||||
CityStateUniqueLuxury("Provides a unique luxury", UniqueTarget.CityState), // No conditional support as of yet
|
CityStateUniqueLuxury("Provides a unique luxury", UniqueTarget.CityState), // No conditional support as of yet
|
||||||
|
|
||||||
NaturalWonderNeighborCount("Must be adjacent to [amount] [terrainFilter] tiles", UniqueTarget.Terrain),
|
NaturalWonderNeighborCount("Must be adjacent to [amount] [simpleTerrain] tiles", UniqueTarget.Terrain),
|
||||||
NaturalWonderNeighborsRange("Must be adjacent to [amount] to [amount] [terrainFilter] tiles", UniqueTarget.Terrain),
|
NaturalWonderNeighborsRange("Must be adjacent to [amount] to [amount] [simpleTerrain] tiles", UniqueTarget.Terrain),
|
||||||
NaturalWonderLandmass("Must not be on [amount] largest landmasses", UniqueTarget.Terrain),
|
NaturalWonderSmallerLandmass("Must not be on [amount] largest landmasses", UniqueTarget.Terrain),
|
||||||
|
NaturalWonderLargerLandmass("Must be on [amount] largest landmasses", UniqueTarget.Terrain),
|
||||||
NaturalWonderLatitude("Occurs on latitudes from [amount] to [amount] percent of distance equator to pole", UniqueTarget.Terrain),
|
NaturalWonderLatitude("Occurs on latitudes from [amount] to [amount] percent of distance equator to pole", UniqueTarget.Terrain),
|
||||||
NaturalWonderGroups("Occurs in groups of [amount] to [amount] tiles", UniqueTarget.Terrain),
|
NaturalWonderGroups("Occurs in groups of [amount] to [amount] tiles", UniqueTarget.Terrain),
|
||||||
NaturalWonderConvertNeighbors("Neighboring tiles will convert to [baseTerrain]", UniqueTarget.Terrain),
|
NaturalWonderConvertNeighbors("Neighboring tiles will convert to [baseTerrain]", UniqueTarget.Terrain),
|
||||||
|
|
||||||
// The "Except [terrainFilter]" could theoretically be implemented with a conditional
|
// The "Except [terrainFilter]" could theoretically be implemented with a conditional
|
||||||
NaturalWonderConvertNeighborsExcept("Neighboring tiles except [terrainFilter] will convert to [baseTerrain]", UniqueTarget.Terrain),
|
NaturalWonderConvertNeighborsExcept("Neighboring tiles except [baseTerrain] will convert to [baseTerrain]", UniqueTarget.Terrain),
|
||||||
|
|
||||||
TerrainGrantsPromotion("Grants [promotion] ([comment]) to adjacent [mapUnitFilter] units for the rest of the game", UniqueTarget.Terrain),
|
TerrainGrantsPromotion("Grants [promotion] ([comment]) to adjacent [mapUnitFilter] units for the rest of the game", UniqueTarget.Terrain),
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user