Added initial map editor! Woohoo!

This commit is contained in:
Yair Morgenstern 2019-02-02 22:43:02 +02:00
parent 8e2c3287f6
commit 3a990434f3
15 changed files with 255 additions and 72 deletions

1
.gitignore vendored
View File

@ -132,3 +132,4 @@ android/release/android-release.apk
android/release/release/android.aab
android/assets/fonts/
android/release/android.aab
android/assets/maps/

View File

@ -4,12 +4,25 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Json
import com.unciv.GameSettings
import com.unciv.logic.map.TileMap
import com.unciv.ui.saves.Gzip
class GameSaver {
private val saveFilesFolder = "SaveFiles"
private val mapsFolder = "maps"
fun json() = Json().apply { setIgnoreDeprecated(true); ignoreUnknownFields = true } // Json() is NOT THREAD SAFE so we need to create a new one for each function
fun getMap(mapName:String) = Gdx.files.local("$mapsFolder/$mapName")
fun saveMap(mapName: String,tileMap: TileMap){
getMap(mapName).writeString(Gzip.zip(json().toJson(tileMap)), false)
}
fun loadMap(mapName: String): TileMap {
val gzippedString = getMap(mapName).readString()
val unzippedJson = Gzip.unzip(gzippedString)
return json().fromJson(TileMap::class.java, unzippedJson)
}
fun getSave(GameName: String): FileHandle {
return Gdx.files.local("$saveFilesFolder/$GameName")
}

View File

@ -18,6 +18,7 @@ enum class MapType {
Perlin,
Default,
Pangaea,
File
}
class CelluarAutomataRandomMapGenerator(): SeedRandomMapGenerator() {

View File

@ -2,6 +2,7 @@ package com.unciv.logic.map
import com.badlogic.gdx.math.Vector2
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.HexMath
import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.gamebasics.GameBasics
@ -31,14 +32,16 @@ class TileMap {
constructor(distance: Int, mapType: MapType) {
val map:HashMap<String,TileInfo>
val mapValues:Collection<TileInfo>
if(mapType==MapType.Perlin)
map = PerlinNoiseRandomMapGenerator().generateMap(distance)
if(mapType == MapType.File)
mapValues = GameSaver().loadMap("Test").values
else if(mapType==MapType.Perlin)
mapValues = PerlinNoiseRandomMapGenerator().generateMap(distance).values
else
map = CelluarAutomataRandomMapGenerator(mapType).generateMap(distance)
mapValues = CelluarAutomataRandomMapGenerator(mapType).generateMap(distance).values
tileList.addAll(map.values)
tileList.addAll(mapValues)
// tileList.addAll(AlexanderRandomMapGenerator().generateMap(distance,0.8f).values)
setTransients()

View File

@ -84,11 +84,9 @@ class LanguagePickerScreen: PickerScreen(){
YesNoPopupTable("This language requires you to download fonts.\n" +
"Do you want to download fonts for $spaceSplitLang?",
{
val downloading = PopupTable()
val downloading = PopupTable(this)
downloading.add(Label("Downloading...",skin))
downloading.pack()
downloading.center(stage)
stage.addActor(downloading)
downloading.open()
Gdx.input.inputProcessor = null // no interaction until download is over
kotlin.concurrent.thread {

View File

@ -0,0 +1,143 @@
package com.unciv.ui
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Image
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.scenes.scene2d.ui.TextButton
import com.unciv.UnCivGame
import com.unciv.logic.GameSaver
import com.unciv.logic.map.MapType
import com.unciv.logic.map.TileMap
import com.unciv.models.gamebasics.GameBasics
import com.unciv.models.gamebasics.tile.Terrain
import com.unciv.models.gamebasics.tile.TerrainType
import com.unciv.models.gamebasics.tile.TileResource
import com.unciv.ui.tilegroups.TileGroup
import com.unciv.ui.utils.CameraStageBaseScreen
import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.center
import com.unciv.ui.utils.onClick
import com.unciv.ui.worldscreen.TileGroupMap
class MapEditorScreen: CameraStageBaseScreen(){
var clearTerrainFeature=false
var selectedTerrain : Terrain?=null
var clearResource=false
var selectedResource:TileResource?=null
fun clearSelection(){
clearTerrainFeature=false
selectedTerrain=null
clearResource=false
selectedResource=null
}
fun getHex(color: Color, image: Actor?=null): Table {
val hex = ImageGetter.getImage("TerrainIcons/Hexagon.png")
hex.color = color
val group = Table().apply { add(hex).size(hex.width*0.3f,hex.height*0.3f); pack() }
if(image!=null) {
image.setSize(40f, 40f)
image.center(group)
group.addActor(image)
}
return group
}
init{
val tileMap = TileMap(20, MapType.Default)
val scrollPane = getMapHolder(tileMap)
stage.addActor(scrollPane)
val scrollTable = getTileEditorOptions()
stage.addActor(scrollTable)
val saveMapButton = TextButton("Save map",skin)
saveMapButton.onClick {
GameSaver().saveMap("Test",tileMap)
UnCivGame.Current.setWorldScreen()
}
stage.addActor(saveMapButton)
}
private fun getMapHolder(tileMap: TileMap): ScrollPane {
val tileGroups = tileMap.values.map { TileGroup(it) }
for (tileGroup in tileGroups) {
tileGroup.showEntireMap = true
tileGroup.update(true, true, true)
tileGroup.onClick {
val tileInfo = tileGroup.tileInfo
when {
clearTerrainFeature -> tileInfo.terrainFeature = null
clearResource -> tileInfo.resource = null
selectedResource != null -> tileInfo.resource = selectedResource!!.name
selectedTerrain != null -> {
if (selectedTerrain!!.type == TerrainType.TerrainFeature)
tileGroup.tileInfo.terrainFeature = selectedTerrain!!.name
else tileGroup.tileInfo.baseTerrain = selectedTerrain!!.name
}
}
tileGroup.tileInfo.setTransients()
tileGroup.update(true, true, true)
}
}
val mapHolder = TileGroupMap(tileGroups, 300f)
val scrollPane = ScrollPane(mapHolder)
scrollPane.setSize(stage.width, stage.height)
return scrollPane
}
private fun getTileEditorOptions(): Table {
val baseTerrainHolder = Table()
val terrainFeatureHolder = Table()
terrainFeatureHolder.add(getHex(Color.WHITE).apply { onClick { clearSelection(); clearTerrainFeature = true } })
for (terrain in GameBasics.Terrains.values) {
var icon: Image? = null
var color = Color.WHITE
if (terrain.type == TerrainType.TerrainFeature)
icon = ImageGetter.getImage("TerrainIcons/${terrain.name}.png")
else {
color = terrain.getColor()
val imagePath = "TerrainIcons/" + terrain.name
if (ImageGetter.imageExists(imagePath)) {
icon = ImageGetter.getImage(imagePath)
}
}
val group = getHex(color, icon)
group.onClick { clearSelection(); selectedTerrain = terrain }
if (terrain.type == TerrainType.TerrainFeature) terrainFeatureHolder.add(group)
else baseTerrainHolder.add(group)
}
baseTerrainHolder.pack()
terrainFeatureHolder.pack()
val resourcesHolder = Table()
resourcesHolder.add(getHex(Color.WHITE).apply { onClick { clearSelection(); clearResource = true } })
for (resource in GameBasics.TileResources.values) {
val resourceHex = getHex(Color.WHITE, ImageGetter.getResourceImage(resource.name, 40f))
resourceHex.onClick { clearSelection(); selectedResource = resource }
resourcesHolder.add(resourceHex)
}
val scrollTable = Table()
scrollTable.background = ImageGetter.getBackground(Color.GRAY.cpy().apply { a = 0.7f })
scrollTable.add(baseTerrainHolder).width(stage.width).row()
scrollTable.add(terrainFeatureHolder).width(stage.width).row()
scrollTable.add(ScrollPane(resourcesHolder)).width(stage.width).row()
scrollTable.pack()
scrollTable.setPosition(0f, stage.height - scrollTable.height)
return scrollTable
}
}

View File

@ -11,6 +11,7 @@ import com.badlogic.gdx.utils.Array
import com.unciv.GameStarter
import com.unciv.UnCivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.map.MapType
import com.unciv.models.gamebasics.GameBasics
import com.unciv.models.gamebasics.tr
@ -74,6 +75,7 @@ class NewGameScreen: PickerScreen(){
newGameOptionsTable.add("{Map type}:".tr())
val mapTypes = LinkedHashMap<String, MapType>()
for (type in MapType.values()) {
if(type==MapType.File && !GameSaver().getMap("Test").exists()) continue
mapTypes[type.toString()] = type
}
val mapTypeSelectBox = TranslatedSelectBox(mapTypes.keys, newGameParameters.mapType.toString(), skin)
@ -190,4 +192,5 @@ class TranslatedSelectBox(values : Collection<String>, default:String, skin: Ski
items = array
selected = array.first { it.value==default }
}
}
}

View File

@ -10,7 +10,10 @@ import java.util.zip.GZIPOutputStream
object Gzip {
fun compress(data: String): ByteArray {
fun zip(data:String):String = encoder(compress(data))
fun unzip(data:String):String = decompress(decoder(data))
private fun compress(data: String): ByteArray {
val bos = ByteArrayOutputStream(data.length)
val gzip = GZIPOutputStream(bos)
gzip.write(data.toByteArray())
@ -20,7 +23,7 @@ object Gzip {
return compressed
}
fun decompress(compressed: ByteArray): String {
private fun decompress(compressed: ByteArray): String {
val bis = ByteArrayInputStream(compressed)
val gis = GZIPInputStream(bis)
val br = BufferedReader(InputStreamReader(gis, "UTF-8"))
@ -37,11 +40,11 @@ object Gzip {
}
fun encoder(bytes:ByteArray): String{
private fun encoder(bytes:ByteArray): String{
return String(Base64Coder.encode(bytes))
}
fun decoder(base64Str: String): ByteArray{
private fun decoder(base64Str: String): ByteArray{
return Base64Coder.decode(base64Str)
}
}

View File

@ -65,7 +65,7 @@ class LoadScreen : PickerScreen() {
loadFromClipboardButton.onClick {
try{
val clipboardContentsString = Gdx.app.clipboard.contents
val decoded = Gzip.decompress(Gzip.decoder(clipboardContentsString))
val decoded = Gzip.unzip(clipboardContentsString)
val loadedGame = Json().fromJson(GameInfo::class.java, decoded)
loadedGame.setTransients()
UnCivGame.Current.loadGame(loadedGame)

View File

@ -50,7 +50,7 @@ class SaveScreen : PickerScreen() {
val copyJsonButton = TextButton("Copy game info".tr(),skin)
copyJsonButton.onClick {
val json = Json().toJson(game.gameInfo)
val base64Gzip = Gzip.encoder(Gzip.compress(json))
val base64Gzip = Gzip.zip(json)
Gdx.app.clipboard.contents = base64Gzip
}
newSave.add(copyJsonButton)

View File

@ -19,10 +19,13 @@ import com.unciv.ui.utils.center
open class TileGroup(var tileInfo: TileInfo) : Group() {
protected val hexagon = ImageGetter.getImage("TerrainIcons/Hexagon.png")
protected var baseTerrainImage: Image? = null
protected var baseTerrain:String=""
protected var terrainFeatureImage: Image? = null
protected var terrainFeature:String?=null
protected var cityImage: Image? = null
var resourceImage: Actor? = null
var resource:String?=null
var improvementImage: Actor? = null
var populationImage: Image? = null //reuse for acquire icon
private val roadImages = HashMap<TileInfo, RoadImage>()
@ -34,6 +37,8 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
protected val fogImage = ImageGetter.getImage("TerrainIcons/CrosshatchHexagon")
var yieldGroup = YieldGroup()
var showEntireMap = UnCivGame.Current.viewEntireMapForDebug
class RoadImage {
var roadStatus: RoadStatus = RoadStatus.None
var image: Image? = null
@ -46,21 +51,9 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
addCircleImage()
addFogImage(groupSize)
addCrosshairImage()
addBaseTerrainImage()
isTransform = false
}
private fun addBaseTerrainImage() {
val imagePath = "TerrainIcons/"+tileInfo.baseTerrain
if(!ImageGetter.imageExists(imagePath)) return
baseTerrainImage = ImageGetter.getImage(imagePath)
baseTerrainImage!!.run {
color.a=0.25f
setSize(40f,40f)
center(this@TileGroup)
}
addActor(baseTerrainImage)
}
private fun addCircleImage() {
circleImage.width = 50f
@ -135,13 +128,14 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
open fun update(isViewable: Boolean, showResourcesAndImprovements:Boolean, showSubmarine: Boolean) {
hideCircle()
if (!UnCivGame.Current.viewEntireMapForDebug
if (!showEntireMap
&& !tileInfo.tileMap.gameInfo.getCurrentPlayerCivilization().exploredTiles.contains(tileInfo.position)) {
hexagon.color = Color.BLACK
return
}
updateTerrainFeatureImage()
updateTerrainBaseImage()
updateCityImage()
updateTileColor(isViewable)
@ -159,7 +153,26 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
crosshairImage.isVisible = false
fogImage.toFront()
fogImage.isVisible = !(isViewable || UnCivGame.Current.viewEntireMapForDebug)
fogImage.isVisible = !(isViewable || showEntireMap)
}
private fun updateTerrainBaseImage() {
if (tileInfo.baseTerrain == baseTerrain) return
if(baseTerrainImage!=null){
baseTerrainImage!!.remove()
baseTerrainImage=null
}
val imagePath = "TerrainIcons/" + tileInfo.baseTerrain
if (!ImageGetter.imageExists(imagePath)) return
baseTerrainImage = ImageGetter.getImage(imagePath)
baseTerrainImage!!.run {
color.a = 0.25f
setSize(40f, 40f)
center(this@TileGroup)
}
addActor(baseTerrainImage)
}
private fun updateCityImage() {
@ -282,19 +295,20 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
}
private fun updateTerrainFeatureImage() {
if (terrainFeatureImage == null && tileInfo.terrainFeature != null) {
terrainFeatureImage = ImageGetter.getImage("TerrainIcons/${tileInfo.terrainFeature}.png")
addActor(terrainFeatureImage)
terrainFeatureImage!!.run {
setSize(30f, 30f)
setColor(1f, 1f, 1f, 0.5f)
center(this@TileGroup)
}
}
if (terrainFeatureImage != null && tileInfo.terrainFeature == null) {
terrainFeatureImage!!.remove()
if (tileInfo.terrainFeature != terrainFeature) {
terrainFeature = tileInfo.terrainFeature
if(terrainFeatureImage!=null) terrainFeatureImage!!.remove()
terrainFeatureImage = null
if(terrainFeature!=null) {
terrainFeatureImage = ImageGetter.getImage("TerrainIcons/$terrainFeature.png")
addActor(terrainFeatureImage)
terrainFeatureImage!!.run {
setSize(30f, 30f)
setColor(1f, 1f, 1f, 0.5f)
center(this@TileGroup)
}
}
}
}
@ -320,24 +334,24 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
}
private fun updateResourceImage(showResourcesAndImprovements: Boolean) {
val shouldDisplayResource = showResourcesAndImprovements
val shouldDisplayResource =
if(showEntireMap) tileInfo.resource!=null
else showResourcesAndImprovements
&& tileInfo.hasViewableResource(tileInfo.tileMap.gameInfo.getCurrentPlayerCivilization())
if (resourceImage != null && !shouldDisplayResource) {
resourceImage!!.remove()
resourceImage = null
if(resource!=tileInfo.resource){
resource=tileInfo.resource
if (resourceImage != null) resourceImage!!.remove()
resourceImage=null
}
if (resourceImage == null && shouldDisplayResource) { // Need to add the resource image!
if (resourceImage == null && shouldDisplayResource) { // This could happen on any turn, since resources need certain techs to reveal them
resourceImage = ImageGetter.getResourceImage(tileInfo.resource!!, 20f)
resourceImage!!.center(this)
resourceImage!!.x = resourceImage!!.x - 22 // left
resourceImage!!.y = resourceImage!!.y + 10 // top
addActor(resourceImage!!)
}
if (resourceImage != null) {
resourceImage!!.color = Color.WHITE.cpy().apply { a = 0.7f }
}
}
@ -375,6 +389,4 @@ open class TileGroup(var tileInfo: TileInfo) : Group() {
circleImage.isVisible = false
}
}

View File

@ -96,7 +96,7 @@ class WorldScreenTopBar(val screen: WorldScreen) : Table() {
menuButton.color = Color.WHITE
menuButton.onClick {
if(screen.stage.actors.none { it is WorldScreenOptionsTable })
screen.stage.addActor(WorldScreenOptionsTable())
WorldScreenOptionsTable(screen)
}
menuButton.centerY(this)
menuButton.x = menuButton.y

View File

@ -11,7 +11,7 @@ import com.unciv.ui.utils.ImageGetter
import com.unciv.ui.utils.center
import com.unciv.ui.utils.onClick
open class PopupTable: Table(CameraStageBaseScreen.skin){
open class PopupTable(val screen: CameraStageBaseScreen): Table(CameraStageBaseScreen.skin){
init {
val tileTableBackground = ImageGetter.getBackground(ImageGetter.getBlue().lerp(Color.BLACK, 0.5f))
background = tileTableBackground
@ -20,6 +20,12 @@ open class PopupTable: Table(CameraStageBaseScreen.skin){
this.defaults().pad(5f)
}
fun open(){
pack()
center(screen.stage)
screen.stage.addActor(this)
}
fun addButton(text:String, action:()->Unit){
val button = TextButton(text.tr(), skin).apply { color= ImageGetter.getBlue() }
button.onClick(action)
@ -28,7 +34,7 @@ open class PopupTable: Table(CameraStageBaseScreen.skin){
}
class YesNoPopupTable(question:String, action:()->Unit,
screen: CameraStageBaseScreen = UnCivGame.Current.worldScreen) : PopupTable(){
screen: CameraStageBaseScreen = UnCivGame.Current.worldScreen) : PopupTable(screen){
init{
if(!isOpen) {
isOpen=true
@ -36,9 +42,7 @@ class YesNoPopupTable(question:String, action:()->Unit,
add(TextButton("No".tr(), skin).apply { onClick { close() } })
add(TextButton("Yes".tr(), skin).apply { onClick { close(); action() } })
pack()
center(screen.stage)
screen.stage.addActor(this)
open()
}
}

View File

@ -30,15 +30,15 @@ class Language(val language:String){
}
}
class WorldScreenDisplayOptionsTable : PopupTable(){
class WorldScreenDisplayOptionsTable(screen:WorldScreen) : PopupTable(screen){
val languageSelectBox = SelectBox<Language>(skin)
init {
update()
open()
}
fun update() {
val settings = UnCivGame.Current.settings
settings.save()
@ -67,7 +67,7 @@ class WorldScreenDisplayOptionsTable : PopupTable(){
UnCivGame.Current.settings.save()
UnCivGame.Current.worldScreen = WorldScreen()
UnCivGame.Current.setWorldScreen()
UnCivGame.Current.worldScreen.stage.addActor(WorldScreenDisplayOptionsTable())
WorldScreenDisplayOptionsTable(UnCivGame.Current.worldScreen)
}
})
@ -109,11 +109,9 @@ class WorldScreenDisplayOptionsTable : PopupTable(){
"Do you want to download fonts for $spaceSplitLang?",
{
val downloading = PopupTable()
val downloading = PopupTable(screen)
downloading.add(Label("Downloading...", skin))
downloading.pack()
downloading.center(stage)
stage.addActor(downloading)
downloading.open()
Gdx.input.inputProcessor = null // no interaction until download is over
thread {
@ -122,7 +120,6 @@ class WorldScreenDisplayOptionsTable : PopupTable(){
// This means that we have to tell the table to create it on render.
shouldSelectLanguage = true
}
})
}
}
@ -149,7 +146,7 @@ class WorldScreenDisplayOptionsTable : PopupTable(){
UnCivGame.Current.worldScreen = WorldScreen()
UnCivGame.Current.setWorldScreen()
UnCivGame.Current.worldScreen.stage.addActor(WorldScreenDisplayOptionsTable())
WorldScreenDisplayOptionsTable(UnCivGame.Current.worldScreen)
}
var shouldSelectLanguage = false

View File

@ -3,16 +3,22 @@ package com.unciv.ui.worldscreen.optionstable
import com.unciv.UnCivGame
import com.unciv.models.gamebasics.tr
import com.unciv.ui.CivilopediaScreen
import com.unciv.ui.MapEditorScreen
import com.unciv.ui.NewGameScreen
import com.unciv.ui.VictoryScreen
import com.unciv.ui.pickerscreens.PolicyPickerScreen
import com.unciv.ui.saves.LoadScreen
import com.unciv.ui.saves.SaveScreen
import com.unciv.ui.utils.center
import com.unciv.ui.worldscreen.WorldScreen
class WorldScreenOptionsTable internal constructor() : PopupTable() {
class WorldScreenOptionsTable(val worldScreen: WorldScreen) : PopupTable(worldScreen) {
init {
addButton("Map editor - IN PROGRESS".tr()){
UnCivGame.Current.screen = MapEditorScreen()
remove()
}
addButton("Civilopedia".tr()){
UnCivGame.Current.screen = CivilopediaScreen()
remove()
@ -38,14 +44,13 @@ class WorldScreenOptionsTable internal constructor() : PopupTable() {
addButton("Display options".tr()){
UnCivGame.Current.worldScreen.stage.addActor(WorldScreenDisplayOptionsTable())
UnCivGame.Current.worldScreen.stage.addActor(WorldScreenDisplayOptionsTable(worldScreen))
remove()
}
addButton("Close".tr()){ remove() }
pack() // Needed to show the background.
center(UnCivGame.Current.worldScreen.stage)
open()
}
}