mirror of
https://github.com/yairm210/Unciv.git
synced 2025-03-03 22:22:51 +07:00
Dynamic minimap (#8794)
* Dynamic minimap * Fixed minimap size * Fix for rectangular maps * Fix minimap for spectator * Proper fix for spectator * Resizing the game window no longer breaks the minimap * Implemented the camera rectangle + Explored region more accurate positioning * ExploredRectangle is calculated only after expanding the region
This commit is contained in:
parent
0d12fc7dfc
commit
6d72c7b85f
@ -1,5 +1,6 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.badlogic.gdx.math.Rectangle
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||
import com.unciv.logic.map.HexMath.getLatitude
|
||||
@ -9,21 +10,35 @@ import com.unciv.logic.map.MapParameters
|
||||
import com.unciv.logic.map.MapShape
|
||||
import com.unciv.ui.components.tilegroups.TileGroupMap
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class ExploredRegion () : IsPartOfGameInfoSerialization {
|
||||
|
||||
@Transient
|
||||
private var isWorldWrap = false
|
||||
private var worldWrap = false
|
||||
|
||||
@Transient
|
||||
private var evenMapWidth = false
|
||||
|
||||
@Transient
|
||||
private var rectangularMap = false
|
||||
|
||||
@Transient
|
||||
private var mapRadius = 0f
|
||||
|
||||
@Transient
|
||||
private val tileRadius = (TileGroupMap.groupSize + 4) * 0.75f
|
||||
private val tileRadius = TileGroupMap.groupSize * 0.8f
|
||||
|
||||
@Transient
|
||||
private var shouldRecalculateCoords = true
|
||||
|
||||
@Transient
|
||||
private var shouldUpdateMinimap = true
|
||||
|
||||
// Rectangle for positioning the camera viewport on the minimap
|
||||
@Transient
|
||||
private val exploredRectangle = Rectangle()
|
||||
|
||||
@Transient
|
||||
private var shouldRestrictX = false
|
||||
|
||||
@ -43,6 +58,8 @@ class ExploredRegion () : IsPartOfGameInfoSerialization {
|
||||
|
||||
// Getters
|
||||
fun shouldRecalculateCoords(): Boolean = shouldRecalculateCoords
|
||||
fun shouldUpdateMinimap(): Boolean = shouldUpdateMinimap
|
||||
fun getRectangle(): Rectangle = exploredRectangle
|
||||
fun shouldRestrictX(): Boolean = shouldRestrictX
|
||||
fun getLeftX(): Float = topLeftStage.x
|
||||
fun getRightX():Float = bottomRightStage.x
|
||||
@ -57,16 +74,21 @@ class ExploredRegion () : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
|
||||
fun setMapParameters(mapParameters: MapParameters) {
|
||||
isWorldWrap = mapParameters.worldWrap
|
||||
this.worldWrap = mapParameters.worldWrap
|
||||
evenMapWidth = worldWrap
|
||||
|
||||
if (mapParameters.shape == MapShape.rectangular)
|
||||
if (mapParameters.shape == MapShape.rectangular) {
|
||||
mapRadius = (mapParameters.mapSize.width / 2).toFloat()
|
||||
evenMapWidth = mapParameters.mapSize.width % 2 == 0 || evenMapWidth
|
||||
rectangularMap = true
|
||||
}
|
||||
else
|
||||
mapRadius = mapParameters.mapSize.radius.toFloat()
|
||||
}
|
||||
|
||||
// Check if tilePosition is beyond explored region
|
||||
fun checkTilePosition(tilePosition: Vector2, explorerPosition: Vector2?) {
|
||||
var mapExplored = false
|
||||
var longitude = getLongitude(tilePosition)
|
||||
val latitude = getLatitude(tilePosition)
|
||||
|
||||
@ -81,14 +103,14 @@ class ExploredRegion () : IsPartOfGameInfoSerialization {
|
||||
if (topLeft.x >= bottomRight.x) {
|
||||
if (longitude > topLeft.x) {
|
||||
// For world wrap maps when the maximumX is reached, we move to a minimumX - 1f
|
||||
if (isWorldWrap && longitude == mapRadius) longitude = mapRadius * -1f
|
||||
if (worldWrap && longitude == mapRadius) longitude = mapRadius * -1f
|
||||
topLeft.x = longitude
|
||||
shouldRecalculateCoords = true
|
||||
mapExplored = true
|
||||
} else if (longitude < bottomRight.x) {
|
||||
// For world wrap maps when the minimumX is reached, we move to a maximumX + 1f
|
||||
if (isWorldWrap && longitude == (mapRadius * -1f + 1f)) longitude = mapRadius + 1f
|
||||
if (worldWrap && longitude == (mapRadius * -1f + 1f)) longitude = mapRadius + 1f
|
||||
bottomRight.x = longitude
|
||||
shouldRecalculateCoords = true
|
||||
mapExplored = true
|
||||
}
|
||||
} else {
|
||||
// When we cross the map edge with world wrap, the vectors are swapped along the x-axis
|
||||
@ -125,17 +147,22 @@ class ExploredRegion () : IsPartOfGameInfoSerialization {
|
||||
else
|
||||
bottomRight.x = longitude
|
||||
|
||||
shouldRecalculateCoords = true
|
||||
mapExplored = true
|
||||
}
|
||||
}
|
||||
|
||||
// Check Y coord
|
||||
if (latitude > topLeft.y) {
|
||||
topLeft.y = latitude
|
||||
shouldRecalculateCoords = true
|
||||
mapExplored = true
|
||||
} else if (latitude < bottomRight.y) {
|
||||
bottomRight.y = latitude
|
||||
mapExplored = true
|
||||
}
|
||||
|
||||
if(mapExplored){
|
||||
shouldRecalculateCoords = true
|
||||
shouldUpdateMinimap = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +177,7 @@ class ExploredRegion () : IsPartOfGameInfoSerialization {
|
||||
val bottomRightWorld = worldFromLatLong(bottomRight, tileRadius)
|
||||
|
||||
// Convert X to the stage coords
|
||||
val mapCenterX = if (isWorldWrap) mapMaxX * 0.5f + tileRadius else mapMaxX * 0.5f
|
||||
val mapCenterX = if (evenMapWidth) (mapMaxX + TileGroupMap.groupSize + 4f) * 0.5f else mapMaxX * 0.5f
|
||||
var left = mapCenterX + topLeftWorld.x
|
||||
var right = mapCenterX + bottomRightWorld.x
|
||||
|
||||
@ -159,11 +186,41 @@ class ExploredRegion () : IsPartOfGameInfoSerialization {
|
||||
if (right < 0f) right = mapMaxX - 10f
|
||||
|
||||
// Convert Y to the stage coords
|
||||
val mapCenterY = mapMaxY * 0.5f
|
||||
val mapCenterY = if (rectangularMap) mapMaxY * 0.5f + TileGroupMap.groupSize * 0.25f else mapMaxY * 0.5f
|
||||
val top = mapCenterY-topLeftWorld.y
|
||||
val bottom = mapCenterY-bottomRightWorld.y
|
||||
|
||||
topLeftStage = Vector2(left, top)
|
||||
bottomRightStage = Vector2(right, bottom)
|
||||
|
||||
// Calculate rectangle for positioning the camera viewport on the minimap
|
||||
val yOffset = tileRadius * sqrt(3f) * 0.5f
|
||||
exploredRectangle.x = left - tileRadius
|
||||
exploredRectangle.y = mapMaxY - bottom - yOffset * 0.5f
|
||||
exploredRectangle.width = getWidth() * tileRadius * 1.5f
|
||||
exploredRectangle.height = getHeight() * yOffset
|
||||
}
|
||||
|
||||
fun isPositionInRegion(postition: Vector2): Boolean {
|
||||
val long = getLongitude(postition)
|
||||
val lat = getLatitude(postition)
|
||||
return if (topLeft.x > bottomRight.x)
|
||||
(long <= topLeft.x && long >= bottomRight.x && lat <= topLeft.y && lat >= bottomRight.y)
|
||||
else
|
||||
(((long >= topLeft.x && long >= bottomRight.x) || (long <= topLeft.x && long <= bottomRight.x)) && lat <= topLeft.y && lat >= bottomRight.y)
|
||||
}
|
||||
|
||||
fun getWidth(): Int {
|
||||
val result: Float
|
||||
if (topLeft.x > bottomRight.x) result = topLeft.x - bottomRight.x
|
||||
else result = mapRadius * 2f - (bottomRight.x - topLeft.x)
|
||||
return result.toInt() + 1
|
||||
}
|
||||
|
||||
fun getHeight(): Int = (topLeft.y - bottomRight.y).toInt() + 1
|
||||
|
||||
fun getMinimapLeft(tileSize: Float): Float {
|
||||
shouldUpdateMinimap = false
|
||||
return (topLeft.x + 1f) * tileSize * -0.75f
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,10 @@ class TileGroupMap<T: TileGroup>(
|
||||
/** Vertical size of a hex in world coordinates, or the distance between the centers of any two opposing edges
|
||||
* (the hex is oriented so it has corners to the left and right of the center and its upper and lower bounds are horizontal edges) */
|
||||
const val groupSize = 50f
|
||||
|
||||
/** Length of the diagonal of a hex, or distance between two opposing corners */
|
||||
const val groupSizeDiagonal = groupSize * 1.1547005f // groupSize * sqrt(4/3)
|
||||
|
||||
/** Horizontal displacement per hex, meaning the increase in overall map size (in world coordinates) when adding a column.
|
||||
* On the hex, this can be visualized as the horizontal distance between the leftmost corner and the
|
||||
* line connecting the two corners at 2 and 4 o'clock. */
|
||||
@ -74,21 +76,19 @@ class TileGroupMap<T: TileGroup>(
|
||||
HexMath.hex2WorldCoords(tileGroup.tile.position)
|
||||
}
|
||||
|
||||
tileGroup.setPosition(positionalVector.x * 0.8f * groupSize,
|
||||
positionalVector.y * 0.8f * groupSize
|
||||
tileGroup.setPosition(
|
||||
positionalVector.x * 0.8f * groupSize,
|
||||
positionalVector.y * 0.8f * groupSize
|
||||
)
|
||||
|
||||
topX =
|
||||
if (worldWrap)
|
||||
if (worldWrap)
|
||||
// Well it's not pretty but it works
|
||||
// This is so topX is the same no matter what worldWrap is
|
||||
// wrapped worlds are missing one tile width on the right side
|
||||
// which would result in a smaller topX
|
||||
// The resulting topX was always missing 1.2 * groupSize in every possible
|
||||
// combination of map size and shape
|
||||
max(topX, tileGroup.x + groupSize * 2.2f)
|
||||
else
|
||||
max(topX, tileGroup.x + groupSize)
|
||||
max(topX, tileGroup.x + groupSize * 1.2f)
|
||||
else
|
||||
max(topX, tileGroup.x + groupSize + 4f)
|
||||
|
||||
topY = max(topY, tileGroup.y + groupSize)
|
||||
bottomX = min(bottomX, tileGroup.x)
|
||||
@ -114,14 +114,14 @@ class TileGroupMap<T: TileGroup>(
|
||||
// Apparently the sortedByDescending is kinda memory-intensive because it needs to sort ALL the tiles
|
||||
for (group in tileGroups.sortedByDescending { it.tile.position.x + it.tile.position.y }) {
|
||||
// now, we steal the subgroups from all the tilegroups, that's how we form layers!
|
||||
baseLayers.add(group.layerTerrain.apply { setPosition(group.x,group.y) })
|
||||
featureLayers.add(group.layerFeatures.apply { setPosition(group.x,group.y) })
|
||||
borderLayers.add(group.layerBorders.apply { setPosition(group.x,group.y) })
|
||||
miscLayers.add(group.layerMisc.apply { setPosition(group.x,group.y) })
|
||||
pixelUnitLayers.add(group.layerUnitArt.apply { setPosition(group.x,group.y) })
|
||||
circleFogCrosshairLayers.add(group.layerOverlay.apply { setPosition(group.x,group.y) })
|
||||
unitLayers.add(group.layerUnitFlag.apply { setPosition(group.x,group.y) })
|
||||
cityButtonLayers.add(group.layerCityButton.apply { setPosition(group.x,group.y) })
|
||||
baseLayers.add(group.layerTerrain.apply { setPosition(group.x, group.y) })
|
||||
featureLayers.add(group.layerFeatures.apply { setPosition(group.x, group.y) })
|
||||
borderLayers.add(group.layerBorders.apply { setPosition(group.x, group.y) })
|
||||
miscLayers.add(group.layerMisc.apply { setPosition(group.x, group.y) })
|
||||
pixelUnitLayers.add(group.layerUnitArt.apply { setPosition(group.x, group.y) })
|
||||
circleFogCrosshairLayers.add(group.layerOverlay.apply { setPosition(group.x, group.y) })
|
||||
unitLayers.add(group.layerUnitFlag.apply { setPosition(group.x, group.y) })
|
||||
cityButtonLayers.add(group.layerCityButton.apply { setPosition(group.x, group.y) })
|
||||
}
|
||||
|
||||
for (group in baseLayers) addActor(group)
|
||||
@ -136,11 +136,7 @@ class TileGroupMap<T: TileGroup>(
|
||||
|
||||
// there are tiles "below the zero",
|
||||
// so we zero out the starting position of the whole board so they will be displayed as well
|
||||
// Map's width is reduced by groupSize if it is wrapped, because wrapped map will miss a tile on the right.
|
||||
// This ensures that wrapped maps have a smooth transition.
|
||||
// If map is not wrapped, Map's width doesn't need to be reduce by groupSize
|
||||
if (worldWrap) setSize(topX - bottomX - groupSize, topY - bottomY)
|
||||
else setSize(topX - bottomX, topY - bottomY)
|
||||
setSize(topX - bottomX, topY - bottomY)
|
||||
|
||||
cullingArea = Rectangle(0f, 0f, width, height)
|
||||
|
||||
@ -153,9 +149,9 @@ class TileGroupMap<T: TileGroup>(
|
||||
fun getPositionalVector(stageCoords: Vector2): Vector2 {
|
||||
val trueGroupSize = 0.8f * groupSize
|
||||
return Vector2(bottomX, bottomY)
|
||||
.add(stageCoords)
|
||||
.sub(groupSize / 2f, groupSize / 2f)
|
||||
.scl(1f / trueGroupSize)
|
||||
.add(stageCoords)
|
||||
.sub(groupSize / 2f, groupSize / 2f)
|
||||
.scl(1f / trueGroupSize)
|
||||
}
|
||||
|
||||
override fun act(delta: Float) {
|
||||
@ -173,8 +169,9 @@ class TileGroupMap<T: TileGroup>(
|
||||
|
||||
if (worldWrap) {
|
||||
// Prevent flickering when zoomed out so you can see entire map
|
||||
val visibleMapWidth = if (mapHolder.width > maxVisibleMapWidth) maxVisibleMapWidth
|
||||
else mapHolder.width
|
||||
val visibleMapWidth =
|
||||
if (mapHolder.width > maxVisibleMapWidth) maxVisibleMapWidth
|
||||
else mapHolder.width
|
||||
|
||||
// Where is viewport's boundaries
|
||||
val rightSide = mapHolder.scrollX + visibleMapWidth / 2f
|
||||
@ -202,11 +199,11 @@ class TileGroupMap<T: TileGroup>(
|
||||
it.x += width
|
||||
} else if (beyondLeft) {
|
||||
// Move from right to left
|
||||
if (it.x + groupSize >= drawTopX + diffLeft)
|
||||
if (it.x + groupSize + 4f >= drawTopX + diffLeft)
|
||||
it.x -= width
|
||||
}
|
||||
newBottomX = min(newBottomX, it.x)
|
||||
newTopX = max(newTopX, it.x + groupSize)
|
||||
newTopX = max(newTopX, it.x + groupSize + 4f)
|
||||
}
|
||||
|
||||
drawBottomX = newBottomX
|
||||
@ -215,5 +212,4 @@ class TileGroupMap<T: TileGroup>(
|
||||
}
|
||||
super.draw(batch, parentAlpha)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -381,6 +381,9 @@ class WorldScreen(
|
||||
if(uiEnabled){
|
||||
displayTutorialsOnUpdate()
|
||||
|
||||
if (fogOfWar) minimapWrapper.update(selectedCiv)
|
||||
else minimapWrapper.update(viewingCiv)
|
||||
|
||||
bottomUnitTable.update()
|
||||
bottomTileInfoTable.updateTileTable(mapHolder.selectedTile)
|
||||
bottomTileInfoTable.x = stage.width - bottomTileInfoTable.width
|
||||
@ -391,9 +394,6 @@ class WorldScreen(
|
||||
|
||||
displayTutorialTaskOnUpdate()
|
||||
|
||||
if (fogOfWar) minimapWrapper.update(selectedCiv)
|
||||
else minimapWrapper.update(viewingCiv)
|
||||
|
||||
unitActionsTable.update(bottomUnitTable.selectedUnit)
|
||||
unitActionsTable.y = bottomUnitTable.height
|
||||
}
|
||||
|
@ -16,23 +16,33 @@ import com.unciv.ui.components.extensions.*
|
||||
import com.unciv.ui.screens.worldscreen.WorldMapHolder
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int) : Group() {
|
||||
class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int, private val civInfo: Civilization?) : Group() {
|
||||
private val tileLayer = Group()
|
||||
private val minimapTiles: List<MinimapTile>
|
||||
private val scrollPositionIndicators: List<ClippingImage>
|
||||
private var lastViewingCiv: Civilization? = null
|
||||
|
||||
private var tileSize = 0f
|
||||
private var tileMapWidth = 0f
|
||||
private var tileMapHeight = 0f
|
||||
|
||||
init {
|
||||
// don't try to resize rotate etc - this table has a LOT of children so that's valuable render time!
|
||||
isTransform = false
|
||||
|
||||
var topX = 0f
|
||||
var topY = 0f
|
||||
var bottomX = 0f
|
||||
var bottomY = 0f
|
||||
var topX = -Float.MAX_VALUE
|
||||
var topY = -Float.MAX_VALUE
|
||||
var bottomX = Float.MAX_VALUE
|
||||
var bottomY = Float.MAX_VALUE
|
||||
|
||||
val tileSize = calcTileSize(minimapSize)
|
||||
// Set fixed minimap size
|
||||
val stageMinimapSize = calcMinimapSize(minimapSize)
|
||||
setSize(stageMinimapSize.x, stageMinimapSize.y)
|
||||
|
||||
// Calculate max tileSize to fit in mimimap
|
||||
tileSize = calcTileSize(stageMinimapSize)
|
||||
minimapTiles = createMinimapTiles(tileSize)
|
||||
for (image in minimapTiles.map { it.image }) {
|
||||
tileLayer.addActor(image)
|
||||
@ -44,37 +54,98 @@ class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int) : Group() {
|
||||
bottomY = min(bottomY, image.y)
|
||||
}
|
||||
|
||||
for (group in tileLayer.children) {
|
||||
group.moveBy(-bottomX, -bottomY)
|
||||
}
|
||||
|
||||
// there are tiles "below the zero",
|
||||
// so we zero out the starting position of the whole board so they will be displayed as well
|
||||
tileLayer.setSize(topX - bottomX, topY - bottomY)
|
||||
tileLayer.setSize(width, height)
|
||||
|
||||
// Center tiles in minimap holder
|
||||
tileMapWidth = topX - bottomX
|
||||
tileMapHeight = topY - bottomY
|
||||
val padX = (stageMinimapSize.x - tileMapWidth) * 0.5f - bottomX
|
||||
val padY = (stageMinimapSize.y - tileMapHeight) * 0.5f - bottomY
|
||||
for (group in tileLayer.children) {
|
||||
group.moveBy(padX, padY)
|
||||
}
|
||||
|
||||
scrollPositionIndicators = createScrollPositionIndicators()
|
||||
scrollPositionIndicators.forEach(tileLayer::addActor)
|
||||
|
||||
setSize(tileLayer.width, tileLayer.height)
|
||||
addActor(tileLayer)
|
||||
|
||||
mapHolder.onViewportChangedListener = ::updateScrollPosition
|
||||
}
|
||||
|
||||
private fun calcTileSize(minimapSize: Int): Float {
|
||||
// Support rectangular maps with extreme aspect ratios by scaling to the larger coordinate with a slight weighting to make the bounding box 4:3
|
||||
val effectiveRadius = with(mapHolder.tileMap.mapParameters) {
|
||||
if (shape != MapShape.rectangular) mapSize.radius
|
||||
else max(
|
||||
mapSize.height,
|
||||
mapSize.width * 3 / 4
|
||||
) * MapSize.Huge.radius / MapSize.Huge.height
|
||||
private fun calcTileSize(minimapSize: Vector2): Float {
|
||||
val height: Float
|
||||
val width: Float
|
||||
val mapParameters = mapHolder.tileMap.mapParameters
|
||||
|
||||
if (civInfo != null) {
|
||||
height = civInfo.exploredRegion.getHeight().toFloat()
|
||||
width = civInfo.exploredRegion.getWidth().toFloat()
|
||||
} else {
|
||||
if (mapParameters.shape != MapShape.rectangular) {
|
||||
val diameter = mapParameters.mapSize.radius * 2f + 1f
|
||||
height = diameter.toFloat()
|
||||
width = diameter.toFloat()
|
||||
} else {
|
||||
height = mapParameters.mapSize.height.toFloat()
|
||||
width = mapParameters.mapSize.width.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
val result =
|
||||
min(
|
||||
minimapSize.y / (height + 1.5f) / sqrt(3f) * 4f, // 1.5 - padding, hex height = sqrt(3) / 2 * d / 2 -> d = height / sqrt(3) * 2 * 2
|
||||
minimapSize.x / (width + 0.5f) / 0.75f // 0.5 - padding, hex width = 0.75 * d -> d = width / 0.75
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun calcMinTileSize(minimapSize: Int): Float {
|
||||
// Support rectangular maps with extreme aspect ratios by scaling to the larger coordinate with a slight weighting to make the bounding box 4:3
|
||||
val effectiveRadius =
|
||||
with(mapHolder.tileMap.mapParameters) {
|
||||
if (shape != MapShape.rectangular) mapSize.radius
|
||||
else max(
|
||||
mapSize.height,
|
||||
mapSize.width * 3 / 4
|
||||
) * MapSize.Huge.radius / MapSize.Huge.height
|
||||
}
|
||||
|
||||
val mapSizePercent = if (minimapSize < 22) minimapSize + 9 else minimapSize * 5 - 75
|
||||
val smallerWorldDimension = mapHolder.worldScreen.stage.let { min(it.width, it.height) }
|
||||
return smallerWorldDimension * mapSizePercent / 100 / effectiveRadius
|
||||
}
|
||||
|
||||
private fun calcMinimapSize(minimapSize: Int): Vector2 {
|
||||
val minimapTileSize = calcMinTileSize(minimapSize)
|
||||
var height: Float
|
||||
var width: Float
|
||||
val mapParameters = mapHolder.tileMap.mapParameters
|
||||
|
||||
if (mapParameters.shape != MapShape.rectangular) {
|
||||
val diameter = mapParameters.mapSize.radius * 2f + 1f
|
||||
height = diameter
|
||||
width = diameter
|
||||
} else {
|
||||
height = mapParameters.mapSize.height.toFloat()
|
||||
width = mapParameters.mapSize.width.toFloat()
|
||||
}
|
||||
|
||||
// hex height = sqrt(3) / 2 * d / 2, number of rows = mapDiameter * 2
|
||||
height *= minimapTileSize * sqrt(3f) * 0.5f
|
||||
// hex width = 0.75 * d
|
||||
width =
|
||||
if (mapParameters.worldWrap)
|
||||
(width - 1f) * minimapTileSize * 0.75f
|
||||
else
|
||||
width * minimapTileSize * 0.75f
|
||||
|
||||
return Vector2(width, height)
|
||||
}
|
||||
|
||||
private fun createScrollPositionIndicators(): List<ClippingImage> {
|
||||
// If we are continuous scrolling (world wrap), add another 2 scrollPositionIndicators which
|
||||
// get drawn at proper offsets to simulate looping
|
||||
@ -89,10 +160,19 @@ class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int) : Group() {
|
||||
|
||||
private fun createMinimapTiles(tileSize: Float): List<MinimapTile> {
|
||||
val tiles = ArrayList<MinimapTile>()
|
||||
val pad = if (mapHolder.tileMap.mapParameters.shape != MapShape.rectangular)
|
||||
mapHolder.tileMap.mapParameters.mapSize.radius * tileSize * 1.5f
|
||||
else
|
||||
(mapHolder.tileMap.mapParameters.mapSize.width - 1f) * tileSize * 0.75f
|
||||
val leftSide =
|
||||
if (civInfo != null) civInfo.exploredRegion.getMinimapLeft(tileSize) else -Float.MAX_VALUE
|
||||
for (tileInfo in mapHolder.tileMap.values) {
|
||||
if (civInfo?.exploredRegion?.isPositionInRegion(tileInfo.position) == false) continue
|
||||
val minimapTile = MinimapTile(tileInfo, tileSize, onClick = {
|
||||
mapHolder.setCenterPosition(tileInfo.position)
|
||||
})
|
||||
if (minimapTile.image.x < leftSide)
|
||||
minimapTile.image.x += pad
|
||||
tiles.add(minimapTile)
|
||||
}
|
||||
return tiles
|
||||
@ -102,8 +182,14 @@ class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int) : Group() {
|
||||
*
|
||||
* Requires [scrollPositionIndicator] to be a [ClippingImage] to keep the displayed portion of the indicator within the bounds of the minimap.
|
||||
*/
|
||||
private fun updateScrollPosition(worldWidth: Float, worldHeight: Float, worldViewport: Rectangle) {
|
||||
operator fun Rectangle.times(other: Vector2) = Rectangle(x * other.x, y * other.y, width * other.x, height * other.y)
|
||||
private fun updateScrollPosition(
|
||||
worldWidth: Float,
|
||||
worldHeight: Float,
|
||||
worldViewport: Rectangle
|
||||
) {
|
||||
operator fun Rectangle.times(other: Vector2) =
|
||||
Rectangle(x * other.x, y * other.y, width * other.x, height * other.y)
|
||||
|
||||
fun Actor.setViewport(rect: Rectangle) {
|
||||
x = rect.x;
|
||||
y = rect.y;
|
||||
@ -111,16 +197,38 @@ class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int) : Group() {
|
||||
height = rect.height
|
||||
}
|
||||
|
||||
val worldToMiniFactor = Vector2(tileLayer.width / worldWidth, tileLayer.height / worldHeight)
|
||||
val miniViewport = worldViewport * worldToMiniFactor
|
||||
val worldToMiniFactor: Vector2
|
||||
var miniViewport = worldViewport
|
||||
|
||||
if (civInfo != null) {
|
||||
if (civInfo.exploredRegion.shouldRecalculateCoords()) civInfo.exploredRegion.calculateStageCoords(
|
||||
worldWidth,
|
||||
worldHeight
|
||||
)
|
||||
|
||||
val exploredRectangle = civInfo.exploredRegion.getRectangle()
|
||||
worldToMiniFactor = Vector2(
|
||||
tileMapWidth / exploredRectangle.width,
|
||||
tileMapHeight / exploredRectangle.height
|
||||
)
|
||||
miniViewport.x -= exploredRectangle.x
|
||||
miniViewport.y -= exploredRectangle.y
|
||||
} else
|
||||
worldToMiniFactor =
|
||||
Vector2(tileLayer.width / worldWidth, tileLayer.height / worldHeight)
|
||||
|
||||
miniViewport *= worldToMiniFactor
|
||||
miniViewport.x += (tileLayer.width - tileMapWidth) * 0.5f
|
||||
miniViewport.y += (tileLayer.height - tileMapHeight) * 0.5f
|
||||
// This _could_ place parts of the 'camera' icon outside the minimap if it were a standard Image, thus the ClippingImage helper class
|
||||
scrollPositionIndicators[0].setViewport(miniViewport)
|
||||
|
||||
// If world wrap enabled, draw another 2 viewports at proper offset to simulate wrapping
|
||||
if (scrollPositionIndicators.size != 1) {
|
||||
miniViewport.x -= tileLayer.width
|
||||
val offset = worldWidth * worldToMiniFactor.x
|
||||
miniViewport.x -= offset
|
||||
scrollPositionIndicators[1].setViewport(miniViewport)
|
||||
miniViewport.x += tileLayer.width * 2
|
||||
miniViewport.x += offset * 2f
|
||||
scrollPositionIndicators[2].setViewport(miniViewport)
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.Batch
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.GUI
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
@ -43,20 +44,19 @@ class MinimapHolder(val mapHolder: WorldMapHolder) : Table() {
|
||||
backgroundColor = Color.GREEN
|
||||
)
|
||||
|
||||
init {
|
||||
rebuildIfSizeChanged()
|
||||
}
|
||||
|
||||
private fun rebuildIfSizeChanged() {
|
||||
private fun rebuildIfSizeChanged(civInfo: Civilization) {
|
||||
// For Spectator should not restrict minimap
|
||||
var civInfo: Civilization? = civInfo
|
||||
if(GUI.getViewingPlayer().isSpectator()) civInfo = null
|
||||
val newMinimapSize = worldScreen.game.settings.minimapSize
|
||||
if (newMinimapSize == minimapSize) return
|
||||
if (newMinimapSize == minimapSize && civInfo?.exploredRegion?.shouldUpdateMinimap() != true) return
|
||||
minimapSize = newMinimapSize
|
||||
rebuild()
|
||||
rebuild(civInfo)
|
||||
}
|
||||
|
||||
private fun rebuild(){
|
||||
private fun rebuild(civInfo: Civilization?){
|
||||
this.clear()
|
||||
minimap = Minimap(mapHolder, minimapSize)
|
||||
minimap = Minimap(mapHolder, minimapSize, civInfo)
|
||||
add(getToggleIcons()).align(Align.bottom)
|
||||
add(getWrappedMinimap())
|
||||
pack()
|
||||
@ -97,7 +97,7 @@ class MinimapHolder(val mapHolder: WorldMapHolder) : Table() {
|
||||
}
|
||||
|
||||
fun update(civInfo: Civilization) {
|
||||
rebuildIfSizeChanged()
|
||||
rebuildIfSizeChanged(civInfo)
|
||||
isVisible = UncivGame.Current.settings.showMinimap
|
||||
if (isVisible) {
|
||||
minimap.update(civInfo)
|
||||
|
Loading…
Reference in New Issue
Block a user