diff --git a/core/src/com/unciv/logic/GameStarter.kt b/core/src/com/unciv/logic/GameStarter.kt index 5cdf62b2f2..6e0e0c4605 100644 --- a/core/src/com/unciv/logic/GameStarter.kt +++ b/core/src/com/unciv/logic/GameStarter.kt @@ -84,7 +84,7 @@ object GameStarter { if (tileMap.continentSizes.isEmpty()) // Probably saved map without continent data runAndMeasure("assignContinents") { - tileMap.assignContinents() + tileMap.assignContinents(TileMap.AssignContinentsMode.Ensure) } runAndMeasure("addCivStartingUnits") { @@ -353,7 +353,7 @@ object GameStarter { tileMap: TileMap, startScores: HashMap ): Map { - if (tileMap.continentSizes.isEmpty()) tileMap.assignContinents() + tileMap.assignContinents(TileMap.AssignContinentsMode.Ensure) // We want to distribute starting locations fairly, and thus not place anybody on a small island // - unless necessary. Old code would only consider landmasses >= 20 tiles. diff --git a/core/src/com/unciv/logic/MapSaver.kt b/core/src/com/unciv/logic/MapSaver.kt index 898021be4e..6dae1468e4 100644 --- a/core/src/com/unciv/logic/MapSaver.kt +++ b/core/src/com/unciv/logic/MapSaver.kt @@ -33,6 +33,7 @@ object MapSaver { } } fun mapToSavedString(tileMap: TileMap): String { + tileMap.assignContinents(TileMap.AssignContinentsMode.Reassign) val mapJson = json().toJson(tileMap) return if (saveZipped) Gzip.zip(mapJson) else mapJson } diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 1fe80bd170..5b56801827 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -804,5 +804,8 @@ open class TileInfo { this.continent = continent } + /** Clear continent ID, for map editor */ + fun clearContinent() { continent = -1 } + //endregion } diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index b7e82748ee..0abe4b19d2 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -34,7 +34,6 @@ class TileMap { var mapParameters = MapParameters() private var tileList = ArrayList() - val continentSizes = HashMap() // Continent ID, Continent size /** Structure geared for simple serialization by Gdx.Json (which is a little blind to kotlin collections, especially HashSet) * @param position [Vector2] of the location @@ -79,6 +78,9 @@ class TileMap { @Transient val startingLocationsByNation = HashMap>() + @Transient + val continentSizes = HashMap() // Continent ID, Continent size + //endregion //region Constructors @@ -124,7 +126,6 @@ class TileMap { toReturn.startingLocations.clear() toReturn.startingLocations.ensureCapacity(startingLocations.size) toReturn.startingLocations.addAll(startingLocations) - toReturn.continentSizes.putAll(continentSizes) return toReturn } @@ -347,6 +348,7 @@ class TileMap { } fun isWaterMap(): Boolean { + assignContinents(AssignContinentsMode.Ensure) val bigIslands = continentSizes.count { it.value > 20 } val players = gameInfo.gameParameters.players.count() return bigIslands >= players @@ -558,13 +560,41 @@ class TileMap { startingLocationsByNation.clear() } + /** Behaviour of [assignContinents] */ + enum class AssignContinentsMode { Assign, Reassign, Ensure, 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 + * @param mode As follows: + * [Assign][AssignContinentsMode.Assign] = initial assign, throw if tiles have continents. + * [Reassign][AssignContinentsMode.Reassign] = clear continent data and redo for map editor. + * [Ensure][AssignContinentsMode.Ensure] = regenerate continent sizes from tile data, and if that is empty, Assign. + * @throws Exception when `mode==Assign` and any land tile already has a continent ID + * @return A map of continent sizes (continent ID to tile count) */ - fun assignContinents() { + fun assignContinents(mode: AssignContinentsMode) { + if (mode == AssignContinentsMode.Clear) { + values.forEach { it.clearContinent() } + continentSizes.clear() + return + } + + if (mode == AssignContinentsMode.Ensure) { + if (continentSizes.isNotEmpty()) return + for (tile in values) { + val continent = tile.getContinent() + if (continent == -1) continue + continentSizes[continent] = 1 + (continentSizes[continent] ?: 0) + } + if (continentSizes.isNotEmpty()) return + } + var landTiles = values.filter { it.isLand && !it.isImpassible() } var currentContinent = 0 + continentSizes.clear() + + if (mode == AssignContinentsMode.Reassign) + values.forEach { it.clearContinent() } while (landTiles.any()) { val bfs = BFS(landTiles.random()) { it.isLand && !it.isImpassible() } diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt index f043f8d149..ffc3dd4b26 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt @@ -74,7 +74,7 @@ class MapGenerator(val ruleset: Ruleset) { spawnIce(map) } runAndMeasure("assignContinents") { - map.assignContinents() + map.assignContinents(TileMap.AssignContinentsMode.Assign) } runAndMeasure("NaturalWonderGenerator") { NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map) diff --git a/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt b/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt index f2aa0b90bc..3293102ea6 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroupIcons.kt @@ -184,6 +184,7 @@ class TileGroupIcons(val tileGroup: TileGroup) { // different stacking order of the same nations in the same editing session val nations = tileInfo.tileMap.startingLocationsByNation.asSequence() .filter { tileInfo in it.value } + .filter { it.key in tileInfo.tileMap.ruleset!!.nations } // Ignore missing nations .map { it.key to tileInfo.tileMap.ruleset!!.nations[it.key]!! } .sortedWith(compareBy({ it.second.isCityState() }, { it.first })) .toList()