Map editor menu (#1739)

* Refactor: map size is enum

* Feature: map editor menu

* Cleanup

* Added "Empty" map generation type for the map editor
This commit is contained in:
lyrjie 2020-01-22 14:10:39 +03:00 committed by Yair Morgenstern
parent 617eea92cf
commit 9e0ed36bba
7 changed files with 171 additions and 44 deletions

View File

@ -977,6 +977,9 @@ Version =
Resolution = Resolution =
Tileset = Tileset =
Map editor = Map editor =
Create =
New map =
Empty =
Language = Language =
Terrains & Resources = Terrains & Resources =
Improvements = Improvements =

View File

@ -4,6 +4,7 @@ package com.unciv.logic.civilization
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.map.MapSize
import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.RoadStatus
import com.unciv.models.ruleset.tech.Technology import com.unciv.models.ruleset.tech.Technology
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
@ -60,11 +61,11 @@ class TechManager {
// https://forums.civfanatics.com/threads/the-mechanics-of-overflow-inflation.517970/ // https://forums.civfanatics.com/threads/the-mechanics-of-overflow-inflation.517970/
techCost /= 1 + techsResearchedKnownCivs / undefeatedCivs.toFloat() * 0.3f techCost /= 1 + techsResearchedKnownCivs / undefeatedCivs.toFloat() * 0.3f
// http://www.civclub.net/bbs/forum.php?mod=viewthread&tid=123976 // http://www.civclub.net/bbs/forum.php?mod=viewthread&tid=123976
val worldSizeModifier = when(civInfo.gameInfo.tileMap.mapParameters.radius) { val worldSizeModifier = when(civInfo.gameInfo.tileMap.mapParameters.size) {
20 -> floatArrayOf(1.1f, 0.05f) // Medium Size MapSize.Medium -> floatArrayOf(1.1f, 0.05f)
30 -> floatArrayOf(1.2f, 0.03f) // Large Size MapSize.Large -> floatArrayOf(1.2f, 0.03f)
40 -> floatArrayOf(1.3f, 0.02f) // Huge Size MapSize.Huge -> floatArrayOf(1.3f, 0.02f)
else -> floatArrayOf(1f, 0.05f) // Tiny and Small Size else -> floatArrayOf(1f, 0.05f)
} }
techCost *= worldSizeModifier[0] techCost *= worldSizeModifier[0]
techCost *= 1 + (civInfo.cities.size -1) * worldSizeModifier[1] techCost *= 1 + (civInfo.cities.size -1) * worldSizeModifier[1]

View File

@ -24,18 +24,22 @@ class MapType {
val continents = "Continents" val continents = "Continents"
val pangaea = "Pangaea" val pangaea = "Pangaea"
val custom="Custom" val custom="Custom"
val empty="Empty"
} }
} }
class MapGenerator { class MapGenerator {
fun generateMap(mapParameters: MapParameters, ruleset: Ruleset): TileMap { fun generateMap(mapParameters: MapParameters, ruleset: Ruleset): TileMap {
val mapRadius = mapParameters.radius val mapRadius = mapParameters.size.radius
val mapType = mapParameters.type val mapType = mapParameters.type
val map = TileMap(mapRadius, ruleset) val map = TileMap(mapRadius, ruleset)
map.mapParameters = mapParameters 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 // Step one - separate land and water, in form of Grasslands and Oceans
if (mapType == MapType.perlin) if (mapType == MapType.perlin)
MapLandmassGenerator().generateLandPerlin(map) MapLandmassGenerator().generateLandPerlin(map)

View File

@ -1,9 +1,17 @@
package com.unciv.logic.map package com.unciv.logic.map
enum class MapSize(val radius: Int) {
Tiny(10),
Small(15),
Medium(20),
Large(30),
Huge(40)
}
class MapParameters { class MapParameters {
var name = "" var name = ""
var type = MapType.pangaea var type = MapType.pangaea
var radius = 20 var size: MapSize = MapSize.Medium
var noRuins = false var noRuins = false
var noNaturalWonders = true var noNaturalWonders = true
} }

View File

@ -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())
}
}
}

View File

@ -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.ui.Table
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener
import com.unciv.logic.map.MapParameters import com.unciv.logic.map.MapParameters
import com.unciv.logic.map.MapSize
import com.unciv.logic.map.MapType import com.unciv.logic.map.MapType
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.utils.CameraStageBaseScreen import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.toLabel 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 /** Table for editing [mapParameters]
class MapParametersTable(val mapParameters: MapParameters): Table(){ *
* 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 { init {
addMapTypeSelectBox() addMapTypeSelectBox()
@ -23,35 +33,41 @@ class MapParametersTable(val mapParameters: MapParameters): Table(){
private fun addMapTypeSelectBox() { private fun addMapTypeSelectBox() {
add("{Map generation type}:".toLabel()) add("{Map generation type}:".toLabel())
val mapTypes = listOf(MapType.default, MapType.pangaea, MapType.continents, MapType.perlin) val mapTypes = listOfNotNull(
val mapTypeSelectBox = TranslatedSelectBox(mapTypes, mapParameters.type, CameraStageBaseScreen.skin) 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() { mapTypeSelectBox.addListener(object : ChangeListener() {
override fun changed(event: ChangeEvent?, actor: Actor?) { 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() add(mapTypeSelectBox).row()
} }
private fun addWorldSizeSelectBox(){ private fun addWorldSizeSelectBox() {
val worldSizeLabel = "{World size}:".toLabel() val worldSizeLabel = "{World size}:".toLabel()
val worldSizeToRadius = LinkedHashMap<String, Int>() val worldSizeSelectBox = TranslatedSelectBox(
worldSizeToRadius["Tiny"] = 10 MapSize.values().map { it.name },
worldSizeToRadius["Small"] = 15 mapParameters.size.name,
worldSizeToRadius["Medium"] = 20 CameraStageBaseScreen.skin
worldSizeToRadius["Large"] = 30 )
worldSizeToRadius["Huge"] = 40
val currentWorldSizeName = worldSizeToRadius.entries
.first { it.value == mapParameters.radius }.key
val worldSizeSelectBox = TranslatedSelectBox(worldSizeToRadius.keys, currentWorldSizeName, CameraStageBaseScreen.skin)
worldSizeSelectBox.addListener(object : ChangeListener() { worldSizeSelectBox.addListener(object : ChangeListener() {
override fun changed(event: ChangeEvent?, actor: Actor?) { 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() { 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.isChecked = mapParameters.noRuins
noRuinsCheckbox.addListener(object : ChangeListener() { noRuinsCheckbox.addListener(object : ChangeListener() {
override fun changed(event: ChangeEvent?, actor: Actor?) { override fun changed(event: ChangeEvent?, actor: Actor?) {
@ -71,7 +87,7 @@ class MapParametersTable(val mapParameters: MapParameters): Table(){
} }
private fun addNoNaturalWondersCheckbox() { 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.isChecked = mapParameters.noNaturalWonders
noNaturalWondersCheckbox.addListener(object : ChangeListener() { noNaturalWondersCheckbox.addListener(object : ChangeListener() {
override fun changed(event: ChangeEvent?, actor: Actor?) { override fun changed(event: ChangeEvent?, actor: Actor?) {

View File

@ -3,21 +3,17 @@ package com.unciv.ui.worldscreen.mainmenu
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.map.RoadStatus
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import com.unciv.ui.CivilopediaScreen import com.unciv.ui.CivilopediaScreen
import com.unciv.ui.VictoryScreen 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.newgamescreen.NewGameScreen
import com.unciv.ui.saves.LoadGameScreen import com.unciv.ui.saves.LoadGameScreen
import com.unciv.ui.saves.SaveGameScreen import com.unciv.ui.saves.SaveGameScreen
import com.unciv.ui.utils.Popup import com.unciv.ui.utils.*
import com.unciv.ui.utils.addSeparator
import com.unciv.ui.utils.disable
import com.unciv.ui.utils.toLabel
import com.unciv.ui.worldscreen.WorldScreen import com.unciv.ui.worldscreen.WorldScreen
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.concurrent.thread import kotlin.concurrent.thread
class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) { class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) {
@ -26,17 +22,7 @@ class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) {
val width = 200f val width = 200f
val height = 30f val height = 30f
addSquareButton("Map editor".tr()){ addSquareButton("Map editor".tr()){
val tileMapClone = worldScreen.gameInfo.tileMap.clone() openMapEditorPopup()
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))
remove() remove()
}.size(width,height) }.size(width,height)
addSeparator() addSeparator()
@ -137,6 +123,36 @@ class WorldScreenMenuPopup(val worldScreen: WorldScreen) : Popup(worldScreen) {
multiplayerPopup.addCloseButton() multiplayerPopup.addCloseButton()
multiplayerPopup.open() 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) { class WorldScreenCommunityPopup(val worldScreen: WorldScreen) : Popup(worldScreen) {