[Editor] Add support for importing maps from files
This commit is contained in:
@@ -18,6 +18,7 @@ class AssetsListView : View() {
|
|||||||
|
|
||||||
private val maps = AssetCategory("Maps").apply {
|
private val maps = AssetCategory("Maps").apply {
|
||||||
menuitem("New Map...") { mainController.createEmptyMap() }
|
menuitem("New Map...") { mainController.createEmptyMap() }
|
||||||
|
menuitem("Import Map...") { mainController.importMap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val tileSets = AssetCategory("Tile Sets").apply {
|
private val tileSets = AssetCategory("Tile Sets").apply {
|
||||||
|
|||||||
@@ -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.asset.GameMapAsset
|
||||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
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.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.GameMapBuilderVM
|
||||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||||
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
|
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
|
||||||
@@ -35,6 +36,7 @@ import javafx.scene.control.TextInputDialog
|
|||||||
import javafx.stage.FileChooser
|
import javafx.stage.FileChooser
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
import java.io.File
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
|
|
||||||
@Component
|
@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() {
|
fun openProject() {
|
||||||
chooseFile(
|
chooseFile(
|
||||||
title = "Load Project",
|
title = "Load Project",
|
||||||
@@ -262,6 +279,7 @@ class MainController : Controller() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun closeScript(fsNode: FileNode) {
|
fun closeScript(fsNode: FileNode) {
|
||||||
openItems.entries.firstOrNull { (_, item) -> item is CodeVM && item.fileNode.absolutePath == fsNode.absolutePath }?.key?.let {
|
openItems.entries.firstOrNull { (_, item) -> item is CodeVM && item.fileNode.absolutePath == fsNode.absolutePath }?.key?.let {
|
||||||
openItems.remove(it)
|
openItems.remove(it)
|
||||||
@@ -274,7 +292,6 @@ class MainController : Controller() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun clearResources() {
|
fun clearResources() {
|
||||||
openItems.clear()
|
openItems.clear()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ class MainMenuView : View() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menu("Import") {
|
menu("Import") {
|
||||||
|
item("Map...") {
|
||||||
|
action {
|
||||||
|
mainController.importMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
item("Tile Set...") {
|
item("Tile Set...") {
|
||||||
action {
|
action {
|
||||||
mainController.importTileSet()
|
mainController.importTileSet()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import javafx.beans.property.SimpleObjectProperty
|
|||||||
import javafx.beans.property.SimpleStringProperty
|
import javafx.beans.property.SimpleStringProperty
|
||||||
import tornadofx.getValue
|
import tornadofx.getValue
|
||||||
import tornadofx.setValue
|
import tornadofx.setValue
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class GameMapBuilder {
|
class GameMapBuilder {
|
||||||
val tileSetAssetProperty = SimpleObjectProperty<TileSetAsset>()
|
val tileSetAssetProperty = SimpleObjectProperty<TileSetAsset>()
|
||||||
@@ -22,4 +23,7 @@ class GameMapBuilder {
|
|||||||
|
|
||||||
val handlerProperty = SimpleStringProperty()
|
val handlerProperty = SimpleStringProperty()
|
||||||
var handler by handlerProperty
|
var handler by handlerProperty
|
||||||
|
|
||||||
|
val fileProperty = SimpleStringProperty("")
|
||||||
|
var file by fileProperty
|
||||||
}
|
}
|
||||||
@@ -2,5 +2,9 @@ package com.bartlomiejpluta.base.editor.map.serial
|
|||||||
|
|
||||||
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
|
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
|
||||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
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
|
||||||
|
}
|
||||||
@@ -35,6 +35,21 @@ class ProtobufMapDeserializer : MapDeserializer {
|
|||||||
return map
|
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 {
|
private fun deserializeLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
|
||||||
return when {
|
return when {
|
||||||
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)
|
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,4 +20,7 @@ class GameMapBuilderVM : ItemViewModel<GameMapBuilder>(GameMapBuilder()) {
|
|||||||
|
|
||||||
val handlerProperty = bind(GameMapBuilder::handlerProperty, autocommit = true)
|
val handlerProperty = bind(GameMapBuilder::handlerProperty, autocommit = true)
|
||||||
var handler by handlerProperty
|
var handler by handlerProperty
|
||||||
|
|
||||||
|
val fileProperty = bind(GameMapBuilder::fileProperty, autocommit = true)
|
||||||
|
var file by fileProperty
|
||||||
}
|
}
|
||||||
@@ -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 {
|
override fun loadMap(uid: String) = project?.let {
|
||||||
val asset = it.maps.firstOrNull { map -> map.uid == uid }
|
val asset = it.maps.firstOrNull { map -> map.uid == uid }
|
||||||
?: throw IllegalStateException("The map with uid [$uid] does not exist ")
|
?: throw IllegalStateException("The map with uid [$uid] does not exist ")
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface ProjectContext {
|
|||||||
fun createNewProject(project: Project)
|
fun createNewProject(project: Project)
|
||||||
|
|
||||||
fun importMap(name: String, map: GameMap)
|
fun importMap(name: String, map: GameMap)
|
||||||
|
fun importMapFromFile(name: String, handler: String, file: File, tileSet: TileSet): GameMap
|
||||||
fun loadMap(uid: String): GameMap
|
fun loadMap(uid: String): GameMap
|
||||||
fun saveMap(map: GameMap)
|
fun saveMap(map: GameMap)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user