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 =
Tileset =
Map editor =
Create =
New map =
Empty =
Language =
Terrains & Resources =
Improvements =

View file

@ -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]

View file

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

View file

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

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.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<String, Int>()
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?) {

View file

@ -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) {