mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-26 13:27:22 -04:00
GameStarter wrap and shape aware (#5107)
- Starting locations code aware of wrap and map shape - RiverGenerator wrap aware - RiverGenerator guard against endless loop
This commit is contained in:
parent
7f386da2bc
commit
7118e9779c
@ -1,6 +1,5 @@
|
||||
package com.unciv.logic
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.*
|
||||
@ -9,12 +8,12 @@ import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.logic.map.mapgenerator.MapGenerator
|
||||
import com.unciv.models.metadata.GameParameters
|
||||
import com.unciv.models.metadata.GameSetupInfo
|
||||
import com.unciv.models.ruleset.Era
|
||||
import com.unciv.models.ruleset.ModOptionsConstants
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.metadata.GameSetupInfo
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
@ -167,7 +166,7 @@ object GameStarter {
|
||||
availableCivNames.addAll(ruleset.nations.filter { it.value.isMajorCiv() }.keys.shuffled())
|
||||
availableCivNames.removeAll(newGameParameters.players.map { it.chosenCiv })
|
||||
availableCivNames.remove(Constants.barbarians)
|
||||
|
||||
|
||||
val startingTechs = ruleset.technologies.values.filter { it.uniques.contains("Starting tech") }
|
||||
|
||||
if (!newGameParameters.noBarbarians && ruleset.nations.containsKey(Constants.barbarians)) {
|
||||
@ -256,7 +255,7 @@ object GameStarter {
|
||||
// An unusually bad spawning location
|
||||
addConsolationPrize(gameInfo, startingLocation, 45 - startingLocation.getTileStartScore().toInt())
|
||||
}
|
||||
|
||||
|
||||
if(civ.isCityState())
|
||||
addCityStateLuxury(gameInfo, startingLocation)
|
||||
|
||||
@ -271,19 +270,19 @@ object GameStarter {
|
||||
fun placeNearStartingPosition(unitName: String) {
|
||||
civ.placeUnitNearTile(startingLocation.position, unitName)
|
||||
}
|
||||
|
||||
|
||||
// Determine starting units based on starting era
|
||||
startingUnits = ruleSet.eras[startingEra]!!.getStartingUnits().toMutableList()
|
||||
eraUnitReplacement = ruleSet.eras[startingEra]!!.startingMilitaryUnit
|
||||
|
||||
|
||||
// Add extra units granted by difficulty
|
||||
startingUnits.addAll(when {
|
||||
civ.isPlayerCivilization() -> gameInfo.getDifficulty().playerBonusStartingUnits
|
||||
civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivBonusStartingUnits
|
||||
else -> gameInfo.getDifficulty().aiCityStateBonusStartingUnits
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
fun getEquivalentUnit(civ: CivilizationInfo, unitParam: String): String? {
|
||||
var unit = unitParam // We want to change it and this is the easiest way to do so
|
||||
if (unit == Constants.eraSpecificUnit) unit = eraUnitReplacement
|
||||
@ -315,11 +314,11 @@ object GameStarter {
|
||||
startingUnits.clear()
|
||||
startingUnits.add(startingSettlers.random())
|
||||
}
|
||||
|
||||
|
||||
// One city challengers should spawn with one settler only regardless of era and difficulty
|
||||
if (civ.playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge) {
|
||||
val startingSettlers = startingUnits.filter { settlerLikeUnits.contains(it) }
|
||||
|
||||
|
||||
startingUnits.removeAll(startingSettlers)
|
||||
startingUnits.add(startingSettlers.random())
|
||||
}
|
||||
@ -358,8 +357,10 @@ object GameStarter {
|
||||
|
||||
for (minimumDistanceBetweenStartingLocations in tileMap.tileMatrix.size / 4 downTo 0) {
|
||||
val freeTiles = landTilesInBigEnoughGroup
|
||||
.filter { vectorIsAtLeastNTilesAwayFromEdge(it.position, (minimumDistanceBetweenStartingLocations * 2) /3, tileMap) }
|
||||
.toMutableList()
|
||||
.filter {
|
||||
HexMath.getDistanceFromEdge(it.position, tileMap.mapParameters) >=
|
||||
(minimumDistanceBetweenStartingLocations * 2) /3
|
||||
}.toMutableList()
|
||||
|
||||
val startingLocations = HashMap<CivilizationInfo, TileInfo>()
|
||||
for (civ in civsOrderedByAvailableLocations) {
|
||||
@ -446,13 +447,4 @@ object GameStarter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun vectorIsAtLeastNTilesAwayFromEdge(vector: Vector2, n: Int, tileMap: TileMap): Boolean {
|
||||
// Since all maps are HEXAGONAL, the easiest way of checking if a tile is n steps away from the
|
||||
// edge is checking the distance to the CENTER POINT
|
||||
// Can't believe we used a dumb way of calculating this before!
|
||||
val hexagonalRadius = -tileMap.leftX
|
||||
val distanceFromCenter = HexMath.getDistance(vector, Vector2.Zero)
|
||||
return hexagonalRadius - distanceFromCenter >= n
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package com.unciv.logic
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.badlogic.gdx.math.Vector3
|
||||
import com.unciv.logic.map.MapParameters
|
||||
import com.unciv.logic.map.MapShape
|
||||
import kotlin.math.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "unused") // this is a library offering optional services
|
||||
@ -20,7 +22,7 @@ object HexMath {
|
||||
if (size < 0) return 0
|
||||
return 1 + 6 * size * (size + 1) / 2
|
||||
}
|
||||
|
||||
|
||||
/** Almost inverse of [getNumberOfTilesInHexagon] - get equivalent fractional Hexagon radius for an Area */
|
||||
fun getHexagonalRadiusForArea(numberOfTiles: Int) =
|
||||
if (numberOfTiles < 1) 0f else ((sqrt(12f * numberOfTiles - 3) - 3) / 6)
|
||||
@ -194,4 +196,34 @@ object HexMath {
|
||||
|
||||
fun getClockDirectionToWorldVector(clockDirection: Int): Vector2 =
|
||||
clockToWorldVectors[clockDirection] ?: Vector2.Zero
|
||||
|
||||
fun getDistanceFromEdge(vector: Vector2, mapParameters: MapParameters): Int {
|
||||
val x = vector.x.toInt()
|
||||
val y = vector.y.toInt()
|
||||
if (mapParameters.shape == MapShape.rectangular) {
|
||||
val height = mapParameters.mapSize.height
|
||||
val width = mapParameters.mapSize.width
|
||||
val left = if (mapParameters.worldWrap) Int.MAX_VALUE else width / 2 - (x - y)
|
||||
val right = if (mapParameters.worldWrap) Int.MAX_VALUE else (width - 1) / 2 - (y - x)
|
||||
val top = height / 2 - (x + y) / 2
|
||||
// kotlin's Int division rounds in different directions depending on sign! Thus 1 extra `-1`
|
||||
val bottom = (x + y - 1) / 2 + (height - 1) / 2
|
||||
return min(min(left, right), min(top, bottom))
|
||||
} else {
|
||||
val radius = mapParameters.mapSize.radius
|
||||
if (mapParameters.worldWrap) {
|
||||
// The non-wrapping method holds in the upper two and lower two 'triangles' of the hexagon
|
||||
// but needs special casing for left and right 'wedges', where only distance from the
|
||||
// 'choke points' counts (upper and lower hex at the 'seam' where height is smallest).
|
||||
// These are at (radius,0) and (0,-radius)
|
||||
if (x.sign == y.sign) return radius - getDistance(vector, Vector2.Zero)
|
||||
// left wedge - the 'choke points' are not wrapped relative to us
|
||||
if (x > 0) return min(getDistance(vector, Vector2(radius.toFloat(),0f)), getDistance(vector, Vector2(0f, -radius.toFloat())))
|
||||
// right wedge - compensate wrap by using a hex 1 off along the edge - same result
|
||||
return min(getDistance(vector, Vector2(1f, radius.toFloat())), getDistance(vector, Vector2(-radius.toFloat(), -1f)))
|
||||
} else {
|
||||
return radius - getDistance(vector, Vector2.Zero)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,24 +276,27 @@ open class TileInfo {
|
||||
fun getTileStartScore(): Float {
|
||||
var sum = 0f
|
||||
for (tile in getTilesInDistance(2)) {
|
||||
if (tile == this)
|
||||
continue
|
||||
sum += tile.getTileStartYield()
|
||||
val tileYield = tile.getTileStartYield(tile == this)
|
||||
sum += tileYield
|
||||
if (tile in neighbors)
|
||||
sum += tile.getTileStartYield()
|
||||
sum += tileYield
|
||||
}
|
||||
|
||||
if (isHill())
|
||||
sum -= 2
|
||||
sum -= 2f
|
||||
if (isAdjacentToRiver())
|
||||
sum += 2
|
||||
sum += 2f
|
||||
if (neighbors.any { it.baseTerrain == Constants.mountain })
|
||||
sum += 2
|
||||
sum += 2f
|
||||
if (isCoastalTile())
|
||||
sum += 3f
|
||||
if (!isCoastalTile() && neighbors.any { it.isCoastalTile() })
|
||||
sum -= 7f
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
private fun getTileStartYield(): Float {
|
||||
private fun getTileStartYield(isCenter: Boolean): Float {
|
||||
var stats = getBaseTerrain().clone()
|
||||
|
||||
for (terrainFeatureBase in getTerrainFeatures()) {
|
||||
@ -303,7 +306,12 @@ open class TileInfo {
|
||||
stats.add(terrainFeatureBase)
|
||||
}
|
||||
if (resource != null) stats.add(getTileResource())
|
||||
|
||||
if (stats.production < 0) stats.production = 0f
|
||||
if (isCenter) {
|
||||
if (stats.food < 2) stats.food = 2f
|
||||
if (stats.production < 1) stats.production = 1f
|
||||
}
|
||||
|
||||
return stats.food + stats.production + stats.gold
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ class TileMap {
|
||||
}.filterNotNull()
|
||||
|
||||
/** @return tile at hex coordinates ([x],[y]) or null if they are outside the map. Respects map edges and world wrap. */
|
||||
private fun getIfTileExistsOrNull(x: Int, y: Int): TileInfo? {
|
||||
fun getIfTileExistsOrNull(x: Int, y: Int): TileInfo? {
|
||||
if (contains(x, y))
|
||||
return get(x, y)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.unciv.logic.map.mapgenerator
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.HexMath
|
||||
import com.unciv.logic.map.*
|
||||
@ -9,7 +8,10 @@ import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.Terrain
|
||||
import com.unciv.models.ruleset.tile.TerrainType
|
||||
import kotlin.math.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sign
|
||||
import kotlin.random.Random
|
||||
|
||||
|
||||
@ -19,7 +21,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
const val consoleOutput = false
|
||||
private const val consoleTimings = false
|
||||
}
|
||||
|
||||
|
||||
private var randomness = MapGenerationRandomness()
|
||||
|
||||
fun generateMap(mapParameters: MapParameters): TileMap {
|
||||
@ -73,7 +75,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
NaturalWonderGenerator(ruleset, randomness).spawnNaturalWonders(map)
|
||||
}
|
||||
runAndMeasure("RiverGenerator") {
|
||||
RiverGenerator(randomness).spawnRivers(map)
|
||||
RiverGenerator(map, randomness).spawnRivers()
|
||||
}
|
||||
runAndMeasure("spreadResources") {
|
||||
spreadResources(map)
|
||||
@ -459,7 +461,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
|
||||
}
|
||||
|
||||
class MapGenerationRandomness{
|
||||
class MapGenerationRandomness {
|
||||
var RNG = Random(42)
|
||||
|
||||
fun seedRNG(seed: Long = 42) {
|
||||
@ -532,30 +534,3 @@ class MapGenerationRandomness{
|
||||
throw Exception()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RiverCoordinate(val position: Vector2, val bottomRightOrLeft: BottomRightOrLeft) {
|
||||
enum class BottomRightOrLeft {
|
||||
/** 7 O'Clock of the tile */
|
||||
BottomLeft,
|
||||
|
||||
/** 5 O'Clock of the tile */
|
||||
BottomRight
|
||||
}
|
||||
|
||||
fun getAdjacentPositions(): Sequence<RiverCoordinate> {
|
||||
// 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
|
||||
return if (bottomRightOrLeft == BottomRightOrLeft.BottomLeft) {
|
||||
sequenceOf(RiverCoordinate(position, BottomRightOrLeft.BottomRight), // same tile, other side
|
||||
RiverCoordinate(position.cpy().add(1f, 0f), BottomRightOrLeft.BottomRight), // tile to MY top-left, take its bottom right corner
|
||||
RiverCoordinate(position.cpy().add(0f, -1f), BottomRightOrLeft.BottomRight) // Tile to MY bottom-left, take its bottom right
|
||||
)
|
||||
} else {
|
||||
sequenceOf(RiverCoordinate(position, BottomRightOrLeft.BottomLeft), // same tile, other side
|
||||
RiverCoordinate(position.cpy().add(0f, 1f), BottomRightOrLeft.BottomLeft), // tile to MY top-right, take its bottom left
|
||||
RiverCoordinate(position.cpy().add(-1f, 0f), BottomRightOrLeft.BottomLeft) // tile to MY bottom-right, take its bottom left
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,111 +1,167 @@
|
||||
package com.unciv.logic.map.mapgenerator
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
|
||||
class RiverGenerator(val randomness: MapGenerationRandomness) {
|
||||
class RiverGenerator(
|
||||
private val tileMap: TileMap,
|
||||
private val randomness: MapGenerationRandomness
|
||||
) {
|
||||
companion object{
|
||||
const val MAP_TILES_PER_RIVER = 100
|
||||
const val MIN_RIVER_LENGTH = 5
|
||||
const val MAX_RIVER_LENGTH = 666 // Do not set < max map radius
|
||||
}
|
||||
|
||||
fun spawnRivers(map: TileMap) {
|
||||
val numberOfRivers = map.values.count { it.isLand } / 100
|
||||
fun spawnRivers() {
|
||||
val numberOfRivers = tileMap.values.count { it.isLand } / MAP_TILES_PER_RIVER
|
||||
|
||||
var optionalTiles = map.values.asSequence()
|
||||
.filter { it.baseTerrain == Constants.mountain && it.aerialDistanceTo(getClosestWaterTile(it)) > 4 }.toMutableList()
|
||||
var optionalTiles = tileMap.values.asSequence()
|
||||
.filter { it.baseTerrain == Constants.mountain && it.isFarEnoughFromWater() }.toMutableList()
|
||||
if (optionalTiles.size < numberOfRivers)
|
||||
optionalTiles.addAll(map.values.filter { it.isHill() && it.aerialDistanceTo(getClosestWaterTile(it)) > 4 })
|
||||
optionalTiles.addAll(tileMap.values.filter { it.isHill() && it.isFarEnoughFromWater() })
|
||||
if (optionalTiles.size < numberOfRivers)
|
||||
optionalTiles = map.values.filter { it.isLand && it.aerialDistanceTo(getClosestWaterTile(it)) > 4 }.toMutableList()
|
||||
optionalTiles = tileMap.values.filter { it.isLand && it.isFarEnoughFromWater() }.toMutableList()
|
||||
|
||||
val mapRadius = map.mapParameters.mapSize.radius
|
||||
val mapRadius = tileMap.mapParameters.mapSize.radius
|
||||
val riverStarts = randomness.chooseSpreadOutLocations(numberOfRivers, optionalTiles, mapRadius)
|
||||
for (tile in riverStarts) spawnRiver(tile, map)
|
||||
for (tile in riverStarts) spawnRiver(tile)
|
||||
|
||||
for (tile in map.values) {
|
||||
for (tile in tileMap.values) {
|
||||
if (tile.isAdjacentToRiver()) {
|
||||
if (tile.baseTerrain == Constants.desert && tile.terrainFeatures.isEmpty()) tile.terrainFeatures.add(Constants.floodPlains)
|
||||
else if (tile.baseTerrain == Constants.snow) tile.baseTerrain = Constants.tundra
|
||||
else if (tile.baseTerrain == Constants.tundra) tile.baseTerrain = Constants.plains
|
||||
when {
|
||||
tile.baseTerrain == Constants.desert && tile.terrainFeatures.isEmpty() ->
|
||||
tile.terrainFeatures.add(Constants.floodPlains)
|
||||
tile.baseTerrain == Constants.snow -> tile.baseTerrain = Constants.tundra
|
||||
tile.baseTerrain == Constants.tundra -> tile.baseTerrain = Constants.plains
|
||||
}
|
||||
tile.setTerrainTransients()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getClosestWaterTile(tile: TileInfo): TileInfo {
|
||||
var distance = 1
|
||||
while (true) {
|
||||
val waterTiles = tile.getTilesAtDistance(distance).filter { it.isWater }
|
||||
if (waterTiles.none()) {
|
||||
distance++
|
||||
continue
|
||||
}
|
||||
return waterTiles.toList().random(randomness.RNG)
|
||||
private fun TileInfo.isFarEnoughFromWater(): Boolean {
|
||||
for (distance in 1 until MIN_RIVER_LENGTH) {
|
||||
if (getTilesAtDistance(distance).any { it.isWater }) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun spawnRiver(initialPosition: TileInfo, map: TileMap) {
|
||||
private fun getClosestWaterTile(tile: TileInfo): TileInfo {
|
||||
for (distance in 1..MAX_RIVER_LENGTH) {
|
||||
val waterTiles = tile.getTilesAtDistance(distance).filter { it.isWater }
|
||||
if (waterTiles.any())
|
||||
return waterTiles.toList().random(randomness.RNG)
|
||||
}
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
private fun spawnRiver(initialPosition: TileInfo) {
|
||||
// Recommendation: Draw a bunch of hexagons on paper before trying to understand this, it's super helpful!
|
||||
val endPosition = getClosestWaterTile(initialPosition)
|
||||
|
||||
var riverCoordinate = RiverCoordinate(initialPosition.position,
|
||||
RiverCoordinate.BottomRightOrLeft.values().random(randomness.RNG))
|
||||
|
||||
|
||||
while (getAdjacentTiles(riverCoordinate, map).none { it.isWater }) {
|
||||
val possibleCoordinates = riverCoordinate.getAdjacentPositions()
|
||||
.filter { map.contains(it.position) }
|
||||
for (step in 1..MAX_RIVER_LENGTH) { // Arbitrary max on river length, otherwise this will go in circles - rarely
|
||||
if (riverCoordinate.getAdjacentTiles(tileMap).any { it.isWater }) return
|
||||
val possibleCoordinates = riverCoordinate.getAdjacentPositions(tileMap)
|
||||
if (possibleCoordinates.none()) return // end of the line
|
||||
val newCoordinate = possibleCoordinates
|
||||
.groupBy {
|
||||
getAdjacentTiles(it, map).map { it.aerialDistanceTo(endPosition) }
|
||||
.groupBy { newCoordinate ->
|
||||
newCoordinate.getAdjacentTiles(tileMap).map { it.aerialDistanceTo(endPosition) }
|
||||
.minOrNull()!!
|
||||
}
|
||||
.minByOrNull { it.key }!!
|
||||
.component2().random(randomness.RNG)
|
||||
|
||||
// set new rivers in place
|
||||
val riverCoordinateTile = map[riverCoordinate.position]
|
||||
val riverCoordinateTile = tileMap[riverCoordinate.position]
|
||||
if (newCoordinate.position == riverCoordinate.position) // same tile, switched right-to-left
|
||||
riverCoordinateTile.hasBottomRiver = true
|
||||
else if (riverCoordinate.bottomRightOrLeft == RiverCoordinate.BottomRightOrLeft.BottomRight) {
|
||||
if (getAdjacentTiles(newCoordinate, map).contains(riverCoordinateTile)) // moved from our 5 O'Clock to our 3 O'Clock
|
||||
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!
|
||||
map[newCoordinate.position].hasBottomLeftRiver = true
|
||||
tileMap[newCoordinate.position].hasBottomLeftRiver = true
|
||||
} else { // riverCoordinate.bottomRightOrLeft==RiverCoordinate.BottomRightOrLeft.Left
|
||||
if (getAdjacentTiles(newCoordinate, map).contains(riverCoordinateTile)) // moved from our 7 O'Clock to our 9 O'Clock
|
||||
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
|
||||
map[newCoordinate.position].hasBottomRightRiver = true
|
||||
tileMap[newCoordinate.position].hasBottomRightRiver = true
|
||||
}
|
||||
riverCoordinate = newCoordinate
|
||||
}
|
||||
|
||||
println("River reached max length!")
|
||||
}
|
||||
|
||||
fun getAdjacentTiles(riverCoordinate: RiverCoordinate, map: TileMap): Sequence<TileInfo> {
|
||||
val potentialPositions = sequenceOf(
|
||||
riverCoordinate.position,
|
||||
riverCoordinate.position.cpy().add(-1f, -1f), // tile directly below us,
|
||||
if (riverCoordinate.bottomRightOrLeft == RiverCoordinate.BottomRightOrLeft.BottomLeft)
|
||||
riverCoordinate.position.cpy().add(0f, -1f) // tile to our bottom-left
|
||||
else riverCoordinate.position.cpy().add(-1f, 0f) // tile to our bottom-right
|
||||
)
|
||||
return potentialPositions.map { if (map.contains(it)) map[it] else null }.filterNotNull()
|
||||
}
|
||||
|
||||
fun numberOfConnectedRivers(riverCoordinate: RiverCoordinate, map: TileMap): Int {
|
||||
/*
|
||||
fun numberOfConnectedRivers(riverCoordinate: RiverCoordinate): Int {
|
||||
var sum = 0
|
||||
if (map.contains(riverCoordinate.position) && map[riverCoordinate.position].hasBottomRiver) sum += 1
|
||||
if (tileMap.contains(riverCoordinate.position) && tileMap[riverCoordinate.position].hasBottomRiver) sum += 1
|
||||
if (riverCoordinate.bottomRightOrLeft == RiverCoordinate.BottomRightOrLeft.BottomLeft) {
|
||||
if (map.contains(riverCoordinate.position) && map[riverCoordinate.position].hasBottomLeftRiver) sum += 1
|
||||
if (tileMap.contains(riverCoordinate.position) && tileMap[riverCoordinate.position].hasBottomLeftRiver) sum += 1
|
||||
val bottomLeftTilePosition = riverCoordinate.position.cpy().add(0f, -1f)
|
||||
if (map.contains(bottomLeftTilePosition) && map[bottomLeftTilePosition].hasBottomRightRiver) sum += 1
|
||||
if (tileMap.contains(bottomLeftTilePosition) && tileMap[bottomLeftTilePosition].hasBottomRightRiver) sum += 1
|
||||
} else {
|
||||
if (map.contains(riverCoordinate.position) && map[riverCoordinate.position].hasBottomRightRiver) sum += 1
|
||||
if (tileMap.contains(riverCoordinate.position) && tileMap[riverCoordinate.position].hasBottomRightRiver) sum += 1
|
||||
val bottomLeftTilePosition = riverCoordinate.position.cpy().add(-1f, 0f)
|
||||
if (map.contains(bottomLeftTilePosition) && map[bottomLeftTilePosition].hasBottomLeftRiver) sum += 1
|
||||
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) {
|
||||
enum class BottomRightOrLeft {
|
||||
/** 7 O'Clock of the tile */
|
||||
BottomLeft,
|
||||
|
||||
/** 5 O'Clock of the tile */
|
||||
BottomRight
|
||||
}
|
||||
|
||||
/** 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 {
|
||||
// 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)
|
||||
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)
|
||||
if (myBottomLeft != null)
|
||||
yield(RiverCoordinate(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)
|
||||
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)
|
||||
if (myBottomRight != null)
|
||||
yield(RiverCoordinate(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<TileInfo> = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.CivilizationInfo
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.ui.cityscreen.YieldGroup
|
||||
import com.unciv.ui.utils.ImageGetter
|
||||
import com.unciv.ui.utils.center
|
||||
|
Loading…
x
Reference in New Issue
Block a user