mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-21 05:09:25 +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)))
|
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>()
|
val vectors = mutableListOf<Vector2>()
|
||||||
if (distance == 0) {
|
if (distance == 0) {
|
||||||
vectors += origin.cpy()
|
vectors += origin.cpy()
|
||||||
@ -136,21 +136,23 @@ object HexMath {
|
|||||||
}
|
}
|
||||||
for (i in 0 until distance) { // 8 to 10
|
for (i in 0 until distance) { // 8 to 10
|
||||||
vectors += current.cpy()
|
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)
|
current.add(1f, 1f)
|
||||||
}
|
}
|
||||||
for (i in 0 until distance) { // 10 to 12
|
for (i in 0 until distance) { // 10 to 12
|
||||||
vectors += current.cpy()
|
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)
|
current.add(0f, 1f)
|
||||||
}
|
}
|
||||||
return vectors
|
return vectors
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getVectorsInDistance(origin: Vector2, distance: Int): List<Vector2> {
|
fun getVectorsInDistance(origin: Vector2, distance: Int, worldWrap: Boolean): List<Vector2> {
|
||||||
val hexesToReturn = mutableListOf<Vector2>()
|
val hexesToReturn = mutableListOf<Vector2>()
|
||||||
for (i in 0 .. distance) {
|
for (i in 0 .. distance) {
|
||||||
hexesToReturn += getVectorsAtDistance(origin, i)
|
hexesToReturn += getVectorsAtDistance(origin, i, distance, worldWrap)
|
||||||
}
|
}
|
||||||
return hexesToReturn
|
return hexesToReturn
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ class MapParameters {
|
|||||||
var size: MapSize = MapSize.Medium
|
var size: MapSize = MapSize.Medium
|
||||||
var noRuins = false
|
var noRuins = false
|
||||||
var noNaturalWonders = 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 */
|
/** 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>()
|
var mods = LinkedHashSet<String>()
|
||||||
|
@ -407,16 +407,15 @@ open class TileInfo {
|
|||||||
/** The two tiles have a river between them */
|
/** The two tiles have a river between them */
|
||||||
fun isConnectedByRiver(otherTile: TileInfo): Boolean {
|
fun isConnectedByRiver(otherTile: TileInfo): Boolean {
|
||||||
if (otherTile == this) throw Exception("Should not be called to compare to self!")
|
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 {
|
return when (tileMap.getNeighborTileClockPosition(this, otherTile)) {
|
||||||
yDifference < -1f || xDifference < -1f || yDifference > 1f || xDifference > 1f ->
|
2 -> otherTile.hasBottomLeftRiver // we're to the bottom-left of it
|
||||||
throw Exception("Should never call this function on a non-neighbor!")
|
4 -> hasBottomRightRiver // we're to the top-right of it
|
||||||
xDifference == 1f && yDifference == 1f -> hasBottomRiver // we're directly above it
|
6 -> hasBottomRiver // we're directly above it
|
||||||
xDifference == 1f -> hasBottomRightRiver // we're to the top-left of it
|
8 -> hasBottomLeftRiver // we're to the top-left of it
|
||||||
yDifference == 1f -> hasBottomLeftRiver // we're to the top-right of it
|
10 -> otherTile.hasBottomRightRiver // we're to the bottom-right of it
|
||||||
else -> otherTile.isConnectedByRiver(this) // we're below it, check the other tile
|
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()
|
constructor()
|
||||||
|
|
||||||
/** generates an hexagonal map of given radius */
|
/** generates an hexagonal map of given radius */
|
||||||
constructor(radius: Int, ruleset: Ruleset) {
|
constructor(radius: Int, ruleset: Ruleset, worldWrap: Boolean = false) {
|
||||||
for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius))
|
for (vector in HexMath.getVectorsInDistance(Vector2.Zero, radius, worldWrap))
|
||||||
tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland })
|
tileList.add(TileInfo().apply { position = vector; baseTerrain = Constants.grassland })
|
||||||
setTransients(ruleset)
|
setTransients(ruleset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** generates a rectangular map of given width and height*/
|
/** generates a rectangular map of given width and height*/
|
||||||
constructor(width: Int, height: Int, ruleset: Ruleset) {
|
constructor(width: Int, height: Int, ruleset: Ruleset, worldWrap: Boolean = false) {
|
||||||
for (x in -width / 2..width / 2)
|
val halfway = if(worldWrap) width/2-1 else width/2
|
||||||
|
for (x in -width / 2 .. halfway)
|
||||||
for (y in -height / 2..height / 2)
|
for (y in -height / 2..height / 2)
|
||||||
tileList.add(TileInfo().apply {
|
tileList.add(TileInfo().apply {
|
||||||
position = HexMath.evenQ2HexCoords(Vector2(x.toFloat(), y.toFloat()))
|
position = HexMath.evenQ2HexCoords(Vector2(x.toFloat(), y.toFloat()))
|
||||||
@ -99,8 +100,6 @@ class TileMap {
|
|||||||
sequenceOf(get(origin))
|
sequenceOf(get(origin))
|
||||||
else
|
else
|
||||||
sequence {
|
sequence {
|
||||||
fun getIfTileExistsOrNull(x: Int, y: Int) = if (contains(x, y)) get(x, y) else null
|
|
||||||
|
|
||||||
val centerX = origin.x.toInt()
|
val centerX = origin.x.toInt()
|
||||||
val centerY = origin.y.toInt()
|
val centerY = origin.y.toInt()
|
||||||
|
|
||||||
@ -129,6 +128,30 @@ class TileMap {
|
|||||||
}
|
}
|
||||||
}.filterNotNull()
|
}.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]
|
/** Tries to place the [unitName] into the [TileInfo] closest to the given the [position]
|
||||||
* @param civInfo civilization to assign unit to
|
* @param civInfo civilization to assign unit to
|
||||||
* @return created [MapUnit] or null if no suitable location was found
|
* @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)
|
val unit = gameInfo.ruleSet.units[unitName]!!.getMapUnit(gameInfo.ruleSet)
|
||||||
|
|
||||||
fun getPassableNeighbours(tileInfo: TileInfo): Set<TileInfo> =
|
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
|
// both the civ name and actual civ need to be in here in order to calculate the canMoveTo...Darn
|
||||||
unit.assignOwner(civInfo, false)
|
unit.assignOwner(civInfo, false)
|
||||||
@ -293,4 +316,31 @@ class TileMap {
|
|||||||
tileInfo.setUnitTransients(setUnitCivTransients)
|
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) {
|
if (mapParameters.shape == MapShape.rectangular) {
|
||||||
val size = HexMath.getEquivalentRectangularSize(mapRadius)
|
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
|
else
|
||||||
map = TileMap(mapRadius, ruleset)
|
map = TileMap(mapRadius, ruleset, mapParameters.worldWrap)
|
||||||
|
|
||||||
map.mapParameters = mapParameters
|
map.mapParameters = mapParameters
|
||||||
map.mapParameters.seed = seed
|
map.mapParameters.seed = seed
|
||||||
|
@ -21,6 +21,7 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
|||||||
lateinit var mapTypeSelectBox: TranslatedSelectBox
|
lateinit var mapTypeSelectBox: TranslatedSelectBox
|
||||||
lateinit var noRuinsCheckbox: CheckBox
|
lateinit var noRuinsCheckbox: CheckBox
|
||||||
lateinit var noNaturalWondersCheckbox: CheckBox
|
lateinit var noNaturalWondersCheckbox: CheckBox
|
||||||
|
lateinit var worldWrapCheckbox: CheckBox
|
||||||
|
|
||||||
init {
|
init {
|
||||||
skin = CameraStageBaseScreen.skin
|
skin = CameraStageBaseScreen.skin
|
||||||
@ -30,6 +31,8 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
|||||||
addWorldSizeSelectBox()
|
addWorldSizeSelectBox()
|
||||||
addNoRuinsCheckbox()
|
addNoRuinsCheckbox()
|
||||||
addNoNaturalWondersCheckbox()
|
addNoNaturalWondersCheckbox()
|
||||||
|
//uncomment when whole worldWrap feature is added
|
||||||
|
//addWorldWrapCheckbox()
|
||||||
addAdvancedSettings()
|
addAdvancedSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +108,15 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
|||||||
add(noNaturalWondersCheckbox).colspan(2).row()
|
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() {
|
private fun addAdvancedSettings() {
|
||||||
|
|
||||||
val advancedSettingsTable = getAdvancedSettingsTable()
|
val advancedSettingsTable = getAdvancedSettingsTable()
|
||||||
|
@ -45,6 +45,7 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (Gdx.app.type == Application.ApplicationType.Desktop) this.setFlingTime(0f)
|
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
|
// 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