From 25db68fa2a123beebc05c64bee60c577bb6c96c7 Mon Sep 17 00:00:00 2001 From: GGGuenni Date: Wed, 3 Mar 2021 11:54:49 +0100 Subject: [PATCH] World wrap Part 2 (#3609) * Changed map generation for hex maps * Adding WorldWrap UI * Is now compileable again * Changed map gen for rec maps well that was easy * Units can now move and see across wrap * well this is smarter * World wrap checkbox commented ou should be available only after everything has been merged * simplify WW gen conditions * reduced indentation * Refactoring for better readablity --- core/src/com/unciv/logic/HexMath.kt | 12 ++-- core/src/com/unciv/logic/map/MapParameters.kt | 1 + core/src/com/unciv/logic/map/TileInfo.kt | 17 +++-- core/src/com/unciv/logic/map/TileMap.kt | 64 +++++++++++++++++-- .../logic/map/mapgenerator/MapGenerator.kt | 4 +- .../ui/newgamescreen/MapParametersTable.kt | 12 ++++ .../unciv/ui/worldscreen/WorldMapHolder.kt | 1 + 7 files changed, 88 insertions(+), 23 deletions(-) diff --git a/core/src/com/unciv/logic/HexMath.kt b/core/src/com/unciv/logic/HexMath.kt index 7fb67e1847..c0ea640719 100644 --- a/core/src/com/unciv/logic/HexMath.kt +++ b/core/src/com/unciv/logic/HexMath.kt @@ -122,7 +122,7 @@ object HexMath { return cubic2HexCoords(roundCubicCoords(hex2CubicCoords(hexCoord))) } - fun getVectorsAtDistance(origin: Vector2, distance: Int): List { + fun getVectorsAtDistance(origin: Vector2, distance: Int, maxDistance: Int, worldWrap: Boolean): List { val vectors = mutableListOf() if (distance == 0) { vectors += origin.cpy() @@ -136,21 +136,23 @@ object HexMath { } for (i in 0 until distance) { // 8 to 10 vectors += current.cpy() - vectors += origin.cpy().scl(2f).sub(current) // Get vector on other side of clock + if (!worldWrap || distance != maxDistance) + vectors += origin.cpy().scl(2f).sub(current) // Get vector on other side of clock current.add(1f, 1f) } for (i in 0 until distance) { // 10 to 12 vectors += current.cpy() - vectors += origin.cpy().scl(2f).sub(current) // Get vector on other side of clock + if (!worldWrap || distance != maxDistance || i != 0) + vectors += origin.cpy().scl(2f).sub(current) // Get vector on other side of clock current.add(0f, 1f) } return vectors } - fun getVectorsInDistance(origin: Vector2, distance: Int): List { + fun getVectorsInDistance(origin: Vector2, distance: Int, worldWrap: Boolean): List { val hexesToReturn = mutableListOf() for (i in 0 .. distance) { - hexesToReturn += getVectorsAtDistance(origin, i) + hexesToReturn += getVectorsAtDistance(origin, i, distance, worldWrap) } return hexesToReturn } diff --git a/core/src/com/unciv/logic/map/MapParameters.kt b/core/src/com/unciv/logic/map/MapParameters.kt index c98c5181d1..41fb9ac99c 100644 --- a/core/src/com/unciv/logic/map/MapParameters.kt +++ b/core/src/com/unciv/logic/map/MapParameters.kt @@ -36,6 +36,7 @@ class MapParameters { var size: MapSize = MapSize.Medium var noRuins = false var noNaturalWonders = false + var worldWrap = false /** This is used mainly for the map editor, so you can continue editing a map under the ame ruleset you started with */ var mods = LinkedHashSet() diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index b0facc1373..6b88680ddb 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -407,16 +407,15 @@ open class TileInfo { /** The two tiles have a river between them */ fun isConnectedByRiver(otherTile: TileInfo): Boolean { if (otherTile == this) throw Exception("Should not be called to compare to self!") - val xDifference = this.position.x - otherTile.position.x - val yDifference = this.position.y - otherTile.position.y - return when { - yDifference < -1f || xDifference < -1f || yDifference > 1f || xDifference > 1f -> - throw Exception("Should never call this function on a non-neighbor!") - xDifference == 1f && yDifference == 1f -> hasBottomRiver // we're directly above it - xDifference == 1f -> hasBottomRightRiver // we're to the top-left of it - yDifference == 1f -> hasBottomLeftRiver // we're to the top-right of it - else -> otherTile.isConnectedByRiver(this) // we're below it, check the other tile + return when (tileMap.getNeighborTileClockPosition(this, otherTile)) { + 2 -> otherTile.hasBottomLeftRiver // we're to the bottom-left of it + 4 -> hasBottomRightRiver // we're to the top-right of it + 6 -> hasBottomRiver // we're directly above it + 8 -> hasBottomLeftRiver // we're to the top-left of it + 10 -> otherTile.hasBottomRightRiver // we're to the bottom-right of it + 12 -> otherTile.hasBottomRiver // we're directly below it + else -> throw Exception("Should never call this function on a non-neighbor!") } } diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 4f2085a5fd..0126914099 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -44,15 +44,16 @@ class TileMap { constructor() /** generates an hexagonal map of given radius */ - constructor(radius: Int, ruleset: Ruleset) { - for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius)) + constructor(radius: Int, ruleset: Ruleset, worldWrap: Boolean = false) { + for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius, worldWrap)) tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland }) setTransients(ruleset) } /** generates a rectangular map of given width and height*/ - constructor(width: Int, height: Int, ruleset: Ruleset) { - for (x in -width / 2..width / 2) + constructor(width: Int, height: Int, ruleset: Ruleset, worldWrap: Boolean = false) { + val halfway = if(worldWrap) width/2-1 else width/2 + for (x in -width / 2 .. halfway) for (y in -height / 2..height / 2) tileList.add(TileInfo().apply { position = HexMath.evenQ2HexCoords(Vector2(x.toFloat(), y.toFloat())) @@ -99,8 +100,6 @@ class TileMap { sequenceOf(get(origin)) else sequence { - fun getIfTileExistsOrNull(x: Int, y: Int) = if (contains(x, y)) get(x, y) else null - val centerX = origin.x.toInt() val centerY = origin.y.toInt() @@ -129,6 +128,30 @@ class TileMap { } }.filterNotNull() + private fun getIfTileExistsOrNull(x: Int, y: Int) : TileInfo? { + if (contains(x, y)) + return get(x, y) + + if (!mapParameters.worldWrap) + return null + + var radius = mapParameters.size.radius + if (mapParameters.shape == MapShape.rectangular) + radius = HexMath.getEquivalentRectangularSize(radius).x.toInt() / 2 + + //tile is outside of the map + if (contains(x + radius, y - radius)) { //tile is on right side + //get tile wrapped around from right to left + return get(x + radius, y - radius) + } else if (contains(x - radius, y + radius)) { //tile is on left side + //get tile wrapped around from left to right + return get(x - radius, y + radius) + } + + return null + } + + /** Tries to place the [unitName] into the [TileInfo] closest to the given the [position] * @param civInfo civilization to assign unit to * @return created [MapUnit] or null if no suitable location was found @@ -141,7 +164,7 @@ class TileMap { val unit = gameInfo.ruleSet.units[unitName]!!.getMapUnit(gameInfo.ruleSet) fun getPassableNeighbours(tileInfo: TileInfo): Set = - getTilesAtDistance(tileInfo.position, 1).filter { unit.movement.canPassThrough(it) }.toSet() + tileInfo.neighbors.filter { unit.movement.canPassThrough(it) }.toSet() // both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn unit.assignOwner(civInfo, false) @@ -293,4 +316,31 @@ class TileMap { tileInfo.setUnitTransients(setUnitCivTransients) } } + + /** + * Returns the clockPosition of otherTile seen from tile's position + * Returns -1 if not neighbors + */ + fun getNeighborTileClockPosition(tile: TileInfo, otherTile: TileInfo): Int{ + var radius = mapParameters.size.radius + if (mapParameters.shape == MapShape.rectangular) + radius = HexMath.getEquivalentRectangularSize(radius).x.toInt() / 2 + + val xDifference = tile.position.x - otherTile.position.x + val yDifference = tile.position.y - otherTile.position.y + val xWrapDifferenceBottom = tile.position.x - (otherTile.position.x - radius) + val yWrapDifferenceBottom = tile.position.y - (otherTile.position.y - radius) + val xWrapDifferenceTop = tile.position.x - (otherTile.position.x + radius) + val yWrapDifferenceTop = tile.position.y - (otherTile.position.y + radius) + + return when { + xDifference == 1f && yDifference == 1f -> 6 // otherTile is below + xDifference == -1f && yDifference == -1f -> 12 // otherTile is above + xDifference == 1f || xWrapDifferenceBottom == 1f -> 4 // otherTile is bottom-right + yDifference == 1f || yWrapDifferenceBottom == 1f -> 8 // otherTile is bottom-left + xDifference == -1f || xWrapDifferenceTop == -1f -> 10 // otherTile is top-left + yDifference == -1f || yWrapDifferenceTop == -1f -> 2 // otherTile is top-right + else -> -1 + } + } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt index bd762b8b89..86b23903bc 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt @@ -27,10 +27,10 @@ class MapGenerator(val ruleset: Ruleset) { if (mapParameters.shape == MapShape.rectangular) { val size = HexMath.getEquivalentRectangularSize(mapRadius) - map = TileMap(size.x.toInt(), size.y.toInt(), ruleset) + map = TileMap(size.x.toInt(), size.y.toInt(), ruleset, mapParameters.worldWrap) } else - map = TileMap(mapRadius, ruleset) + map = TileMap(mapRadius, ruleset, mapParameters.worldWrap) map.mapParameters = mapParameters map.mapParameters.seed = seed diff --git a/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt b/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt index 1a12a7fe17..6ee89f0340 100644 --- a/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt @@ -21,6 +21,7 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed lateinit var mapTypeSelectBox: TranslatedSelectBox lateinit var noRuinsCheckbox: CheckBox lateinit var noNaturalWondersCheckbox: CheckBox + lateinit var worldWrapCheckbox: CheckBox init { skin = CameraStageBaseScreen.skin @@ -30,6 +31,8 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed addWorldSizeSelectBox() addNoRuinsCheckbox() addNoNaturalWondersCheckbox() + //uncomment when whole worldWrap feature is added + //addWorldWrapCheckbox() addAdvancedSettings() } @@ -105,6 +108,15 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed add(noNaturalWondersCheckbox).colspan(2).row() } + private fun addWorldWrapCheckbox() { + worldWrapCheckbox = CheckBox("World Wrap".tr(), skin) + worldWrapCheckbox.isChecked = mapParameters.worldWrap + worldWrapCheckbox.onChange { + mapParameters.worldWrap = worldWrapCheckbox.isChecked + } + add(worldWrapCheckbox).colspan(2).row() + } + private fun addAdvancedSettings() { val advancedSettingsTable = getAdvancedSettingsTable() diff --git a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt index e77dcdd38a..427959be3c 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt @@ -45,6 +45,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap init { if (Gdx.app.type == Application.ApplicationType.Desktop) this.setFlingTime(0f) + continousScrollingX = tileMap.mapParameters.worldWrap } // Used to transfer data on the "move here" button that should be created, from the side thread to the main thread