package net.torvald.spriteassembler import com.badlogic.gdx.Game import com.badlogic.gdx.Gdx import com.badlogic.gdx.backends.lwjgl.LwjglApplication import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.GL20 import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.g2d.SpriteBatch import net.torvald.terrarum.inUse import java.awt.BorderLayout import java.awt.Font import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import java.io.StringReader import java.util.* import javax.swing.* import javax.swing.tree.DefaultMutableTreeNode import javax.swing.tree.DefaultTreeModel /** * Created by minjaesong on 2019-01-05. */ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() { private val panelProperties = JTree() private val panelAnimationsList = JList() private val panelBodypartsList = JList() private val panelImageFilesList = JList() private val panelSkeletonsList = JList() private val panelTransformsList = JList() private val panelCode = JTextPane() private val statBar = JTextArea("Null.") private lateinit var adProperties: ADProperties private val props = Properties() private val lang = Properties() private val captionProperties = "" + // dummy string to make IDE happy with the auto indent "id=ID of this block\n" + "drop=ID of the block this very block should drop when mined\n" + "name=String identifier of the block\n" + "shdr=Shade Red (light absorption). Valid range 0.0-4.0\n" + "shdg=Shade Green (light absorption). Valid range 0.0-4.0\n" + "shdb=Shade Blue (light absorption). Valid range 0.0-4.0\n" + "shduv=Shade UV (light absorbtion). Valid range 0.0-4.0\n" + "lumr=Luminosity Red (light intensity). Valid range 0.0-4.0\n" + "lumg=Luminosity Green (light intensity). Valid range 0.0-4.0\n" + "lumb=Luminosity Blue (light intensity). Valid range 0.0-4.0\n" + "lumuv=Luminosity UV (light intensity). Valid range 0.0-4.0\n" + "str=Strength of the block\n" + "dsty=Density of the block. Water have 1000 in the in-game scale\n" + "mate=Material of the block\n" + "solid=Whether the file has full collision\n" + "plat=Whether the block should behave like a platform\n" + "wall=Whether the block can be used as a wall\n" + "fall=Whether the block should fall through the empty space\n" + "dlfn=Dynamic Light Function. 0=Static. Please see notes\n" + "fv=Vertical friction when player slide on the cliff. 0 means not slide-able\n" + "fr=Horizontal friction. <16:slippery 16:regular >16:sticky\n" /** * ¤ is used as a \n marker */ private val translations = "" + "WARNING_CONTINUE=Continue?\n" + "WARNING_YOUR_DATA_WILL_GONE=Existing edits will be lost.\n" + "OPERATION_CANCELLED=Operation cancelled.\n" + "NO_SUCH_FILE=No such file exists, operation cancelled.\n" + "NEW_ROWS=Enter the number of rows to initialise the new CSV.¤Remember, you can always add or delete rows later.\n" + "ADD_ROWS=Enter the number of rows to add:\n" + "WRITE_FAIL=Writing to file has failed:\n" + "STAT_INIT=Creating a new CSV. You can still open existing file.\n" + "STAT_SAVE_SUCCESSFUL=File saved successfully.\n" + "STAT_NEW_FILE=New CSV created.\n" + "STAT_LOAD_SUCCESSFUL=File loaded successfully.\n" + "ERROR_INTERNAL=Something went wrong.\n" + "ERROR_PARSE_FAIL=Parsing failed\n" + "SPRITE_DEF_LOAD_SUCCESSFUL=Sprite definition loaded.\n" + "SPRITE_ASSEMBLE_SUCCESSFUL=Sprite assembled." init { // setup application properties // try { props.load(StringReader(captionProperties)) lang.load(StringReader(translations)) } catch (e: Throwable) { } panelCode.font = Font(Font.MONOSPACED, Font.PLAIN, 11) panelAnimationsList.model = DefaultListModel() panelBodypartsList.model = DefaultListModel() panelImageFilesList.model = DefaultListModel() panelSkeletonsList.model = DefaultListModel() panelTransformsList.model = DefaultListModel() val panelPartsList = JTabbedPane(JTabbedPane.TOP) panelPartsList.add("Animations", JScrollPane(panelAnimationsList)) panelPartsList.add("Bodyparts", JScrollPane(panelBodypartsList)) panelPartsList.add("Images", JScrollPane(panelImageFilesList)) panelPartsList.add("Skeletons", JScrollPane(panelSkeletonsList)) panelPartsList.add("Transforms", JScrollPane(panelTransformsList)) val panelDataView = JSplitPane(JSplitPane.VERTICAL_SPLIT, JScrollPane(panelProperties), panelPartsList) panelDataView.resizeWeight = 0.333 // to disable text wrap //val panelCodeNoWrap = JPanel(BorderLayout()) //panelCodeNoWrap.add(panelCode) val panelMain = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, JScrollPane(panelCode), panelDataView) panelMain.resizeWeight = 0.666 val menu = JMenuBar() menu.add(JMenu("File")) menu.add(JMenu("Parse")).addMouseListener(object : MouseAdapter() { override fun mousePressed(e: MouseEvent?) { try { adProperties = ADProperties(StringReader(panelCode.text)) statBar.text = lang.getProperty("SPRITE_DEF_LOAD_SUCCESSFUL") val propRoot = DefaultMutableTreeNode("Properties") adProperties.forEach { s, list -> // build tree node for the properties display val propNode = DefaultMutableTreeNode(s) propRoot.add(propNode) list.forEach { propNode.add(DefaultMutableTreeNode(it.toString())) } } panelProperties.model = DefaultTreeModel(propRoot) // clean the data views panelAnimationsList.model = DefaultListModel() panelBodypartsList.model = DefaultListModel() panelImageFilesList.model = DefaultListModel() panelSkeletonsList.model = DefaultListModel() panelTransformsList.model = DefaultListModel() // populate animations view adProperties.animations.forEach { (panelAnimationsList.model as DefaultListModel).addElement("${it.value}") } // populate bodyparts view adProperties.bodyparts.forEach { partName -> (panelBodypartsList.model as DefaultListModel).addElement(partName) } // populate image file list view adProperties.bodypartFiles.forEach { partName -> (panelImageFilesList.model as DefaultListModel).addElement(partName) } // populate skeletons view adProperties.skeletons.forEach { (panelSkeletonsList.model as DefaultListModel).addElement("${it.value}") } // populate transforms view adProperties.transforms.forEach { (panelTransformsList.model as DefaultListModel).addElement("$it") } } catch (fehler: Throwable) { displayError("ERROR_PARSE_FAIL", fehler) fehler.printStackTrace() } } }) menu.add(JMenu("Run")).addMouseListener(object : MouseAdapter() { override fun mousePressed(e: MouseEvent?) { try { gdxWindow.requestAssemblyTest(adProperties, "ANIM_IDLE_2") statBar.text = lang.getProperty("SPRITE_ASSEMBLE_SUCCESSFUL") } catch (fehler: Throwable) { displayError("ERROR_PARSE_FAIL", fehler) fehler.printStackTrace() } } }) this.layout = BorderLayout() this.add(menu, BorderLayout.NORTH) this.add(panelMain, BorderLayout.CENTER) this.add(statBar, BorderLayout.SOUTH) this.title = "Terrarum Sprite Assembler and Viewer" this.isVisible = true this.setSize(1154, 768) this.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE } private fun displayMessage(messageKey: String) { JOptionPane.showOptionDialog( null, lang.getProperty(messageKey), null, JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, arrayOf("OK", "Cancel"), "Cancel" ) } private fun displayError(messageKey: String, cause: Throwable) { JOptionPane.showOptionDialog(null, lang.getProperty(messageKey) + "\n" + cause.toString(), null, JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, arrayOf("OK", "Cancel"), "Cancel" ) } } class SpriteAssemblerPreview: Game() { private lateinit var batch: SpriteBatch private lateinit var renderTexture: Texture private var image: Pixmap? = null set(value) { renderTexture.dispose() field?.dispose() field = value renderTexture = Texture(field) } override fun create() { Gdx.graphics.setTitle("Sprite Assembler Preview") batch = SpriteBatch() renderTexture = Texture(1, 1, Pixmap.Format.RGBA8888) } private val bgCol = Color(.62f,.79f,1f,1f) private var doAssemble = false private lateinit var assembleProp: ADProperties private lateinit var assembleFrameName: String override fun render() { if (doAssemble) { // assembly requires GL context doAssemble = false assembleImageTest(assembleProp, assembleFrameName) } Gdx.gl.glClearColor(.62f,.79f,1f,1f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) Gdx.gl.glEnable(GL20.GL_TEXTURE_2D) Gdx.gl.glEnable(GL20.GL_BLEND) Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA) batch.inUse { batch.color = Color.WHITE batch.draw(renderTexture, 0f, 0f) } } private fun assembleImageTest(prop: ADProperties, frameName: String) { image = AssembleFramePixmap(prop, frameName) } fun requestAssemblyTest(prop: ADProperties, frameName: String) { doAssemble = true assembleProp = prop assembleFrameName = frameName } } fun main(args: Array) { val appConfig = LwjglApplicationConfiguration() appConfig.resizable = true appConfig.width = 512 appConfig.height = 512 appConfig.foregroundFPS = 5 appConfig.backgroundFPS = 5 val gdxWindow = SpriteAssemblerPreview() LwjglApplication(gdxWindow, appConfig) SpriteAssemblerApp(gdxWindow) }