From 9e0ed36bba18f5d2f8a144bc078d7ea782ca49e4 Mon Sep 17 00:00:00 2001 From: lyrjie Date: Wed, 22 Jan 2020 14:10:39 +0300 Subject: [PATCH] Map editor menu (#1739) * Refactor: map size is enum * Feature: map editor menu * Cleanup * Added "Empty" map generation type for the map editor --- .../template.properties | 3 + .../unciv/logic/civilization/TechManager.kt | 11 +-- core/src/com/unciv/logic/map/MapGenerator.kt | 6 +- core/src/com/unciv/logic/map/MapParameters.kt | 10 ++- .../com/unciv/ui/mapeditor/NewMapScreen.kt | 79 +++++++++++++++++++ .../ui/newgamescreen/MapParametersTable.kt | 54 ++++++++----- .../mainmenu/WorldScreenMenuPopup.kt | 52 +++++++----- 7 files changed, 171 insertions(+), 44 deletions(-) create mode 100644 core/src/com/unciv/ui/mapeditor/NewMapScreen.kt diff --git a/android/assets/jsons/translationsByLanguage/template.properties b/android/assets/jsons/translationsByLanguage/template.properties index 9fc99aa9..357777e2 100644 --- a/android/assets/jsons/translationsByLanguage/template.properties +++ b/android/assets/jsons/translationsByLanguage/template.properties @@ -977,6 +977,9 @@ Version = Resolution = Tileset = Map editor = +Create = +New map = +Empty = Language = Terrains & Resources = Improvements = diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 72b7a125..53a5c6c3 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -4,6 +4,7 @@ package com.unciv.logic.civilization import com.badlogic.gdx.graphics.Color import com.unciv.Constants import com.unciv.UncivGame +import com.unciv.logic.map.MapSize import com.unciv.logic.map.RoadStatus import com.unciv.models.ruleset.tech.Technology import com.unciv.models.ruleset.unit.BaseUnit @@ -60,11 +61,11 @@ 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.radius) { - 20 -> floatArrayOf(1.1f, 0.05f) // Medium Size - 30 -> floatArrayOf(1.2f, 0.03f) // Large Size - 40 -> floatArrayOf(1.3f, 0.02f) // Huge Size - else -> floatArrayOf(1f, 0.05f) // Tiny and Small Size + 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) + else -> floatArrayOf(1f, 0.05f) } techCost *= worldSizeModifier[0] techCost *= 1 + (civInfo.cities.size -1) * worldSizeModifier[1] diff --git a/core/src/com/unciv/logic/map/MapGenerator.kt b/core/src/com/unciv/logic/map/MapGenerator.kt index 8dacbaba..b02dd34b 100644 --- a/core/src/com/unciv/logic/map/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/MapGenerator.kt @@ -24,18 +24,22 @@ class MapType { val continents = "Continents" val pangaea = "Pangaea" val custom="Custom" + val empty="Empty" } } class MapGenerator { fun generateMap(mapParameters: MapParameters, ruleset: Ruleset): TileMap { - val mapRadius = mapParameters.radius + val mapRadius = mapParameters.size.radius val mapType = mapParameters.type val map = TileMap(mapRadius, ruleset) map.mapParameters = mapParameters + // Is the empty map is requested, there's no need for further generation + if (mapType == MapType.empty) return map + // Step one - separate land and water, in form of Grasslands and Oceans if (mapType == MapType.perlin) MapLandmassGenerator().generateLandPerlin(map) diff --git a/core/src/com/unciv/logic/map/MapParameters.kt b/core/src/com/unciv/logic/map/MapParameters.kt index faa5371d..8e3b80f4 100644 --- a/core/src/com/unciv/logic/map/MapParameters.kt +++ b/core/src/com/unciv/logic/map/MapParameters.kt @@ -1,9 +1,17 @@ package com.unciv.logic.map +enum class MapSize(val radius: Int) { + Tiny(10), + Small(15), + Medium(20), + Large(30), + Huge(40) +} + class MapParameters { var name = "" var type = MapType.pangaea - var radius = 20 + var size: MapSize = MapSize.Medium var noRuins = false var noNaturalWonders = true } \ No newline at end of file diff --git a/core/src/com/unciv/ui/mapeditor/NewMapScreen.kt b/core/src/com/unciv/ui/mapeditor/NewMapScreen.kt new file mode 100644 index 00000000..27560ac0 --- /dev/null +++ b/core/src/com/unciv/ui/mapeditor/NewMapScreen.kt @@ -0,0 +1,79 @@ +package com.unciv.ui.mapeditor + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.unciv.UncivGame +import com.unciv.logic.map.MapGenerator +import com.unciv.logic.map.MapParameters +import com.unciv.logic.map.TileMap +import com.unciv.models.ruleset.RulesetCache +import com.unciv.models.translations.tr +import com.unciv.ui.newgamescreen.MapParametersTable +import com.unciv.ui.pickerscreens.PickerScreen +import com.unciv.ui.utils.* +import kotlin.concurrent.thread + +/** New map generation screen */ +class NewMapScreen : PickerScreen() { + + private val mapParameters = MapParameters() + private var generatedMap: TileMap? = null + + init { + setDefaultCloseAction() + + val newMapScreenOptionsTable = Table(skin).apply { + pad(10f) + add("Map options".toLabel(fontSize = 24)).row() + add(MapParametersTable(mapParameters, isEmptyMapAllowed = true)).row() + pack() + } + + + topTable.apply { + add(ScrollPane(newMapScreenOptionsTable)).height(topTable.parent.height) + pack() + setFillParent(true) + } + + rightButtonSetEnabled(true) + rightSideButton.onClick { + Gdx.input.inputProcessor = null // remove input processing - nothing will be clicked! + rightButtonSetEnabled(false) + + thread(name = "MapGenerator") { + try { + // Map generation can take a while and we don't want ANRs + val ruleset = RulesetCache.getBaseRuleset() + generatedMap = MapGenerator().generateMap(mapParameters, ruleset) + + Gdx.app.postRunnable { + UncivGame.Current.setScreen(MapEditorScreen(generatedMap!!)) + } + + } catch (exception: Exception) { + rightButtonSetEnabled(true) + val cantMakeThatMapPopup = Popup(this) + cantMakeThatMapPopup.addGoodSizedLabel("It looks like we can't make a map with the parameters you requested!".tr()) + .row() + cantMakeThatMapPopup.addCloseButton() + cantMakeThatMapPopup.open() + Gdx.input.inputProcessor = stage + } + } + + } + } + + /** Changes the state and the text of the [rightSideButton] */ + private fun rightButtonSetEnabled(enabled: Boolean) { + if (enabled) { + rightSideButton.enable() + rightSideButton.setText("Create".tr()) + } else { + rightSideButton.disable() + rightSideButton.setText("Working...".tr()) + } + } +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt b/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt index 57cc8adc..3b2f837f 100644 --- a/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/MapParametersTable.kt @@ -5,13 +5,23 @@ import com.badlogic.gdx.scenes.scene2d.ui.CheckBox import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener import com.unciv.logic.map.MapParameters +import com.unciv.logic.map.MapSize import com.unciv.logic.map.MapType import com.unciv.models.translations.tr import com.unciv.ui.utils.CameraStageBaseScreen import com.unciv.ui.utils.toLabel -// This is a separate class, because it should be in use both in the New Game screen and the Map Editor screen -class MapParametersTable(val mapParameters: MapParameters): Table(){ +/** Table for editing [mapParameters] + * + * This is a separate class, because it should be in use both in the New Game screen and the Map Editor screen + * + * @param isEmptyMapAllowed whether the [MapType.empty] option should be present. Is used by the Map Editor, but should **never** be used with the New Game + * */ +class MapParametersTable(val mapParameters: MapParameters, val isEmptyMapAllowed: Boolean = false) : + Table() { + + lateinit var noRuinsCheckbox: CheckBox + lateinit var noNaturalWondersCheckbox: CheckBox init { addMapTypeSelectBox() @@ -23,35 +33,41 @@ class MapParametersTable(val mapParameters: MapParameters): Table(){ private fun addMapTypeSelectBox() { add("{Map generation type}:".toLabel()) - val mapTypes = listOf(MapType.default, MapType.pangaea, MapType.continents, MapType.perlin) - val mapTypeSelectBox = TranslatedSelectBox(mapTypes, mapParameters.type, CameraStageBaseScreen.skin) + val mapTypes = listOfNotNull( + MapType.default, + MapType.pangaea, + MapType.continents, + MapType.perlin, + if (isEmptyMapAllowed) MapType.empty else null + ) + val mapTypeSelectBox = + TranslatedSelectBox(mapTypes, mapParameters.type, CameraStageBaseScreen.skin) mapTypeSelectBox.addListener(object : ChangeListener() { override fun changed(event: ChangeEvent?, actor: Actor?) { - mapParameters.type=mapTypeSelectBox.selected.value + mapParameters.type = mapTypeSelectBox.selected.value + + // If the map won't be generated, these options are irrelevant and are hidden + noRuinsCheckbox.isVisible = mapParameters.type != MapType.empty + noNaturalWondersCheckbox.isVisible = mapParameters.type != MapType.empty } }) add(mapTypeSelectBox).row() } - private fun addWorldSizeSelectBox(){ + private fun addWorldSizeSelectBox() { val worldSizeLabel = "{World size}:".toLabel() - val worldSizeToRadius = LinkedHashMap() - worldSizeToRadius["Tiny"] = 10 - worldSizeToRadius["Small"] = 15 - worldSizeToRadius["Medium"] = 20 - worldSizeToRadius["Large"] = 30 - worldSizeToRadius["Huge"] = 40 - - val currentWorldSizeName = worldSizeToRadius.entries - .first { it.value == mapParameters.radius }.key - val worldSizeSelectBox = TranslatedSelectBox(worldSizeToRadius.keys, currentWorldSizeName, CameraStageBaseScreen.skin) + val worldSizeSelectBox = TranslatedSelectBox( + MapSize.values().map { it.name }, + mapParameters.size.name, + CameraStageBaseScreen.skin + ) worldSizeSelectBox.addListener(object : ChangeListener() { override fun changed(event: ChangeEvent?, actor: Actor?) { - mapParameters.radius = worldSizeToRadius[worldSizeSelectBox.selected.value]!! + mapParameters.size = MapSize.valueOf(worldSizeSelectBox.selected.value) } }) @@ -60,7 +76,7 @@ class MapParametersTable(val mapParameters: MapParameters): Table(){ } private fun addNoRuinsCheckbox() { - val noRuinsCheckbox = CheckBox("No ancient ruins".tr(), CameraStageBaseScreen.skin) + noRuinsCheckbox = CheckBox("No ancient ruins".tr(), CameraStageBaseScreen.skin) noRuinsCheckbox.isChecked = mapParameters.noRuins noRuinsCheckbox.addListener(object : ChangeListener() { override fun changed(event: ChangeEvent?, actor: Actor?) { @@ -71,7 +87,7 @@ class MapParametersTable(val mapParameters: MapParameters): Table(){ } private fun addNoNaturalWondersCheckbox() { - val noNaturalWondersCheckbox = CheckBox("No Natural Wonders".tr(), CameraStageBaseScreen.skin) + noNaturalWondersCheckbox = CheckBox("No Natural Wonders".tr(), CameraStageBaseScreen.skin) noNaturalWondersCheckbox.isChecked = mapParameters.noNaturalWonders noNaturalWondersCheckbox.addListener(object : ChangeListener() { override fun changed(event: ChangeEvent?, actor: Actor?) { diff --git a/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt b/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt index 1e51e6fb..ee4cf8e7 100644 --- a/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt +++ b/core/src/com/unciv/ui/worldscreen/mainmenu/WorldScreenMenuPopup.kt @@ -3,21 +3,17 @@ package com.unciv.ui.worldscreen.mainmenu import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color import com.unciv.UncivGame -import com.unciv.logic.map.RoadStatus import com.unciv.models.translations.tr import com.unciv.ui.CivilopediaScreen import com.unciv.ui.VictoryScreen -import com.unciv.ui.mapeditor.MapEditorScreen +import com.unciv.ui.mapeditor.LoadMapScreen +import com.unciv.ui.mapeditor.NewMapScreen import com.unciv.ui.newgamescreen.NewGameScreen import com.unciv.ui.saves.LoadGameScreen import com.unciv.ui.saves.SaveGameScreen -import com.unciv.ui.utils.Popup -import com.unciv.ui.utils.addSeparator -import com.unciv.ui.utils.disable -import com.unciv.ui.utils.toLabel +import com.unciv.ui.utils.* import com.unciv.ui.worldscreen.WorldScreen import java.util.* -import kotlin.collections.ArrayList import kotlin.concurrent.thread class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) { @@ -26,17 +22,7 @@ class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) { val width = 200f val height = 30f addSquareButton("Map editor".tr()){ - val tileMapClone = worldScreen.gameInfo.tileMap.clone() - for(tile in tileMapClone.values){ - tile.militaryUnit=null - tile.civilianUnit=null - tile.airUnits=ArrayList() - tile.improvement=null - tile.improvementInProgress=null - tile.turnsToImprovement=0 - tile.roadStatus=RoadStatus.None - } - UncivGame.Current.setScreen(MapEditorScreen(tileMapClone)) + openMapEditorPopup() remove() }.size(width,height) addSeparator() @@ -137,6 +123,36 @@ class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) { multiplayerPopup.addCloseButton() multiplayerPopup.open() } + + /** Shows the [Popup] with the map editor initialization options */ + private fun openMapEditorPopup() { + + close() + val mapEditorPopup = Popup(screen) + + mapEditorPopup.addGoodSizedLabel("Map editor".tr()).row() + + // Create a new map + mapEditorPopup.addButton("New map") { + UncivGame.Current.setScreen(NewMapScreen()) + mapEditorPopup.close() + } + + // Load the map + mapEditorPopup.addButton("Load map") { + val loadMapScreen = LoadMapScreen(null) + loadMapScreen.closeButton.isVisible = true + loadMapScreen.closeButton.onClick { + UncivGame.Current.setWorldScreen() + loadMapScreen.dispose() } + UncivGame.Current.setScreen(loadMapScreen) + mapEditorPopup.close() + } + + mapEditorPopup.addCloseButton() + mapEditorPopup.open() + } + } class WorldScreenCommunityPopup(val worldScreen: WorldScreen) : Popup(worldScreen) {