Migrate CRLF file endings to LF

This commit is contained in:
2021-02-10 17:16:37 +01:00
parent 98ac49cca0
commit e60add30c5
141 changed files with 4973 additions and 4978 deletions

100
editor/build.gradle Executable file → Normal file
View File

@@ -1,50 +1,50 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.10'
id 'org.openjfx.javafxplugin' version '0.0.8'
id 'org.springframework.boot' version "$springBootVersion"
id 'io.spring.dependency-management' version "$springDependencyManagementVersion"
id 'idea'
}
group 'com.bartlomiejpluta.base'
version 'unspecified'
repositories {
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
sourceSets {
main.kotlin.srcDirs += 'src/main/kotlin'
}
javafx {
version = "11.0.2"
modules = ['javafx.controls', 'javafx.graphics']
}
compileKotlin {
kotlinOptions.jvmTarget = "14"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "14"
}
dependencies {
implementation project(":proto")
implementation "org.jetbrains.kotlin:kotlin-stdlib"
implementation "no.tornado:tornadofx:${tornadoFxVersion}"
implementation platform("org.kordamp.ikonli:ikonli-bom:${ikonliVersion}")
implementation 'org.kordamp.ikonli:ikonli-javafx'
implementation 'org.kordamp.ikonli:ikonli-fontawesome-pack'
// Spring
implementation 'org.springframework.boot:spring-boot-starter'
}
build {
dependsOn(":proto:build")
}
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.10'
id 'org.openjfx.javafxplugin' version '0.0.8'
id 'org.springframework.boot' version "$springBootVersion"
id 'io.spring.dependency-management' version "$springDependencyManagementVersion"
id 'idea'
}
group 'com.bartlomiejpluta.base'
version 'unspecified'
repositories {
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
sourceSets {
main.kotlin.srcDirs += 'src/main/kotlin'
}
javafx {
version = "11.0.2"
modules = ['javafx.controls', 'javafx.graphics']
}
compileKotlin {
kotlinOptions.jvmTarget = "14"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "14"
}
dependencies {
implementation project(":proto")
implementation "org.jetbrains.kotlin:kotlin-stdlib"
implementation "no.tornado:tornadofx:${tornadoFxVersion}"
implementation platform("org.kordamp.ikonli:ikonli-bom:${ikonliVersion}")
implementation 'org.kordamp.ikonli:ikonli-javafx'
implementation 'org.kordamp.ikonli:ikonli-fontawesome-pack'
// Spring
implementation 'org.springframework.boot:spring-boot-starter'
}
build {
dependsOn(":proto:build")
}

View File

@@ -1,35 +1,35 @@
package com.bartlomiejpluta.base.editor
import com.bartlomiejpluta.base.editor.main.view.MainView
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.ConfigurableApplicationContext
import tornadofx.App
import tornadofx.DIContainer
import tornadofx.FX
import tornadofx.launch
import kotlin.reflect.KClass
@SpringBootApplication
open class EditorApp : App(MainView::class) {
private lateinit var context: ConfigurableApplicationContext
override fun init() {
this.context = SpringApplication.run(this.javaClass)
context.autowireCapableBeanFactory.autowireBean(this)
FX.dicontainer = object : DIContainer {
override fun <T : Any> getInstance(type: KClass<T>): T = context.getBean(type.java)
override fun <T : Any> getInstance(type: KClass<T>, name: String): T = context.getBean(name, type.java)
}
}
override fun stop() {
super.stop()
context.close()
}
}
fun main(args: Array<String>) {
launch<EditorApp>(args)
package com.bartlomiejpluta.base.editor
import com.bartlomiejpluta.base.editor.main.view.MainView
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.ConfigurableApplicationContext
import tornadofx.App
import tornadofx.DIContainer
import tornadofx.FX
import tornadofx.launch
import kotlin.reflect.KClass
@SpringBootApplication
open class EditorApp : App(MainView::class) {
private lateinit var context: ConfigurableApplicationContext
override fun init() {
this.context = SpringApplication.run(this.javaClass)
context.autowireCapableBeanFactory.autowireBean(this)
FX.dicontainer = object : DIContainer {
override fun <T : Any> getInstance(type: KClass<T>): T = context.getBean(type.java)
override fun <T : Any> getInstance(type: KClass<T>, name: String): T = context.getBean(name, type.java)
}
}
override fun stop() {
super.stop()
context.close()
}
}
fun main(args: Array<String>) {
launch<EditorApp>(args)
}

View File

@@ -1,3 +1,3 @@
package com.bartlomiejpluta.base.editor.command.context
package com.bartlomiejpluta.base.editor.command.context
interface UndoableContext

View File

@@ -1,5 +1,5 @@
package com.bartlomiejpluta.base.editor.command.context
import tornadofx.Scope
package com.bartlomiejpluta.base.editor.command.context
import tornadofx.Scope
class UndoableScope : UndoableContext, Scope()

View File

@@ -1,5 +1,5 @@
package com.bartlomiejpluta.base.editor.command.model.base
interface Command {
fun execute()
package com.bartlomiejpluta.base.editor.command.model.base
interface Command {
fun execute()
}

View File

@@ -1,21 +1,21 @@
package com.bartlomiejpluta.base.editor.command.model.base
class SimpleCommand<T>(
override val commandName: String,
private val formerValue: T,
private val value: T,
private val execute: (T) -> Unit
) : Undoable, Command {
override fun undo() {
execute(formerValue)
}
override fun redo() {
execute()
}
override fun execute() {
execute(value)
}
package com.bartlomiejpluta.base.editor.command.model.base
class SimpleCommand<T>(
override val commandName: String,
private val formerValue: T,
private val value: T,
private val execute: (T) -> Unit
) : Undoable, Command {
override fun undo() {
execute(formerValue)
}
override fun redo() {
execute()
}
override fun execute() {
execute(value)
}
}

View File

@@ -1,7 +1,7 @@
package com.bartlomiejpluta.base.editor.command.model.base
interface Undoable {
fun undo()
fun redo()
val commandName: String
package com.bartlomiejpluta.base.editor.command.model.base
interface Undoable {
fun undo()
fun redo()
val commandName: String
}

View File

@@ -1,23 +1,23 @@
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
class CreateLayerCommand(private val map: GameMap, private val layer: Layer): Undoable, Command {
override fun execute() {
map.layers += layer
}
override fun undo() {
map.layers -= layer
}
override fun redo() {
execute()
}
override val commandName = "Create map layer"
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
class CreateLayerCommand(private val map: GameMap, private val layer: Layer): Undoable, Command {
override fun execute() {
map.layers += layer
}
override fun undo() {
map.layers -= layer
}
override fun redo() {
execute()
}
override val commandName = "Create map layer"
}

View File

@@ -1,22 +1,22 @@
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import tornadofx.swap
class MoveLayerCommand(private val map: GameMap, private val currentIndex: Int, private val newIndex: Int) : Undoable, Command {
override fun execute() {
map.layers.swap(currentIndex, newIndex)
}
override fun undo() {
map.layers.swap(newIndex, currentIndex)
}
override fun redo() {
execute()
}
override val commandName = "Move layer"
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import tornadofx.swap
class MoveLayerCommand(private val map: GameMap, private val currentIndex: Int, private val newIndex: Int) : Undoable, Command {
override fun execute() {
map.layers.swap(currentIndex, newIndex)
}
override fun undo() {
map.layers.swap(newIndex, currentIndex)
}
override fun redo() {
execute()
}
override val commandName = "Move layer"
}

View File

@@ -1,25 +1,25 @@
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
class RemoveLayerCommand(private val map: GameMap, private val layerIndex: Int) : Undoable, Command {
private var layer: Layer? = null
override fun execute() {
layer = map.layers.removeAt(layerIndex)
}
override fun undo() {
map.layers.add(layerIndex, layer)
layer = null
}
override fun redo() {
execute()
}
override val commandName = "Remove layer"
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
class RemoveLayerCommand(private val map: GameMap, private val layerIndex: Int) : Undoable, Command {
private var layer: Layer? = null
override fun execute() {
layer = map.layers.removeAt(layerIndex)
}
override fun undo() {
map.layers.add(layerIndex, layer)
layer = null
}
override fun redo() {
execute()
}
override val commandName = "Remove layer"
}

View File

@@ -1,23 +1,23 @@
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
class RenameLayerCommand(private val layer: Layer, private val newName: String) : Undoable, Command {
private val formerName = layer.name
override fun execute() {
layer.name = newName
}
override fun undo() {
layer.name = formerName
}
override fun redo() {
execute()
}
override val commandName = "Rename layer"
package com.bartlomiejpluta.base.editor.command.model.map
import com.bartlomiejpluta.base.editor.command.model.base.Command
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
class RenameLayerCommand(private val layer: Layer, private val newName: String) : Undoable, Command {
private val formerName = layer.name
override fun execute() {
layer.name = newName
}
override fun undo() {
layer.name = formerName
}
override fun redo() {
execute()
}
override val commandName = "Rename layer"
}

View File

@@ -1,122 +1,122 @@
package com.bartlomiejpluta.base.editor.command.service
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.lang.Integer.toHexString
import java.util.*
@Component
class DefaultUndoRedoService : UndoRedoService {
private val undo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
private val redo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
var sizeMax = 30
set(value) {
if (value >= 0) {
for (i in 0 until undo.size - value) {
undo.removeLast()
}
field = value
}
}
override fun push(undoable: Undoable) {
push(undoable, GLOBAL_CONTEXT)
}
override fun push(undoable: Undoable, context: UndoableContext) {
if (undo.size == sizeMax) {
log.debug("The max size of [undo] stack has been reached. Removing the last item...")
undo.removeLast()
}
log.debug("Pushing item to [undo] stack: ${undoable.commandName} (ctx: ${toHexString(context.hashCode())})")
undo.push(undoable to context)
redo.clear()
}
override fun undo() {
if (undo.isNotEmpty()) {
undo.pop().let {
log.debug("Performing undo: ${it.first.commandName}")
it.first.undo()
redo.push(it)
}
}
}
override fun undo(context: UndoableContext) {
if (undo.isNotEmpty()) {
undo.firstOrNull { it.second === context }?.let {
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) undo: ${it.first.commandName}")
undo.remove(it)
it.first.undo()
redo.push(it)
}
}
}
override fun redo() {
if (redo.isNotEmpty()) {
redo.pop().let {
log.debug("Performing redo: ${it.first.commandName}")
it.first.redo()
undo.push(it)
}
}
}
override fun redo(context: UndoableContext) {
if (redo.isNotEmpty()) {
redo.firstOrNull { it.second === context }?.let {
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) redo: ${it.first.commandName}")
redo.remove(it)
it.first.redo()
undo.push(it)
}
}
}
override fun clear() {
log.debug("Clearing [undo] and [redo] stacks")
undo.clear()
redo.clear()
}
override fun clear(context: UndoableContext) {
log.debug("Clearing [undo] and [redo] stacks (ctx: ${toHexString(context.hashCode())})")
undo.removeIf { it.second == context }
redo.removeIf { it.second == context }
}
override val lastUndoable: Undoable?
get() = undo.first?.first
override val lastRedoable: Undoable?
get() = redo.first?.first
override val undoCommandName: String
get() = undo.first?.first?.commandName ?: ""
override val redoCommandName: String
get() = redo.first?.first?.commandName ?: ""
override fun lastUndoable(context: UndoableContext) = undo.firstOrNull { it.second === context }?.first
override fun lastRedoable(context: UndoableContext) = redo.firstOrNull { it.second === context }?.first
override fun undoCommandName(context: UndoableContext) =
undo.firstOrNull { it.second === context }?.first?.commandName ?: ""
override fun redoCommandName(context: UndoableContext) =
redo.firstOrNull { it.second === context }?.first?.commandName ?: ""
companion object {
private val log = LoggerFactory.getLogger(DefaultUndoRedoService::class.java)
private val GLOBAL_CONTEXT = object : UndoableContext {}
}
package com.bartlomiejpluta.base.editor.command.service
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.lang.Integer.toHexString
import java.util.*
@Component
class DefaultUndoRedoService : UndoRedoService {
private val undo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
private val redo: Deque<Pair<Undoable, UndoableContext>> = ArrayDeque()
var sizeMax = 30
set(value) {
if (value >= 0) {
for (i in 0 until undo.size - value) {
undo.removeLast()
}
field = value
}
}
override fun push(undoable: Undoable) {
push(undoable, GLOBAL_CONTEXT)
}
override fun push(undoable: Undoable, context: UndoableContext) {
if (undo.size == sizeMax) {
log.debug("The max size of [undo] stack has been reached. Removing the last item...")
undo.removeLast()
}
log.debug("Pushing item to [undo] stack: ${undoable.commandName} (ctx: ${toHexString(context.hashCode())})")
undo.push(undoable to context)
redo.clear()
}
override fun undo() {
if (undo.isNotEmpty()) {
undo.pop().let {
log.debug("Performing undo: ${it.first.commandName}")
it.first.undo()
redo.push(it)
}
}
}
override fun undo(context: UndoableContext) {
if (undo.isNotEmpty()) {
undo.firstOrNull { it.second === context }?.let {
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) undo: ${it.first.commandName}")
undo.remove(it)
it.first.undo()
redo.push(it)
}
}
}
override fun redo() {
if (redo.isNotEmpty()) {
redo.pop().let {
log.debug("Performing redo: ${it.first.commandName}")
it.first.redo()
undo.push(it)
}
}
}
override fun redo(context: UndoableContext) {
if (redo.isNotEmpty()) {
redo.firstOrNull { it.second === context }?.let {
log.debug("Performing contextual (ctx: ${toHexString(context.hashCode())}) redo: ${it.first.commandName}")
redo.remove(it)
it.first.redo()
undo.push(it)
}
}
}
override fun clear() {
log.debug("Clearing [undo] and [redo] stacks")
undo.clear()
redo.clear()
}
override fun clear(context: UndoableContext) {
log.debug("Clearing [undo] and [redo] stacks (ctx: ${toHexString(context.hashCode())})")
undo.removeIf { it.second == context }
redo.removeIf { it.second == context }
}
override val lastUndoable: Undoable?
get() = undo.first?.first
override val lastRedoable: Undoable?
get() = redo.first?.first
override val undoCommandName: String
get() = undo.first?.first?.commandName ?: ""
override val redoCommandName: String
get() = redo.first?.first?.commandName ?: ""
override fun lastUndoable(context: UndoableContext) = undo.firstOrNull { it.second === context }?.first
override fun lastRedoable(context: UndoableContext) = redo.firstOrNull { it.second === context }?.first
override fun undoCommandName(context: UndoableContext) =
undo.firstOrNull { it.second === context }?.first?.commandName ?: ""
override fun redoCommandName(context: UndoableContext) =
redo.firstOrNull { it.second === context }?.first?.commandName ?: ""
companion object {
private val log = LoggerFactory.getLogger(DefaultUndoRedoService::class.java)
private val GLOBAL_CONTEXT = object : UndoableContext {}
}
}

View File

@@ -1,26 +1,26 @@
package com.bartlomiejpluta.base.editor.command.service
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
interface UndoRedoService {
fun push(undoable: Undoable)
fun undo()
fun redo()
fun clear()
fun push(undoable: Undoable, context: UndoableContext)
fun undo(context: UndoableContext)
fun redo(context: UndoableContext)
fun clear(context: UndoableContext)
val lastUndoable: Undoable?
val lastRedoable: Undoable?
val undoCommandName: String
val redoCommandName: String
fun lastUndoable(context: UndoableContext): Undoable?
fun lastRedoable(context: UndoableContext): Undoable?
fun undoCommandName(context: UndoableContext): String
fun redoCommandName(context: UndoableContext): String
package com.bartlomiejpluta.base.editor.command.service
import com.bartlomiejpluta.base.editor.command.context.UndoableContext
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
interface UndoRedoService {
fun push(undoable: Undoable)
fun undo()
fun redo()
fun clear()
fun push(undoable: Undoable, context: UndoableContext)
fun undo(context: UndoableContext)
fun redo(context: UndoableContext)
fun clear(context: UndoableContext)
val lastUndoable: Undoable?
val lastRedoable: Undoable?
val undoCommandName: String
val redoCommandName: String
fun lastUndoable(context: UndoableContext): Undoable?
fun lastRedoable(context: UndoableContext): Undoable?
fun undoCommandName(context: UndoableContext): String
fun redoCommandName(context: UndoableContext): String
}

View File

@@ -1,7 +1,7 @@
package com.bartlomiejpluta.base.editor.common.serial
import java.io.InputStream
interface Deserializer<T> {
fun deserialize(input: InputStream): T
package com.bartlomiejpluta.base.editor.common.serial
import java.io.InputStream
interface Deserializer<T> {
fun deserialize(input: InputStream): T
}

View File

@@ -1,8 +1,8 @@
package com.bartlomiejpluta.base.editor.common.serial
import java.io.InputStream
import java.io.OutputStream
interface Serializer<T> {
fun serialize(item: T, output: OutputStream)
package com.bartlomiejpluta.base.editor.common.serial
import java.io.InputStream
import java.io.OutputStream
interface Serializer<T> {
fun serialize(item: T, output: OutputStream)
}

View File

@@ -1,7 +1,7 @@
package com.bartlomiejpluta.base.editor.event
import tornadofx.EventBus.RunOn.ApplicationThread
import tornadofx.EventBus.RunOn.BackgroundThread
import tornadofx.FXEvent
package com.bartlomiejpluta.base.editor.event
import tornadofx.EventBus.RunOn.ApplicationThread
import tornadofx.EventBus.RunOn.BackgroundThread
import tornadofx.FXEvent
object RedrawMapRequestEvent : FXEvent(ApplicationThread)

View File

@@ -1,59 +1,59 @@
package com.bartlomiejpluta.base.editor.main.controller
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.editor.map.view.MapSettingsFragment
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import com.bartlomiejpluta.base.editor.project.manager.ProjectManager
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.editor.project.view.ProjectSettingsFragment
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import javafx.beans.property.SimpleObjectProperty
import javafx.stage.FileChooser
import org.springframework.stereotype.Component
import tornadofx.*
import kotlin.collections.set
@Component
class MainController : Controller() {
// In the future it'll be pulled from TileSetService or something like that
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
private val projectManager: ProjectManager by di()
val openProject = SimpleObjectProperty<Project?>()
val openMaps = observableMapOf<Scope, GameMap>()
fun createEmptyProject() {
val project = Project()
val vm = ProjectVM(project)
setInScope(vm)
val modal = find<ProjectSettingsFragment>().apply { openModal(block = true, resizable = false) }
if(modal.result) {
openProject.value = project
projectManager.saveProject(project)
}
}
fun createEmptyMap() {
val map = GameMap(tileset)
val scope = UndoableScope()
val vm = GameMapVM(map)
setInScope(vm, scope)
val modal = find<MapSettingsFragment>(scope).apply { openModal(block = true, resizable = false) }
if (modal.result) {
openMaps[scope] = map
}
}
fun loadProject() {
chooseFile(
title = "Load Project",
filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")),
).getOrNull(0)?.let { openProject.value = projectManager.openProject(it) }
}
package com.bartlomiejpluta.base.editor.main.controller
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.editor.map.view.MapSettingsFragment
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import com.bartlomiejpluta.base.editor.project.manager.ProjectManager
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.editor.project.view.ProjectSettingsFragment
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import javafx.beans.property.SimpleObjectProperty
import javafx.stage.FileChooser
import org.springframework.stereotype.Component
import tornadofx.*
import kotlin.collections.set
@Component
class MainController : Controller() {
// In the future it'll be pulled from TileSetService or something like that
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
private val projectManager: ProjectManager by di()
val openProject = SimpleObjectProperty<Project?>()
val openMaps = observableMapOf<Scope, GameMap>()
fun createEmptyProject() {
val project = Project()
val vm = ProjectVM(project)
setInScope(vm)
val modal = find<ProjectSettingsFragment>().apply { openModal(block = true, resizable = false) }
if(modal.result) {
openProject.value = project
projectManager.saveProject(project)
}
}
fun createEmptyMap() {
val map = GameMap(tileset)
val scope = UndoableScope()
val vm = GameMapVM(map)
setInScope(vm, scope)
val modal = find<MapSettingsFragment>(scope).apply { openModal(block = true, resizable = false) }
if (modal.result) {
openMaps[scope] = map
}
}
fun loadProject() {
chooseFile(
title = "Load Project",
filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")),
).getOrNull(0)?.let { openProject.value = projectManager.openProject(it) }
}
}

View File

@@ -1,40 +1,40 @@
package com.bartlomiejpluta.base.editor.main.view
import com.bartlomiejpluta.base.editor.main.controller.MainController
import tornadofx.*
class MainMenuView : View() {
private val mainController: MainController by di()
override val root = menubar {
menu("File") {
menu("New") {
item("Project...") {
action {
mainController.createEmptyProject()
}
}
item("Map...") {
enableWhen(mainController.openProject.isNotNull)
action {
mainController.createEmptyMap()
}
}
}
menu("Open") {
item("Project...") {
action {
mainController.loadProject()
}
}
}
}
menu("Edit") {
item("Undo")
item("Redo")
}
}
package com.bartlomiejpluta.base.editor.main.view
import com.bartlomiejpluta.base.editor.main.controller.MainController
import tornadofx.*
class MainMenuView : View() {
private val mainController: MainController by di()
override val root = menubar {
menu("File") {
menu("New") {
item("Project...") {
action {
mainController.createEmptyProject()
}
}
item("Map...") {
enableWhen(mainController.openProject.isNotNull)
action {
mainController.createEmptyMap()
}
}
}
menu("Open") {
item("Project...") {
action {
mainController.loadProject()
}
}
}
}
menu("Edit") {
item("Undo")
item("Redo")
}
}
}

View File

@@ -1,34 +1,34 @@
package com.bartlomiejpluta.base.editor.main.view
import com.bartlomiejpluta.base.editor.main.controller.MainController
import com.bartlomiejpluta.base.editor.map.view.MapFragment
import javafx.scene.control.Tab
import tornadofx.*
class MainView : View("BASE Game Editor") {
private val mainController: MainController by di()
private val mainMenuView = find<MainMenuView>()
init {
mainController.openProject.addListener { _, _, project ->
val projectName = project?.let { " :: ${it.name} (${it.sourceDirectory.absolutePath})" } ?: ""
title = "BASE Game Editor$projectName"
}
}
override val root = borderpane {
top = mainMenuView.root
center = tabpane {
tabs.bind(mainController.openMaps) { scope, map ->
Tab().apply {
textProperty().bindBidirectional(map.nameProperty)
content = find<MapFragment>(scope).root
setOnClosed { mainController.openMaps.remove(scope) }
}
}
}
}
package com.bartlomiejpluta.base.editor.main.view
import com.bartlomiejpluta.base.editor.main.controller.MainController
import com.bartlomiejpluta.base.editor.map.view.MapFragment
import javafx.scene.control.Tab
import tornadofx.*
class MainView : View("BASE Game Editor") {
private val mainController: MainController by di()
private val mainMenuView = find<MainMenuView>()
init {
mainController.openProject.addListener { _, _, project ->
val projectName = project?.let { " :: ${it.name} (${it.sourceDirectory.absolutePath})" } ?: ""
title = "BASE Game Editor$projectName"
}
}
override val root = borderpane {
top = mainMenuView.root
center = tabpane {
tabs.bind(mainController.openMaps) { scope, map ->
Tab().apply {
textProperty().bindBidirectional(map.nameProperty)
content = find<MapFragment>(scope).root
setOnClosed { mainController.openMaps.remove(scope) }
}
}
}
}
}

View File

@@ -1,98 +1,98 @@
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.render.model.Renderable
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.canvas.GraphicsContext
import javafx.scene.paint.Color
class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, private val painter: MapPainter) : Renderable {
var tileSet = map.tileSet
private var tileWidth = map.tileWidth
private var tileHeight = map.tileHeight
override fun render(gc: GraphicsContext) {
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
renderBackground(gc)
renderUnderlyingLayers(gc)
renderCover(gc)
renderSelectedLayer(gc)
renderGrid(gc)
painter.render(gc)
}
private fun renderSelectedLayer(gc: GraphicsContext) {
map.layers.getOrNull(editorStateVM.selectedLayer) ?. let { dispatchLayerRender(gc, it) }
}
private fun renderCover(gc: GraphicsContext) {
if(!editorStateVM.coverUnderlyingLayers) {
return
}
gc.fill = Color.color(0.0, 0.0, 0.0, 0.4)
gc.fillRect(0.0, 0.0, map.width, map.height)
}
private fun renderUnderlyingLayers(gc: GraphicsContext) {
for(layer in map.layers.dropLast(if(editorStateVM.selectedLayer < 0) 0 else map.layers.size - editorStateVM.selectedLayer)) {
dispatchLayerRender(gc, layer)
}
}
private fun dispatchLayerRender(gc: GraphicsContext, layer: Layer) {
when (layer) {
is TileLayer -> renderTileLayer(gc, layer)
}
}
private fun renderBackground(gc: GraphicsContext) {
for (row in 0 until map.rows) {
for (column in 0 until map.columns) {
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
gc.fillRect(column * tileWidth, row * tileHeight, tileWidth, tileHeight)
}
}
}
private fun renderTileLayer(gc: GraphicsContext, tileLayer: TileLayer) {
for ((row, columns) in tileLayer.layer.withIndex()) {
for ((column, tile) in columns.withIndex()) {
if (tile != null) {
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
}
}
}
}
private fun renderGrid(gc: GraphicsContext) {
if(!editorStateVM.showGrid) {
return
}
gc.lineWidth = 1.5
gc.strokeLine(0.0, 0.0, map.width, 0.0)
gc.strokeLine(0.0, 0.0, 0.0, map.height)
gc.strokeLine(map.width, 0.0, map.width, map.height)
gc.strokeLine(0.0, map.height, map.width, map.height)
for (row in 0 until map.rows) {
gc.strokeLine(0.0, row * tileHeight, map.width, row * tileHeight)
}
for (column in 0 until map.columns) {
gc.strokeLine(column * tileWidth, 0.0, column * tileWidth, map.height)
}
}
companion object {
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
private val BACKGROUND_COLOR2 = Color.color(0.8, 0.8, 0.8, 1.0)
}
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.render.model.Renderable
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.canvas.GraphicsContext
import javafx.scene.paint.Color
class MapCanvas(val map: GameMapVM, private val editorStateVM: EditorStateVM, private val painter: MapPainter) : Renderable {
var tileSet = map.tileSet
private var tileWidth = map.tileWidth
private var tileHeight = map.tileHeight
override fun render(gc: GraphicsContext) {
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
renderBackground(gc)
renderUnderlyingLayers(gc)
renderCover(gc)
renderSelectedLayer(gc)
renderGrid(gc)
painter.render(gc)
}
private fun renderSelectedLayer(gc: GraphicsContext) {
map.layers.getOrNull(editorStateVM.selectedLayer) ?. let { dispatchLayerRender(gc, it) }
}
private fun renderCover(gc: GraphicsContext) {
if(!editorStateVM.coverUnderlyingLayers) {
return
}
gc.fill = Color.color(0.0, 0.0, 0.0, 0.4)
gc.fillRect(0.0, 0.0, map.width, map.height)
}
private fun renderUnderlyingLayers(gc: GraphicsContext) {
for(layer in map.layers.dropLast(if(editorStateVM.selectedLayer < 0) 0 else map.layers.size - editorStateVM.selectedLayer)) {
dispatchLayerRender(gc, layer)
}
}
private fun dispatchLayerRender(gc: GraphicsContext, layer: Layer) {
when (layer) {
is TileLayer -> renderTileLayer(gc, layer)
}
}
private fun renderBackground(gc: GraphicsContext) {
for (row in 0 until map.rows) {
for (column in 0 until map.columns) {
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
gc.fillRect(column * tileWidth, row * tileHeight, tileWidth, tileHeight)
}
}
}
private fun renderTileLayer(gc: GraphicsContext, tileLayer: TileLayer) {
for ((row, columns) in tileLayer.layer.withIndex()) {
for ((column, tile) in columns.withIndex()) {
if (tile != null) {
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
}
}
}
}
private fun renderGrid(gc: GraphicsContext) {
if(!editorStateVM.showGrid) {
return
}
gc.lineWidth = 1.5
gc.strokeLine(0.0, 0.0, map.width, 0.0)
gc.strokeLine(0.0, 0.0, 0.0, map.height)
gc.strokeLine(map.width, 0.0, map.width, map.height)
gc.strokeLine(0.0, map.height, map.width, map.height)
for (row in 0 until map.rows) {
gc.strokeLine(0.0, row * tileHeight, map.width, row * tileHeight)
}
for (column in 0 until map.columns) {
gc.strokeLine(column * tileWidth, 0.0, column * tileWidth, map.height)
}
}
companion object {
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
private val BACKGROUND_COLOR2 = Color.color(0.8, 0.8, 0.8, 1.0)
}
}

View File

@@ -1,94 +1,94 @@
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
import com.bartlomiejpluta.base.editor.render.model.Renderable
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.scene.canvas.GraphicsContext
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.scene.paint.Color
class MapPainter(
private val mapVM: GameMapVM,
private val brushVM: BrushVM,
private val editorStateVM: EditorStateVM,
private val paintingCallback: (MapPaintingTrace) -> Unit
) : Renderable, MapMouseEventHandler {
private val tileWidth = mapVM.tileSet.tileWidth.toDouble()
private val tileHeight = mapVM.tileSet.tileHeight.toDouble()
private var currentTrace: MapPaintingTrace? = null
override fun render(gc: GraphicsContext) {
val alpha = gc.globalAlpha
gc.globalAlpha = 0.4
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
renderTile(gc, row, column, centerRow, centerColumn, tile)
}
gc.globalAlpha = alpha
}
private fun renderTile(gc: GraphicsContext, row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) {
val x = tileWidth * (editorStateVM.cursorColumn - centerColumn + column)
val y = tileHeight * (editorStateVM.cursorRow - centerRow + row)
when {
tile != null -> renderPaintingBrushTile(gc, tile, x, y)
else -> renderEraserTile(gc, x, y)
}
}
private fun renderPaintingBrushTile(gc: GraphicsContext, tile: Tile, x: Double, y: Double) =
gc.drawImage(tile.image, x, y)
private fun renderEraserTile(gc: GraphicsContext, x: Double, y: Double) {
gc.fill = Color.WHITE
gc.fillRect(x, y, tileWidth, tileHeight)
}
override fun handleMouseInput(event: MapMouseEvent) {
editorStateVM.cursorRowProperty.value = event.row
editorStateVM.cursorColumnProperty.value = event.column
when (event.type) {
MouseEvent.MOUSE_PRESSED -> beginTrace(event)
MouseEvent.MOUSE_DRAGGED -> proceedTrace(event)
MouseEvent.MOUSE_RELEASED -> commitTrace(event)
}
}
private fun beginTrace(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY && editorStateVM.selectedLayer >= 0) {
currentTrace = MapPaintingTrace(mapVM, "Paint trace").apply {
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
}
}
}
}
private fun proceedTrace(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
currentTrace?.apply {
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
}
}
}
}
private fun commitTrace(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
currentTrace?.let {
paintingCallback(it)
currentTrace = null
}
}
}
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
import com.bartlomiejpluta.base.editor.render.model.Renderable
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.scene.canvas.GraphicsContext
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.scene.paint.Color
class MapPainter(
private val mapVM: GameMapVM,
private val brushVM: BrushVM,
private val editorStateVM: EditorStateVM,
private val paintingCallback: (MapPaintingTrace) -> Unit
) : Renderable, MapMouseEventHandler {
private val tileWidth = mapVM.tileSet.tileWidth.toDouble()
private val tileHeight = mapVM.tileSet.tileHeight.toDouble()
private var currentTrace: MapPaintingTrace? = null
override fun render(gc: GraphicsContext) {
val alpha = gc.globalAlpha
gc.globalAlpha = 0.4
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
renderTile(gc, row, column, centerRow, centerColumn, tile)
}
gc.globalAlpha = alpha
}
private fun renderTile(gc: GraphicsContext, row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) {
val x = tileWidth * (editorStateVM.cursorColumn - centerColumn + column)
val y = tileHeight * (editorStateVM.cursorRow - centerRow + row)
when {
tile != null -> renderPaintingBrushTile(gc, tile, x, y)
else -> renderEraserTile(gc, x, y)
}
}
private fun renderPaintingBrushTile(gc: GraphicsContext, tile: Tile, x: Double, y: Double) =
gc.drawImage(tile.image, x, y)
private fun renderEraserTile(gc: GraphicsContext, x: Double, y: Double) {
gc.fill = Color.WHITE
gc.fillRect(x, y, tileWidth, tileHeight)
}
override fun handleMouseInput(event: MapMouseEvent) {
editorStateVM.cursorRowProperty.value = event.row
editorStateVM.cursorColumnProperty.value = event.column
when (event.type) {
MouseEvent.MOUSE_PRESSED -> beginTrace(event)
MouseEvent.MOUSE_DRAGGED -> proceedTrace(event)
MouseEvent.MOUSE_RELEASED -> commitTrace(event)
}
}
private fun beginTrace(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY && editorStateVM.selectedLayer >= 0) {
currentTrace = MapPaintingTrace(mapVM, "Paint trace").apply {
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
}
}
}
}
private fun proceedTrace(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
currentTrace?.apply {
brushVM.forEach { row, column, centerRow, centerColumn, tile ->
paint(editorStateVM.selectedLayer, editorStateVM.cursorRow - centerRow + row, editorStateVM.cursorColumn - centerColumn + column, tile)
}
}
}
}
private fun commitTrace(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
currentTrace?.let {
paintingCallback(it)
currentTrace = null
}
}
}
}

View File

@@ -1,56 +1,56 @@
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
data class MapPaintingTrace(val map: GameMapVM, override val commandName: String) : Undoable {
private val trace = mutableListOf<Element>()
fun paint(layerIndex: Int, row: Int, column: Int, tile: Tile?) {
if (row >= map.rows || column >= map.columns || row < 0 || column < 0 || layerIndex < 0) {
return
}
val layer = (map.layers[layerIndex] as TileLayer).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
}
}
override fun undo() {
trace.forEach {
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.formerTile
}
}
override fun redo() {
trace.forEach {
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.tile
}
}
companion object {
private data class Element(
val layerIndex: Int,
val row: Int,
val column: Int,
val formerTile: Tile?,
val tile: Tile?
)
}
package com.bartlomiejpluta.base.editor.map.canvas
import com.bartlomiejpluta.base.editor.command.model.base.Undoable
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
data class MapPaintingTrace(val map: GameMapVM, override val commandName: String) : Undoable {
private val trace = mutableListOf<Element>()
fun paint(layerIndex: Int, row: Int, column: Int, tile: Tile?) {
if (row >= map.rows || column >= map.columns || row < 0 || column < 0 || layerIndex < 0) {
return
}
val layer = (map.layers[layerIndex] as TileLayer).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
}
}
override fun undo() {
trace.forEach {
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.formerTile
}
}
override fun redo() {
trace.forEach {
(map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.tile
}
}
companion object {
private data class Element(
val layerIndex: Int,
val row: Int,
val column: Int,
val formerTile: Tile?,
val tile: Tile?
)
}
}

View File

@@ -1,53 +1,53 @@
package com.bartlomiejpluta.base.editor.map.component
import com.bartlomiejpluta.base.editor.map.canvas.MapCanvas
import com.bartlomiejpluta.base.editor.map.canvas.MapPainter
import com.bartlomiejpluta.base.editor.map.canvas.MapPaintingTrace
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
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.event.EventHandler
import javafx.scene.canvas.Canvas
import javafx.scene.input.MouseEvent
class MapPane(
private val mapVM: GameMapVM,
brushVM: BrushVM,
editorStateVM: EditorStateVM,
paintingCallback: (MapPaintingTrace) -> Unit
) : Canvas(), EventHandler<MouseEvent> {
private val painter = MapPainter(mapVM, brushVM, editorStateVM, paintingCallback)
private val mapCanvas = MapCanvas(mapVM, editorStateVM, painter)
init {
onMouseMoved = this
onMouseDragged = this
onMousePressed = this
onMouseReleased = this
widthProperty().bind(mapVM.widthProperty)
heightProperty().bind(mapVM.heightProperty)
mapVM.item.rowsProperty.addListener { _, _, _ -> render() }
mapVM.item.columnsProperty.addListener { _, _, _ -> render() }
editorStateVM.showGridProperty.addListener { _, _, _ -> render() }
editorStateVM.selectedLayerProperty.addListener { _, _, _ -> render() }
editorStateVM.coverUnderlyingLayersProperty.addListener { _, _, _ -> render() }
render()
}
fun render() {
mapCanvas.render(graphicsContext2D)
}
override fun handle(event: MouseEvent?) {
if (event != null) {
painter.handleMouseInput(MapMouseEvent.of(event, mapVM.tileSet))
}
mapCanvas.render(graphicsContext2D)
}
package com.bartlomiejpluta.base.editor.map.component
import com.bartlomiejpluta.base.editor.map.canvas.MapCanvas
import com.bartlomiejpluta.base.editor.map.canvas.MapPainter
import com.bartlomiejpluta.base.editor.map.canvas.MapPaintingTrace
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
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.event.EventHandler
import javafx.scene.canvas.Canvas
import javafx.scene.input.MouseEvent
class MapPane(
private val mapVM: GameMapVM,
brushVM: BrushVM,
editorStateVM: EditorStateVM,
paintingCallback: (MapPaintingTrace) -> Unit
) : Canvas(), EventHandler<MouseEvent> {
private val painter = MapPainter(mapVM, brushVM, editorStateVM, paintingCallback)
private val mapCanvas = MapCanvas(mapVM, editorStateVM, painter)
init {
onMouseMoved = this
onMouseDragged = this
onMousePressed = this
onMouseReleased = this
widthProperty().bind(mapVM.widthProperty)
heightProperty().bind(mapVM.heightProperty)
mapVM.item.rowsProperty.addListener { _, _, _ -> render() }
mapVM.item.columnsProperty.addListener { _, _, _ -> render() }
editorStateVM.showGridProperty.addListener { _, _, _ -> render() }
editorStateVM.selectedLayerProperty.addListener { _, _, _ -> render() }
editorStateVM.coverUnderlyingLayersProperty.addListener { _, _, _ -> render() }
render()
}
fun render() {
mapCanvas.render(graphicsContext2D)
}
override fun handle(event: MouseEvent?) {
if (event != null) {
painter.handleMouseInput(MapMouseEvent.of(event, mapVM.tileSet))
}
mapCanvas.render(graphicsContext2D)
}
}

View File

@@ -1,97 +1,97 @@
package com.bartlomiejpluta.base.editor.map.model.brush
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.ObservableList
import tornadofx.asObservable
import tornadofx.getValue
import tornadofx.observableListOf
import tornadofx.setValue
class Brush {
val brush: ObservableList<Tile>
val rowsProperty = SimpleIntegerProperty(0)
var rows by rowsProperty
private set
val columnsProperty = SimpleIntegerProperty(0)
var columns by columnsProperty
private set
val rangeProperty = SimpleIntegerProperty(1)
var range by rangeProperty
private set
val modeProperty = SimpleObjectProperty(BrushMode.PAINTING_MODE)
var mode by modeProperty
private set
private constructor(brushArray: Array<Array<Tile>>) {
rowsProperty.value = brushArray.size
brush = observableListOf()
brushArray.forEach { brush.addAll(it) }
if (rowsProperty.value > 0) {
columns = brush.size / rowsProperty.value
}
}
private constructor(brush: List<Tile>, rows: Int, columns: Int) {
this.rows = rows
this.columns = columns
this.brush = brush.asObservable()
}
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
return when {
range > 1 || mode == BrushMode.ERASING_MODE -> forEachInRangedBrush(consumer)
else -> forEachInRegularBrush(consumer)
}
}
private fun forEachInRangedBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
val center = range / 2
(0 until range).forEach { row ->
(0 until range).forEach { column ->
consumer(row, column, center, center, getTileByMode(brush[0]))
}
}
}
private fun getTileByMode(tile: Tile) = when (mode) {
BrushMode.PAINTING_MODE -> tile
BrushMode.ERASING_MODE -> null
}
private fun forEachInRegularBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
val centerRow = rows / 2
val centerColumn = columns / 2
brush.forEachIndexed { id, tile ->
consumer(id / columns, id % columns, centerRow, centerColumn, getTileByMode(tile))
}
}
private fun clone() = Brush(brush, rows, columns).apply {
this.range = this@Brush.range
this.mode = this@Brush.mode
}
fun withRange(range: Int) = clone().apply {
this.range = range
}
fun withMode(mode: BrushMode) = clone().apply {
this.mode = mode
}
companion object {
fun of(brushArray: Array<Array<Tile>>) = Brush(brushArray)
}
}
package com.bartlomiejpluta.base.editor.map.model.brush
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.ObservableList
import tornadofx.asObservable
import tornadofx.getValue
import tornadofx.observableListOf
import tornadofx.setValue
class Brush {
val brush: ObservableList<Tile>
val rowsProperty = SimpleIntegerProperty(0)
var rows by rowsProperty
private set
val columnsProperty = SimpleIntegerProperty(0)
var columns by columnsProperty
private set
val rangeProperty = SimpleIntegerProperty(1)
var range by rangeProperty
private set
val modeProperty = SimpleObjectProperty(BrushMode.PAINTING_MODE)
var mode by modeProperty
private set
private constructor(brushArray: Array<Array<Tile>>) {
rowsProperty.value = brushArray.size
brush = observableListOf()
brushArray.forEach { brush.addAll(it) }
if (rowsProperty.value > 0) {
columns = brush.size / rowsProperty.value
}
}
private constructor(brush: List<Tile>, rows: Int, columns: Int) {
this.rows = rows
this.columns = columns
this.brush = brush.asObservable()
}
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
return when {
range > 1 || mode == BrushMode.ERASING_MODE -> forEachInRangedBrush(consumer)
else -> forEachInRegularBrush(consumer)
}
}
private fun forEachInRangedBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
val center = range / 2
(0 until range).forEach { row ->
(0 until range).forEach { column ->
consumer(row, column, center, center, getTileByMode(brush[0]))
}
}
}
private fun getTileByMode(tile: Tile) = when (mode) {
BrushMode.PAINTING_MODE -> tile
BrushMode.ERASING_MODE -> null
}
private fun forEachInRegularBrush(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) {
val centerRow = rows / 2
val centerColumn = columns / 2
brush.forEachIndexed { id, tile ->
consumer(id / columns, id % columns, centerRow, centerColumn, getTileByMode(tile))
}
}
private fun clone() = Brush(brush, rows, columns).apply {
this.range = this@Brush.range
this.mode = this@Brush.mode
}
fun withRange(range: Int) = clone().apply {
this.range = range
}
fun withMode(mode: BrushMode) = clone().apply {
this.mode = mode
}
companion object {
fun of(brushArray: Array<Array<Tile>>) = Brush(brushArray)
}
}

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.map.model.brush
enum class BrushMode {
PAINTING_MODE,
ERASING_MODE
package com.bartlomiejpluta.base.editor.map.model.brush
enum class BrushMode {
PAINTING_MODE,
ERASING_MODE
}

View File

@@ -1,12 +1,12 @@
package com.bartlomiejpluta.base.editor.map.model.layer
import javafx.beans.property.StringProperty
interface Layer {
var name: String
val nameProperty: StringProperty
fun resize(rows: Int, columns: Int)
fun clone(): Layer
package com.bartlomiejpluta.base.editor.map.model.layer
import javafx.beans.property.StringProperty
interface Layer {
var name: String
val nameProperty: StringProperty
fun resize(rows: Int, columns: Int)
fun clone(): Layer
}

View File

@@ -1,31 +1,31 @@
package com.bartlomiejpluta.base.editor.map.model.layer
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.setValue
class TileLayer(
name: String,
rows: Int,
columns: Int,
layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
) : Layer {
var layer = layer
private set
override val nameProperty = SimpleStringProperty(name)
override var name: String by nameProperty
override fun resize(rows: Int, columns: Int) {
layer = Array(rows) { row ->
Array(columns) { column ->
layer.getOrNull(row)?.getOrNull(column)
}
}
}
override fun clone() = TileLayer(name, 0, 0).apply {
layer = this@TileLayer.layer
}
package com.bartlomiejpluta.base.editor.map.model.layer
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.setValue
class TileLayer(
name: String,
rows: Int,
columns: Int,
layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
) : Layer {
var layer = layer
private set
override val nameProperty = SimpleStringProperty(name)
override var name: String by nameProperty
override fun resize(rows: Int, columns: Int) {
layer = Array(rows) { row ->
Array(columns) { column ->
layer.getOrNull(row)?.getOrNull(column)
}
}
}
override fun clone() = TileLayer(name, 0, 0).apply {
layer = this@TileLayer.layer
}
}

View File

@@ -1,54 +1,54 @@
package com.bartlomiejpluta.base.editor.map.model.map
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import javafx.beans.property.SimpleDoubleProperty
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.observableListOf
import tornadofx.setValue
class GameMap(val tileSet: TileSet) {
val layers = observableListOf<Layer>()
val nameProperty = SimpleStringProperty()
var name by nameProperty
val tileWidth = tileSet.tileWidth.toDouble()
val tileHeight = tileSet.tileHeight.toDouble()
val rowsProperty = SimpleIntegerProperty(INITIAL_ROWS)
var rows by rowsProperty
val columnsProperty = SimpleIntegerProperty(INITIAL_COLUMNS)
var columns by columnsProperty
val widthProperty = SimpleDoubleProperty(INITIAL_COLUMNS * tileWidth)
var width by widthProperty
private set
val heightProperty = SimpleDoubleProperty(INITIAL_ROWS * tileHeight)
var height by heightProperty
private set
init {
rowsProperty.addListener { _, _, newValue ->
val newRows = newValue.toInt()
height = newRows * tileWidth
layers.forEach { it.resize(newRows, columns) }
}
columnsProperty.addListener { _, _, newValue ->
val newColumns = newValue.toInt()
width = newColumns * tileWidth
layers.forEach { it.resize(rows, newColumns) }
}
}
companion object {
private const val INITIAL_ROWS = 20
private const val INITIAL_COLUMNS = 20
}
}
package com.bartlomiejpluta.base.editor.map.model.map
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import javafx.beans.property.SimpleDoubleProperty
import javafx.beans.property.SimpleIntegerProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.observableListOf
import tornadofx.setValue
class GameMap(val tileSet: TileSet) {
val layers = observableListOf<Layer>()
val nameProperty = SimpleStringProperty()
var name by nameProperty
val tileWidth = tileSet.tileWidth.toDouble()
val tileHeight = tileSet.tileHeight.toDouble()
val rowsProperty = SimpleIntegerProperty(INITIAL_ROWS)
var rows by rowsProperty
val columnsProperty = SimpleIntegerProperty(INITIAL_COLUMNS)
var columns by columnsProperty
val widthProperty = SimpleDoubleProperty(INITIAL_COLUMNS * tileWidth)
var width by widthProperty
private set
val heightProperty = SimpleDoubleProperty(INITIAL_ROWS * tileHeight)
var height by heightProperty
private set
init {
rowsProperty.addListener { _, _, newValue ->
val newRows = newValue.toInt()
height = newRows * tileWidth
layers.forEach { it.resize(newRows, columns) }
}
columnsProperty.addListener { _, _, newValue ->
val newColumns = newValue.toInt()
width = newColumns * tileWidth
layers.forEach { it.resize(rows, newColumns) }
}
}
companion object {
private const val INITIAL_ROWS = 20
private const val INITIAL_COLUMNS = 20
}
}

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
interface MapDeserializer : Deserializer<GameMap>

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.common.serial.Serializer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.common.serial.Serializer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
interface MapSerializer : Serializer<GameMap>

View File

@@ -1,52 +1,52 @@
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import com.bartlomiejpluta.base.proto.GameMapProto
import org.springframework.stereotype.Component
import tornadofx.ResourceLookup
import java.io.InputStream
@Component
class ProtobufMapDeserializer : MapDeserializer {
private val resources = ResourceLookup(this)
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
override fun deserialize(input: InputStream): GameMap {
val map = GameMap(tileset)
val proto = GameMapProto.GameMap.parseFrom(input)
map.name = proto.name
map.rows = proto.rows
map.columns = proto.columns
proto.layersList.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 {
return when {
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)
else -> throw IllegalStateException("Not supported layer type")
}
}
private fun deserializeTileLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
val layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
proto.tileLayer.tilesList.forEachIndexed { index, tile ->
layer[index / columns][index % columns] = when(tile) {
0 -> null
else -> tileSet.getTile(tile-1)
}
}
return TileLayer(proto.name, rows, columns, layer)
}
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import com.bartlomiejpluta.base.proto.GameMapProto
import org.springframework.stereotype.Component
import tornadofx.ResourceLookup
import java.io.InputStream
@Component
class ProtobufMapDeserializer : MapDeserializer {
private val resources = ResourceLookup(this)
private val tileset = TileSet(resources.image("/textures/tileset.png"), 160, 8)
override fun deserialize(input: InputStream): GameMap {
val map = GameMap(tileset)
val proto = GameMapProto.GameMap.parseFrom(input)
map.name = proto.name
map.rows = proto.rows
map.columns = proto.columns
proto.layersList.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 {
return when {
proto.hasTileLayer() -> deserializeTileLayer(rows, columns, tileSet, proto)
else -> throw IllegalStateException("Not supported layer type")
}
}
private fun deserializeTileLayer(rows: Int, columns: Int, tileSet: TileSet, proto: GameMapProto.Layer): Layer {
val layer: Array<Array<Tile?>> = Array(rows) { Array(columns) { null } }
proto.tileLayer.tilesList.forEachIndexed { index, tile ->
layer[index / columns][index % columns] = when(tile) {
0 -> null
else -> tileSet.getTile(tile-1)
}
}
return TileLayer(proto.name, rows, columns, layer)
}
}

View File

@@ -1,34 +1,34 @@
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.proto.GameMapProto
import org.springframework.stereotype.Component
import java.io.OutputStream
@Component
class ProtobufMapSerializer : MapSerializer {
override fun serialize(item: GameMap, output: OutputStream) {
val protoMap = GameMapProto.GameMap.newBuilder()
protoMap.name = item.name
protoMap.rows = item.rows
protoMap.columns = item.columns
item.layers.forEach { layer -> protoMap.addLayers(serializeLayer(layer)) }
protoMap.build().writeTo(output)
}
private fun serializeLayer(layer: Layer): GameMapProto.Layer {
return when (layer) {
is TileLayer -> layer.layer.flatMap { it.asIterable() }
.fold(GameMapProto.TileLayer.newBuilder()) { acc, tile -> acc.addTiles((tile?.id?.plus(1)) ?: 0) }
.build()
.let { GameMapProto.Layer.newBuilder().setName(layer.name).setTileLayer(it).build() }
else -> throw IllegalStateException("Not supported layer type")
}
}
package com.bartlomiejpluta.base.editor.map.serial
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.layer.TileLayer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import com.bartlomiejpluta.base.proto.GameMapProto
import org.springframework.stereotype.Component
import java.io.OutputStream
@Component
class ProtobufMapSerializer : MapSerializer {
override fun serialize(item: GameMap, output: OutputStream) {
val protoMap = GameMapProto.GameMap.newBuilder()
protoMap.name = item.name
protoMap.rows = item.rows
protoMap.columns = item.columns
item.layers.forEach { layer -> protoMap.addLayers(serializeLayer(layer)) }
protoMap.build().writeTo(output)
}
private fun serializeLayer(layer: Layer): GameMapProto.Layer {
return when (layer) {
is TileLayer -> layer.layer.flatMap { it.asIterable() }
.fold(GameMapProto.TileLayer.newBuilder()) { acc, tile -> acc.addTiles((tile?.id?.plus(1)) ?: 0) }
.build()
.let { GameMapProto.Layer.newBuilder().setName(layer.name).setTileLayer(it).build() }
else -> throw IllegalStateException("Not supported layer type")
}
}
}

View File

@@ -1,36 +1,36 @@
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.tileset.view.TileSetView
import tornadofx.*
class MapFragment : Fragment() {
override val scope = super.scope as UndoableScope
private val mapView = find<MapView>()
private val layersView = find<MapLayersView>()
private val tileSetView = find<TileSetView>()
private val toolbarView = find<MapToolbarView>()
private val statusBarView = find<MapStatusBarView>()
override val root = borderpane {
top = toolbarView.root
center = mapView.root
right = drawer(multiselect = true) {
item("Layers", expanded = true) {
this += layersView.root
}
item("Tile Set", expanded = true) {
this += tileSetView.root.apply {
maxHeightProperty().bind(this@item.heightProperty())
}
}
}
bottom = statusBarView.root
}
}
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.tileset.view.TileSetView
import tornadofx.*
class MapFragment : Fragment() {
override val scope = super.scope as UndoableScope
private val mapView = find<MapView>()
private val layersView = find<MapLayersView>()
private val tileSetView = find<TileSetView>()
private val toolbarView = find<MapToolbarView>()
private val statusBarView = find<MapStatusBarView>()
override val root = borderpane {
top = toolbarView.root
center = mapView.root
right = drawer(multiselect = true) {
item("Layers", expanded = true) {
this += layersView.root
}
item("Tile Set", expanded = true) {
this += tileSetView.root.apply {
maxHeightProperty().bind(this@item.heightProperty())
}
}
}
bottom = statusBarView.root
}
}

View File

@@ -1,95 +1,95 @@
package com.bartlomiejpluta.base.editor.map.view
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
import com.bartlomiejpluta.base.editor.command.model.map.RemoveLayerCommand
import com.bartlomiejpluta.base.editor.command.model.map.RenameLayerCommand
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
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
import javafx.scene.control.TableView
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapLayersView : View() {
private val undoRedoService: UndoRedoService by di()
override val scope = super.scope as UndoableScope
private val mapVM = find<GameMapVM>()
private val editorStateVM = find<EditorStateVM>()
private var layersPane = TableView(mapVM.layers).apply {
column("Layer Name", Layer::nameProperty).makeEditable().setOnEditCommit {
val command = RenameLayerCommand(it.rowValue, it.newValue)
command.execute()
undoRedoService.push(command, scope)
}
editorStateVM.selectedLayerProperty.bind(selectionModel.selectedIndexProperty())
}
override val root = borderpane {
center = layersPane
bottom = toolbar {
button(graphic = FontIcon("fa-plus")) {
action {
val tileLayer = TileLayer("Layer ${mapVM.layers.size + 1}", mapVM.rows, mapVM.columns)
val command = CreateLayerCommand(mapVM.item, tileLayer)
command.execute()
layersPane.selectionModel.select(mapVM.layers.size - 1)
undoRedoService.push(command, scope)
}
}
button(graphic = FontIcon("fa-chevron-up")) {
enableWhen(editorStateVM.selectedLayerProperty.greaterThan(0))
action {
val newIndex = editorStateVM.selectedLayer - 1
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
command.execute()
layersPane.selectionModel.select(newIndex)
fire(RedrawMapRequestEvent)
undoRedoService.push(command, scope)
}
}
button(graphic = FontIcon("fa-chevron-down")) {
enableWhen(
editorStateVM.selectedLayerProperty.lessThan(mapVM.layers.sizeProperty().minus(1))
.and(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
)
action {
val newIndex = editorStateVM.selectedLayer + 1
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
command.execute()
layersPane.selectionModel.select(newIndex)
fire(RedrawMapRequestEvent)
undoRedoService.push(command, scope)
}
}
button(graphic = FontIcon("fa-trash")) {
enableWhen(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
action {
var index = editorStateVM.selectedLayer
val command = RemoveLayerCommand(mapVM.item, index)
command.execute()
if (--index >= 0) {
layersPane.selectionModel.select(index)
}
fire(RedrawMapRequestEvent)
undoRedoService.push(command, scope)
}
}
}
}
package com.bartlomiejpluta.base.editor.map.view
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
import com.bartlomiejpluta.base.editor.command.model.map.RemoveLayerCommand
import com.bartlomiejpluta.base.editor.command.model.map.RenameLayerCommand
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
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
import javafx.scene.control.TableView
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapLayersView : View() {
private val undoRedoService: UndoRedoService by di()
override val scope = super.scope as UndoableScope
private val mapVM = find<GameMapVM>()
private val editorStateVM = find<EditorStateVM>()
private var layersPane = TableView(mapVM.layers).apply {
column("Layer Name", Layer::nameProperty).makeEditable().setOnEditCommit {
val command = RenameLayerCommand(it.rowValue, it.newValue)
command.execute()
undoRedoService.push(command, scope)
}
editorStateVM.selectedLayerProperty.bind(selectionModel.selectedIndexProperty())
}
override val root = borderpane {
center = layersPane
bottom = toolbar {
button(graphic = FontIcon("fa-plus")) {
action {
val tileLayer = TileLayer("Layer ${mapVM.layers.size + 1}", mapVM.rows, mapVM.columns)
val command = CreateLayerCommand(mapVM.item, tileLayer)
command.execute()
layersPane.selectionModel.select(mapVM.layers.size - 1)
undoRedoService.push(command, scope)
}
}
button(graphic = FontIcon("fa-chevron-up")) {
enableWhen(editorStateVM.selectedLayerProperty.greaterThan(0))
action {
val newIndex = editorStateVM.selectedLayer - 1
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
command.execute()
layersPane.selectionModel.select(newIndex)
fire(RedrawMapRequestEvent)
undoRedoService.push(command, scope)
}
}
button(graphic = FontIcon("fa-chevron-down")) {
enableWhen(
editorStateVM.selectedLayerProperty.lessThan(mapVM.layers.sizeProperty().minus(1))
.and(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
)
action {
val newIndex = editorStateVM.selectedLayer + 1
val command = MoveLayerCommand(mapVM.item, editorStateVM.selectedLayer, newIndex)
command.execute()
layersPane.selectionModel.select(newIndex)
fire(RedrawMapRequestEvent)
undoRedoService.push(command, scope)
}
}
button(graphic = FontIcon("fa-trash")) {
enableWhen(editorStateVM.selectedLayerProperty.greaterThanOrEqualTo(0))
action {
var index = editorStateVM.selectedLayer
val command = RemoveLayerCommand(mapVM.item, index)
command.execute()
if (--index >= 0) {
layersPane.selectionModel.select(index)
}
fire(RedrawMapRequestEvent)
undoRedoService.push(command, scope)
}
}
}
}
}

View File

@@ -1,83 +1,83 @@
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapSettingsFragment : Fragment("Map Settings") {
override val scope = super.scope as UndoableScope
private val undoRedoService: UndoRedoService by di()
private val mapVM = find<GameMapVM>()
var result = false
private set
override val root = form {
icon = FontIcon("fa-map-o")
fieldset("Map Settings") {
field("Map name") {
textfield(mapVM.nameProperty) {
required()
whenDocked { requestFocus() }
}
}
field("Rows") {
textfield(mapVM.rowsProperty) {
stripNonInteger()
required()
validator {
when (it?.toIntOrNull()) {
in 1..50 -> null
in 50..100 -> warning("The map sizes over 50 can impact game performance")
else -> error("The map size must be between 1 and 100")
}
}
}
}
field("Columns") {
textfield(mapVM.columnsProperty) {
stripNonInteger()
required()
validator {
when (it?.toIntOrNull()) {
in 1..50 -> null
in 50..100 -> warning("The map sizes over 50 can impact game performance")
else -> error("The map size must be between 1 and 100")
}
}
}
}
label("Warning: Submitting the form will clear related undo/redo stacks!")
}
buttonbar {
button("Ok") {
shortcut("Enter")
action {
mapVM.commit {
result = true
undoRedoService.clear(scope)
close()
}
}
}
button("Reset") {
action { mapVM.rollback() }
}
button("Cancel") {
action { close() }
}
}
}
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapSettingsFragment : Fragment("Map Settings") {
override val scope = super.scope as UndoableScope
private val undoRedoService: UndoRedoService by di()
private val mapVM = find<GameMapVM>()
var result = false
private set
override val root = form {
icon = FontIcon("fa-map-o")
fieldset("Map Settings") {
field("Map name") {
textfield(mapVM.nameProperty) {
required()
whenDocked { requestFocus() }
}
}
field("Rows") {
textfield(mapVM.rowsProperty) {
stripNonInteger()
required()
validator {
when (it?.toIntOrNull()) {
in 1..50 -> null
in 50..100 -> warning("The map sizes over 50 can impact game performance")
else -> error("The map size must be between 1 and 100")
}
}
}
}
field("Columns") {
textfield(mapVM.columnsProperty) {
stripNonInteger()
required()
validator {
when (it?.toIntOrNull()) {
in 1..50 -> null
in 50..100 -> warning("The map sizes over 50 can impact game performance")
else -> error("The map size must be between 1 and 100")
}
}
}
}
label("Warning: Submitting the form will clear related undo/redo stacks!")
}
buttonbar {
button("Ok") {
shortcut("Enter")
action {
mapVM.commit {
result = true
undoRedoService.clear(scope)
close()
}
}
}
button("Reset") {
action { mapVM.rollback() }
}
button("Cancel") {
action { close() }
}
}
}
}

View File

@@ -1,34 +1,34 @@
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
import javafx.beans.binding.Bindings
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapStatusBarView : View() {
private val editorStateVM = find<EditorStateVM>()
override val root = hbox {
spacing = 50.0
paddingAll = 5.0
hbox {
this += FontIcon("fa-search-minus")
slider(0.5..5.0) {
bind(editorStateVM.zoomProperty)
}
this += FontIcon("fa-search-plus")
}
label(
Bindings.format(
"Cursor: %d, %d",
editorStateVM.cursorColumnProperty.add(1),
editorStateVM.cursorRowProperty.add(1)
)
)
}
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.map.viewmodel.EditorStateVM
import javafx.beans.binding.Bindings
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapStatusBarView : View() {
private val editorStateVM = find<EditorStateVM>()
override val root = hbox {
spacing = 50.0
paddingAll = 5.0
hbox {
this += FontIcon("fa-search-minus")
slider(0.5..5.0) {
bind(editorStateVM.zoomProperty)
}
this += FontIcon("fa-search-plus")
}
label(
Bindings.format(
"Cursor: %d, %d",
editorStateVM.cursorColumnProperty.add(1),
editorStateVM.cursorRowProperty.add(1)
)
)
}
}

View File

@@ -1,105 +1,105 @@
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
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 com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.control.ToggleGroup
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapToolbarView : View() {
private val undoRedoService: UndoRedoService by di()
override val scope = super.scope as UndoableScope
private val mapVM = find<GameMapVM>()
private val brushVM = find<BrushVM>()
private val editorStateVM = find<EditorStateVM>()
private val brushMode = ToggleGroup().apply {
brushVM.itemProperty.addListener { _, _, brush ->
selectedValueProperty<BrushMode>().value = brush.mode
}
}
override val root = toolbar {
button(graphic = FontIcon("fa-undo")) {
shortcut("Ctrl+Z")
action {
undoRedoService.undo(scope)
fire(RedrawMapRequestEvent)
}
}
button(graphic = FontIcon("fa-repeat")) {
shortcut("Ctrl+Shift+Z")
action {
undoRedoService.redo(scope)
fire(RedrawMapRequestEvent)
}
}
togglebutton {
graphic = FontIcon("fa-window-restore")
action {
editorStateVM.coverUnderlyingLayers = isSelected
}
}
togglebutton {
graphic = FontIcon("fa-th")
action {
editorStateVM.showGrid = isSelected
}
}
togglebutton(value = BrushMode.PAINTING_MODE, group = brushMode) {
graphic = FontIcon("fa-paint-brush")
action {
brushVM.item = brushVM.withMode(BrushMode.PAINTING_MODE)
brushVM.commit()
}
}
togglebutton(value = BrushMode.ERASING_MODE, group = brushMode) {
graphic = FontIcon("fa-eraser")
action {
brushVM.item = brushVM.withMode(BrushMode.ERASING_MODE)
brushVM.commit()
}
}
this += FontIcon("fa-paint-brush").apply { iconSize = 10 }
slider(1..5) {
majorTickUnit = 1.0
isSnapToTicks = true
minorTickCount = 0
valueProperty().addListener { _, _, newValue ->
brushVM.item = brushVM.withRange(newValue.toInt())
brushVM.commit()
}
brushVM.itemProperty.addListener { _, _, brush ->
value = brush.range.toDouble()
}
}
this += FontIcon("fa-paint-brush").apply { iconSize = 15 }
button(graphic = FontIcon("fa-sliders")) {
action {
find<MapSettingsFragment>().openModal(block = true, resizable = false)
}
}
}
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
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 com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.control.ToggleGroup
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
class MapToolbarView : View() {
private val undoRedoService: UndoRedoService by di()
override val scope = super.scope as UndoableScope
private val mapVM = find<GameMapVM>()
private val brushVM = find<BrushVM>()
private val editorStateVM = find<EditorStateVM>()
private val brushMode = ToggleGroup().apply {
brushVM.itemProperty.addListener { _, _, brush ->
selectedValueProperty<BrushMode>().value = brush.mode
}
}
override val root = toolbar {
button(graphic = FontIcon("fa-undo")) {
shortcut("Ctrl+Z")
action {
undoRedoService.undo(scope)
fire(RedrawMapRequestEvent)
}
}
button(graphic = FontIcon("fa-repeat")) {
shortcut("Ctrl+Shift+Z")
action {
undoRedoService.redo(scope)
fire(RedrawMapRequestEvent)
}
}
togglebutton {
graphic = FontIcon("fa-window-restore")
action {
editorStateVM.coverUnderlyingLayers = isSelected
}
}
togglebutton {
graphic = FontIcon("fa-th")
action {
editorStateVM.showGrid = isSelected
}
}
togglebutton(value = BrushMode.PAINTING_MODE, group = brushMode) {
graphic = FontIcon("fa-paint-brush")
action {
brushVM.item = brushVM.withMode(BrushMode.PAINTING_MODE)
brushVM.commit()
}
}
togglebutton(value = BrushMode.ERASING_MODE, group = brushMode) {
graphic = FontIcon("fa-eraser")
action {
brushVM.item = brushVM.withMode(BrushMode.ERASING_MODE)
brushVM.commit()
}
}
this += FontIcon("fa-paint-brush").apply { iconSize = 10 }
slider(1..5) {
majorTickUnit = 1.0
isSnapToTicks = true
minorTickCount = 0
valueProperty().addListener { _, _, newValue ->
brushVM.item = brushVM.withRange(newValue.toInt())
brushVM.commit()
}
brushVM.itemProperty.addListener { _, _, brush ->
value = brush.range.toDouble()
}
}
this += FontIcon("fa-paint-brush").apply { iconSize = 15 }
button(graphic = FontIcon("fa-sliders")) {
action {
find<MapSettingsFragment>().openModal(block = true, resizable = false)
}
}
}
}

View File

@@ -1,64 +1,64 @@
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
import com.bartlomiejpluta.base.editor.map.component.MapPane
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.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.scene.transform.Scale
import tornadofx.View
import tornadofx.group
import tornadofx.plusAssign
import tornadofx.scrollpane
class MapView : View() {
private val undoRedoService: UndoRedoService by di()
override val scope = super.scope as UndoableScope
private val mapVM = find<GameMapVM>()
private val brushVM = find<BrushVM>()
private val editorStateVM = find<EditorStateVM>()
private val mapPane = MapPane(mapVM, brushVM, editorStateVM) { undoRedoService.push(it, scope) }
private val zoom = Scale(1.0, 1.0, 0.0, 0.0).apply {
xProperty().bind(editorStateVM.zoomProperty)
yProperty().bind(editorStateVM.zoomProperty)
}
init {
brushVM.item = mapVM.tileSet.baseBrush
brushVM.commit()
subscribe<RedrawMapRequestEvent> { mapPane.render() }
}
override val root = scrollpane {
prefWidth = 640.0
prefHeight = 480.0
isPannable = true
group {
// Let the ScrollPane.viewRect only pan on middle button.
addEventHandler(MouseEvent.ANY) {
if (it.button != MouseButton.MIDDLE) {
it.consume()
}
}
group {
this += mapPane
transforms += zoom
}
}
}
package com.bartlomiejpluta.base.editor.map.view
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.command.service.UndoRedoService
import com.bartlomiejpluta.base.editor.event.RedrawMapRequestEvent
import com.bartlomiejpluta.base.editor.map.component.MapPane
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.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.scene.transform.Scale
import tornadofx.View
import tornadofx.group
import tornadofx.plusAssign
import tornadofx.scrollpane
class MapView : View() {
private val undoRedoService: UndoRedoService by di()
override val scope = super.scope as UndoableScope
private val mapVM = find<GameMapVM>()
private val brushVM = find<BrushVM>()
private val editorStateVM = find<EditorStateVM>()
private val mapPane = MapPane(mapVM, brushVM, editorStateVM) { undoRedoService.push(it, scope) }
private val zoom = Scale(1.0, 1.0, 0.0, 0.0).apply {
xProperty().bind(editorStateVM.zoomProperty)
yProperty().bind(editorStateVM.zoomProperty)
}
init {
brushVM.item = mapVM.tileSet.baseBrush
brushVM.commit()
subscribe<RedrawMapRequestEvent> { mapPane.render() }
}
override val root = scrollpane {
prefWidth = 640.0
prefHeight = 480.0
isPannable = true
group {
// Let the ScrollPane.viewRect only pan on middle button.
addEventHandler(MouseEvent.ANY) {
if (it.button != MouseButton.MIDDLE) {
it.consume()
}
}
group {
this += mapPane
transforms += zoom
}
}
}
}

View File

@@ -1,29 +1,29 @@
package com.bartlomiejpluta.base.editor.map.viewmodel
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import tornadofx.ItemViewModel
import tornadofx.getValue
class BrushVM : ItemViewModel<Brush>(Brush.of(arrayOf(arrayOf()))) {
val brush = bind(Brush::brush)
val rowsProperty = bind(Brush::rowsProperty)
val rows by rowsProperty
val columnsProperty = bind(Brush::columnsProperty)
val columns by columnsProperty
val rangeProperty = bind(Brush::rangeProperty)
val range by rangeProperty
val modeProperty = bind(Brush::modeProperty)
val mode by modeProperty
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) = item.forEach(consumer)
fun withRange(range: Int) = item.withRange(range)
fun withMode(mode: BrushMode) = item.withMode(mode)
package com.bartlomiejpluta.base.editor.map.viewmodel
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
import com.bartlomiejpluta.base.editor.map.model.brush.BrushMode
import com.bartlomiejpluta.base.editor.tileset.model.Tile
import tornadofx.ItemViewModel
import tornadofx.getValue
class BrushVM : ItemViewModel<Brush>(Brush.of(arrayOf(arrayOf()))) {
val brush = bind(Brush::brush)
val rowsProperty = bind(Brush::rowsProperty)
val rows by rowsProperty
val columnsProperty = bind(Brush::columnsProperty)
val columns by columnsProperty
val rangeProperty = bind(Brush::rangeProperty)
val range by rangeProperty
val modeProperty = bind(Brush::modeProperty)
val mode by modeProperty
fun forEach(consumer: (row: Int, column: Int, centerRow: Int, centerColumn: Int, tile: Tile?) -> Unit) = item.forEach(consumer)
fun withRange(range: Int) = item.withRange(range)
fun withMode(mode: BrushMode) = item.withMode(mode)
}

View File

@@ -1,28 +1,28 @@
package com.bartlomiejpluta.base.editor.map.viewmodel
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleDoubleProperty
import javafx.beans.property.SimpleIntegerProperty
import tornadofx.ViewModel
import tornadofx.getValue
import tornadofx.setValue
class EditorStateVM : ViewModel() {
val selectedLayerProperty = SimpleIntegerProperty(0)
var selectedLayer by selectedLayerProperty
val showGridProperty = SimpleBooleanProperty(true)
var showGrid by showGridProperty
val coverUnderlyingLayersProperty = SimpleBooleanProperty(true)
var coverUnderlyingLayers by coverUnderlyingLayersProperty
val zoomProperty = SimpleDoubleProperty(1.0)
var zoom by zoomProperty
val cursorRowProperty = SimpleIntegerProperty(-1)
val cursorRow by cursorRowProperty
val cursorColumnProperty = SimpleIntegerProperty(-1)
val cursorColumn by cursorColumnProperty
package com.bartlomiejpluta.base.editor.map.viewmodel
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleDoubleProperty
import javafx.beans.property.SimpleIntegerProperty
import tornadofx.ViewModel
import tornadofx.getValue
import tornadofx.setValue
class EditorStateVM : ViewModel() {
val selectedLayerProperty = SimpleIntegerProperty(0)
var selectedLayer by selectedLayerProperty
val showGridProperty = SimpleBooleanProperty(true)
var showGrid by showGridProperty
val coverUnderlyingLayersProperty = SimpleBooleanProperty(true)
var coverUnderlyingLayers by coverUnderlyingLayersProperty
val zoomProperty = SimpleDoubleProperty(1.0)
var zoom by zoomProperty
val cursorRowProperty = SimpleIntegerProperty(-1)
val cursorRow by cursorRowProperty
val cursorColumnProperty = SimpleIntegerProperty(-1)
val cursorColumn by cursorColumnProperty
}

View File

@@ -1,39 +1,39 @@
package com.bartlomiejpluta.base.editor.map.viewmodel
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import javafx.beans.property.SimpleListProperty
import tornadofx.ItemViewModel
import tornadofx.getValue
import tornadofx.setValue
class GameMapVM(map: GameMap) : ItemViewModel<GameMap>(map) {
val layers: SimpleListProperty<Layer> = bind(GameMap::layers)
val nameProperty = bind(GameMap::nameProperty)
var name by nameProperty
val tileSetProperty = bind(GameMap::tileSet)
val tileSet by tileSetProperty
val rowsProperty = bind(GameMap::rowsProperty)
var rows by rowsProperty
val columnsProperty = bind(GameMap::columnsProperty)
var columns by columnsProperty
val tileWidthProperty = bind(GameMap::tileWidth)
val tileWidth by tileWidthProperty
val tileHeightProperty = bind(GameMap::tileHeight)
val tileHeight by tileHeightProperty
val widthProperty = bind(GameMap::widthProperty)
val width by widthProperty
val heightProperty = bind(GameMap::heightProperty)
val height by heightProperty
}
package com.bartlomiejpluta.base.editor.map.viewmodel
import com.bartlomiejpluta.base.editor.map.model.layer.Layer
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
import javafx.beans.property.SimpleListProperty
import tornadofx.ItemViewModel
import tornadofx.getValue
import tornadofx.setValue
class GameMapVM(map: GameMap) : ItemViewModel<GameMap>(map) {
val layers: SimpleListProperty<Layer> = bind(GameMap::layers)
val nameProperty = bind(GameMap::nameProperty)
var name by nameProperty
val tileSetProperty = bind(GameMap::tileSet)
val tileSet by tileSetProperty
val rowsProperty = bind(GameMap::rowsProperty)
var rows by rowsProperty
val columnsProperty = bind(GameMap::columnsProperty)
var columns by columnsProperty
val tileWidthProperty = bind(GameMap::tileWidth)
val tileWidth by tileWidthProperty
val tileHeightProperty = bind(GameMap::tileHeight)
val tileHeight by tileHeightProperty
val widthProperty = bind(GameMap::widthProperty)
val width by widthProperty
val heightProperty = bind(GameMap::heightProperty)
val height by heightProperty
}

View File

@@ -1,32 +1,32 @@
package com.bartlomiejpluta.base.editor.project.manager
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.editor.project.serial.ProjectDeserializer
import com.bartlomiejpluta.base.editor.project.serial.ProjectSerializer
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
@Component
class DefaultProjectManager : ProjectManager {
@Autowired
private lateinit var projectSerializer: ProjectSerializer
@Autowired
private lateinit var projectDeserializer: ProjectDeserializer
override fun saveProject(project: Project) {
project.sourceDirectory.mkdirs()
FileOutputStream(File(project.sourceDirectory, "project.bep")).use {
projectSerializer.serialize(project, it)
}
}
override fun openProject(file: File) = FileInputStream(file)
.use { projectDeserializer.deserialize(it) }
.apply { sourceDirectoryProperty.value = file.parentFile }
package com.bartlomiejpluta.base.editor.project.manager
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.editor.project.serial.ProjectDeserializer
import com.bartlomiejpluta.base.editor.project.serial.ProjectSerializer
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
@Component
class DefaultProjectManager : ProjectManager {
@Autowired
private lateinit var projectSerializer: ProjectSerializer
@Autowired
private lateinit var projectDeserializer: ProjectDeserializer
override fun saveProject(project: Project) {
project.sourceDirectory.mkdirs()
FileOutputStream(File(project.sourceDirectory, "project.bep")).use {
projectSerializer.serialize(project, it)
}
}
override fun openProject(file: File) = FileInputStream(file)
.use { projectDeserializer.deserialize(it) }
.apply { sourceDirectoryProperty.value = file.parentFile }
}

View File

@@ -1,9 +1,9 @@
package com.bartlomiejpluta.base.editor.project.manager
import com.bartlomiejpluta.base.editor.project.model.Project
import java.io.File
interface ProjectManager {
fun saveProject(project: Project)
fun openProject(file: File): Project
package com.bartlomiejpluta.base.editor.project.manager
import com.bartlomiejpluta.base.editor.project.model.Project
import java.io.File
interface ProjectManager {
fun saveProject(project: Project)
fun openProject(file: File): Project
}

View File

@@ -1,14 +1,14 @@
package com.bartlomiejpluta.base.editor.project.model
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.*
import java.io.File
class Project {
val nameProperty = SimpleStringProperty()
var name by nameProperty
val sourceDirectoryProperty = SimpleObjectProperty<File>()
val sourceDirectory by sourceDirectoryProperty
}
package com.bartlomiejpluta.base.editor.project.model
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.*
import java.io.File
class Project {
val nameProperty = SimpleStringProperty()
var name by nameProperty
val sourceDirectoryProperty = SimpleObjectProperty<File>()
val sourceDirectory by sourceDirectoryProperty
}

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
import com.bartlomiejpluta.base.editor.project.model.Project
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.common.serial.Deserializer
import com.bartlomiejpluta.base.editor.project.model.Project
interface ProjectDeserializer : Deserializer<Project>

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.common.serial.Serializer
import com.bartlomiejpluta.base.editor.project.model.Project
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.common.serial.Serializer
import com.bartlomiejpluta.base.editor.project.model.Project
interface ProjectSerializer : Serializer<Project>

View File

@@ -1,18 +1,18 @@
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.proto.ProjectProto
import org.springframework.stereotype.Component
import java.io.InputStream
@Component
class ProtobufProjectDeserializer : ProjectDeserializer {
override fun deserialize(input: InputStream): Project {
val proto = ProjectProto.Project.parseFrom(input)
val project = Project()
project.name = proto.name
return project
}
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.proto.ProjectProto
import org.springframework.stereotype.Component
import java.io.InputStream
@Component
class ProtobufProjectDeserializer : ProjectDeserializer {
override fun deserialize(input: InputStream): Project {
val proto = ProjectProto.Project.parseFrom(input)
val project = Project()
project.name = proto.name
return project
}
}

View File

@@ -1,16 +1,16 @@
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.proto.ProjectProto
import org.springframework.stereotype.Component
import java.io.OutputStream
@Component
class ProtobufProjectSerializer : ProjectSerializer {
override fun serialize(item: Project, output: OutputStream) {
val proto = ProjectProto.Project.newBuilder()
proto.name = item.name
proto.build().writeTo(output)
}
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.proto.ProjectProto
import org.springframework.stereotype.Component
import java.io.OutputStream
@Component
class ProtobufProjectSerializer : ProjectSerializer {
override fun serialize(item: Project, output: OutputStream) {
val proto = ProjectProto.Project.newBuilder()
proto.name = item.name
proto.build().writeTo(output)
}
}

View File

@@ -1,96 +1,96 @@
package com.bartlomiejpluta.base.editor.project.view
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
import javafx.beans.binding.Bindings
import javafx.beans.binding.Bindings.createStringBinding
import javafx.beans.property.SimpleStringProperty
import tornadofx.*
import java.io.File
class ProjectSettingsFragment : Fragment("Project Settings") {
private val projectVM = find<ProjectVM>(FX.defaultScope)
private val rootDirectory = SimpleStringProperty("")
private val projectDirectory = SimpleStringProperty("")
private val directory = createStringBinding({
File(rootDirectory.value, projectDirectory.value).absolutePath
}, rootDirectory, projectDirectory)
init {
directory.addListener { _, _, path -> projectVM.sourceDirectoryProperty.value = File(path) }
}
var result = false
private set
override val root = form {
fieldset("Project Settings") {
field("Project Name") {
textfield(projectVM.nameProperty) {
required()
trimWhitespace()
whenDocked { requestFocus() }
}
}
field("Project Location") {
hbox {
textfield(rootDirectory) {
trimWhitespace()
projectVM.validationContext.addValidator(this, rootDirectory) {
when {
it.isNullOrBlank() -> error("Field is required")
!File(it).exists() -> error("Provide valid path to the direction")
else -> null
}
}
}
button("Choose") {
action {
rootDirectory.value = chooseDirectory("Project Location")?.absolutePath
}
}
}
}
field("Project Directory Name") {
textfield(projectDirectory) {
trimWhitespace()
projectVM.validationContext.addValidator(this, projectDirectory) {
when {
it.isNullOrBlank() -> error("Field is required")
File(directory.value).exists() -> error("The directory ${directory.value} already exists")
else -> null
}
}
}
}
label(Bindings.format("Directory:\n%s", directory))
}
buttonbar {
button("Ok") {
action {
projectVM.commit {
result = true
close()
}
}
shortcut("Enter")
}
button("Reset") {
action { projectVM.rollback() }
}
button("Cancel") {
action { close() }
}
}
}
package com.bartlomiejpluta.base.editor.project.view
import com.bartlomiejpluta.base.editor.project.viewmodel.ProjectVM
import javafx.beans.binding.Bindings
import javafx.beans.binding.Bindings.createStringBinding
import javafx.beans.property.SimpleStringProperty
import tornadofx.*
import java.io.File
class ProjectSettingsFragment : Fragment("Project Settings") {
private val projectVM = find<ProjectVM>(FX.defaultScope)
private val rootDirectory = SimpleStringProperty("")
private val projectDirectory = SimpleStringProperty("")
private val directory = createStringBinding({
File(rootDirectory.value, projectDirectory.value).absolutePath
}, rootDirectory, projectDirectory)
init {
directory.addListener { _, _, path -> projectVM.sourceDirectoryProperty.value = File(path) }
}
var result = false
private set
override val root = form {
fieldset("Project Settings") {
field("Project Name") {
textfield(projectVM.nameProperty) {
required()
trimWhitespace()
whenDocked { requestFocus() }
}
}
field("Project Location") {
hbox {
textfield(rootDirectory) {
trimWhitespace()
projectVM.validationContext.addValidator(this, rootDirectory) {
when {
it.isNullOrBlank() -> error("Field is required")
!File(it).exists() -> error("Provide valid path to the direction")
else -> null
}
}
}
button("Choose") {
action {
rootDirectory.value = chooseDirectory("Project Location")?.absolutePath
}
}
}
}
field("Project Directory Name") {
textfield(projectDirectory) {
trimWhitespace()
projectVM.validationContext.addValidator(this, projectDirectory) {
when {
it.isNullOrBlank() -> error("Field is required")
File(directory.value).exists() -> error("The directory ${directory.value} already exists")
else -> null
}
}
}
}
label(Bindings.format("Directory:\n%s", directory))
}
buttonbar {
button("Ok") {
action {
projectVM.commit {
result = true
close()
}
}
shortcut("Enter")
}
button("Reset") {
action { projectVM.rollback() }
}
button("Cancel") {
action { close() }
}
}
}
}

View File

@@ -1,12 +1,12 @@
package com.bartlomiejpluta.base.editor.project.viewmodel
import com.bartlomiejpluta.base.editor.project.model.Project
import tornadofx.*
class ProjectVM(project: Project) : ItemViewModel<Project>(project) {
val nameProperty = bind(Project::nameProperty)
val name by nameProperty
val sourceDirectoryProperty = bind(Project::sourceDirectoryProperty)
val sourceDirectory by sourceDirectoryProperty
package com.bartlomiejpluta.base.editor.project.viewmodel
import com.bartlomiejpluta.base.editor.project.model.Project
import tornadofx.*
class ProjectVM(project: Project) : ItemViewModel<Project>(project) {
val nameProperty = bind(Project::nameProperty)
val name by nameProperty
val sourceDirectoryProperty = bind(Project::sourceDirectoryProperty)
val sourceDirectory by sourceDirectoryProperty
}

View File

@@ -1,18 +1,18 @@
package com.bartlomiejpluta.base.editor.render.input
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import javafx.event.EventType
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
class MapMouseEvent(val row: Int, val column: Int, val type: EventType<out MouseEvent>, val button: MouseButton) {
companion object {
fun of(event: MouseEvent, tileSet: TileSet) = MapMouseEvent(
(event.y / tileSet.tileHeight).toInt(),
(event.x / tileSet.tileWidth).toInt(),
event.eventType,
event.button
)
}
package com.bartlomiejpluta.base.editor.render.input
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import javafx.event.EventType
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
class MapMouseEvent(val row: Int, val column: Int, val type: EventType<out MouseEvent>, val button: MouseButton) {
companion object {
fun of(event: MouseEvent, tileSet: TileSet) = MapMouseEvent(
(event.y / tileSet.tileHeight).toInt(),
(event.x / tileSet.tileWidth).toInt(),
event.eventType,
event.button
)
}
}

View File

@@ -1,5 +1,5 @@
package com.bartlomiejpluta.base.editor.render.input
interface MapMouseEventHandler {
fun handleMouseInput(event: MapMouseEvent)
package com.bartlomiejpluta.base.editor.render.input
interface MapMouseEventHandler {
fun handleMouseInput(event: MapMouseEvent)
}

View File

@@ -1,7 +1,7 @@
package com.bartlomiejpluta.base.editor.render.model
import javafx.scene.canvas.GraphicsContext
interface Renderable {
fun render(gc: GraphicsContext)
package com.bartlomiejpluta.base.editor.render.model
import javafx.scene.canvas.GraphicsContext
interface Renderable {
fun render(gc: GraphicsContext)
}

View File

@@ -1,8 +1,8 @@
package com.bartlomiejpluta.base.editor.resource.uid.manager
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
interface UIDManager {
fun nextUID(target: UIDTarget): String
fun loadData(target: UIDTarget, uids: Set<String>)
package com.bartlomiejpluta.base.editor.resource.uid.manager
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
interface UIDManager {
fun nextUID(target: UIDTarget): String
fun loadData(target: UIDTarget, uids: Set<String>)
}

View File

@@ -1,26 +1,26 @@
package com.bartlomiejpluta.base.editor.resource.uid.manager
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
import org.springframework.stereotype.Component
import java.util.*
@Component
class UUIDBasedUIDManager : UIDManager {
private val registry = mutableMapOf<UIDTarget, Set<String>>()
override fun nextUID(target: UIDTarget): String {
val set = registry.putIfAbsent(target, mutableSetOf())!!
var uid = ""
do {
uid = UUID.randomUUID().toString()
} while (uid !in set)
return uid
}
override fun loadData(target: UIDTarget, uids: Set<String>) {
TODO("Not yet implemented")
}
package com.bartlomiejpluta.base.editor.resource.uid.manager
import com.bartlomiejpluta.base.editor.resource.uid.model.UIDTarget
import org.springframework.stereotype.Component
import java.util.*
@Component
class UUIDBasedUIDManager : UIDManager {
private val registry = mutableMapOf<UIDTarget, Set<String>>()
override fun nextUID(target: UIDTarget): String {
val set = registry.putIfAbsent(target, mutableSetOf())!!
var uid = ""
do {
uid = UUID.randomUUID().toString()
} while (uid !in set)
return uid
}
override fun loadData(target: UIDTarget, uids: Set<String>) {
TODO("Not yet implemented")
}
}

View File

@@ -1,5 +1,5 @@
package com.bartlomiejpluta.base.editor.resource.uid.model
enum class UIDTarget {
MAP
package com.bartlomiejpluta.base.editor.resource.uid.model
enum class UIDTarget {
MAP
}

View File

@@ -1,69 +1,69 @@
package com.bartlomiejpluta.base.editor.tileset.canvas
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
import com.bartlomiejpluta.base.editor.render.model.Renderable
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.canvas.GraphicsContext
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.scene.paint.Color
class TileSetCanvas(private val gameMapVM: GameMapVM, private val selection: TileSetSelection) : Renderable, MapMouseEventHandler {
private var mouseRow = -1
private var mouseColumn = -1
override fun render(gc: GraphicsContext) {
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
renderTiles(gc)
selection.render(gc)
}
private fun renderTiles(gc: GraphicsContext) {
gameMapVM.tileSet.forEach { row, column, tile ->
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
gc.fillRect(
column * tile.image.width,
row * tile.image.height,
gameMapVM.tileSet.tileWidth.toDouble(),
gameMapVM.tileSet.tileHeight.toDouble()
)
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
}
}
override fun handleMouseInput(event: MapMouseEvent) {
mouseRow = event.row
mouseColumn = event.column
when (event.type) {
MouseEvent.MOUSE_PRESSED -> beginSelection(event)
MouseEvent.MOUSE_DRAGGED -> proceedSelection(event)
MouseEvent.MOUSE_RELEASED -> finishSelection(event)
}
}
private fun beginSelection(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
selection.begin(event.row.toDouble(), event.column.toDouble())
}
}
private fun proceedSelection(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
selection.proceed(event.row.toDouble(), event.column.toDouble())
}
}
private fun finishSelection(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
selection.finish(event.row.toDouble(), event.column.toDouble())
}
}
companion object {
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
private val BACKGROUND_COLOR2 = Color.color(0.95, 0.95, 0.95, 0.95)
}
package com.bartlomiejpluta.base.editor.tileset.canvas
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
import com.bartlomiejpluta.base.editor.render.input.MapMouseEventHandler
import com.bartlomiejpluta.base.editor.render.model.Renderable
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.canvas.GraphicsContext
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.scene.paint.Color
class TileSetCanvas(private val gameMapVM: GameMapVM, private val selection: TileSetSelection) : Renderable, MapMouseEventHandler {
private var mouseRow = -1
private var mouseColumn = -1
override fun render(gc: GraphicsContext) {
gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height)
renderTiles(gc)
selection.render(gc)
}
private fun renderTiles(gc: GraphicsContext) {
gameMapVM.tileSet.forEach { row, column, tile ->
gc.fill = if ((row + column) % 2 == 0) BACKGROUND_COLOR1 else BACKGROUND_COLOR2
gc.fillRect(
column * tile.image.width,
row * tile.image.height,
gameMapVM.tileSet.tileWidth.toDouble(),
gameMapVM.tileSet.tileHeight.toDouble()
)
gc.drawImage(tile.image, column * tile.image.width, row * tile.image.height)
}
}
override fun handleMouseInput(event: MapMouseEvent) {
mouseRow = event.row
mouseColumn = event.column
when (event.type) {
MouseEvent.MOUSE_PRESSED -> beginSelection(event)
MouseEvent.MOUSE_DRAGGED -> proceedSelection(event)
MouseEvent.MOUSE_RELEASED -> finishSelection(event)
}
}
private fun beginSelection(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
selection.begin(event.row.toDouble(), event.column.toDouble())
}
}
private fun proceedSelection(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
selection.proceed(event.row.toDouble(), event.column.toDouble())
}
}
private fun finishSelection(event: MapMouseEvent) {
if (event.button == MouseButton.PRIMARY) {
selection.finish(event.row.toDouble(), event.column.toDouble())
}
}
companion object {
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
private val BACKGROUND_COLOR2 = Color.color(0.95, 0.95, 0.95, 0.95)
}
}

View File

@@ -1,80 +1,80 @@
package com.bartlomiejpluta.base.editor.tileset.canvas
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
import com.bartlomiejpluta.base.editor.render.model.Renderable
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.canvas.GraphicsContext
import javafx.scene.paint.Color
import kotlin.math.abs
import kotlin.math.min
class TileSetSelection(private val gameMapVM: GameMapVM, private val brushVM: BrushVM) : Renderable {
private var startRow = 0.0
private var startColumn = 0.0
private var offsetRow = 0.0
private var offsetColumn = 0.0
private var x = 0.0
private var y = 0.0
private var width = gameMapVM.tileSet.tileWidth.toDouble()
private var height = gameMapVM.tileSet.tileHeight.toDouble()
fun begin(row: Double, column: Double) {
startRow = row
offsetRow = 0.0
startColumn = column
offsetColumn = 0.0
updateRect(row, column)
}
private fun updateRect(row: Double, column: Double) {
x = min(column, startColumn) * gameMapVM.tileSet.tileWidth
y = min(row, startRow) * gameMapVM.tileSet.tileHeight
width = (offsetColumn + 1) * gameMapVM.tileSet.tileWidth
height = (offsetRow + 1) * gameMapVM.tileSet.tileHeight
}
fun proceed(row: Double, column: Double) {
offsetRow = abs(row - startRow)
offsetColumn = abs(column - startColumn)
updateRect(row, column)
}
fun finish(row: Double, column: Double) {
proceed(row, column)
startRow = min(row, startRow)
startColumn = min(column, startColumn)
val firstRow = startRow.toInt()
val firstColumn = startColumn.toInt()
val rows = offsetRow.toInt() + 1
val columns = offsetColumn.toInt() + 1
val brushArray = Array(rows) { rowIndex ->
Array(columns) { columnIndex ->
gameMapVM.tileSet.getTile(firstRow + rowIndex, firstColumn + columnIndex)
}
}
brushVM.item = Brush.of(brushArray)
brushVM.commit()
}
override fun render(gc: GraphicsContext) {
gc.fill = SELECTION_FILL_COLOR
gc.fillRect(x, y, width, height)
gc.stroke = SELECTION_STROKE_COLOR
gc.strokeRect(x, y, width, height)
}
companion object {
private val SELECTION_FILL_COLOR = Color.color(0.0, 0.7, 1.0, 0.4)
private val SELECTION_STROKE_COLOR = Color.color(0.0, 0.7, 1.0, 1.0)
}
package com.bartlomiejpluta.base.editor.tileset.canvas
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
import com.bartlomiejpluta.base.editor.render.model.Renderable
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.scene.canvas.GraphicsContext
import javafx.scene.paint.Color
import kotlin.math.abs
import kotlin.math.min
class TileSetSelection(private val gameMapVM: GameMapVM, private val brushVM: BrushVM) : Renderable {
private var startRow = 0.0
private var startColumn = 0.0
private var offsetRow = 0.0
private var offsetColumn = 0.0
private var x = 0.0
private var y = 0.0
private var width = gameMapVM.tileSet.tileWidth.toDouble()
private var height = gameMapVM.tileSet.tileHeight.toDouble()
fun begin(row: Double, column: Double) {
startRow = row
offsetRow = 0.0
startColumn = column
offsetColumn = 0.0
updateRect(row, column)
}
private fun updateRect(row: Double, column: Double) {
x = min(column, startColumn) * gameMapVM.tileSet.tileWidth
y = min(row, startRow) * gameMapVM.tileSet.tileHeight
width = (offsetColumn + 1) * gameMapVM.tileSet.tileWidth
height = (offsetRow + 1) * gameMapVM.tileSet.tileHeight
}
fun proceed(row: Double, column: Double) {
offsetRow = abs(row - startRow)
offsetColumn = abs(column - startColumn)
updateRect(row, column)
}
fun finish(row: Double, column: Double) {
proceed(row, column)
startRow = min(row, startRow)
startColumn = min(column, startColumn)
val firstRow = startRow.toInt()
val firstColumn = startColumn.toInt()
val rows = offsetRow.toInt() + 1
val columns = offsetColumn.toInt() + 1
val brushArray = Array(rows) { rowIndex ->
Array(columns) { columnIndex ->
gameMapVM.tileSet.getTile(firstRow + rowIndex, firstColumn + columnIndex)
}
}
brushVM.item = Brush.of(brushArray)
brushVM.commit()
}
override fun render(gc: GraphicsContext) {
gc.fill = SELECTION_FILL_COLOR
gc.fillRect(x, y, width, height)
gc.stroke = SELECTION_STROKE_COLOR
gc.strokeRect(x, y, width, height)
}
companion object {
private val SELECTION_FILL_COLOR = Color.color(0.0, 0.7, 1.0, 0.4)
private val SELECTION_STROKE_COLOR = Color.color(0.0, 0.7, 1.0, 1.0)
}
}

View File

@@ -1,42 +1,42 @@
package com.bartlomiejpluta.base.editor.tileset.component
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetCanvas
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetSelection
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.event.EventHandler
import javafx.scene.canvas.Canvas
import javafx.scene.input.MouseEvent
class TileSetPane(private val gameMapVM: GameMapVM, brushVM: BrushVM) : Canvas(), EventHandler<MouseEvent> {
private val selection = TileSetSelection(gameMapVM, brushVM)
private val tileSetCanvas = TileSetCanvas(gameMapVM, selection)
init {
onMouseMoved = this
onMouseDragged = this
onMousePressed = this
onMouseReleased = this
width = gameMapVM.tileSet.width.toDouble()
height = gameMapVM.tileSet.height.toDouble()
brushVM.itemProperty.addListener { _, _, _ -> render() }
render()
}
private fun render() {
tileSetCanvas.render(graphicsContext2D)
}
override fun handle(event: MouseEvent?) {
if (event != null) {
tileSetCanvas.handleMouseInput(MapMouseEvent.of(event, gameMapVM.tileSet))
}
tileSetCanvas.render(graphicsContext2D)
}
package com.bartlomiejpluta.base.editor.tileset.component
import com.bartlomiejpluta.base.editor.render.input.MapMouseEvent
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetCanvas
import com.bartlomiejpluta.base.editor.tileset.canvas.TileSetSelection
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import javafx.event.EventHandler
import javafx.scene.canvas.Canvas
import javafx.scene.input.MouseEvent
class TileSetPane(private val gameMapVM: GameMapVM, brushVM: BrushVM) : Canvas(), EventHandler<MouseEvent> {
private val selection = TileSetSelection(gameMapVM, brushVM)
private val tileSetCanvas = TileSetCanvas(gameMapVM, selection)
init {
onMouseMoved = this
onMouseDragged = this
onMousePressed = this
onMouseReleased = this
width = gameMapVM.tileSet.width.toDouble()
height = gameMapVM.tileSet.height.toDouble()
brushVM.itemProperty.addListener { _, _, _ -> render() }
render()
}
private fun render() {
tileSetCanvas.render(graphicsContext2D)
}
override fun handle(event: MouseEvent?) {
if (event != null) {
tileSetCanvas.handleMouseInput(MapMouseEvent.of(event, gameMapVM.tileSet))
}
tileSetCanvas.render(graphicsContext2D)
}
}

View File

@@ -1,55 +1,55 @@
package com.bartlomiejpluta.base.editor.tileset.model
import javafx.scene.image.Image
import javafx.scene.image.PixelReader
import javafx.scene.image.WritableImage
class Tile(
tileSet: TileSet,
row: Int,
column: Int,
val image: Image,
) {
val id = row * tileSet.columns + column
private fun cloneImage(image: Image): Image {
val height = image.height.toInt()
val width = image.width.toInt()
val pixelReader = image.pixelReader
val writableImage = WritableImage(width, height)
val pixelWriter = writableImage.pixelWriter
for (y in 0 until height) {
for (x in 0 until width) {
val color = pixelReader.getColor(x, y)
pixelWriter.setColor(x, y, color)
}
}
return writableImage
}
fun scale(image: Image, factor: Int): Image {
val width = image.width.toInt()
val height = image.height.toInt()
val output = WritableImage(width * factor, height * factor)
val reader: PixelReader = image.pixelReader
val writer = output.pixelWriter
for (y in 0 until height) {
for (x in 0 until width) {
val argb = reader.getArgb(x, y)
for (dy in 0 until factor) {
for (dx in 0 until factor) {
writer.setArgb(x * factor + dx, y * factor + dy, argb)
}
}
}
}
return output
}
package com.bartlomiejpluta.base.editor.tileset.model
import javafx.scene.image.Image
import javafx.scene.image.PixelReader
import javafx.scene.image.WritableImage
class Tile(
tileSet: TileSet,
row: Int,
column: Int,
val image: Image,
) {
val id = row * tileSet.columns + column
private fun cloneImage(image: Image): Image {
val height = image.height.toInt()
val width = image.width.toInt()
val pixelReader = image.pixelReader
val writableImage = WritableImage(width, height)
val pixelWriter = writableImage.pixelWriter
for (y in 0 until height) {
for (x in 0 until width) {
val color = pixelReader.getColor(x, y)
pixelWriter.setColor(x, y, color)
}
}
return writableImage
}
fun scale(image: Image, factor: Int): Image {
val width = image.width.toInt()
val height = image.height.toInt()
val output = WritableImage(width * factor, height * factor)
val reader: PixelReader = image.pixelReader
val writer = output.pixelWriter
for (y in 0 until height) {
for (x in 0 until width) {
val argb = reader.getArgb(x, y)
for (dy in 0 until factor) {
for (dx in 0 until factor) {
writer.setArgb(x * factor + dx, y * factor + dy, argb)
}
}
}
}
return output
}
}

View File

@@ -1,63 +1,63 @@
package com.bartlomiejpluta.base.editor.tileset.model
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
import javafx.beans.property.SimpleIntegerProperty
import javafx.scene.image.Image
import javafx.scene.image.PixelFormat
import javafx.scene.image.WritableImage
import tornadofx.getValue
import tornadofx.toObservable
import java.nio.ByteBuffer
class TileSet(private val image: Image, rows: Int, columns: Int) {
val rowsProperty = SimpleIntegerProperty(rows)
val rows by rowsProperty
val columnsProperty = SimpleIntegerProperty(columns)
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
val tiles = (0 until rows * columns).map { cropTile(it / columns, it % columns) }.toObservable()
val baseBrush: Brush
get() = Brush.of(arrayOf(arrayOf(tiles[0])))
private fun cropTile(row: Int, column: Int): Tile {
val reader = image.pixelReader
val buffer = ByteBuffer.allocate(tileWidth * tileHeight * 4)
reader.getPixels(
column * tileWidth,
row * tileHeight,
tileWidth,
tileHeight,
PixelFormat.getByteBgraInstance(),
buffer,
4 * tileWidth
)
val tile = WritableImage(tileWidth, tileHeight)
val writer = tile.pixelWriter
writer.setPixels(0, 0, tileWidth, tileHeight, PixelFormat.getByteBgraInstance(), buffer, 4 * tileWidth)
return Tile(this, row, column, tile)
}
fun getTile(row: Int, column: Int) = tiles[(row.coerceIn(0 until rows)) * columns + column.coerceIn(0 until columns)]
fun getTile(id: Int) = tiles[id]
fun forEach(consumer: (row: Int, column: Int, tile: Tile) -> Unit) = tiles.forEachIndexed { id, tile ->
consumer(id / columns, id % columns, tile)
}
package com.bartlomiejpluta.base.editor.tileset.model
import com.bartlomiejpluta.base.editor.map.model.brush.Brush
import javafx.beans.property.SimpleIntegerProperty
import javafx.scene.image.Image
import javafx.scene.image.PixelFormat
import javafx.scene.image.WritableImage
import tornadofx.getValue
import tornadofx.toObservable
import java.nio.ByteBuffer
class TileSet(private val image: Image, rows: Int, columns: Int) {
val rowsProperty = SimpleIntegerProperty(rows)
val rows by rowsProperty
val columnsProperty = SimpleIntegerProperty(columns)
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
val tiles = (0 until rows * columns).map { cropTile(it / columns, it % columns) }.toObservable()
val baseBrush: Brush
get() = Brush.of(arrayOf(arrayOf(tiles[0])))
private fun cropTile(row: Int, column: Int): Tile {
val reader = image.pixelReader
val buffer = ByteBuffer.allocate(tileWidth * tileHeight * 4)
reader.getPixels(
column * tileWidth,
row * tileHeight,
tileWidth,
tileHeight,
PixelFormat.getByteBgraInstance(),
buffer,
4 * tileWidth
)
val tile = WritableImage(tileWidth, tileHeight)
val writer = tile.pixelWriter
writer.setPixels(0, 0, tileWidth, tileHeight, PixelFormat.getByteBgraInstance(), buffer, 4 * tileWidth)
return Tile(this, row, column, tile)
}
fun getTile(row: Int, column: Int) = tiles[(row.coerceIn(0 until rows)) * columns + column.coerceIn(0 until columns)]
fun getTile(id: Int) = tiles[id]
fun forEach(consumer: (row: Int, column: Int, tile: Tile) -> Unit) = tiles.forEachIndexed { id, tile ->
consumer(id / columns, id % columns, tile)
}
}

View File

@@ -1,19 +1,19 @@
package com.bartlomiejpluta.base.editor.tileset.view
import com.bartlomiejpluta.base.editor.tileset.component.TileSetPane
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import tornadofx.View
import tornadofx.plusAssign
import tornadofx.scrollpane
class TileSetView : View() {
private val gameMapVM = find<GameMapVM>()
private val brushVM = find<BrushVM>()
private val tileSetPane = TileSetPane(gameMapVM, brushVM)
override val root = scrollpane {
this += tileSetPane
}
package com.bartlomiejpluta.base.editor.tileset.view
import com.bartlomiejpluta.base.editor.tileset.component.TileSetPane
import com.bartlomiejpluta.base.editor.map.viewmodel.BrushVM
import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM
import tornadofx.View
import tornadofx.plusAssign
import tornadofx.scrollpane
class TileSetView : View() {
private val gameMapVM = find<GameMapVM>()
private val brushVM = find<BrushVM>()
private val tileSetPane = TileSetPane(gameMapVM, brushVM)
override val root = scrollpane {
this += tileSetPane
}
}

4
editor/src/main/resources/application.yml Executable file → Normal file
View File

@@ -1,3 +1,3 @@
logging:
level:
logging:
level:
com.bartlomiejpluta.base.editor: DEBUG