[Editor] Create cursor for Auto Tile Layer edition

This commit is contained in:
2022-08-26 19:30:42 +02:00
parent 6fb0a0a24b
commit f4d2c41922
11 changed files with 280 additions and 13 deletions

View File

@@ -0,0 +1,37 @@
package com.bartlomiejpluta.base.editor.autotile.model
import javafx.beans.property.ReadOnlyStringWrapper
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.scene.image.Image
import tornadofx.getValue
class AutoTile(uid: String, name: String, image: Image) {
val uidProperty = ReadOnlyStringWrapper(uid)
val uid by uidProperty
val nameProperty = SimpleStringProperty(name)
val name by nameProperty
val imageProperty = SimpleObjectProperty(image)
val image by imageProperty
val rowsProperty = SimpleIntegerProperty(6)
val rows by rowsProperty
val columnsProperty = SimpleIntegerProperty(4)
val columns by columnsProperty
val tileWidthProperty = SimpleIntegerProperty(image.width.toInt() / columns)
val tileWidth by tileWidthProperty
val tileHeightProperty = SimpleIntegerProperty(image.height.toInt() / rows)
val tileHeight by tileHeightProperty
val widthProperty = SimpleIntegerProperty(tileWidth * columns)
val width by widthProperty
val heightProperty = SimpleIntegerProperty(tileHeight * rows)
val height by heightProperty
}

View File

@@ -0,0 +1,57 @@
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
import javafx.scene.canvas.GraphicsContext
import javafx.scene.paint.Color
class AutoTilePaintingCursor(
private val tileWidth: Double,
private val tileHeight: Double,
private val editorStateVM: EditorStateVM,
private val brushVM: BrushVM
) : PaintingCursor {
override fun render(gc: GraphicsContext) {
brushVM.forEach { row, column, centerRow, centerColumn, _ ->
renderTile(gc, row, column, centerRow, centerColumn)
}
}
private fun renderTile(gc: GraphicsContext, row: Int, column: Int, centerRow: Int, centerColumn: Int) {
val alpha = gc.globalAlpha
val stroke = gc.stroke
val width = gc.lineWidth
gc.globalAlpha = 1.0
gc.stroke = when (brushVM.mode!!) {
BrushMode.PAINTING_MODE -> Color.WHITE
BrushMode.ERASING_MODE -> Color.RED
}
gc.lineWidth = 3.0
val x = tileWidth * (editorStateVM.cursorColumn - centerColumn + column)
val y = tileHeight * (editorStateVM.cursorRow - centerRow + row)
val centerX = x + tileWidth / 2
val topY = y + (1 - SIZE) * tileHeight
val bottomY = y + SIZE * tileHeight
val centerY = y + tileHeight / 2
val leftX = x + (1 - SIZE) * tileWidth
val rightX = x + SIZE * tileWidth
gc.strokeLine(centerX, topY, rightX, centerY)
gc.strokeLine(rightX, centerY, centerX, bottomY)
gc.strokeLine(centerX, bottomY, leftX, centerY)
gc.strokeLine(leftX, centerY, centerX, topY)
gc.globalAlpha = alpha
gc.stroke = stroke
gc.lineWidth = width
}
companion object {
private const val SIZE = 0.3
}
}

View File

@@ -0,0 +1,98 @@
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
import com.bartlomiejpluta.base.editor.map.model.layer.AutoTileLayer
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.render.input.MapMouseEvent
import javafx.scene.input.MouseButton
class AutoTilePaintingTrace(val map: GameMapVM, override val commandName: String) : PaintingTrace {
private val trace = mutableListOf<Element>()
override var executed = false
private set
private fun paint(layerIndex: Int, row: Int, column: Int, tile: Boolean) {
if (row >= map.rows || column >= map.columns || row < 0 || column < 0 || layerIndex < 0) {
return
}
val layer = (map.layers[layerIndex] as AutoTileLayer).layer
val formerTile = layer[row][column]
if (trace.isEmpty()) {
trace += Element(layerIndex, row, column, formerTile, tile)
layer[row][column] = tile
return
}
val tileAlreadyPainted =
trace.find { it.layerIndex == layerIndex && it.row == row && it.column == column } != null
if (!tileAlreadyPainted) {
trace += Element(layerIndex, row, column, formerTile, tile)
layer[row][column] = tile
executed = true
}
}
override fun beginTrace(editorStateVM: EditorStateVM, brushVM: BrushVM, mouseEvent: MapMouseEvent) {
brushVM.forEach { row, column, centerRow, centerColumn, _ ->
paint(
editorStateVM.selectedLayerIndex,
editorStateVM.cursorRow - centerRow + row,
editorStateVM.cursorColumn - centerColumn + column,
when {
brushVM.mode == BrushMode.ERASING_MODE -> false
mouseEvent.button == MouseButton.PRIMARY -> true
else -> false
}
)
}
}
override fun proceedTrace(editorStateVM: EditorStateVM, brushVM: BrushVM, mouseEvent: MapMouseEvent) {
brushVM.forEach { row, column, centerRow, centerColumn, _ ->
paint(
editorStateVM.selectedLayerIndex,
editorStateVM.cursorRow - centerRow + row,
editorStateVM.cursorColumn - centerColumn + column,
when {
brushVM.mode == BrushMode.ERASING_MODE -> false
mouseEvent.button == MouseButton.PRIMARY -> true
else -> false
}
)
}
}
override fun commitTrace(editorStateVM: EditorStateVM, brushVM: BrushVM, mouseEvent: MapMouseEvent) {
}
override fun undo() {
trace.forEach {
(map.layers[it.layerIndex] as AutoTileLayer).layer[it.row][it.column] = it.formerTile
}
}
override fun redo() {
trace.forEach {
(map.layers[it.layerIndex] as AutoTileLayer).layer[it.row][it.column] = it.tile
}
}
override val supportedButtons = arrayOf(MouseButton.PRIMARY, MouseButton.SECONDARY)
companion object {
private data class Element(
val layerIndex: Int,
val row: Int,
val column: Int,
val formerTile: Boolean,
val tile: Boolean
)
}
}

View File

@@ -1,6 +1,7 @@
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.map.model.brush.BrushTool
import com.bartlomiejpluta.base.editor.map.model.layer.AutoTileLayer
import com.bartlomiejpluta.base.editor.map.model.layer.ImageLayer
import com.bartlomiejpluta.base.editor.map.model.layer.ObjectLayer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
@@ -35,6 +36,7 @@ class MapPainter(
private fun updateCursor() {
cursor = when (editorStateVM.selectedLayer) {
is TileLayer -> TilePaintingCursor(tileWidth, tileHeight, editorStateVM, brushVM)
is AutoTileLayer -> AutoTilePaintingCursor(tileWidth, tileHeight, editorStateVM, brushVM)
is ObjectLayer -> when (brushVM.tool) {
BrushTool.PASSAGE -> PassageAbilityPaintingCursor(tileWidth, tileHeight, editorStateVM, brushVM)
else -> null
@@ -67,6 +69,7 @@ class MapPainter(
if (currentTrace == null && editorStateVM.selectedLayerIndex >= 0) {
currentTrace = when (editorStateVM.selectedLayer) {
is TileLayer -> TilePaintingTrace(mapVM, "Paint trace")
is AutoTileLayer -> AutoTilePaintingTrace(mapVM, "Paint trace")
is ObjectLayer -> when (brushVM.tool) {
BrushTool.DEFAULT -> ObjectPaintingTrace(projectContext, mapVM, "Update object")
else -> PassageAbilityPaintingTrace(mapVM, "Toggle passage")

View File

@@ -1,9 +1,7 @@
package com.bartlomiejpluta.base.editor.map.model.layer
import com.bartlomiejpluta.base.editor.autotile.asset.AutoTileAsset
import com.bartlomiejpluta.base.editor.tileset.asset.TileSetAsset
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import com.bartlomiejpluta.base.editor.autotile.model.AutoTile
import javafx.beans.binding.Bindings
import javafx.beans.property.SimpleStringProperty
import javafx.scene.image.Image
@@ -24,11 +22,10 @@ class AutoTileLayer(
val autoTileAssetProperty = autoTileAsset.toProperty()
var autoTileAsset by autoTileAssetProperty
// val autoTileProperty = Bindings.createObjectBinding({
// autoTileAsset.file.inputStream().use { fis ->
// // create AutoTile
// }
// }, autoTileAssetProperty)
val autoTileProperty = Bindings.createObjectBinding({
autoTileAsset.file.inputStream().use { fis -> AutoTile(autoTileAsset.uid, autoTileAsset.name, Image(fis)) }
}, autoTileAssetProperty)
val autoTile by autoTileProperty
override val nameProperty = SimpleStringProperty(name)

View File

@@ -0,0 +1,27 @@
package com.bartlomiejpluta.base.editor.map.parameter.layer
import com.bartlomiejpluta.base.editor.common.parameter.model.GraphicAssetParameter
import com.bartlomiejpluta.base.editor.common.parameter.model.Parameter
import com.bartlomiejpluta.base.editor.map.model.layer.AutoTileLayer
import com.bartlomiejpluta.base.editor.project.model.Project
import javafx.collections.ObservableList
import org.springframework.stereotype.Component
@Component
class AutoTileLayerParametersBinder : LayerParametersBinder<AutoTileLayer> {
override fun bind(
layer: AutoTileLayer,
parameters: ObservableList<Parameter<*>>,
project: Project,
onCommit: () -> Unit
) {
val autoTile = GraphicAssetParameter("autoTile", layer.autoTileAsset, true, project.autoTiles) { _, _, submit ->
onCommit()
submit()
}
autoTile.bindBidirectional(layer.autoTileAssetProperty)
parameters.addAll(autoTile)
}
}

View File

@@ -3,9 +3,11 @@ package com.bartlomiejpluta.base.editor.map.view.editor
import com.bartlomiejpluta.base.editor.common.parameter.model.Parameter
import com.bartlomiejpluta.base.editor.common.parameter.view.ParametersTableFragment
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
import com.bartlomiejpluta.base.editor.map.model.layer.AutoTileLayer
import com.bartlomiejpluta.base.editor.map.model.layer.ColorLayer
import com.bartlomiejpluta.base.editor.map.model.layer.ImageLayer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.map.parameter.layer.AutoTileLayerParametersBinder
import com.bartlomiejpluta.base.editor.map.parameter.layer.ColorLayerParametersBinder
import com.bartlomiejpluta.base.editor.map.parameter.layer.ImageLayerParametersBinder
import com.bartlomiejpluta.base.editor.map.parameter.layer.TileLayerParametersBinder
@@ -23,6 +25,7 @@ class MapLayerParameters : View() {
private val colorLayerParametersBinder: ColorLayerParametersBinder by di()
private val imageLayerParametersBinder: ImageLayerParametersBinder by di()
private val tileLayerParametersBinder: TileLayerParametersBinder by di()
private val autoTileLayerParametersBinder: AutoTileLayerParametersBinder by di()
private val parameters = observableListOf<Parameter<*>>()
@@ -36,6 +39,10 @@ class MapLayerParameters : View() {
fire(RedrawMapRequestEvent)
}
is AutoTileLayer -> autoTileLayerParametersBinder.bind(layer, parameters, projectContext.project!!) {
fire(RedrawMapRequestEvent)
}
is ColorLayer -> colorLayerParametersBinder.bind(layer, parameters, projectContext.project!!) {
fire(RedrawMapRequestEvent)
}

View File

@@ -1,6 +1,7 @@
package com.bartlomiejpluta.base.editor.map.view.editor
import com.bartlomiejpluta.base.editor.asset.view.select.SelectGraphicAssetFragment
import com.bartlomiejpluta.base.editor.autotile.asset.AutoTileAsset
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.command.model.map.CreateLayerCommand
import com.bartlomiejpluta.base.editor.command.model.map.MoveLayerCommand
@@ -54,7 +55,7 @@ class MapLayersView : View() {
bottom = toolbar {
menubutton(graphic = FontIcon("fa-plus")) {
item("Tile Layer", graphic = FontIcon("fa-th-large")) {
item("Tile Layer", graphic = FontIcon("fa-th")) {
action {
val scope = UndoableScope()
find<SelectGraphicAssetFragment<TileSetAsset>>(
@@ -74,6 +75,23 @@ class MapLayersView : View() {
}
}
item("Auto Tile Layer", graphic = FontIcon("fa-th-large")) {
action {
val scope = UndoableScope()
find<SelectGraphicAssetFragment<AutoTileAsset>>(scope, SelectGraphicAssetFragment<AutoTileAsset>::assets to projectContext.project?.autoTiles!!).apply {
onComplete {
val layer = AutoTileLayer("Layer ${mapVM.layers.size + 1}", mapVM.rows, mapVM.columns, it)
val command = CreateLayerCommand(mapVM.item, layer)
command.execute()
layersPane.selectionModel.select(mapVM.layers.size - 1)
undoRedoService.push(command, scope)
}
openModal(block = true, resizable = false)
}
}
}
item("Object Layer", graphic = FontIcon("fa-cube")) {
action {
val layer = ObjectLayer("Layer ${mapVM.layers.size + 1}", mapVM.rows, mapVM.columns)
@@ -198,7 +216,8 @@ class MapLayersView : View() {
text = item.name
graphic = when (item) {
is TileLayer -> FontIcon("fa-th-large")
is TileLayer -> FontIcon("fa-th")
is AutoTileLayer -> FontIcon("fa-th-large")
is ObjectLayer -> FontIcon("fa-cube")
is ColorLayer -> FontIcon("fa-paint-brush")
is ImageLayer -> FontIcon("fa-image")

View File

@@ -6,6 +6,7 @@ import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
import com.bartlomiejpluta.base.editor.map.controller.MapController
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
import com.bartlomiejpluta.base.editor.map.model.brush.BrushTool
import com.bartlomiejpluta.base.editor.map.model.layer.AutoTileLayer
import com.bartlomiejpluta.base.editor.map.model.layer.ObjectLayer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
@@ -35,6 +36,11 @@ class MapToolbarView : View() {
editorStateVM.selectedLayerProperty
)
private val isAutoTileLayerSelected = Bindings.createBooleanBinding(
{ editorStateVM.selectedLayer is AutoTileLayer },
editorStateVM.selectedLayerProperty
)
private val isObjectLayerSelected = Bindings.createBooleanBinding(
{ editorStateVM.selectedLayer is ObjectLayer },
editorStateVM.selectedLayerProperty
@@ -95,7 +101,7 @@ class MapToolbarView : View() {
togglebutton(value = BrushMode.PAINTING_MODE, group = brushMode) {
graphic = FontIcon("fa-paint-brush")
enableWhen(isTileLayerSelected.or(isObjectLayerSelected))
enableWhen(isTileLayerSelected.or(isAutoTileLayerSelected).or(isObjectLayerSelected))
action {
brushVM.item = brushVM.withMode(BrushMode.PAINTING_MODE)
@@ -106,7 +112,7 @@ class MapToolbarView : View() {
togglebutton(value = BrushMode.ERASING_MODE, group = brushMode) {
graphic = FontIcon("fa-eraser")
enableWhen(isTileLayerSelected.or(isObjectLayerSelected))
enableWhen(isTileLayerSelected.or(isAutoTileLayerSelected).or(isObjectLayerSelected))
action {
brushVM.item = brushVM.withMode(BrushMode.ERASING_MODE)
@@ -121,7 +127,7 @@ class MapToolbarView : View() {
isSnapToTicks = true
minorTickCount = 0
enableWhen(isTileLayerSelected.or(isObjectLayerSelected))
enableWhen(isTileLayerSelected.or(isAutoTileLayerSelected).or(isObjectLayerSelected))
valueProperty().addListener { _, _, newValue ->
brushVM.item = brushVM.withRange(newValue.toInt())

View File

@@ -7,6 +7,7 @@ import com.bartlomiejpluta.base.editor.audio.asset.SoundAsset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAssetData
import com.bartlomiejpluta.base.editor.autotile.asset.AutoTileAsset
import com.bartlomiejpluta.base.editor.autotile.asset.AutoTileAssetData
import com.bartlomiejpluta.base.editor.autotile.model.AutoTile
import com.bartlomiejpluta.base.editor.characterset.asset.CharacterSetAsset
import com.bartlomiejpluta.base.editor.characterset.asset.CharacterSetAssetData
import com.bartlomiejpluta.base.editor.code.model.Code
@@ -48,6 +49,7 @@ import java.util.*
@Component
class DefaultProjectContext : ProjectContext {
private val tileSetCache = mutableMapOf<String, TileSet>()
private val autoTileCache = mutableMapOf<String, AutoTile>()
@Autowired
private lateinit var projectSerializer: ProjectSerializer
@@ -193,6 +195,17 @@ class DefaultProjectContext : ProjectContext {
}
}
override fun loadAutoTile(uid: String) = autoTileCache.getOrPut(uid) {
project?.let {
val asset = it.autoTiles.firstOrNull { tileSet -> tileSet.uid == uid }
?: throw IllegalStateException("The Auto Tile with uid [$uid] does not exist ")
val image = File(it.autoTilesDirectory, asset.source).inputStream().use { fis -> Image(fis) }
AutoTile(uid, asset.name, image)
} ?: throw IllegalStateException("There is no open project in the context")
}
override fun loadTileSet(uid: String) = tileSetCache.getOrPut(uid) {
project?.let {
val asset = it.tileSets.firstOrNull { tileSet -> tileSet.uid == uid }
@@ -204,6 +217,7 @@ class DefaultProjectContext : ProjectContext {
} ?: throw IllegalStateException("There is no open project in the context")
}
override fun findTileSetAsset(uid: String) = project?.let {
it.tileSets.firstOrNull { tileSets -> tileSets.uid == uid }
?: throw IllegalStateException("The Tile Set with uid [$uid] does not exist ")

View File

@@ -5,6 +5,7 @@ import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAssetData
import com.bartlomiejpluta.base.editor.autotile.asset.AutoTileAsset
import com.bartlomiejpluta.base.editor.autotile.asset.AutoTileAssetData
import com.bartlomiejpluta.base.editor.autotile.model.AutoTile
import com.bartlomiejpluta.base.editor.characterset.asset.CharacterSetAssetData
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.file.model.FileNode
@@ -42,6 +43,7 @@ interface ProjectContext {
fun findTileSetAsset(uid: String): TileSetAsset
fun importAutoTile(data: AutoTileAssetData)
fun loadAutoTile(uid: String): AutoTile
fun findAutoTileAsset(uid: String): AutoTileAsset
fun importImage(data: ImageAssetData)