[Editor] Improve generating asset index class for map assets

This commit is contained in:
2022-08-23 17:55:29 +02:00
parent b8400bd23b
commit 09af7d0f8a
4 changed files with 203 additions and 16 deletions

View File

@@ -1,11 +1,14 @@
package com.bartlomiejpluta.base.editor.code.build.generator
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset
import com.bartlomiejpluta.base.editor.map.serial.MapDeserializer
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
import com.bartlomiejpluta.base.editor.project.model.Project
import com.squareup.javapoet.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.util.*
import javax.lang.model.element.Modifier
@Component
@@ -14,13 +17,16 @@ class AssetMapCodeGenerator : CodeGenerator {
@Autowired
private lateinit var projectContext: ProjectContext
@Autowired
private lateinit var mapDeserializer: MapDeserializer
override fun generate() {
projectContext.project?.let(::generateAssetClasses)
}
private fun generateAssetClasses(project: Project) {
listOf(
generateAssetClass("maps", project.maps),
generateMapAssetClass("maps", project.maps),
generateAssetClass("tilesets", project.tileSets),
generateAssetClass("charsets", project.characterSets),
generateAssetClass("images", project.images),
@@ -44,11 +50,13 @@ class AssetMapCodeGenerator : CodeGenerator {
.classBuilder(className)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addField(
FieldSpec.builder(ParameterizedTypeName.get(
ClassName.get(java.util.Map::class.java),
ClassName.get(String::class.java),
className
), "_map", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
FieldSpec.builder(
ParameterizedTypeName.get(
ClassName.get(java.util.Map::class.java),
ClassName.get(String::class.java),
className
), "_map", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL
)
.initializer("new \$T<>()", java.util.HashMap::class.java)
.build()
)
@@ -91,8 +99,176 @@ class AssetMapCodeGenerator : CodeGenerator {
.build()
}
private fun getAssetName(asset: Asset) = asset.name
private fun generateMapAssetClass(name: String, assets: List<GameMapAsset>): TypeSpec {
val className = ClassName.get("A", name)
val mapLayers = assets
.map { asset -> asset to mapDeserializer.deserialize(asset.file.inputStream()) }
.associate { (asset, map) -> asset to map.layers }
val abstractAssetClassName = ClassName.get("", "GameMapAsset")
return TypeSpec
.classBuilder(className)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addField(
FieldSpec.builder(
ParameterizedTypeName.get(
ClassName.get(java.util.Map::class.java),
ClassName.get(String::class.java),
abstractAssetClassName
), "_maps", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL
)
.initializer("new \$T<>()", java.util.HashMap::class.java)
.build()
)
.addField(
FieldSpec.builder(
ParameterizedTypeName.get(
ClassName.get(java.util.Map::class.java),
ClassName.get(String::class.java),
ClassName.get(Integer::class.java)
), "_layers", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL
)
.initializer("new \$T<>()", java.util.HashMap::class.java)
.build()
)
.addStaticBlock(CodeBlock.builder()
.apply {
assets.forEach {
addStatement("_maps.put(\"${it.name}\", ${getAssetName(it)})")
}
}
.apply {
mapLayers.forEach { asset, layers ->
layers.forEach { layer ->
addStatement(
"_layers.put(\"${asset.name}::${layer.name}\", ${getAssetName(asset)}.layers.${
getAssetName(
layer.name
)
})"
)
}
}
}
.build()
)
.addMethod(
MethodSpec
.methodBuilder("get")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
.returns(abstractAssetClassName)
.addParameter(TypeName.get(String::class.java), "name")
.addStatement("return _maps.get(name)")
.build()
)
.addMethod(
MethodSpec
.methodBuilder("getLayer")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
.returns(ClassName.get(Integer::class.java))
.addParameter(TypeName.get(String::class.java), "mapName")
.addParameter(TypeName.get(String::class.java), "layerName")
.addStatement("return _layers.get(mapName + \"::\" + layerName)")
.build()
)
.addType(
TypeSpec.classBuilder(abstractAssetClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.ABSTRACT)
.addField(String::class.java, "uid", Modifier.PUBLIC, Modifier.FINAL)
.addField(
FieldSpec.builder(
ParameterizedTypeName.get(
ClassName.get(java.util.Map::class.java),
ClassName.get(String::class.java),
ClassName.get(Integer::class.java)
), "_layers", Modifier.PROTECTED, Modifier.FINAL
)
.initializer("new \$T<>()", java.util.HashMap::class.java)
.build()
)
.addMethod(
MethodSpec.constructorBuilder()
.addParameter(ClassName.get(String::class.java), "uid")
.addStatement("this.uid = uid")
.build()
)
.addMethod(
MethodSpec.methodBuilder("get")
.addModifiers(Modifier.PUBLIC)
.addParameter(ClassName.get(String::class.java), "name")
.returns(ClassName.get(Integer::class.java))
.addStatement("return this._layers.get(name)")
.build()
)
.build()
)
.apply {
mapLayers.forEach { (asset, layers) ->
val assetClassName = ClassName.get("", "GameMapAsset_${getCapitalizedAssetName(asset)}")
val layersClassName = ClassName.get("", "GameMapAsset_Layers_${getCapitalizedAssetName(asset)}")
addField(
FieldSpec
.builder(assetClassName, getAssetName(asset), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("new \$T()", assetClassName)
.build()
)
addType(
TypeSpec.classBuilder(assetClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.superclass(abstractAssetClassName)
.addField(
FieldSpec.builder(layersClassName, "layers", Modifier.PUBLIC, Modifier.FINAL)
.initializer("new \$T()", layersClassName)
.build()
)
.addMethod(
MethodSpec.constructorBuilder()
.addStatement("super(\"${asset.uid}\")")
.apply {
layers.forEach { layer ->
addStatement("this._layers.put(\"${layer.name}\", layers.${getAssetName(layer.name)})")
}
}
.build()
)
.build()
)
addType(
TypeSpec.classBuilder(layersClassName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.apply {
layers.forEachIndexed { index, layer ->
addField(
FieldSpec.builder(
TypeName.INT,
getAssetName(layer.name),
Modifier.PUBLIC,
Modifier.FINAL
)
.initializer(index.toString())
.build()
)
}
}
.build()
)
}
}
.build()
}
private fun getAssetName(asset: Asset) = getAssetName(asset.name)
private fun getAssetName(name: String) = name
.split("\\s+".toRegex())
.joinToString("_") { it.lowercase() }
private fun getCapitalizedAssetName(asset: Asset) = getCapitalizedAssetName(asset.name)
private fun getCapitalizedAssetName(name: String) = name
.split("\\s+".toRegex())
.joinToString("") { part -> part.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } }
}

View File

@@ -13,6 +13,7 @@ import com.bartlomiejpluta.base.editor.map.model.layer.*
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
import javafx.collections.ObservableList
import javafx.scene.control.ListCell
import javafx.scene.control.ListView
import javafx.scene.control.cell.TextFieldListCell
@@ -35,7 +36,7 @@ class MapLayersView : View() {
isEditable = true
setCellFactory {
LayerListViewItem { layer, name ->
LayerListViewItem(mapVM.layers) { layer, name ->
RenameLayerCommand(layer, name).let {
it.execute()
undoRedoService.push(it, scope)
@@ -150,6 +151,7 @@ class MapLayersView : View() {
private class LayerListViewItemConverter(
private val cell: ListCell<Layer>,
private val layers: ObservableList<Layer>,
private val onUpdate: (layer: Layer, name: String) -> Unit
) : StringConverter<Layer>() {
override fun toString(layer: Layer?) = layer?.name ?: ""
@@ -160,15 +162,17 @@ class MapLayersView : View() {
// so that the Command object has access to the actual as well as the new value of layer name.
// That's why we are running the submission logic in the converter.
override fun fromString(string: String?): Layer = cell.item.apply {
string?.takeIf { it != name }?.let {
onUpdate(this, it)
}
string
?.takeIf { str -> str != name }
?.takeIf { str -> layers.none { it.name == str }}
?.let { onUpdate(this, it) }
}
}
private class LayerListViewItem(onUpdate: (layer: Layer, name: String) -> Unit) : TextFieldListCell<Layer>() {
private class LayerListViewItem(layers: ObservableList<Layer>, onUpdate: (layer: Layer, name: String) -> Unit) :
TextFieldListCell<Layer>() {
init {
converter = LayerListViewItemConverter(this, onUpdate)
converter = LayerListViewItemConverter(this, layers, onUpdate)
}
override fun updateItem(item: Layer?, empty: Boolean) {

View File

@@ -5,11 +5,11 @@ import com.bartlomiejpluta.base.editor.animation.asset.AnimationAssetData
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAsset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAssetData
import com.bartlomiejpluta.base.editor.characterset.asset.CharacterSetAsset
import com.bartlomiejpluta.base.editor.characterset.asset.CharacterSetAssetData
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.code.model.CodeType
import com.bartlomiejpluta.base.editor.code.service.JavaClassService
import com.bartlomiejpluta.base.editor.characterset.asset.CharacterSetAsset
import com.bartlomiejpluta.base.editor.characterset.asset.CharacterSetAssetData
import com.bartlomiejpluta.base.editor.file.model.FileNode
import com.bartlomiejpluta.base.editor.gui.font.asset.FontAsset
import com.bartlomiejpluta.base.editor.gui.font.asset.FontAssetData
@@ -108,6 +108,14 @@ class DefaultProjectContext : ProjectContext {
javaClassService.createClassFile(map.handler, it.codeFSNode, "map_handler.ftl") { model ->
model["mapUid"] = uid
model["mapName"] = name
model["mapCode"] = name.split("\\s+".toRegex()).joinToString("") { part ->
part.replaceFirstChar { c ->
if (c.isLowerCase()) c.titlecase(Locale.getDefault())
else c.toString()
}
}
model["map_code"] = name.split("\\s+".toRegex()).joinToString("_") { part -> part.lowercase() }
}
File(it.mapsDirectory, asset.source).outputStream().use { fos -> mapSerializer.serialize(map, fos) }

View File

@@ -7,7 +7,6 @@ import com.bartlomiejpluta.base.api.map.handler.MapHandler;
import com.bartlomiejpluta.base.api.screen.Screen;
public class ${className} implements MapHandler {
public static final String UID = "${mapUid}";
@Override
public void onCreate(Context context, GameMap map) {