[Editor] Enable drawing passage map on ObjectLayer
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
|
||||
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.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
@@ -48,6 +49,7 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr
|
||||
private fun dispatchLayerRender(gc: GraphicsContext, layer: Layer) {
|
||||
when (layer) {
|
||||
is TileLayer -> renderTileLayer(gc, layer)
|
||||
is ObjectLayer -> renderObjectPassageMap(gc, layer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,12 +72,26 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderGrid(gc: GraphicsContext) {
|
||||
if(!editorStateVM.showGrid) {
|
||||
private fun renderObjectPassageMap(gc: GraphicsContext, objectLayer: ObjectLayer) {
|
||||
if (editorStateVM.selectedLayer !is ObjectLayer) {
|
||||
return
|
||||
}
|
||||
|
||||
for ((row, columns) in objectLayer.passageMap.withIndex()) {
|
||||
for ((column, passage) in columns.withIndex()) {
|
||||
PassageAbilitySymbol.render(gc, column * tileWidth, row * tileHeight, tileWidth, tileHeight, passage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderGrid(gc: GraphicsContext) {
|
||||
if (!editorStateVM.showGrid) {
|
||||
return
|
||||
}
|
||||
|
||||
val lineWidth = gc.lineWidth
|
||||
gc.lineWidth = 1.5
|
||||
gc.setLineDashes(0.7)
|
||||
|
||||
gc.strokeLine(0.0, 0.0, map.width, 0.0)
|
||||
gc.strokeLine(0.0, 0.0, 0.0, map.height)
|
||||
@@ -89,6 +105,8 @@ class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, pr
|
||||
for (column in 0 until map.columns) {
|
||||
gc.strokeLine(column * tileWidth, 0.0, column * tileWidth, map.height)
|
||||
}
|
||||
|
||||
gc.lineWidth = lineWidth
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
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
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
@@ -27,6 +28,7 @@ class MapPainter(
|
||||
editorStateVM.selectedLayerProperty.addListener { _, _, layer ->
|
||||
cursor = when (layer) {
|
||||
is TileLayer -> TilePaintingCursor(tileWidth, tileHeight, editorStateVM, brushVM)
|
||||
is ObjectLayer -> ObjectPaintingCursor(tileWidth, tileHeight, editorStateVM)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -56,6 +58,7 @@ class MapPainter(
|
||||
if (event.button == MouseButton.PRIMARY && editorStateVM.selectedLayerIndex >= 0) {
|
||||
currentTrace = when (editorStateVM.selectedLayer) {
|
||||
is TileLayer -> TilePaintingTrace(mapVM, "Paint trace")
|
||||
is ObjectLayer -> ObjectPaintingTrace(mapVM, "Toggle passage")
|
||||
else -> null
|
||||
}?.apply { beginTrace(editorStateVM, brushVM) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
class ObjectPaintingCursor(
|
||||
private val tileWidth: Double,
|
||||
private val tileHeight: Double,
|
||||
private val editorStateVM: EditorStateVM
|
||||
) : PaintingCursor {
|
||||
|
||||
override fun render(gc: GraphicsContext) {
|
||||
val alpha = gc.globalAlpha
|
||||
val stroke = gc.stroke
|
||||
val width = gc.lineWidth
|
||||
gc.globalAlpha = 1.0
|
||||
gc.stroke = Color.WHITE
|
||||
gc.lineWidth = 3.0
|
||||
|
||||
val x = editorStateVM.cursorColumn * tileWidth
|
||||
val y = editorStateVM.cursorRow * tileHeight
|
||||
gc.strokeLine(x + tileWidth / 2, y + (1 - SIZE) * tileHeight, x + tileWidth / 2, y + SIZE * tileHeight)
|
||||
gc.strokeLine(x + (1 - SIZE) * tileWidth, y + tileHeight / 2, x + SIZE * tileWidth, y + tileHeight / 2)
|
||||
|
||||
gc.globalAlpha = alpha
|
||||
gc.stroke = stroke
|
||||
gc.lineWidth = width
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SIZE = 0.3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
|
||||
import com.bartlomiejpluta.base.editor.map.model.enumeration.PassageAbility
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.ObjectLayer
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
|
||||
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
|
||||
|
||||
class ObjectPaintingTrace(val map: GameMapVM, override val commandName: String) : PaintingTrace {
|
||||
private var layerIndex = 0
|
||||
private var row = 0
|
||||
private var column = 0
|
||||
private lateinit var layer: ObjectLayer
|
||||
private lateinit var formerPassageAbility: PassageAbility
|
||||
private lateinit var passageAbility: PassageAbility
|
||||
|
||||
|
||||
override fun beginTrace(editorStateVM: EditorStateVM, brushVM: BrushVM) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun proceedTrace(editorStateVM: EditorStateVM, brushVM: BrushVM) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun commitTrace(editorStateVM: EditorStateVM, brushVM: BrushVM) {
|
||||
this.layerIndex = editorStateVM.selectedLayerIndex
|
||||
this.row = editorStateVM.cursorRow
|
||||
this.column = editorStateVM.cursorColumn
|
||||
|
||||
if (row >= map.rows || column >= map.columns || row < 0 || column < 0 || layerIndex < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
this.layer = (map.layers[layerIndex] as ObjectLayer)
|
||||
|
||||
formerPassageAbility = layer.passageMap[row][column]
|
||||
|
||||
passageAbility = when (brushVM.mode) {
|
||||
BrushMode.PAINTING_MODE -> PassageAbility.values()[(formerPassageAbility.ordinal + 1) % PassageAbility.values().size]
|
||||
BrushMode.ERASING_MODE -> PassageAbility.ALLOW
|
||||
else -> throw IllegalStateException("Unknown brush mode")
|
||||
}
|
||||
|
||||
layer.passageMap[row][column] = passageAbility
|
||||
}
|
||||
|
||||
override fun undo() {
|
||||
layer.passageMap[row][column] = formerPassageAbility
|
||||
}
|
||||
|
||||
override fun redo() {
|
||||
layer.passageMap[row][column] = passageAbility
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.bartlomiejpluta.base.editor.map.canvas
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.enumeration.PassageAbility
|
||||
import javafx.scene.canvas.GraphicsContext
|
||||
import javafx.scene.paint.Color
|
||||
|
||||
object PassageAbilitySymbol {
|
||||
private const val SIZE = 0.6
|
||||
|
||||
fun render(gc: GraphicsContext, x: Double, y: Double, w: Double, h: Double, passageAbility: PassageAbility) {
|
||||
val fill = gc.fill
|
||||
val alpha = gc.globalAlpha
|
||||
|
||||
when (passageAbility) {
|
||||
PassageAbility.ALLOW -> allow(gc, x, y, w, h)
|
||||
PassageAbility.BLOCK -> block(gc, x, y, w, h)
|
||||
PassageAbility.UP_ONLY -> up(gc, x, y, w, h)
|
||||
PassageAbility.DOWN_ONLY -> down(gc, x, y, w, h)
|
||||
PassageAbility.LEFT_ONLY -> left(gc, x, y, w, h)
|
||||
PassageAbility.RIGHT_ONLY -> right(gc, x, y, w, h)
|
||||
}
|
||||
|
||||
gc.fill = fill
|
||||
gc.globalAlpha = alpha
|
||||
}
|
||||
|
||||
fun allow(gc: GraphicsContext, x: Double, y: Double, w: Double, h: Double) {
|
||||
gc.fill = Color.GREEN
|
||||
gc.globalAlpha = 0.1
|
||||
gc.fillRect(x, y, w, h)
|
||||
}
|
||||
|
||||
fun block(gc: GraphicsContext, x: Double, y: Double, w: Double, h: Double) {
|
||||
gc.fill = Color.RED
|
||||
gc.globalAlpha = 0.4
|
||||
gc.fillRect(x, y, w, h)
|
||||
}
|
||||
|
||||
fun down(gc: GraphicsContext, x: Double, y: Double, w: Double, h: Double) {
|
||||
gc.fill = Color.GREEN
|
||||
gc.globalAlpha = 0.1
|
||||
gc.fillRect(x, y, w, h)
|
||||
gc.globalAlpha = 1.0
|
||||
|
||||
gc.fill = Color.WHITE
|
||||
gc.beginPath()
|
||||
gc.moveTo(x + (1 - SIZE) * w, y + (1 - SIZE) * h)
|
||||
gc.lineTo(x + w * SIZE, y + (1 - SIZE) * h)
|
||||
gc.lineTo(x + w / 2, y + h * SIZE)
|
||||
gc.closePath()
|
||||
|
||||
gc.fill()
|
||||
}
|
||||
|
||||
fun up(gc: GraphicsContext, x: Double, y: Double, w: Double, h: Double) {
|
||||
gc.fill = Color.GREEN
|
||||
gc.globalAlpha = 0.1
|
||||
gc.fillRect(x, y, w, h)
|
||||
gc.globalAlpha = 1.0
|
||||
|
||||
gc.fill = Color.WHITE
|
||||
gc.beginPath()
|
||||
gc.moveTo(x + (1 - SIZE) * w, y + h * SIZE)
|
||||
gc.lineTo(x + w * SIZE, y + h * SIZE)
|
||||
gc.lineTo(x + w / 2, y + (1 - SIZE) * h)
|
||||
gc.closePath()
|
||||
|
||||
gc.fill()
|
||||
}
|
||||
|
||||
fun left(gc: GraphicsContext, x: Double, y: Double, w: Double, h: Double) {
|
||||
gc.fill = Color.GREEN
|
||||
gc.globalAlpha = 0.1
|
||||
gc.fillRect(x, y, w, h)
|
||||
gc.globalAlpha = 1.0
|
||||
|
||||
gc.fill = Color.WHITE
|
||||
gc.beginPath()
|
||||
gc.moveTo(x + (1 - SIZE) * w, y + h / 2)
|
||||
gc.lineTo(x + SIZE * w, y + (1 - SIZE) * h)
|
||||
gc.lineTo(x + w * SIZE, y + h * SIZE)
|
||||
gc.closePath()
|
||||
|
||||
gc.fill()
|
||||
}
|
||||
|
||||
fun right(gc: GraphicsContext, x: Double, y: Double, w: Double, h: Double) {
|
||||
gc.fill = Color.GREEN
|
||||
gc.globalAlpha = 0.1
|
||||
gc.fillRect(x, y, w, h)
|
||||
gc.globalAlpha = 1.0
|
||||
|
||||
gc.fill = Color.WHITE
|
||||
gc.beginPath()
|
||||
gc.moveTo(x + SIZE * w, y + h / 2)
|
||||
gc.lineTo(x + (1 - SIZE) * w, y + (1 - SIZE) * h)
|
||||
gc.lineTo(x + w * (1 - SIZE), y + h * SIZE)
|
||||
gc.closePath()
|
||||
|
||||
gc.fill()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.enumeration
|
||||
|
||||
enum class PassageAbility {
|
||||
ALLOW,
|
||||
BLOCK,
|
||||
UP_ONLY,
|
||||
DOWN_ONLY,
|
||||
LEFT_ONLY,
|
||||
RIGHT_ONLY
|
||||
}
|
||||
@@ -1,14 +1,27 @@
|
||||
package com.bartlomiejpluta.base.editor.map.model.layer
|
||||
|
||||
import com.bartlomiejpluta.base.editor.map.model.enumeration.PassageAbility
|
||||
import javafx.beans.property.SimpleStringProperty
|
||||
import tornadofx.getValue
|
||||
import tornadofx.setValue
|
||||
|
||||
class ObjectLayer(name: String) : Layer {
|
||||
class ObjectLayer(
|
||||
name: String,
|
||||
rows: Int,
|
||||
columns: Int,
|
||||
passageMap: Array<Array<PassageAbility>> = Array(rows) { Array(columns) { PassageAbility.ALLOW } }
|
||||
) : Layer {
|
||||
var passageMap = passageMap
|
||||
private set
|
||||
|
||||
override val nameProperty = SimpleStringProperty(name)
|
||||
|
||||
override fun resize(rows: Int, columns: Int) {
|
||||
// We essentially need to do nothing
|
||||
passageMap = Array(rows) { row ->
|
||||
Array(columns) { column ->
|
||||
passageMap.getOrNull(row)?.getOrNull(column) ?: PassageAbility.ALLOW
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var name by nameProperty
|
||||
|
||||
@@ -37,7 +37,7 @@ class ProtobufMapDeserializer : MapDeserializer {
|
||||
private fun deserializeLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
|
||||
return when {
|
||||
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)
|
||||
proto.hasObjectLayer() -> deserializeObjectLayer(proto)
|
||||
proto.hasObjectLayer() -> deserializeObjectLayer(rows, columns, proto)
|
||||
proto.hasImageLayer() -> deserializeImageLayer(proto)
|
||||
|
||||
else -> throw IllegalStateException("Not supported layer type")
|
||||
@@ -57,8 +57,8 @@ class ProtobufMapDeserializer : MapDeserializer {
|
||||
return TileLayer(proto.name, rows, columns, layer)
|
||||
}
|
||||
|
||||
private fun deserializeObjectLayer(proto: GameMapProto.Layer): Layer {
|
||||
return ObjectLayer(proto.name)
|
||||
private fun deserializeObjectLayer(rows: Int, columns: Int, proto: GameMapProto.Layer): Layer {
|
||||
return ObjectLayer(proto.name, rows, columns)
|
||||
}
|
||||
|
||||
private fun deserializeImageLayer(proto: GameMapProto.Layer): Layer {
|
||||
|
||||
@@ -62,7 +62,7 @@ class MapLayersView : View() {
|
||||
|
||||
item("Object Layer", graphic = FontIcon("fa-cube")) {
|
||||
action {
|
||||
val layer = ObjectLayer("Layer ${mapVM.layers.size + 1}")
|
||||
val layer = ObjectLayer("Layer ${mapVM.layers.size + 1}", mapVM.rows, mapVM.columns)
|
||||
val command = CreateLayerCommand(mapVM.item, layer)
|
||||
command.execute()
|
||||
layersPane.selectionModel.select(mapVM.layers.size - 1)
|
||||
|
||||
@@ -5,9 +5,12 @@ import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
|
||||
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.layer.ObjectLayer
|
||||
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
|
||||
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 javafx.beans.binding.Bindings
|
||||
import javafx.scene.control.ToggleGroup
|
||||
import org.kordamp.ikonli.javafx.FontIcon
|
||||
import tornadofx.*
|
||||
@@ -28,6 +31,16 @@ class MapToolbarView : View() {
|
||||
}
|
||||
}
|
||||
|
||||
private val isTileLayerSelected = Bindings.createBooleanBinding(
|
||||
{ editorStateVM.selectedLayer is TileLayer },
|
||||
editorStateVM.selectedLayerProperty
|
||||
)
|
||||
|
||||
private val isObjectLayerSelected = Bindings.createBooleanBinding(
|
||||
{ editorStateVM.selectedLayer is ObjectLayer },
|
||||
editorStateVM.selectedLayerProperty
|
||||
)
|
||||
|
||||
override val root = toolbar {
|
||||
button(graphic = FontIcon("fa-floppy-o")) {
|
||||
shortcut("Ctrl+S")
|
||||
@@ -71,6 +84,8 @@ class MapToolbarView : View() {
|
||||
togglebutton(value = BrushMode.PAINTING_MODE, group = brushMode) {
|
||||
graphic = FontIcon("fa-paint-brush")
|
||||
|
||||
enableWhen(isTileLayerSelected.or(isObjectLayerSelected))
|
||||
|
||||
action {
|
||||
brushVM.item = brushVM.withMode(BrushMode.PAINTING_MODE)
|
||||
brushVM.commit()
|
||||
@@ -80,6 +95,8 @@ class MapToolbarView : View() {
|
||||
togglebutton(value = BrushMode.ERASING_MODE, group = brushMode) {
|
||||
graphic = FontIcon("fa-eraser")
|
||||
|
||||
enableWhen(isTileLayerSelected.or(isObjectLayerSelected))
|
||||
|
||||
action {
|
||||
brushVM.item = brushVM.withMode(BrushMode.ERASING_MODE)
|
||||
brushVM.commit()
|
||||
@@ -93,6 +110,8 @@ class MapToolbarView : View() {
|
||||
isSnapToTicks = true
|
||||
minorTickCount = 0
|
||||
|
||||
enableWhen(isTileLayerSelected)
|
||||
|
||||
valueProperty().addListener { _, _, newValue ->
|
||||
brushVM.item = brushVM.withRange(newValue.toInt())
|
||||
brushVM.commit()
|
||||
|
||||
Reference in New Issue
Block a user