MainMenu Background Map scaled to Screen and minor MapGen fixes (#7000)

* MapGenerator fixes

* MainMenu Background Map scaled to Screen

* MainMenu Background Map scaled to Screen - groupSize as public const
This commit is contained in:
SomeTroglodyte 2022-06-02 07:57:33 +02:00 committed by GitHub
parent 56f11ccdca
commit cdeabd044d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 36 deletions

View File

@ -10,7 +10,6 @@ import com.unciv.logic.GameInfo
import com.unciv.logic.GameStarter import com.unciv.logic.GameStarter
import com.unciv.logic.map.MapParameters import com.unciv.logic.map.MapParameters
import com.unciv.logic.map.MapShape import com.unciv.logic.map.MapShape
import com.unciv.logic.map.MapSize
import com.unciv.logic.map.MapSizeNew import com.unciv.logic.map.MapSizeNew
import com.unciv.logic.map.MapType import com.unciv.logic.map.MapType
import com.unciv.logic.map.mapgenerator.MapGenerator import com.unciv.logic.map.mapgenerator.MapGenerator
@ -23,6 +22,7 @@ import com.unciv.ui.civilopedia.CivilopediaScreen
import com.unciv.ui.crashhandling.launchCrashHandling import com.unciv.ui.crashhandling.launchCrashHandling
import com.unciv.ui.crashhandling.postCrashHandlingRunnable import com.unciv.ui.crashhandling.postCrashHandlingRunnable
import com.unciv.ui.images.ImageGetter import com.unciv.ui.images.ImageGetter
import com.unciv.ui.map.TileGroupMap
import com.unciv.ui.newgamescreen.NewGameScreen import com.unciv.ui.newgamescreen.NewGameScreen
import com.unciv.ui.pickerscreens.ModManagementScreen import com.unciv.ui.pickerscreens.ModManagementScreen
import com.unciv.ui.popup.* import com.unciv.ui.popup.*
@ -31,6 +31,8 @@ import com.unciv.ui.saves.QuickSave
import com.unciv.ui.utils.* import com.unciv.ui.utils.*
import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip import com.unciv.ui.utils.UncivTooltip.Companion.addTooltip
import com.unciv.ui.worldscreen.mainmenu.WorldScreenMenuPopup import com.unciv.ui.worldscreen.mainmenu.WorldScreenMenuPopup
import kotlin.math.min
class MainMenuScreen: BaseScreen() { class MainMenuScreen: BaseScreen() {
private val backgroundTable = Table().apply { background= ImageGetter.getBackground(Color.WHITE) } private val backgroundTable = Table().apply { background= ImageGetter.getBackground(Color.WHITE) }
@ -76,16 +78,29 @@ class MainMenuScreen: BaseScreen() {
ImageGetter.ruleset = RulesetCache.getVanillaRuleset() ImageGetter.ruleset = RulesetCache.getVanillaRuleset()
launchCrashHandling("ShowMapBackground") { launchCrashHandling("ShowMapBackground") {
val newMap = MapGenerator(RulesetCache.getVanillaRuleset()) var scale = 1f
var mapWidth = stage.width / TileGroupMap.groupHorizontalAdvance
var mapHeight = stage.height / TileGroupMap.groupSize
if (mapWidth * mapHeight > 3000f) { // 3000 as max estimated number of tiles is arbitrary (we had typically 721 before)
scale = mapWidth * mapHeight / 3000f
mapWidth /= scale
mapHeight /= scale
scale = min(scale, 20f)
}
val mapRuleset = RulesetCache.getVanillaRuleset()
val newMap = MapGenerator(mapRuleset)
.generateMap(MapParameters().apply { .generateMap(MapParameters().apply {
shape = MapShape.rectangular shape = MapShape.rectangular
mapSize = MapSizeNew(MapSize.Small) mapSize = MapSizeNew(mapWidth.toInt() + 1, mapHeight.toInt() + 1)
type = MapType.default type = MapType.default
waterThreshold = -0.055f // Gives the same level as when waterThreshold was unused in MapType.default waterThreshold = -0.055f // Gives the same level as when waterThreshold was unused in MapType.default
}) })
postCrashHandlingRunnable { // for GL context postCrashHandlingRunnable { // for GL context
ImageGetter.setNewRuleset(RulesetCache.getVanillaRuleset()) ImageGetter.setNewRuleset(mapRuleset)
val mapHolder = EditorMapHolder(this@MainMenuScreen, newMap) {} val mapHolder = EditorMapHolder(this@MainMenuScreen, newMap) {}
mapHolder.setScale(scale)
backgroundTable.addAction(Actions.sequence( backgroundTable.addAction(Actions.sequence(
Actions.fadeOut(0f), Actions.fadeOut(0f),
Actions.run { Actions.run {

View File

@ -25,6 +25,7 @@ import kotlin.math.max
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.math.sign import kotlin.math.sign
import kotlin.math.ulp
import kotlin.random.Random import kotlin.random.Random
@ -45,13 +46,26 @@ class MapGenerator(val ruleset: Ruleset) {
/** builds a [TerrainOccursRange] for [terrain] from a [unique] (type [UniqueType.TileGenerationConditions]) */ /** builds a [TerrainOccursRange] for [terrain] from a [unique] (type [UniqueType.TileGenerationConditions]) */
constructor(terrain: Terrain, unique: Unique) constructor(terrain: Terrain, unique: Unique)
: this(terrain, : this(terrain,
unique.params[0].toFloat(), unique.params[1].toFloat(), unique.params[0].toFloatMakeInclusive(-1f), unique.params[1].toFloat(),
unique.params[2].toFloat(), unique.params[3].toFloat()) unique.params[2].toFloatMakeInclusive(0f), unique.params[3].toFloat())
/** checks if both [temperature] and [humidity] satisfy their ranges (>From, <=To) */
/** Checks if both [temperature] and [humidity] satisfy their ranges (>From, <=To)
* Note the lowest allowed limit has been made inclusive (temp -1 nudged down by 1 [Float.ulp], humidity at 0)
*/
// Yes this does implicit conversions Float/Double // Yes this does implicit conversions Float/Double
fun matches(temperature: Double, humidity: Double) = fun matches(temperature: Double, humidity: Double) =
tempFrom < temperature && temperature <= tempTo && tempFrom < temperature && temperature <= tempTo &&
humidFrom < humidity && humidity <= humidTo humidFrom < humidity && humidity <= humidTo
companion object {
/** A [toFloat] that also nudges the value slightly down if it matches [limit] to make the resulting range inclusive on the lower end */
private fun String.toFloatMakeInclusive(limit: Float): Float {
val result = toFloat()
if (result != limit) return result
return result - result.ulp
}
}
} }
private fun Terrain.getGenerationConditions() = private fun Terrain.getGenerationConditions() =
getMatchingUniques(UniqueType.TileGenerationConditions) getMatchingUniques(UniqueType.TileGenerationConditions)
@ -463,11 +477,15 @@ class MapGenerator(val ruleset: Ruleset) {
// List is OK here as it's only sequentially scanned // List is OK here as it's only sequentially scanned
val limitsMap: List<TerrainOccursRange> = val limitsMap: List<TerrainOccursRange> =
ruleset.terrains.values.flatMap { ruleset.terrains.values.filter { it.type == TerrainType.Land }
it.getGenerationConditions() .flatMap { it.getGenerationConditions() }
}
val noTerrainUniques = limitsMap.isEmpty() val noTerrainUniques = limitsMap.isEmpty()
val elevationTerrains = arrayOf(Constants.mountain, Constants.hill) val elevationTerrains = ruleset.terrains.values.asSequence()
.filter {
it.hasUnique(UniqueType.OccursInChains)
}.mapTo(mutableSetOf()) { it.name }
if (elevationTerrains.isEmpty())
elevationTerrains.add(Constants.mountain)
for (tile in tileMap.values.asSequence()) { for (tile in tileMap.values.asSequence()) {
if (tile.isWater || tile.baseTerrain in elevationTerrains) if (tile.isWater || tile.baseTerrain in elevationTerrains)
@ -526,6 +544,7 @@ class MapGenerator(val ruleset: Ruleset) {
} }
} }
} }
/** /**
* [MapParameters.rareFeaturesRichness] is the probability of spawning a rare feature * [MapParameters.rareFeaturesRichness] is the probability of spawning a rare feature
*/ */
@ -600,16 +619,21 @@ class MapGenerationRandomness {
/** /**
* Generates a perlin noise channel combining multiple octaves * Generates a perlin noise channel combining multiple octaves
* *
* [nOctaves] is the number of octaves * @param tile Source for x / x coordinates.
* [persistence] is the scaling factor of octave amplitudes * @param seed Misnomer: actually the z value the Perlin cloud is 'cut' on.
* [lacunarity] is the scaling factor of octave frequencies * @param nOctaves is the number of octaves.
* [scale] is the distance the noise is observed from * @param persistence is the scaling factor of octave amplitudes.
* @param lacunarity is the scaling factor of octave frequencies.
* @param scale is the distance the noise is observed from.
*/ */
fun getPerlinNoise(tile: TileInfo, seed: Double, fun getPerlinNoise(
nOctaves: Int = 6, tile: TileInfo,
persistence: Double = 0.5, seed: Double,
lacunarity: Double = 2.0, nOctaves: Int = 6,
scale: Double = 10.0): Double { persistence: Double = 0.5,
lacunarity: Double = 2.0,
scale: Double = 10.0
): Double {
val worldCoords = HexMath.hex2WorldCoords(tile.position) val worldCoords = HexMath.hex2WorldCoords(tile.position)
return Perlin.noise3d(worldCoords.x.toDouble(), worldCoords.y.toDouble(), seed, nOctaves, persistence, lacunarity, scale) return Perlin.noise3d(worldCoords.x.toDouble(), worldCoords.y.toDouble(), seed, nOctaves, persistence, lacunarity, scale)
} }

View File

@ -25,6 +25,21 @@ class TileGroupMap<T: TileGroup>(
worldWrap: Boolean = false, worldWrap: Boolean = false,
tileGroupsToUnwrap: Set<T>? = null tileGroupsToUnwrap: Set<T>? = null
): Group() { ): Group() {
companion object {
/** 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. */
const val groupHorizontalAdvance = groupSizeDiagonal * 3 / 4
//TODO magic numbers that **seem** like they might depend on these values can be found in
// TileGroupMap.getPositionalVector, TileGroup.updateArrows, TileGroup.updateRoadImages
// and other places. I can't understand them so I'm leaving cleanup of hardcoding to someone else.
}
/** If the [act] method should be performed. If this is false, every child within this [TileGroupMap] will not get their [act] method called /** If the [act] method should be performed. If this is false, every child within this [TileGroupMap] will not get their [act] method called
* and thus not perform any [com.badlogic.gdx.scenes.scene2d.Action]s. * and thus not perform any [com.badlogic.gdx.scenes.scene2d.Action]s.
* Most children here already do not do anything in their [act] methods. However, even iterating through all of them */ * Most children here already do not do anything in their [act] methods. However, even iterating through all of them */
@ -34,7 +49,6 @@ class TileGroupMap<T: TileGroup>(
private var topY = -Float.MAX_VALUE private var topY = -Float.MAX_VALUE
private var bottomX = Float.MAX_VALUE private var bottomX = Float.MAX_VALUE
private var bottomY = Float.MAX_VALUE private var bottomY = Float.MAX_VALUE
private val groupSize = 50
private val mirrorTileGroups = HashMap<TileInfo, Pair<T, T>>() private val mirrorTileGroups = HashMap<TileInfo, Pair<T, T>>()
init { init {
@ -54,8 +68,8 @@ class TileGroupMap<T: TileGroup>(
HexMath.hex2WorldCoords(tileGroup.tileInfo.position) HexMath.hex2WorldCoords(tileGroup.tileInfo.position)
} }
tileGroup.setPosition(positionalVector.x * 0.8f * groupSize.toFloat(), tileGroup.setPosition(positionalVector.x * 0.8f * groupSize,
positionalVector.y * 0.8f * groupSize.toFloat()) positionalVector.y * 0.8f * groupSize)
topX = topX =
if (worldWrap) if (worldWrap)
@ -82,12 +96,12 @@ class TileGroupMap<T: TileGroup>(
for (mirrorTiles in mirrorTileGroups.values){ for (mirrorTiles in mirrorTileGroups.values){
val positionalVector = HexMath.hex2WorldCoords(mirrorTiles.first.tileInfo.position) val positionalVector = HexMath.hex2WorldCoords(mirrorTiles.first.tileInfo.position)
mirrorTiles.first.setPosition(positionalVector.x * 0.8f * groupSize.toFloat(), mirrorTiles.first.setPosition(positionalVector.x * 0.8f * groupSize,
positionalVector.y * 0.8f * groupSize.toFloat()) positionalVector.y * 0.8f * groupSize)
mirrorTiles.first.moveBy(-bottomX - bottomX * 2, -bottomY ) mirrorTiles.first.moveBy(-bottomX - bottomX * 2, -bottomY )
mirrorTiles.second.setPosition(positionalVector.x * 0.8f * groupSize.toFloat(), mirrorTiles.second.setPosition(positionalVector.x * 0.8f * groupSize,
positionalVector.y * 0.8f * groupSize.toFloat()) positionalVector.y * 0.8f * groupSize)
mirrorTiles.second.moveBy(-bottomX + bottomX * 2, -bottomY) mirrorTiles.second.moveBy(-bottomX + bottomX * 2, -bottomY)
} }
} }
@ -157,21 +171,22 @@ class TileGroupMap<T: TileGroup>(
* Returns the positional coordinates of the TileGroupMap center. * Returns the positional coordinates of the TileGroupMap center.
*/ */
fun getPositionalVector(stageCoords: Vector2): Vector2 { fun getPositionalVector(stageCoords: Vector2): Vector2 {
val trueGroupSize = 0.8f * groupSize.toFloat() val trueGroupSize = 0.8f * groupSize
return Vector2(bottomX, bottomY) return Vector2(bottomX, bottomY)
.add(stageCoords) .add(stageCoords)
.sub(groupSize.toFloat() / 2f, groupSize.toFloat() / 2f) .sub(groupSize / 2f, groupSize / 2f)
.scl(1f / trueGroupSize) .scl(1f / trueGroupSize)
} }
fun getMirrorTiles(): HashMap<TileInfo, Pair<T, T>> = mirrorTileGroups fun getMirrorTiles(): HashMap<TileInfo, Pair<T, T>> = mirrorTileGroups
override fun act(delta: Float) {
if(shouldAct) {
super.act(delta)
}
}
// For debugging purposes // For debugging purposes
override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) } override fun draw(batch: Batch?, parentAlpha: Float) { super.draw(batch, parentAlpha) }
@Suppress("RedundantOverride")
override fun act(delta: Float) {
if(shouldAct) {
super.act(delta)
}
}
} }