mirror of
https://github.com/yairm210/Unciv.git
synced 2025-01-03 21:40:31 +07:00
Fix ChangesTerrain unique for base terrains (#10043)
* Fix UniqueType.ChangesTerrain not working for base terrain parameter * Fix spawnRiver resultingTiles to include all affected tiles on both sides of the River * Fix terrain conversion for rivers from Map Editor partial generation / paint from-to * forEach linting * Instrumentation for generateSingleStep * forEach linting * Remove lazies
This commit is contained in:
parent
8b9d0af4bf
commit
d758da4d11
@ -33,6 +33,13 @@ import kotlin.math.ulp
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
/** Map generator, used by new game, map editor and main menu background
|
||||
*
|
||||
* Class instance only keeps [ruleset] and [coroutineScope] for easier access, input and output are through methods, namely [generateMap] and [generateSingleStep].
|
||||
*
|
||||
* @param ruleset The Ruleset supplying terrain and resource definitions
|
||||
* @param coroutineScope Enables early abort if this returns `isActive == false`
|
||||
*/
|
||||
class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineScope? = null) {
|
||||
|
||||
companion object {
|
||||
@ -138,7 +145,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
|
||||
runAndMeasure("RiverGenerator") {
|
||||
RiverGenerator(map, randomness, ruleset).spawnRivers()
|
||||
}
|
||||
convertTerrains(map, ruleset)
|
||||
convertTerrains(map.values)
|
||||
|
||||
// Region based map generation - not used when generating maps in map editor
|
||||
if (civilizations.isNotEmpty()) {
|
||||
@ -182,24 +189,27 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
|
||||
|
||||
randomness.seedRNG(map.mapParameters.seed)
|
||||
|
||||
when(step) {
|
||||
MapGeneratorSteps.None -> return
|
||||
MapGeneratorSteps.All -> throw IllegalArgumentException("MapGeneratorSteps.All cannot be used in generateSingleStep")
|
||||
MapGeneratorSteps.Landmass -> MapLandmassGenerator(ruleset, randomness).generateLand(map)
|
||||
MapGeneratorSteps.Elevation -> raiseMountainsAndHills(map)
|
||||
MapGeneratorSteps.HumidityAndTemperature -> applyHumidityAndTemperature(map)
|
||||
MapGeneratorSteps.LakesAndCoast -> spawnLakesAndCoasts(map)
|
||||
MapGeneratorSteps.Vegetation -> spawnVegetation(map)
|
||||
MapGeneratorSteps.RareFeatures -> spawnRareFeatures(map)
|
||||
MapGeneratorSteps.Ice -> spawnIce(map)
|
||||
MapGeneratorSteps.Continents -> map.assignContinents(TileMap.AssignContinentsMode.Reassign)
|
||||
MapGeneratorSteps.NaturalWonders -> NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
|
||||
MapGeneratorSteps.Rivers -> {
|
||||
RiverGenerator(map, randomness, ruleset).spawnRivers()
|
||||
convertTerrains(map, ruleset)
|
||||
runAndMeasure("SingleStep $step") {
|
||||
when (step) {
|
||||
MapGeneratorSteps.None -> Unit
|
||||
MapGeneratorSteps.All -> throw IllegalArgumentException("MapGeneratorSteps.All cannot be used in generateSingleStep")
|
||||
MapGeneratorSteps.Landmass -> MapLandmassGenerator(ruleset, randomness).generateLand(map)
|
||||
MapGeneratorSteps.Elevation -> raiseMountainsAndHills(map)
|
||||
MapGeneratorSteps.HumidityAndTemperature -> applyHumidityAndTemperature(map)
|
||||
MapGeneratorSteps.LakesAndCoast -> spawnLakesAndCoasts(map)
|
||||
MapGeneratorSteps.Vegetation -> spawnVegetation(map)
|
||||
MapGeneratorSteps.RareFeatures -> spawnRareFeatures(map)
|
||||
MapGeneratorSteps.Ice -> spawnIce(map)
|
||||
MapGeneratorSteps.Continents -> map.assignContinents(TileMap.AssignContinentsMode.Reassign)
|
||||
MapGeneratorSteps.NaturalWonders -> NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
|
||||
MapGeneratorSteps.Rivers -> {
|
||||
val resultingTiles = mutableSetOf<Tile>()
|
||||
RiverGenerator(map, randomness, ruleset).spawnRivers(resultingTiles)
|
||||
convertTerrains(resultingTiles)
|
||||
}
|
||||
MapGeneratorSteps.Resources -> spreadResources(map)
|
||||
MapGeneratorSteps.AncientRuins -> spreadAncientRuins(map)
|
||||
}
|
||||
MapGeneratorSteps.Resources -> spreadResources(map)
|
||||
MapGeneratorSteps.AncientRuins -> spreadAncientRuins(map)
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,18 +223,19 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc
|
||||
debug("MapGenerator.%s took %s.%sms", text, delta/1000000L, (delta/10000L).rem(100))
|
||||
}
|
||||
|
||||
private fun convertTerrains(map: TileMap, ruleset: Ruleset) {
|
||||
for (tile in map.values) {
|
||||
fun convertTerrains(tiles: Iterable<Tile>) {
|
||||
for (tile in tiles) {
|
||||
val conversionUnique =
|
||||
tile.getBaseTerrain().getMatchingUniques(UniqueType.ChangesTerrain)
|
||||
.firstOrNull { tile.isAdjacentTo(it.params[1]) }
|
||||
?: continue
|
||||
val terrain = ruleset.terrains[conversionUnique.params[0]] ?: continue
|
||||
if (!terrain.occursOn.contains(tile.lastTerrain.name)) continue
|
||||
|
||||
if (terrain.type == TerrainType.TerrainFeature)
|
||||
if (terrain.type == TerrainType.TerrainFeature) {
|
||||
if (!terrain.occursOn.contains(tile.lastTerrain.name)) continue
|
||||
tile.addTerrainFeature(terrain.name)
|
||||
else tile.baseTerrain = terrain.name
|
||||
} else
|
||||
tile.baseTerrain = terrain.name
|
||||
tile.setTerrainTransients()
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,10 @@ package com.unciv.logic.map.mapgenerator
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.utils.debug
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.utils.debug
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class RiverGenerator(
|
||||
@ -17,7 +17,7 @@ class RiverGenerator(
|
||||
private val minRiverLength = ruleset.modOptions.constants.minRiverLength
|
||||
private val maxRiverLength = ruleset.modOptions.constants.maxRiverLength
|
||||
|
||||
fun spawnRivers() {
|
||||
fun spawnRivers(resultingTiles: MutableSet<Tile>? = null) {
|
||||
if (tileMap.values.none { it.isWater }) return
|
||||
val numberOfRivers = (tileMap.values.count { it.isLand } * riverCountMultiplier).roundToInt()
|
||||
|
||||
@ -33,7 +33,7 @@ class RiverGenerator(
|
||||
val mapRadius = tileMap.mapParameters.mapSize.radius
|
||||
val riverStarts =
|
||||
randomness.chooseSpreadOutLocations(numberOfRivers, optionalTiles, mapRadius)
|
||||
for (tile in riverStarts) spawnRiver(tile)
|
||||
for (tile in riverStarts) spawnRiver(tile, resultingTiles)
|
||||
}
|
||||
|
||||
private fun Tile.isFarEnoughFromWater(): Boolean {
|
||||
@ -52,71 +52,47 @@ class RiverGenerator(
|
||||
return null
|
||||
}
|
||||
|
||||
private fun spawnRiver(initialPosition: Tile) {
|
||||
private fun spawnRiver(initialPosition: Tile, resultingTiles: MutableSet<Tile>?) {
|
||||
val endPosition = getClosestWaterTile(initialPosition)
|
||||
?: error("No water found for river destination")
|
||||
spawnRiver(initialPosition, endPosition)
|
||||
spawnRiver(initialPosition, endPosition, resultingTiles)
|
||||
}
|
||||
|
||||
fun spawnRiver(initialPosition: Tile, endPosition: Tile, resultingTiles: MutableSet<Tile>? = null) {
|
||||
/** Spawns a river from [initialPosition] to [endPosition].
|
||||
* If [resultingTiles] is supplied, it will contain all affected tiles, for map editor. */
|
||||
fun spawnRiver(initialPosition: Tile, endPosition: Tile, resultingTiles: MutableSet<Tile>?) {
|
||||
// Recommendation: Draw a bunch of hexagons on paper before trying to understand this, it's super helpful!
|
||||
|
||||
var riverCoordinate = RiverCoordinate(initialPosition.position,
|
||||
var riverCoordinate = RiverCoordinate(tileMap, initialPosition.position,
|
||||
RiverCoordinate.BottomRightOrLeft.values().random(randomness.RNG))
|
||||
|
||||
repeat(maxRiverLength) { // Arbitrary max on river length, otherwise this will go in circles - rarely
|
||||
val riverCoordinateTile = tileMap[riverCoordinate.position]
|
||||
resultingTiles?.add(riverCoordinateTile)
|
||||
if (riverCoordinate.getAdjacentTiles(tileMap).any { it.isWater }) return
|
||||
val possibleCoordinates = riverCoordinate.getAdjacentPositions(tileMap)
|
||||
if (riverCoordinate.getAdjacentTiles().any { it.isWater }) return
|
||||
val possibleCoordinates = riverCoordinate.getAdjacentPositions()
|
||||
if (possibleCoordinates.none()) return // end of the line
|
||||
val newCoordinate = possibleCoordinates
|
||||
.groupBy { newCoordinate ->
|
||||
newCoordinate.getAdjacentTiles(tileMap).map { it.aerialDistanceTo(endPosition) }
|
||||
newCoordinate.getAdjacentTiles().map { it.aerialDistanceTo(endPosition) }
|
||||
.minOrNull()!!
|
||||
}
|
||||
.minByOrNull { it.key }!!
|
||||
.component2().random(randomness.RNG)
|
||||
|
||||
// set new rivers in place
|
||||
if (newCoordinate.position == riverCoordinate.position) // same tile, switched right-to-left
|
||||
riverCoordinateTile.hasBottomRiver = true
|
||||
else if (riverCoordinate.bottomRightOrLeft == RiverCoordinate.BottomRightOrLeft.BottomRight) {
|
||||
if (newCoordinate.getAdjacentTiles(tileMap).contains(riverCoordinateTile)) // moved from our 5 O'Clock to our 3 O'Clock
|
||||
riverCoordinateTile.hasBottomRightRiver = true
|
||||
else // moved from our 5 O'Clock down in the 5 O'Clock direction - this is the 8 O'Clock river of the tile to our 4 O'Clock!
|
||||
tileMap[newCoordinate.position].hasBottomLeftRiver = true
|
||||
} else { // riverCoordinate.bottomRightOrLeft==RiverCoordinate.BottomRightOrLeft.Left
|
||||
if (newCoordinate.getAdjacentTiles(tileMap).contains(riverCoordinateTile)) // moved from our 7 O'Clock to our 9 O'Clock
|
||||
riverCoordinateTile.hasBottomLeftRiver = true
|
||||
else // moved from our 7 O'Clock down in the 7 O'Clock direction
|
||||
tileMap[newCoordinate.position].hasBottomRightRiver = true
|
||||
}
|
||||
// set one new river edge in place
|
||||
riverCoordinate.paintTo(newCoordinate, resultingTiles)
|
||||
// Move on
|
||||
riverCoordinate = newCoordinate
|
||||
}
|
||||
debug("River reached max length!")
|
||||
}
|
||||
|
||||
/*
|
||||
fun numberOfConnectedRivers(riverCoordinate: RiverCoordinate): Int {
|
||||
var sum = 0
|
||||
if (tileMap.contains(riverCoordinate.position) && tileMap[riverCoordinate.position].hasBottomRiver) sum += 1
|
||||
if (riverCoordinate.bottomRightOrLeft == RiverCoordinate.BottomRightOrLeft.BottomLeft) {
|
||||
if (tileMap.contains(riverCoordinate.position) && tileMap[riverCoordinate.position].hasBottomLeftRiver) sum += 1
|
||||
val bottomLeftTilePosition = riverCoordinate.position.cpy().add(0f, -1f)
|
||||
if (tileMap.contains(bottomLeftTilePosition) && tileMap[bottomLeftTilePosition].hasBottomRightRiver) sum += 1
|
||||
} else {
|
||||
if (tileMap.contains(riverCoordinate.position) && tileMap[riverCoordinate.position].hasBottomRightRiver) sum += 1
|
||||
val bottomLeftTilePosition = riverCoordinate.position.cpy().add(-1f, 0f)
|
||||
if (tileMap.contains(bottomLeftTilePosition) && tileMap[bottomLeftTilePosition].hasBottomLeftRiver) sum += 1
|
||||
}
|
||||
return sum
|
||||
}
|
||||
*/
|
||||
|
||||
/** Describes a _Vertex_ on our hexagonal grid via a neighboring hex and clock direction, normalized
|
||||
* such that always the north-most hex and one of the two clock directions 5 / 7 o'clock are used. */
|
||||
class RiverCoordinate(val position: Vector2, val bottomRightOrLeft: BottomRightOrLeft) {
|
||||
class RiverCoordinate(
|
||||
private val tileMap: TileMap,
|
||||
private val position: Vector2,
|
||||
private val bottomRightOrLeft: BottomRightOrLeft
|
||||
) {
|
||||
enum class BottomRightOrLeft {
|
||||
/** 7 O'Clock of the tile */
|
||||
BottomLeft,
|
||||
@ -125,43 +101,92 @@ class RiverGenerator(
|
||||
BottomRight
|
||||
}
|
||||
|
||||
private val x = position.x.toInt()
|
||||
private val y = position.y.toInt()
|
||||
// Depending on the tile instance, some of the following will never be used. Tested with lazies: ~2% slower
|
||||
private val myTile = tileMap[position]
|
||||
private val myTopLeft = tileMap.getIfTileExistsOrNull(x + 1, y)
|
||||
private val myBottomLeft = tileMap.getIfTileExistsOrNull(x, y - 1)
|
||||
private val myTopRight = tileMap.getIfTileExistsOrNull(x, y + 1)
|
||||
private val myBottomRight = tileMap.getIfTileExistsOrNull(x - 1, y)
|
||||
private val myBottomCenter = tileMap.getIfTileExistsOrNull(x - 1, y - 1)
|
||||
|
||||
/** Lists the three neighboring vertices which have their anchor hex on the map
|
||||
* (yes some positions on the map's outer border will be included, some not) */
|
||||
fun getAdjacentPositions(tileMap: TileMap): Sequence<RiverCoordinate> = sequence {
|
||||
fun getAdjacentPositions(): Sequence<RiverCoordinate> = sequence {
|
||||
// What's nice is that adjacents are always the OPPOSITE in terms of right-left - rights are adjacent to only lefts, and vice-versa
|
||||
// This means that a lot of obviously-wrong assignments are simple to spot
|
||||
val x = position.x.toInt()
|
||||
val y = position.y.toInt()
|
||||
if (bottomRightOrLeft == BottomRightOrLeft.BottomLeft) {
|
||||
yield(RiverCoordinate(position, BottomRightOrLeft.BottomRight)) // same tile, other side
|
||||
val myTopLeft = tileMap.getIfTileExistsOrNull(x + 1, y)
|
||||
yield(RiverCoordinate(tileMap, position, BottomRightOrLeft.BottomRight)) // same tile, other side
|
||||
if (myTopLeft != null)
|
||||
yield(RiverCoordinate(myTopLeft.position, BottomRightOrLeft.BottomRight)) // tile to MY top-left, take its bottom right corner
|
||||
val myBottomLeft = tileMap.getIfTileExistsOrNull(x, y - 1)
|
||||
yield(RiverCoordinate(tileMap, myTopLeft!!.position, BottomRightOrLeft.BottomRight)) // tile to MY top-left, take its bottom right corner
|
||||
if (myBottomLeft != null)
|
||||
yield(RiverCoordinate(myBottomLeft.position, BottomRightOrLeft.BottomRight)) // Tile to MY bottom-left, take its bottom right
|
||||
yield(RiverCoordinate(tileMap, myBottomLeft!!.position, BottomRightOrLeft.BottomRight)) // Tile to MY bottom-left, take its bottom right
|
||||
} else {
|
||||
yield(RiverCoordinate(position, BottomRightOrLeft.BottomLeft)) // same tile, other side
|
||||
val myTopRight = tileMap.getIfTileExistsOrNull(x, y + 1)
|
||||
yield(RiverCoordinate(tileMap, position, BottomRightOrLeft.BottomLeft)) // same tile, other side
|
||||
if (myTopRight != null)
|
||||
yield(RiverCoordinate(myTopRight.position, BottomRightOrLeft.BottomLeft)) // tile to MY top-right, take its bottom left
|
||||
val myBottomRight = tileMap.getIfTileExistsOrNull(x - 1, y)
|
||||
yield(RiverCoordinate(tileMap, myTopRight!!.position, BottomRightOrLeft.BottomLeft)) // tile to MY top-right, take its bottom left
|
||||
if (myBottomRight != null)
|
||||
yield(RiverCoordinate(myBottomRight.position, BottomRightOrLeft.BottomLeft)) // tile to MY bottom-right, take its bottom left
|
||||
yield(RiverCoordinate(tileMap, myBottomRight!!.position, BottomRightOrLeft.BottomLeft)) // tile to MY bottom-right, take its bottom left
|
||||
}
|
||||
}
|
||||
|
||||
/** Lists the three neighboring hexes to this vertex which are on the map */
|
||||
fun getAdjacentTiles(tileMap: TileMap): Sequence<Tile> = sequence {
|
||||
val x = position.x.toInt()
|
||||
val y = position.y.toInt()
|
||||
yield(tileMap[x, y])
|
||||
val below = tileMap.getIfTileExistsOrNull(x - 1, y - 1) // tile directly below us,
|
||||
if (below != null) yield(below)
|
||||
val leftOrRight = if (bottomRightOrLeft == BottomRightOrLeft.BottomLeft)
|
||||
tileMap.getIfTileExistsOrNull(x, y - 1) // tile to our bottom-left
|
||||
else tileMap.getIfTileExistsOrNull(x - 1, y) // tile to our bottom-right
|
||||
if (leftOrRight != null) yield(leftOrRight)
|
||||
fun getAdjacentTiles(): Sequence<Tile> = sequence {
|
||||
yield(myTile)
|
||||
myBottomCenter?.let { yield(it) } // tile directly below us,
|
||||
if (bottomRightOrLeft == BottomRightOrLeft.BottomLeft)
|
||||
myBottomLeft?.let { yield(it) } // tile to our bottom-left
|
||||
else
|
||||
myBottomRight?.let { yield(it) } // tile to our bottom-right
|
||||
}
|
||||
|
||||
fun paintTo(newCoordinate: RiverCoordinate, resultingTiles: MutableSet<Tile>?) {
|
||||
if (newCoordinate.position == position) // same tile, switched right-to-left
|
||||
paintBottom(resultingTiles)
|
||||
else if (bottomRightOrLeft == BottomRightOrLeft.BottomRight) {
|
||||
if (newCoordinate.getAdjacentTiles().contains(myTile)) // moved from our 5 O'Clock to our 3 O'Clock
|
||||
paintBottomRight(resultingTiles)
|
||||
else // moved from our 5 O'Clock down in the 5 O'Clock direction - this is the 8 O'Clock river of the tile to our 4 O'Clock!
|
||||
newCoordinate.paintBottomLeft(resultingTiles)
|
||||
} else { // bottomRightOrLeft == BottomRightOrLeft.BottomLeft
|
||||
if (newCoordinate.getAdjacentTiles().contains(myTile)) // moved from our 7 O'Clock to our 9 O'Clock
|
||||
paintBottomLeft(resultingTiles)
|
||||
else // moved from our 7 O'Clock down in the 7 O'Clock direction
|
||||
newCoordinate.paintBottomRight(resultingTiles)
|
||||
}
|
||||
}
|
||||
|
||||
private fun paintBottom(resultingTiles: MutableSet<Tile>?) {
|
||||
myTile.hasBottomRiver = true
|
||||
if (resultingTiles == null) return
|
||||
resultingTiles.add(myTile)
|
||||
myBottomCenter?.let { resultingTiles.add(it) }
|
||||
}
|
||||
private fun paintBottomLeft(resultingTiles: MutableSet<Tile>?) {
|
||||
myTile.hasBottomLeftRiver = true
|
||||
if (resultingTiles == null) return
|
||||
resultingTiles.add(myTile)
|
||||
myBottomLeft?.let { resultingTiles.add(it) }
|
||||
}
|
||||
private fun paintBottomRight(resultingTiles: MutableSet<Tile>?) {
|
||||
myTile.hasBottomRightRiver = true
|
||||
if (resultingTiles == null) return
|
||||
resultingTiles.add(myTile)
|
||||
myBottomRight?.let { resultingTiles.add(it) }
|
||||
}
|
||||
|
||||
/** Count edges with a river from this vertex */
|
||||
@Suppress("unused") // Keep as how-to just in case
|
||||
fun numberOfConnectedRivers(): Int = sequence {
|
||||
yield(myTile.hasBottomRiver)
|
||||
if (bottomRightOrLeft == BottomRightOrLeft.BottomLeft) {
|
||||
yield(myTile.hasBottomLeftRiver)
|
||||
yield(myBottomLeft?.hasBottomRightRiver == true)
|
||||
} else {
|
||||
yield(myTile.hasBottomRightRiver)
|
||||
yield(myBottomRight?.hasBottomLeftRiver == true)
|
||||
}
|
||||
}.count { it }
|
||||
}
|
||||
}
|
||||
|
@ -21,17 +21,17 @@ import com.unciv.models.metadata.BaseRuleset
|
||||
import com.unciv.models.metadata.GameSetupInfo
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.screens.mapeditorscreen.tabs.MapEditorOptionsTab
|
||||
import com.unciv.ui.popups.ConfirmPopup
|
||||
import com.unciv.ui.components.tilegroups.TileGroup
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.components.input.KeyCharAndCode
|
||||
import com.unciv.ui.components.input.KeyShortcutDispatcherVeto
|
||||
import com.unciv.ui.components.input.KeyboardPanningListener
|
||||
import com.unciv.ui.components.tilegroups.TileGroup
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.images.ImageWithCustomSize
|
||||
import com.unciv.ui.popups.ConfirmPopup
|
||||
import com.unciv.ui.popups.ToastPopup
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.screens.basescreen.RecreateOnResize
|
||||
import com.unciv.ui.screens.mapeditorscreen.tabs.MapEditorOptionsTab
|
||||
import com.unciv.ui.screens.worldscreen.ZoomButtonPair
|
||||
import com.unciv.utils.Concurrency
|
||||
import com.unciv.utils.Dispatcher
|
||||
@ -47,7 +47,6 @@ import kotlinx.coroutines.Job
|
||||
//todo Synergy with Civilopedia for drawing loose tiles / terrain icons
|
||||
//todo left-align everything so a half-open drawer is more useful
|
||||
//todo combined brush
|
||||
//todo New function `convertTerrains` is auto-run after rivers the right decision for step-wise generation? Will paintRiverFromTo need the same? Will painting manually need the conversion?
|
||||
//todo Tooltips for Edit items with info on placeability? Place this info as Brush description? In Expander?
|
||||
//todo Civilopedia links from edit items by right-click/long-tap?
|
||||
//todo Mod tab change base ruleset - disableAllCheckboxes - instead some intelligence to leave those mods on that stay compatible?
|
||||
|
@ -7,16 +7,17 @@ import com.badlogic.gdx.scenes.scene2d.ui.Cell
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.unciv.logic.map.BFS
|
||||
import com.unciv.logic.map.mapgenerator.MapGenerationRandomness
|
||||
import com.unciv.logic.map.mapgenerator.MapGenerator
|
||||
import com.unciv.logic.map.mapgenerator.RiverGenerator
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.input.KeyCharAndCode
|
||||
import com.unciv.ui.components.TabbedPager
|
||||
import com.unciv.ui.components.UncivSlider
|
||||
import com.unciv.ui.components.extensions.addSeparator
|
||||
import com.unciv.ui.components.input.keyShortcuts
|
||||
import com.unciv.ui.components.extensions.toLabel
|
||||
import com.unciv.ui.components.input.KeyCharAndCode
|
||||
import com.unciv.ui.components.input.keyShortcuts
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.popups.ToastPopup
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
@ -211,7 +212,7 @@ class MapEditorEditTab(
|
||||
riverEndTile = tile
|
||||
if (riverStartTile != null) return paintRiverFromTo()
|
||||
}
|
||||
tilesToHighlight.forEach { editorScreen.highlightTile(it, Color.BLUE) }
|
||||
for (tileToHighlight in tilesToHighlight) editorScreen.highlightTile(tileToHighlight, Color.BLUE)
|
||||
}
|
||||
private fun paintRiverFromTo() {
|
||||
val resultingTiles = mutableSetOf<Tile>()
|
||||
@ -219,6 +220,7 @@ class MapEditorEditTab(
|
||||
try {
|
||||
val riverGenerator = RiverGenerator(editorScreen.tileMap, randomness, ruleset)
|
||||
riverGenerator.spawnRiver(riverStartTile!!, riverEndTile!!, resultingTiles)
|
||||
MapGenerator(ruleset).convertTerrains(resultingTiles)
|
||||
} catch (ex: Exception) {
|
||||
Log.error("Exception while generating rivers", ex)
|
||||
ToastPopup("River generation failed!", editorScreen)
|
||||
@ -226,7 +228,7 @@ class MapEditorEditTab(
|
||||
riverStartTile = null
|
||||
riverEndTile = null
|
||||
editorScreen.isDirty = true
|
||||
resultingTiles.forEach { editorScreen.updateAndHighlight(it, Color.SKY) }
|
||||
for (tile in resultingTiles) editorScreen.updateAndHighlight(tile, Color.SKY)
|
||||
}
|
||||
|
||||
internal fun paintTilesWithBrush(tile: Tile) {
|
||||
@ -238,12 +240,12 @@ class MapEditorEditTab(
|
||||
} else {
|
||||
tile.getTilesInDistance(brushSize - 1)
|
||||
}
|
||||
tiles.forEach {
|
||||
for (tileToPaint in tiles) {
|
||||
when (brushHandlerType) {
|
||||
BrushHandlerType.Direct -> directPaintTile(it)
|
||||
BrushHandlerType.Tile -> paintTile(it)
|
||||
BrushHandlerType.Road -> roadPaintTile(it)
|
||||
BrushHandlerType.River -> riverPaintTile(it)
|
||||
BrushHandlerType.Direct -> directPaintTile(tileToPaint)
|
||||
BrushHandlerType.Tile -> paintTile(tileToPaint)
|
||||
BrushHandlerType.Road -> roadPaintTile(tileToPaint)
|
||||
BrushHandlerType.River -> riverPaintTile(tileToPaint)
|
||||
else -> {} // other cases can't reach here
|
||||
}
|
||||
}
|
||||
@ -256,19 +258,22 @@ class MapEditorEditTab(
|
||||
editorScreen.updateAndHighlight(tile)
|
||||
}
|
||||
|
||||
/** Used for rivers - same as directPaintTile but may need to update 10,12 and 2 o'clock neighbor tiles too */
|
||||
/** Used for rivers - same as [directPaintTile] but may need to update 10,12 and 2 o'clock neighbor tiles too
|
||||
*
|
||||
* Note: Unlike [paintRiverFromTo] this does **not** call [MapGenerator.convertTerrains] to allow more freedom.
|
||||
*/
|
||||
private fun riverPaintTile(tile: Tile) {
|
||||
directPaintTile(tile)
|
||||
tile.neighbors.forEach {
|
||||
if (it.position.x > tile.position.x || it.position.y > tile.position.y)
|
||||
editorScreen.updateTile(it)
|
||||
for (neighbor in tile.neighbors) {
|
||||
if (neighbor.position.x > tile.position.x || neighbor.position.y > tile.position.y)
|
||||
editorScreen.updateTile(neighbor)
|
||||
}
|
||||
}
|
||||
|
||||
// Used for roads - same as paintTile but all neighbors need TileGroup.update too
|
||||
private fun roadPaintTile(tile: Tile) {
|
||||
if (!paintTile(tile)) return
|
||||
tile.neighbors.forEach { editorScreen.updateTile(it) }
|
||||
for (neighbor in tile.neighbors) editorScreen.updateTile(neighbor)
|
||||
}
|
||||
|
||||
/** apply brush to a single tile */
|
||||
|
Loading…
Reference in New Issue
Block a user