mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-20 04:38:18 +07:00
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
This commit is contained in:
@ -122,7 +122,7 @@ object HexMath {
|
||||
return cubic2HexCoords(roundCubicCoords(hex2CubicCoords(hexCoord)))
|
||||
}
|
||||
|
||||
fun getVectorsAtDistance(origin: Vector2, distance: Int): List<Vector2> {
|
||||
fun getVectorsAtDistance(origin: Vector2, distance: Int, maxDistance: Int, worldWrap: Boolean): List<Vector2> {
|
||||
val vectors = mutableListOf<Vector2>()
|
||||
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<Vector2> {
|
||||
fun getVectorsInDistance(origin: Vector2, distance: Int, worldWrap: Boolean): List<Vector2> {
|
||||
val hexesToReturn = mutableListOf<Vector2>()
|
||||
for (i in 0 .. distance) {
|
||||
hexesToReturn += getVectorsAtDistance(origin, i)
|
||||
hexesToReturn += getVectorsAtDistance(origin, i, distance, worldWrap)
|
||||
}
|
||||
return hexesToReturn
|
||||
}
|
||||
|
@ -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<String>()
|
||||
|
@ -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!")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<TileInfo> =
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user