mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-09 15:29:32 +07:00
Custom map size (#2876)
* Adding custom map sizes. Initial commit * Custom map sizes UI update * Custom map size with rectangular shape * Added compatibility with older Maps and Game saves * Fixed build errors and added warning messages Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:

committed by
GitHub

parent
b8f9b99bd1
commit
453f5588ac
@ -270,6 +270,8 @@ Extension mods: =
|
||||
|
||||
World Wrap =
|
||||
World wrap maps are very memory intensive - creating large world wrap maps on Android can lead to crashes! =
|
||||
Anything above 80 by 50 may work very slowly on Android! =
|
||||
Anything above 40 may work very slowly on Android! =
|
||||
|
||||
# Multiplayer
|
||||
|
||||
|
@ -76,4 +76,11 @@ object Constants {
|
||||
const val futureEra = "Future era"
|
||||
const val barbarians = "Barbarians"
|
||||
const val spectator = "Spectator"
|
||||
|
||||
const val tiny = "Tiny"
|
||||
const val small = "Small"
|
||||
const val medium = "Medium"
|
||||
const val large = "Large"
|
||||
const val huge = "Huge"
|
||||
const val custom = "Custom"
|
||||
}
|
@ -10,7 +10,7 @@ import com.unciv.logic.GameSaver
|
||||
import com.unciv.logic.GameStarter
|
||||
import com.unciv.logic.map.mapgenerator.MapGenerator
|
||||
import com.unciv.logic.map.MapParameters
|
||||
import com.unciv.logic.map.MapSize
|
||||
import com.unciv.logic.map.MapSizeNew
|
||||
import com.unciv.logic.map.MapType
|
||||
import com.unciv.models.ruleset.RulesetCache
|
||||
import com.unciv.models.translations.tr
|
||||
@ -50,7 +50,7 @@ class MainMenuScreen: CameraStageBaseScreen() {
|
||||
|
||||
thread(name = "ShowMapBackground") {
|
||||
val newMap = MapGenerator(RulesetCache.getBaseRuleset())
|
||||
.generateMap(MapParameters().apply { size = MapSize.Small; type = MapType.default })
|
||||
.generateMap(MapParameters().apply { mapSize = MapSizeNew(Constants.small); type = MapType.default })
|
||||
Gdx.app.postRunnable { // for GL context
|
||||
ImageGetter.setNewRuleset(RulesetCache.getBaseRuleset())
|
||||
val mapHolder = EditorMapHolder(MapEditorScreen(), newMap)
|
||||
|
@ -7,6 +7,7 @@ import com.unciv.logic.automation.NextTurnAutomation
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.city.PerpetualConstruction
|
||||
import com.unciv.logic.civilization.*
|
||||
import com.unciv.logic.map.MapSizeNew
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.logic.map.TileMap
|
||||
import com.unciv.models.metadata.GameParameters
|
||||
@ -272,6 +273,12 @@ class GameInfo {
|
||||
if (currentPlayer == "") currentPlayer = civilizations.first { it.isPlayerCivilization() }.civName
|
||||
currentPlayerCiv = getCivilization(currentPlayer)
|
||||
|
||||
// as of version 3.9.18, added new custom map size
|
||||
// empty mapSize name and non-custom map type means - it is old style map size,
|
||||
// therefore we need to create new mapSize property
|
||||
if (tileMap.mapParameters.mapSize.name == "" && tileMap.mapParameters.type != Constants.custom)
|
||||
tileMap.mapParameters.mapSize = MapSizeNew(tileMap.mapParameters.size.name)
|
||||
|
||||
// this is separated into 2 loops because when we activate updateVisibleTiles in civ.setTransients,
|
||||
// we try to find new civs, and we check if civ is barbarian, which we can't know unless the gameInfo is already set.
|
||||
for (civInfo in civilizations) civInfo.gameInfo = this
|
||||
|
@ -41,6 +41,16 @@ object HexMath {
|
||||
return Vector2(width, height)
|
||||
}
|
||||
|
||||
/** Returns a radius of a hexagonal map that have approximately the same number of
|
||||
* tiles as a rectangular map of a given width/height
|
||||
*/
|
||||
fun getEquivalentHexagonalRadius(width: Int, height: Int): Int {
|
||||
val nTiles = width * height.toFloat()
|
||||
if (nTiles < 1) return 0
|
||||
val radius = ((sqrt(12*nTiles - 3) - 3) / 6).roundToInt()
|
||||
return radius
|
||||
}
|
||||
|
||||
fun getAdjacentVectors(origin: Vector2): ArrayList<Vector2> {
|
||||
val vectors = arrayListOf(
|
||||
Vector2(1f, 0f),
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.unciv.logic.civilization
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.map.MapSize
|
||||
import com.unciv.logic.map.RoadStatus
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.ruleset.Unique
|
||||
@ -84,10 +84,10 @@ class TechManager {
|
||||
// https://forums.civfanatics.com/threads/the-mechanics-of-overflow-inflation.517970/
|
||||
techCost /= 1 + techsResearchedKnownCivs / undefeatedCivs.toFloat() * 0.3f
|
||||
// http://www.civclub.net/bbs/forum.php?mod=viewthread&tid=123976
|
||||
val worldSizeModifier = when (civInfo.gameInfo.tileMap.mapParameters.size) {
|
||||
MapSize.Medium -> floatArrayOf(1.1f, 0.05f)
|
||||
MapSize.Large -> floatArrayOf(1.2f, 0.03f)
|
||||
MapSize.Huge -> floatArrayOf(1.3f, 0.02f)
|
||||
val worldSizeModifier = when (civInfo.gameInfo.tileMap.mapParameters.mapSize.name) {
|
||||
Constants.medium -> floatArrayOf(1.1f, 0.05f)
|
||||
Constants.large -> floatArrayOf(1.2f, 0.03f)
|
||||
Constants.huge -> floatArrayOf(1.3f, 0.02f)
|
||||
else -> floatArrayOf(1f, 0.05f)
|
||||
}
|
||||
techCost *= worldSizeModifier[0]
|
||||
|
@ -1,5 +1,10 @@
|
||||
package com.unciv.logic.map
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.HexMath.getEquivalentHexagonalRadius
|
||||
import com.unciv.logic.HexMath.getEquivalentRectangularSize
|
||||
|
||||
|
||||
enum class MapSize(val radius: Int) {
|
||||
Tiny(10),
|
||||
Small(15),
|
||||
@ -8,6 +13,44 @@ enum class MapSize(val radius: Int) {
|
||||
Huge(40)
|
||||
}
|
||||
|
||||
class MapSizeNew {
|
||||
var radius = 0
|
||||
var width = 0
|
||||
var height = 0
|
||||
var name = ""
|
||||
|
||||
/** Needed for Json parsing */
|
||||
constructor()
|
||||
|
||||
constructor(name: String) {
|
||||
/** Hard coded values from getEquivalentRectangularSize() */
|
||||
when (name) {
|
||||
Constants.tiny -> { radius = 10; width = 23; height = 15 }
|
||||
Constants.small -> { radius = 15; width = 33; height = 21 }
|
||||
Constants.medium -> { radius = 20; width = 44; height = 29 }
|
||||
Constants.large -> { radius = 30; width = 66; height = 43 }
|
||||
Constants.huge -> { radius = 40; width = 87; height = 57 }
|
||||
}
|
||||
}
|
||||
constructor(radius: Int) {
|
||||
name = Constants.custom
|
||||
this.radius = radius
|
||||
val size = getEquivalentRectangularSize(radius)
|
||||
this.width = size.x.toInt()
|
||||
this.height = size.y.toInt()
|
||||
}
|
||||
|
||||
constructor(width: Int, height: Int) {
|
||||
name = Constants.custom
|
||||
this.width = width
|
||||
this.height = height
|
||||
this.radius = getEquivalentHexagonalRadius(width, height)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
object MapShape {
|
||||
const val hexagonal = "Hexagonal"
|
||||
const val rectangular = "Rectangular"
|
||||
@ -33,7 +76,9 @@ class MapParameters {
|
||||
var name = ""
|
||||
var type = MapType.pangaea
|
||||
var shape = MapShape.hexagonal
|
||||
var size: MapSize = MapSize.Medium
|
||||
@Deprecated("replaced by mapSize since 3.19.18")
|
||||
var size = MapSize.Medium
|
||||
var mapSize = MapSizeNew(Constants.medium)
|
||||
var noRuins = false
|
||||
var noNaturalWonders = false
|
||||
var worldWrap = false
|
||||
|
@ -21,16 +21,15 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
private var randomness = MapGenerationRandomness()
|
||||
|
||||
fun generateMap(mapParameters: MapParameters, seed: Long = System.currentTimeMillis()): TileMap {
|
||||
val mapRadius = mapParameters.size.radius
|
||||
val mapSize = mapParameters.mapSize
|
||||
val mapType = mapParameters.type
|
||||
val map: TileMap
|
||||
|
||||
if (mapParameters.shape == MapShape.rectangular) {
|
||||
val size = HexMath.getEquivalentRectangularSize(mapRadius)
|
||||
val size = HexMath.getEquivalentRectangularSize(mapSize.radius)
|
||||
map = TileMap(size.x.toInt(), size.y.toInt(), ruleset, mapParameters.worldWrap)
|
||||
}
|
||||
else
|
||||
map = TileMap(mapRadius, ruleset, mapParameters.worldWrap)
|
||||
else map = TileMap(mapSize.radius, ruleset, mapParameters.worldWrap)
|
||||
|
||||
map.mapParameters = mapParameters
|
||||
map.mapParameters.seed = seed
|
||||
@ -120,7 +119,7 @@ class MapGenerator(val ruleset: Ruleset) {
|
||||
}
|
||||
|
||||
private fun spreadResources(tileMap: TileMap) {
|
||||
val distance = tileMap.mapParameters.size.radius
|
||||
val distance = tileMap.mapParameters.mapSize.radius
|
||||
for (tile in tileMap.values)
|
||||
tile.resource = null
|
||||
|
||||
|
@ -108,7 +108,7 @@ class MapLandmassGenerator(val randomness: MapGenerationRandomness) {
|
||||
}
|
||||
|
||||
private fun percentualDistanceToCenter(tileInfo: TileInfo, tileMap: TileMap): Double {
|
||||
val mapRadius = tileMap.mapParameters.size.radius
|
||||
val mapRadius = tileMap.mapParameters.mapSize.radius
|
||||
if (tileMap.mapParameters.shape == MapShape.hexagonal)
|
||||
return HexMath.getDistance(Vector2.Zero, tileInfo.position).toDouble() / mapRadius
|
||||
else {
|
||||
@ -132,6 +132,8 @@ class MapLandmassGenerator(val randomness: MapGenerationRandomness) {
|
||||
|
||||
// region Cellular automata
|
||||
private fun generateLandCellularAutomata(tileMap: TileMap) {
|
||||
val mapRadius = tileMap.mapParameters.mapSize.radius
|
||||
val mapType = tileMap.mapParameters.type
|
||||
val numSmooth = 4
|
||||
|
||||
// init
|
||||
@ -161,7 +163,7 @@ class MapLandmassGenerator(val randomness: MapGenerationRandomness) {
|
||||
}
|
||||
|
||||
private fun getInitialTerrainCellularAutomata(tileInfo: TileInfo, mapParameters: MapParameters): TerrainType {
|
||||
val mapRadius = mapParameters.size.radius
|
||||
val mapRadius = mapParameters.mapSize.radius
|
||||
|
||||
// default
|
||||
if (HexMath.getDistance(Vector2.Zero, tileInfo.position) > 0.9f * mapRadius) {
|
||||
|
@ -18,7 +18,7 @@ class NaturalWonderGenerator(val ruleset: Ruleset) {
|
||||
fun spawnNaturalWonders(tileMap: TileMap, randomness: MapGenerationRandomness) {
|
||||
if (tileMap.mapParameters.noNaturalWonders)
|
||||
return
|
||||
val mapRadius = tileMap.mapParameters.size.radius
|
||||
val mapRadius = tileMap.mapParameters.mapSize.radius
|
||||
// number of Natural Wonders scales linearly with mapRadius as #wonders = mapRadius * 0.13133208 - 0.56128831
|
||||
val numberToSpawn = round(mapRadius * 0.13133208f - 0.56128831f).toInt()
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
package com.unciv.ui.newgamescreen
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Slider
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextField
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.map.*
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.map.MapParameters
|
||||
import com.unciv.logic.map.MapShape
|
||||
@ -20,16 +24,21 @@ import com.unciv.ui.utils.*
|
||||
class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed: Boolean = false):
|
||||
Table() {
|
||||
lateinit var mapTypeSelectBox: TranslatedSelectBox
|
||||
lateinit var worldSizeSelectBox: TranslatedSelectBox
|
||||
private var customWorldSizeTable = Table ()
|
||||
private var hexagonalSizeTable = Table()
|
||||
private var rectangularSizeTable = Table()
|
||||
lateinit var noRuinsCheckbox: CheckBox
|
||||
lateinit var noNaturalWondersCheckbox: CheckBox
|
||||
lateinit var worldWrapCheckbox: CheckBox
|
||||
|
||||
|
||||
init {
|
||||
skin = CameraStageBaseScreen.skin
|
||||
defaults().pad(5f)
|
||||
addMapShapeSelectBox()
|
||||
addMapTypeSelectBox()
|
||||
addWorldSizeSelectBox()
|
||||
addWorldSizeTable()
|
||||
addNoRuinsCheckbox()
|
||||
addNoNaturalWondersCheckbox()
|
||||
if (UncivGame.Current.settings.showExperimentalWorldWrap) {
|
||||
@ -47,6 +56,7 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
||||
TranslatedSelectBox(mapShapes, mapParameters.shape, skin)
|
||||
mapShapeSelectBox.onChange {
|
||||
mapParameters.shape = mapShapeSelectBox.selected.value
|
||||
updateWorldSizeTable()
|
||||
}
|
||||
|
||||
add ("{Map Shape}:".toLabel()).left()
|
||||
@ -78,20 +88,79 @@ class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed
|
||||
add(mapTypeSelectBox).fillX().row()
|
||||
}
|
||||
|
||||
|
||||
private fun addWorldSizeSelectBox() {
|
||||
val worldSizeSelectBox = TranslatedSelectBox(
|
||||
MapSize.values().map { it.name },
|
||||
mapParameters.size.name,
|
||||
skin
|
||||
private fun addWorldSizeTable() {
|
||||
val mapSizes = listOfNotNull(
|
||||
Constants.tiny,
|
||||
Constants.small,
|
||||
Constants.medium,
|
||||
Constants.large,
|
||||
Constants.huge,
|
||||
Constants.custom
|
||||
)
|
||||
|
||||
worldSizeSelectBox.onChange {
|
||||
mapParameters.size = MapSize.valueOf(worldSizeSelectBox.selected.value)
|
||||
}
|
||||
worldSizeSelectBox = TranslatedSelectBox(mapSizes, mapParameters.mapSize.name, skin)
|
||||
worldSizeSelectBox.onChange { updateWorldSizeTable() }
|
||||
|
||||
addHexagonalSizeTable()
|
||||
addRectangularSizeTable()
|
||||
|
||||
add("{World Size}:".toLabel()).left()
|
||||
add(worldSizeSelectBox).fillX().row()
|
||||
add(customWorldSizeTable).colspan(2).grow().row()
|
||||
|
||||
updateWorldSizeTable()
|
||||
}
|
||||
|
||||
private fun addHexagonalSizeTable() {
|
||||
val defaultRadius = mapParameters.mapSize.radius.toString()
|
||||
val customMapSizeRadius = TextField(defaultRadius, skin).apply {
|
||||
textFieldFilter = TextField.TextFieldFilter.DigitsOnlyFilter()
|
||||
}
|
||||
customMapSizeRadius.onChange {
|
||||
mapParameters.mapSize = MapSizeNew(customMapSizeRadius.text.toIntOrNull() ?: 0 )
|
||||
}
|
||||
hexagonalSizeTable.add("{Radius}:".toLabel()).grow().left()
|
||||
hexagonalSizeTable.add(customMapSizeRadius).right().row()
|
||||
hexagonalSizeTable.add("Anything above 40 may work very slowly on Android!".toLabel(Color.RED)
|
||||
.apply { wrap=true }).width(prefWidth).colspan(hexagonalSizeTable.columns)
|
||||
}
|
||||
|
||||
private fun addRectangularSizeTable() {
|
||||
val defaultWidth = mapParameters.mapSize.width.toString()
|
||||
val customMapWidth = TextField(defaultWidth, skin).apply {
|
||||
textFieldFilter = TextField.TextFieldFilter.DigitsOnlyFilter()
|
||||
}
|
||||
|
||||
val defaultHeight = mapParameters.mapSize.height.toString()
|
||||
val customMapHeight = TextField(defaultHeight, skin).apply {
|
||||
textFieldFilter = TextField.TextFieldFilter.DigitsOnlyFilter()
|
||||
}
|
||||
|
||||
customMapWidth.onChange {
|
||||
mapParameters.mapSize = MapSizeNew(customMapWidth.text.toIntOrNull() ?: 0, customMapHeight.text.toIntOrNull() ?: 0)
|
||||
}
|
||||
customMapHeight.onChange {
|
||||
mapParameters.mapSize = MapSizeNew(customMapWidth.text.toIntOrNull() ?: 0, customMapHeight.text.toIntOrNull() ?: 0)
|
||||
}
|
||||
|
||||
rectangularSizeTable.defaults().pad(5f)
|
||||
rectangularSizeTable.add("{Width}:".toLabel()).grow().left()
|
||||
rectangularSizeTable.add(customMapWidth).right().row()
|
||||
rectangularSizeTable.add("{Height}:".toLabel()).grow().left()
|
||||
rectangularSizeTable.add(customMapHeight).right().row()
|
||||
rectangularSizeTable.add("Anything above 80 by 50 may work very slowly on Android!".toLabel(Color.RED)
|
||||
.apply { wrap=true }).width(prefWidth).colspan(hexagonalSizeTable.columns)
|
||||
}
|
||||
|
||||
private fun updateWorldSizeTable() {
|
||||
customWorldSizeTable.clear()
|
||||
|
||||
if (mapParameters.shape == MapShape.hexagonal && worldSizeSelectBox.selected.value == Constants.custom)
|
||||
customWorldSizeTable.add(hexagonalSizeTable).grow().row()
|
||||
else if (mapParameters.shape == MapShape.rectangular && worldSizeSelectBox.selected.value == Constants.custom)
|
||||
customWorldSizeTable.add(rectangularSizeTable).grow().row()
|
||||
else
|
||||
mapParameters.mapSize = MapSizeNew(worldSizeSelectBox.selected.value)
|
||||
}
|
||||
|
||||
private fun addNoRuinsCheckbox() {
|
||||
|
@ -132,7 +132,6 @@ class MinimapHolder(mapHolder: WorldMapHolder): Table() {
|
||||
pack()
|
||||
}
|
||||
|
||||
|
||||
private fun getWrappedMinimap(): Table {
|
||||
val internalMinimapWrapper = Table()
|
||||
internalMinimapWrapper.add(minimap)
|
||||
|
@ -1,11 +1,12 @@
|
||||
package com.unciv.app.desktop
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.UncivGameParameters
|
||||
import com.unciv.logic.GameStarter
|
||||
import com.unciv.logic.civilization.PlayerType
|
||||
import com.unciv.logic.map.MapParameters
|
||||
import com.unciv.logic.map.MapSize
|
||||
import com.unciv.logic.map.MapSizeNew
|
||||
import com.unciv.models.metadata.GameParameters
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.models.metadata.GameSpeed
|
||||
@ -54,7 +55,7 @@ internal object ConsoleLauncher {
|
||||
|
||||
private fun getMapParameters(): MapParameters {
|
||||
return MapParameters().apply {
|
||||
size = MapSize.Tiny
|
||||
mapSize = MapSizeNew(Constants.tiny)
|
||||
noRuins = true
|
||||
noNaturalWonders = true
|
||||
}
|
||||
|
Reference in New Issue
Block a user