[Editor] Add support for importing maps from files

This commit is contained in:
2021-11-28 18:37:26 +01:00
parent 225dd84b21
commit 67a131bd3e
11 changed files with 156 additions and 2 deletions

View File

@@ -18,6 +18,7 @@ class AssetsListView : View() {
private val maps = AssetCategory("Maps").apply {
menuitem("New Map...") { mainController.createEmptyMap() }
menuitem("Import Map...") { mainController.importMap() }
}
private val tileSets = AssetCategory("Tile Sets").apply {

View File

@@ -23,6 +23,7 @@ import com.bartlomiejpluta.base.editor.image.viewmodel.ImageAssetDataVM
import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.editor.map.view.wizard.MapCreationWizard
import com.bartlomiejpluta.base.editor.map.view.wizard.MapImportWizard
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapBuilderVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
@@ -35,6 +36,7 @@ import javafx.scene.control.TextInputDialog
import javafx.stage.FileChooser
import org.springframework.stereotype.Component
import tornadofx.*
import java.io.File
import kotlin.collections.set
@Component
@@ -78,6 +80,21 @@ class MainController : Controller() {
}
}
fun importMap() {
val scope = UndoableScope()
val vm = GameMapBuilderVM()
setInScope(vm, scope)
find<MapImportWizard>(scope).apply {
onComplete {
val tileSet = projectContext.loadTileSet(vm.tileSetAsset.uid)
val map = projectContext.importMapFromFile(vm.name, vm.handler, File(vm.file), tileSet)
openItems[scope] = GameMapVM(map)
}
openModal(block = true, resizable = false)
}
}
fun openProject() {
chooseFile(
title = "Load Project",
@@ -262,6 +279,7 @@ class MainController : Controller() {
}
}
fun closeScript(fsNode: FileNode) {
openItems.entries.firstOrNull { (_, item) -> item is CodeVM && item.fileNode.absolutePath == fsNode.absolutePath }?.key?.let {
openItems.remove(it)
@@ -274,7 +292,6 @@ class MainController : Controller() {
}
}
fun clearResources() {
openItems.clear()
}

View File

@@ -40,6 +40,12 @@ class MainMenuView : View() {
}
menu("Import") {
item("Map...") {
action {
mainController.importMap()
}
}
item("Tile Set...") {
action {
mainController.importTileSet()

View File

@@ -6,6 +6,7 @@ import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.setValue
import java.io.File
class GameMapBuilder {
val tileSetAssetProperty = SimpleObjectProperty<TileSetAsset>()
@@ -22,4 +23,7 @@ class GameMapBuilder {
val handlerProperty = SimpleStringProperty()
var handler by handlerProperty
val fileProperty = SimpleStringProperty("")
var file by fileProperty
}

View File

@@ -2,5 +2,9 @@ package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import java.io.InputStream
interface MapDeserializer : Deserializer<GameMap>
interface MapDeserializer : Deserializer<GameMap> {
fun deserialize(input: InputStream, tileSet: TileSet): GameMap
}

View File

@@ -35,6 +35,21 @@ class ProtobufMapDeserializer : MapDeserializer {
return map
}
override fun deserialize(input: InputStream, tileSet: TileSet): GameMap {
val proto = GameMapProto.GameMap.parseFrom(input)
val map = GameMap(tileSet)
map.uid = proto.uid
map.rows = proto.rows
map.columns = proto.columns
map.handler = proto.handler
proto.layersList
.filter { it.hasTileLayer() || it.hasObjectLayer() || it.hasColorLayer() }
.forEach { map.layers.add(deserializeLayer(map.rows, map.columns, tileSet, it)) }
return map
}
private fun deserializeLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
return when {
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)

View File

@@ -0,0 +1,65 @@
package com.bartlomiejpluta.base.editor.map.view.wizard
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapBuilderVM
import javafx.beans.binding.Bindings
import javafx.stage.FileChooser
import tornadofx.*
import java.io.File
class MapImportBasicDataView : View("Basic Data") {
private val mapBuilderVM = find<GameMapBuilderVM>()
override val complete = mapBuilderVM.valid(
mapBuilderVM.fileProperty,
mapBuilderVM.nameProperty,
mapBuilderVM.rowsProperty,
mapBuilderVM.columnsProperty,
mapBuilderVM.handlerProperty
)
override val root = form {
fieldset("Map Settings") {
field("Map file") {
hbox {
textfield(mapBuilderVM.fileProperty) {
trimWhitespace()
whenDocked { requestFocus() }
mapBuilderVM.validationContext.addValidator(this, mapBuilderVM.fileProperty) {
when {
it.isNullOrBlank() -> error("Field is required")
!File(it).exists() -> error("Provide valid path to the file")
else -> null
}
}
}
button("Choose") {
action {
mapBuilderVM.fileProperty.value = chooseFile(
title = "Map file location",
filters = arrayOf(FileChooser.ExtensionFilter("Map files (*.dat)", "*.dat"))
).getOrNull(0)?.absolutePath
}
}
}
}
label("Only tile, object and color layers will be imported. Any image layers will be dropped out.")
field("Map name") {
textfield(mapBuilderVM.nameProperty) {
required()
}
}
field("Map Handler class") {
textfield(mapBuilderVM.handlerProperty) {
required()
trimWhitespace()
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
package com.bartlomiejpluta.base.editor.map.view.wizard
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapBuilderVM
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.Wizard
class MapImportWizard : Wizard("Import Map", "Provide map information") {
private val mapBuilderVM = find<GameMapBuilderVM>()
init {
graphic = FontIcon("fa-map").apply {
iconSize = 40
}
add(MapImportBasicDataView::class)
add(MapTileSetSelectionView::class)
}
}

View File

@@ -20,4 +20,7 @@ class GameMapBuilderVM : ItemViewModel<GameMapBuilder>(GameMapBuilder()) {
val handlerProperty = bind(GameMapBuilder::handlerProperty, autocommit = true)
var handler by handlerProperty
val fileProperty = bind(GameMapBuilder::fileProperty, autocommit = true)
var file by fileProperty
}

View File

@@ -113,6 +113,26 @@ class DefaultProjectContext : ProjectContext {
}
}
override fun importMapFromFile(name: String, handler: String, file: File, tileSet: TileSet) =
project?.let { project ->
val map = file.inputStream().use { mapDeserializer.deserialize(it, tileSet) }
UID.next(project.maps.map(Asset::uid)).let { uid ->
val asset = GameMapAsset(project, uid, name)
map.uid = uid
project.maps += asset
save()
javaClassService.createClassFile(handler, project.codeFSNode, "map_handler.ftl") { model ->
model["mapUid"] = uid
}
File(project.mapsDirectory, asset.source).outputStream().use { fos -> mapSerializer.serialize(map, fos) }
map
}
} ?: throw IllegalStateException("There is no open project in the context")
override fun loadMap(uid: String) = project?.let {
val asset = it.maps.firstOrNull { map -> map.uid == uid }
?: throw IllegalStateException("The map with uid [$uid] does not exist ")

View File

@@ -28,6 +28,7 @@ interface ProjectContext {
fun createNewProject(project: Project)
fun importMap(name: String, map: GameMap)
fun importMapFromFile(name: String, handler: String, file: File, tileSet: TileSet): GameMap
fun loadMap(uid: String): GameMap
fun saveMap(map: GameMap)