[Editor] Enable full support for map labels
From now on, the editor is capable to put map labels in the canavs as well as generate the map labels in output map serialized file.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
package com.bartlomiejpluta.base.api.map.layer.object;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
@Builder
|
||||
public class MapPin {
|
||||
String map;
|
||||
int layer;
|
||||
int x;
|
||||
int y;
|
||||
}
|
||||
@@ -2,13 +2,17 @@ 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.model.layer.ObjectLayer
|
||||
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.time.Instant
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import javax.annotation.processing.Generated
|
||||
import javax.lang.model.element.Modifier
|
||||
|
||||
@Component
|
||||
@@ -46,10 +50,17 @@ class AssetMapCodeGenerator : CodeGenerator {
|
||||
}
|
||||
|
||||
private fun generateAssetClass(name: String, assets: List<Asset>): TypeSpec {
|
||||
val generatedAnnotation = AnnotationSpec.builder(Generated::class.java).addMember("value", "\$S", GENERATOR_NAME)
|
||||
.addMember("date", "\$S", DateTimeFormatter.ISO_INSTANT.format(Instant.now()))
|
||||
.addMember("comments", "\$S", "Utility class for $name assets")
|
||||
.build()
|
||||
|
||||
val className = ClassName.get("A", name)
|
||||
|
||||
return TypeSpec
|
||||
.classBuilder(className)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addAnnotation(generatedAnnotation)
|
||||
.addField(
|
||||
FieldSpec.builder(
|
||||
ParameterizedTypeName.get(
|
||||
@@ -69,13 +80,13 @@ class AssetMapCodeGenerator : CodeGenerator {
|
||||
}
|
||||
.build()
|
||||
)
|
||||
.addField(String::class.java, "uid", Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addField(String::class.java, "$", Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addMethod(
|
||||
MethodSpec
|
||||
.constructorBuilder()
|
||||
.addModifiers(Modifier.PRIVATE)
|
||||
.addParameter(TypeName.get(String::class.java), "uid")
|
||||
.addStatement("this.uid = uid")
|
||||
.addStatement("this.\$\$ = uid")
|
||||
.build()
|
||||
)
|
||||
.addMethod(
|
||||
@@ -101,15 +112,22 @@ class AssetMapCodeGenerator : CodeGenerator {
|
||||
}
|
||||
|
||||
private fun generateMapAssetClass(name: String, assets: List<GameMapAsset>): TypeSpec {
|
||||
val generatedAnnotation = AnnotationSpec.builder(Generated::class.java).addMember("value", "\$S", GENERATOR_NAME)
|
||||
.addMember("date", "\$S", DateTimeFormatter.ISO_INSTANT.format(Instant.now()))
|
||||
.addMember("comments", "\$S", "Utility class for $name assets")
|
||||
.build()
|
||||
|
||||
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")
|
||||
val abstractLayerClassName = ClassName.get("", "GameMapAssetLayer")
|
||||
|
||||
return TypeSpec
|
||||
.classBuilder(className)
|
||||
.addAnnotation(generatedAnnotation)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addField(
|
||||
FieldSpec.builder(
|
||||
@@ -122,67 +140,33 @@ class AssetMapCodeGenerator : CodeGenerator {
|
||||
.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")
|
||||
.methodBuilder("byName")
|
||||
.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(String::class.java, "$", 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)
|
||||
abstractLayerClassName
|
||||
), "_layers", Modifier.PROTECTED, Modifier.FINAL
|
||||
)
|
||||
.initializer("new \$T<>()", java.util.HashMap::class.java)
|
||||
@@ -191,23 +175,52 @@ class AssetMapCodeGenerator : CodeGenerator {
|
||||
.addMethod(
|
||||
MethodSpec.constructorBuilder()
|
||||
.addParameter(ClassName.get(String::class.java), "uid")
|
||||
.addStatement("this.uid = uid")
|
||||
.addStatement("this.\$\$ = uid")
|
||||
.build()
|
||||
)
|
||||
.addMethod(
|
||||
MethodSpec.methodBuilder("get")
|
||||
MethodSpec.methodBuilder("layer")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(ClassName.get(String::class.java), "name")
|
||||
.returns(ClassName.get(Integer::class.java))
|
||||
.returns(abstractLayerClassName)
|
||||
.addStatement("return this._layers.get(name)")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
).addType(
|
||||
TypeSpec.classBuilder(abstractLayerClassName)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.ABSTRACT)
|
||||
.addField(TypeName.INT, "$", Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addField(
|
||||
FieldSpec.builder(
|
||||
ParameterizedTypeName.get(
|
||||
ClassName.get(java.util.Map::class.java),
|
||||
ClassName.get(String::class.java),
|
||||
MAP_PIN_TYPE
|
||||
), "_labels", Modifier.PROTECTED, Modifier.FINAL
|
||||
)
|
||||
.initializer("new \$T<>()", java.util.HashMap::class.java)
|
||||
.build()
|
||||
)
|
||||
.addMethod(
|
||||
MethodSpec.constructorBuilder()
|
||||
.addParameter(TypeName.INT, "index")
|
||||
.addStatement("this.\$\$ = index")
|
||||
.build()
|
||||
)
|
||||
.addMethod(
|
||||
MethodSpec.methodBuilder("label")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.addParameter(ClassName.get(String::class.java), "label")
|
||||
.returns(MAP_PIN_TYPE)
|
||||
.addStatement("return this._labels.get(label)")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.apply {
|
||||
mapLayers.forEach { (asset, layers) ->
|
||||
val assetClassName = ClassName.get("", "GameMapAsset_${getCapitalizedAssetName(asset)}")
|
||||
val layersClassName = ClassName.get("", "GameMapAsset_Layers_${getCapitalizedAssetName(asset)}")
|
||||
val assetClassName = ClassName.get("", getCapitalizedAssetName(asset))
|
||||
|
||||
addField(
|
||||
FieldSpec
|
||||
@@ -220,36 +233,62 @@ class AssetMapCodeGenerator : CodeGenerator {
|
||||
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)})")
|
||||
addStatement("this._layers.put(\"${layer.name}\", this.${getAssetName(layer.name)})")
|
||||
}
|
||||
}
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
)
|
||||
addType(
|
||||
TypeSpec.classBuilder(layersClassName)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
|
||||
.apply {
|
||||
layers.forEachIndexed { index, layer ->
|
||||
val layerClass = ClassName.get("", getCapitalizedAssetName(layer.name))
|
||||
val type = TypeSpec.classBuilder(layerClass)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
|
||||
.superclass(abstractLayerClassName)
|
||||
.addMethod(
|
||||
MethodSpec
|
||||
.constructorBuilder()
|
||||
.addStatement("super($index)")
|
||||
.apply {
|
||||
if (layer is ObjectLayer) {
|
||||
layer.labels.forEach { label ->
|
||||
addStatement("this._labels.put(\"${label.label}\", this.${getAssetName(label.label)})")
|
||||
}
|
||||
}
|
||||
}
|
||||
.build()
|
||||
)
|
||||
//.addField(FieldSpec.builder(TypeName.INT, "$", Modifier.PUBLIC, Modifier.FINAL).initializer(index.toString()).build())
|
||||
.apply {
|
||||
if (layer is ObjectLayer) {
|
||||
layer.labels.forEach { label ->
|
||||
addField(FieldSpec.builder(MAP_PIN_TYPE, getAssetName(label.label), Modifier.FINAL, Modifier.PUBLIC)
|
||||
.initializer("\$T.builder()" +
|
||||
".map(\"${asset.uid}\")" +
|
||||
".layer(${index})" +
|
||||
".x(${label.x})" +
|
||||
".y(${label.y})" +
|
||||
".build()", MAP_PIN_TYPE)
|
||||
.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
addType(type)
|
||||
|
||||
addField(
|
||||
FieldSpec.builder(
|
||||
TypeName.INT,
|
||||
layerClass,
|
||||
getAssetName(layer.name),
|
||||
Modifier.PUBLIC,
|
||||
Modifier.FINAL
|
||||
)
|
||||
.initializer(index.toString())
|
||||
.initializer("new \$T()", layerClass)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
@@ -272,4 +311,9 @@ class AssetMapCodeGenerator : CodeGenerator {
|
||||
private fun getCapitalizedAssetName(name: String) = name
|
||||
.split("\\s+".toRegex())
|
||||
.joinToString("") { part -> part.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } }
|
||||
|
||||
companion object {
|
||||
val MAP_PIN_TYPE = ClassName.get("com.bartlomiejpluta.base.api.map.layer.object", "MapPin")
|
||||
val GENERATOR_NAME = AssetMapCodeGenerator::class.java.canonicalName
|
||||
}
|
||||
}
|
||||
@@ -381,12 +381,11 @@ class DataAccessObjectCodeGenerator : CodeGenerator {
|
||||
.addStatement("var list = new \$T<\$T>()", LinkedList::class.java, model)
|
||||
.beginControlFlow("\$T.INSTANCE.getContext().withDatabase(db ->", CONTEXT_HOLDER)
|
||||
.addStatement(
|
||||
"var filter = filters.stream().map(f -> String.format(\"`%s` %s ?\", f.column.column, f.op.getOp())).collect(\$T.joining(\" AND \"))",
|
||||
COLLECTORS_CLASS
|
||||
"var filter = filters.isEmpty() ? \"\" : \" WHERE \" + filters.stream().map(f -> String.format(\"`%s` %s ?\", f.column.column, f.op.getOp())).collect(\$T.joining(\" AND \"))", COLLECTORS_CLASS
|
||||
)
|
||||
.addStatement("var order = ordering.isEmpty() ? \"\" : \" ORDER BY \" + ordering.stream().collect(\$T.joining(\", \"))", COLLECTORS_CLASS)
|
||||
.apply {
|
||||
val sql = "SELECT * FROM `${table.name}` WHERE "
|
||||
val sql = "SELECT * FROM `${table.name}` "
|
||||
addStatement("var statement = db.prepareStatement(\"$sql\" + filter + order)")
|
||||
}
|
||||
.addStatement("var i = 1")
|
||||
@@ -407,6 +406,39 @@ class DataAccessObjectCodeGenerator : CodeGenerator {
|
||||
.addStatement("return list")
|
||||
.build()
|
||||
)
|
||||
.addMethod(
|
||||
MethodSpec.methodBuilder("first")
|
||||
.addModifiers(Modifier.PUBLIC)
|
||||
.returns(model)
|
||||
.addStatement("var list = new \$T<\$T>()", LinkedList::class.java, model)
|
||||
.beginControlFlow("return \$T.INSTANCE.getContext().withDatabase(db ->", CONTEXT_HOLDER)
|
||||
.addStatement(
|
||||
"var filter = filters.isEmpty() ? \"\" : \" WHERE \" + filters.stream().map(f -> String.format(\"`%s` %s ?\", f.column.column, f.op.getOp())).collect(\$T.joining(\" AND \"))",
|
||||
COLLECTORS_CLASS
|
||||
)
|
||||
.addStatement("var order = ordering.isEmpty() ? \"\" : \" ORDER BY \" + ordering.stream().collect(\$T.joining(\", \"))", COLLECTORS_CLASS)
|
||||
.apply {
|
||||
val sql = "SELECT * FROM `${table.name}` "
|
||||
addStatement("var statement = db.prepareStatement(\"$sql\" + filter + order + \" FETCH FIRST ROW ONLY\")")
|
||||
}
|
||||
.addStatement("var i = 1")
|
||||
.beginControlFlow("for (var f : filters)")
|
||||
.addStatement("statement.setObject(i++, f.value)")
|
||||
.endControlFlow()
|
||||
.addStatement("var result = statement.executeQuery()")
|
||||
.beginControlFlow("if(result.next())")
|
||||
.addStatement("var model = ${model.simpleName()}.builder()")
|
||||
.apply {
|
||||
table.columns.forEach { column ->
|
||||
addStatement("model.${snakeToCamelCase(column.name)}(result.${dbToGetMethod(column)}(\"${column.name}\"))")
|
||||
}
|
||||
}
|
||||
.addStatement("return model.build()")
|
||||
.endControlFlow()
|
||||
.addStatement("return null")
|
||||
.endControlFlow(")")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
|
||||
@@ -37,9 +37,11 @@ class MapObjectsCodeGenerator : CodeGenerator {
|
||||
private fun generateMapObjects(asset: GameMapAsset, map: GameMap, project: Project) {
|
||||
val runner = className(project.runner)
|
||||
|
||||
map.layers
|
||||
.mapNotNull { it as? ObjectLayer }
|
||||
.forEach { generateLayerClass(project.buildGeneratedCodeDirectory, asset, map, it, runner) }
|
||||
map.layers.forEachIndexed { index, layer ->
|
||||
if (layer is ObjectLayer) {
|
||||
generateLayerClass(project.buildGeneratedCodeDirectory, asset, map, layer, index, runner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateLayerClass(
|
||||
@@ -47,6 +49,7 @@ class MapObjectsCodeGenerator : CodeGenerator {
|
||||
asset: GameMapAsset,
|
||||
map: GameMap,
|
||||
layer: ObjectLayer,
|
||||
layerIndex: Int,
|
||||
runner: ClassName
|
||||
) {
|
||||
val packageName = "com.bartlomiejpluta.base.generated.map"
|
||||
@@ -77,6 +80,7 @@ class MapObjectsCodeGenerator : CodeGenerator {
|
||||
.addParameter(EngineObjectLayer::class.java, "layer", Modifier.FINAL)
|
||||
.addParameter(TypeName.INT, "x", Modifier.FINAL)
|
||||
.addParameter(TypeName.INT, "y", Modifier.FINAL)
|
||||
.addParameter(MAP_PIN_TYPE, "here", Modifier.FINAL)
|
||||
.addCode(it.code)
|
||||
.build()
|
||||
.let(generatedClass::addMethod)
|
||||
@@ -93,7 +97,7 @@ class MapObjectsCodeGenerator : CodeGenerator {
|
||||
.addStatement("var customHandler = (\$T) handler", handler)
|
||||
|
||||
layer.objects.forEach {
|
||||
runMethod.addStatement("_${it.x}x${it.y}(context, customRunner, customHandler, map, layer, ${it.x}, ${it.y})")
|
||||
runMethod.addStatement("_${it.x}x${it.y}(context, customRunner, customHandler, map, layer, ${it.x}, ${it.y}, \$T.builder().map(\"${map.uid}\").layer($layerIndex).x(${it.x}).y(${it.y}).build())", MAP_PIN_TYPE)
|
||||
}
|
||||
|
||||
generatedClass
|
||||
@@ -132,5 +136,6 @@ class MapObjectsCodeGenerator : CodeGenerator {
|
||||
|
||||
companion object {
|
||||
private val GENERATOR_NAME = MapObjectsCodeGenerator::class.java.canonicalName
|
||||
val MAP_PIN_TYPE = ClassName.get("com.bartlomiejpluta.base.api.map.layer.object", "MapPin")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.bartlomiejpluta.base.editor.common.view
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.*
|
||||
|
||||
class StringInputFragment : Fragment("Enter value") {
|
||||
val valueProperty = SimpleStringProperty()
|
||||
var value by valueProperty
|
||||
|
||||
private var onCompleteConsumer: ((String) -> Unit)? = null
|
||||
|
||||
fun onComplete(consumer: (String) -> Unit) {
|
||||
this.onCompleteConsumer = consumer
|
||||
}
|
||||
|
||||
|
||||
override val root = borderpane {
|
||||
center = textfield(valueProperty) {
|
||||
whenDocked { requestFocus() }
|
||||
}
|
||||
|
||||
bottom = buttonbar {
|
||||
button("Apply") {
|
||||
action {
|
||||
onCompleteConsumer?.let { it(value) }
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
button("Cancel") {
|
||||
action {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.bartlomiejpluta.base.editor.common.viewmodel
|
||||
|
||||
import tornadofx.*
|
||||
|
||||
class StringVM(value: String = "") : ViewModel() {
|
||||
val valueProperty = value.toProperty()
|
||||
val value by valueProperty
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.common.view.StringInputFragment
|
||||
import com.bartlomiejpluta.base.editor.common.viewmodel.StringVM
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.ObjectLayer
|
||||
import com.bartlomiejpluta.base.editor.map.model.obj.MapLabel
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
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 com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.scene.input.MouseButton
|
||||
import tornadofx.Scope
|
||||
import tornadofx.find
|
||||
import tornadofx.setInScope
|
||||
|
||||
class LabelPaintingTrace(
|
||||
private val projectContext: ProjectContext,
|
||||
private val map: GameMapVM,
|
||||
override val commandName: String
|
||||
) : PaintingTrace {
|
||||
private lateinit var labels: ObservableList<MapLabel>
|
||||
private lateinit var event: MapMouseEvent
|
||||
|
||||
private var newLabel: MapLabel? = null
|
||||
private var formerLabel: MapLabel? = null
|
||||
private var x = 0
|
||||
private var y = 0
|
||||
|
||||
override var executed = false
|
||||
private set
|
||||
|
||||
override fun beginTrace(editorStateVM: EditorStateVM, brushVM: BrushVM, mouseEvent: MapMouseEvent) {
|
||||
x = editorStateVM.cursorColumn
|
||||
y = editorStateVM.cursorRow
|
||||
|
||||
if (y >= map.rows || x >= map.columns || y < 0 || x < 0 || editorStateVM.selectedLayerIndex < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
labels = (editorStateVM.selectedLayer as ObjectLayer).labels
|
||||
formerLabel = labels.firstOrNull { it.x == x && it.y == y }
|
||||
|
||||
event = mouseEvent
|
||||
}
|
||||
|
||||
private fun createOrUpdateLabel() {
|
||||
showCodeDialog(formerLabel?.label ?: "")?.let {
|
||||
newLabel = MapLabel(x, y, it)
|
||||
labels.remove(formerLabel)
|
||||
labels.add(newLabel)
|
||||
executed = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCodeDialog(initialContent: String): String? {
|
||||
val scope = Scope()
|
||||
val vm = StringVM(initialContent)
|
||||
setInScope(vm, scope)
|
||||
|
||||
var content: String? = null
|
||||
|
||||
find<StringInputFragment>(scope).apply {
|
||||
title = "Set label"
|
||||
|
||||
onComplete {
|
||||
content = it
|
||||
}
|
||||
|
||||
openModal(block = true)
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
private fun moveLabel(newX: Int, newY: Int) {
|
||||
if (newY >= map.rows || newX >= map.columns || newY < 0 || newX < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
formerLabel?.let {
|
||||
newLabel = MapLabel(newX, newY, it.label)
|
||||
labels.remove(formerLabel)
|
||||
labels.add(newLabel)
|
||||
executed = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeLabel() {
|
||||
labels.remove(formerLabel)
|
||||
executed = true
|
||||
}
|
||||
|
||||
override fun proceedTrace(editorStateVM: EditorStateVM, brushVM: BrushVM, mouseEvent: MapMouseEvent) {
|
||||
}
|
||||
|
||||
override fun commitTrace(editorStateVM: EditorStateVM, brushVM: BrushVM, mouseEvent: MapMouseEvent) {
|
||||
val newX = editorStateVM.cursorColumn
|
||||
val newY = editorStateVM.cursorRow
|
||||
val dx = newX - x
|
||||
val dy = newY - y
|
||||
|
||||
// Moving
|
||||
if (brushVM.mode == BrushMode.PAINTING_MODE && (dx != 0 || dy != 0)) {
|
||||
moveLabel(newX, newY)
|
||||
return
|
||||
}
|
||||
|
||||
// Creating new or updating existing one or removing
|
||||
if (event.event.clickCount > 1 && brushVM.mode == BrushMode.PAINTING_MODE) {
|
||||
when (event.button) {
|
||||
MouseButton.PRIMARY -> createOrUpdateLabel()
|
||||
MouseButton.SECONDARY -> removeLabel()
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Removing
|
||||
if (brushVM.mode == BrushMode.ERASING_MODE) {
|
||||
removeLabel()
|
||||
}
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
labels.remove(newLabel)
|
||||
formerLabel?.let(labels::add)
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
labels.remove(formerLabel)
|
||||
newLabel?.let(labels::add)
|
||||
}
|
||||
|
||||
override val supportedButtons = arrayOf(MouseButton.PRIMARY, MouseButton.SECONDARY)
|
||||
}
|
||||
@@ -6,9 +6,12 @@ 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.render.model.Renderable
|
||||
import javafx.geometry.VPos
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.image.WritableImage
|
||||
import javafx.scene.paint.Color
|
||||
import javafx.scene.text.Font
|
||||
import javafx.scene.text.TextAlignment
|
||||
|
||||
|
||||
class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, private val painter: MapPainter) :
|
||||
@@ -157,6 +160,7 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr
|
||||
}
|
||||
|
||||
renderObjects(gc, objectLayer)
|
||||
renderLabels(gc, objectLayer)
|
||||
}
|
||||
|
||||
private fun renderObjectPassageMap(gc: GraphicsContext, objectLayer: ObjectLayer) {
|
||||
@@ -196,6 +200,60 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderLabels(gc: GraphicsContext, objectLayer: ObjectLayer) {
|
||||
val alpha = gc.globalAlpha
|
||||
val fill = gc.fill
|
||||
val width = gc.lineWidth
|
||||
val align = gc.textAlign
|
||||
val baseLine = gc.textBaseline
|
||||
val font = gc.font
|
||||
|
||||
// gc.font = LABEL_FONT
|
||||
|
||||
for (mapLabel in objectLayer.labels) {
|
||||
|
||||
// gc.globalAlpha = OBJECT_FILL_ALPHA
|
||||
// gc.fill = OBJECT_COLOR
|
||||
//
|
||||
// gc.fillRect(
|
||||
// mapObject.x * tileWidth + OBJECT_MARGIN,
|
||||
// mapObject.y * tileHeight + OBJECT_MARGIN,
|
||||
// tileWidth - 2 * OBJECT_MARGIN,
|
||||
// tileHeight - 2 * OBJECT_MARGIN
|
||||
// )
|
||||
|
||||
gc.globalAlpha = 1.0
|
||||
gc.stroke = LABEL_COLOR
|
||||
gc.fill = LABEL_COLOR
|
||||
gc.lineWidth = LABEL_FONT_WIDTH
|
||||
|
||||
gc.textAlign = TextAlignment.CENTER
|
||||
gc.textBaseline = VPos.CENTER
|
||||
|
||||
gc.fillText(
|
||||
"${mapLabel.label[0]}.${mapLabel.label[mapLabel.label.lastIndex]}",
|
||||
mapLabel.x * tileWidth + tileWidth / 2,
|
||||
mapLabel.y * tileHeight + tileHeight / 2
|
||||
)
|
||||
|
||||
gc.lineWidth = LABEL_WIDTH
|
||||
|
||||
gc.strokeRect(
|
||||
mapLabel.x * tileWidth + LABEL_MARGIN + LABEL_WIDTH/2,
|
||||
mapLabel.y * tileHeight + LABEL_MARGIN + LABEL_WIDTH/2,
|
||||
tileWidth - 2 * LABEL_MARGIN - LABEL_WIDTH/2,
|
||||
tileHeight - 2 * LABEL_MARGIN - LABEL_WIDTH/2
|
||||
)
|
||||
}
|
||||
|
||||
gc.globalAlpha = alpha
|
||||
gc.fill = fill
|
||||
gc.lineWidth = width
|
||||
gc.textAlign = align
|
||||
gc.textBaseline = baseLine
|
||||
gc.font = font
|
||||
}
|
||||
|
||||
private fun renderColorLayer(gc: GraphicsContext, colorLayer: ColorLayer) {
|
||||
val alpha = gc.globalAlpha
|
||||
val color = gc.fill
|
||||
@@ -238,7 +296,11 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr
|
||||
private val BACKGROUND_COLOR2 = Color.color(0.8, 0.8, 0.8, 1.0)
|
||||
|
||||
private val OBJECT_COLOR = Color.WHITE
|
||||
private val LABEL_COLOR = Color.CYAN
|
||||
private val LABEL_WIDTH = 2.0
|
||||
private val LABEL_FONT_WIDTH = 4.0
|
||||
private const val OBJECT_FILL_ALPHA = 0.3
|
||||
private const val OBJECT_MARGIN = 4
|
||||
private const val LABEL_MARGIN = 1
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@ class MapPainter(
|
||||
is AutoTileLayer -> AutoTilePaintingTrace(mapVM, "Paint trace")
|
||||
is ObjectLayer -> when (brushVM.tool) {
|
||||
BrushTool.DEFAULT -> ObjectPaintingTrace(projectContext, mapVM, "Update object")
|
||||
BrushTool.LABEL -> LabelPaintingTrace(projectContext, mapVM, "Update label")
|
||||
else -> PassageAbilityPaintingTrace(mapVM, "Toggle passage")
|
||||
}
|
||||
is ImageLayer -> ImagePositionPaintingTrace(mapVM, "Move Image Layer")
|
||||
|
||||
@@ -84,10 +84,12 @@ class ObjectPaintingTrace(
|
||||
get() = """
|
||||
/*
|
||||
* Following final parameters are available to use:
|
||||
* x: int - the x coordinate of tile the object has been created on
|
||||
* y: int - the y coordinate of tile the object has been created on
|
||||
* layer: ObjectLayer - current object layer
|
||||
* map: GameMap - current map
|
||||
* here: MapPin - the composite object containing current map UID,
|
||||
* layer's index and x,y coordinates of the current tile
|
||||
* x: int - the x coordinate of the current tile
|
||||
* y: int - the y coordinate of the current tile
|
||||
* layer: ObjectLayer - current object layer's index
|
||||
* map: GameMap - current map
|
||||
* handler: ${className(map.handler)} - current map handler
|
||||
* runner: ${className(projectContext.project?.runner)} - the game runner of the project
|
||||
* context: Context - the game context
|
||||
|
||||
@@ -4,5 +4,6 @@ enum class BrushTool {
|
||||
DEFAULT,
|
||||
|
||||
// Object Layer
|
||||
PASSAGE
|
||||
PASSAGE,
|
||||
LABEL
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.layer
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.enumeration.PassageAbility
|
||||
import com.bartlomiejpluta.base.editor.map.model.obj.MapLabel
|
||||
import com.bartlomiejpluta.base.editor.map.model.obj.MapObject
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.asObservable
|
||||
@@ -14,13 +15,16 @@ class ObjectLayer(
|
||||
columns: Int,
|
||||
objects: List<MapObject> = mutableListOf(),
|
||||
javaImports: String = "",
|
||||
passageMap: Array<Array<PassageAbility>> = Array(rows) { Array(columns) { PassageAbility.ALLOW } }
|
||||
passageMap: Array<Array<PassageAbility>> = Array(rows) { Array(columns) { PassageAbility.ALLOW } },
|
||||
labels: List<MapLabel> = mutableListOf()
|
||||
) : Layer {
|
||||
var passageMap = passageMap
|
||||
private set
|
||||
|
||||
val objects = objects.asObservable()
|
||||
|
||||
val labels = labels.asObservable()
|
||||
|
||||
override val nameProperty = SimpleStringProperty(name)
|
||||
|
||||
val javaImportsProperty = javaImports.toProperty()
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.obj
|
||||
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
import tornadofx.toProperty
|
||||
|
||||
class MapLabel(x: Int, y: Int, label: String) {
|
||||
val xProperty = x.toProperty()
|
||||
var x by xProperty
|
||||
|
||||
val yProperty = y.toProperty()
|
||||
var y by yProperty
|
||||
|
||||
val labelProperty = label.toProperty()
|
||||
var label by labelProperty
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import com.bartlomiejpluta.base.editor.map.model.enumeration.ImageLayerMode
|
||||
import com.bartlomiejpluta.base.editor.map.model.enumeration.PassageAbility
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.*
|
||||
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
|
||||
import com.bartlomiejpluta.base.editor.map.model.obj.MapLabel
|
||||
import com.bartlomiejpluta.base.editor.map.model.obj.MapObject
|
||||
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
|
||||
import com.bartlomiejpluta.base.editor.tileset.model.Tile
|
||||
@@ -121,7 +122,11 @@ class ProtobufMapDeserializer : MapDeserializer {
|
||||
MapObject(it.x, it.y, it.code)
|
||||
}
|
||||
|
||||
return ObjectLayer(proto.name, rows, columns, objects, proto.objectLayer.javaImports, passageMap)
|
||||
val labels = proto.objectLayer.labelsList.map {
|
||||
MapLabel(it.x, it.y, it.label)
|
||||
}
|
||||
|
||||
return ObjectLayer(proto.name, rows, columns, objects, proto.objectLayer.javaImports, passageMap, labels)
|
||||
}
|
||||
|
||||
private fun deserializeColorLayer(proto: GameMapProto.Layer): Layer {
|
||||
|
||||
@@ -60,6 +60,15 @@ class ProtobufMapSerializer : MapSerializer {
|
||||
})
|
||||
}
|
||||
}
|
||||
.also { proto ->
|
||||
layer.labels.map {
|
||||
proto.addLabels(GameMapProto.MapLabel.newBuilder().apply {
|
||||
x = it.x
|
||||
y = it.y
|
||||
label = it.label
|
||||
})
|
||||
}
|
||||
}
|
||||
.setJavaImports(layer.javaImports)
|
||||
.build()
|
||||
.let { GameMapProto.Layer.newBuilder().setName(layer.name).setObjectLayer(it).build() }
|
||||
|
||||
@@ -165,5 +165,16 @@ class MapToolbarView : View() {
|
||||
brushVM.commit()
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton(value = BrushTool.LABEL, group = objectLayerTool) {
|
||||
graphic = FontIcon("fa-star")
|
||||
|
||||
visibleWhen(isObjectLayerSelected)
|
||||
|
||||
action {
|
||||
brushVM.tool = BrushTool.LABEL
|
||||
brushVM.commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user