mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-08 04:41:51 +09:00
sprite assembler app can read and disp ADL
garbage code not properly handled
This commit is contained in:
@@ -3,7 +3,9 @@ package net.torvald.spriteassembler
|
||||
import java.io.InputStream
|
||||
import java.io.Reader
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
class ADProperties {
|
||||
private val javaProp = Properties()
|
||||
@@ -11,6 +13,16 @@ class ADProperties {
|
||||
/** Every key is CAPITALISED */
|
||||
private val propTable = HashMap<String, List<ADPropertyObject>>()
|
||||
|
||||
/** list of bodyparts used by all the skeletons */
|
||||
lateinit var bodyparts: List<String>; private set
|
||||
/** properties that are being used as skeletons */
|
||||
lateinit var skeletons: List<String>; private set
|
||||
/** properties that are recognised as animations */
|
||||
lateinit var animations: List<String>; private set
|
||||
|
||||
private val reservedProps = listOf("SPRITESHEET", "EXTENSION")
|
||||
private val animMustContain = listOf("DELAY", "ROW", "SKELETON")
|
||||
|
||||
constructor(reader: Reader) {
|
||||
javaProp.load(reader)
|
||||
continueLoad()
|
||||
@@ -28,6 +40,38 @@ class ADProperties {
|
||||
|
||||
propTable[propName.capitalize()] = propsList
|
||||
}
|
||||
|
||||
val bodyparts = HashSet<String>()
|
||||
val skeletons = HashSet<String>()
|
||||
val animations = ArrayList<String>()
|
||||
// populate skeletons and animations
|
||||
forEach { s, list ->
|
||||
// linear search. If variable == "SKELETON", the 's' is likely an animation
|
||||
// and thus, uses whatever the "input" used by the SKELETON is a skeleton
|
||||
list.forEach {
|
||||
if (it.variable == "SKELETON") {
|
||||
skeletons.add(it.input as String)
|
||||
animations.add(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// populate the bodyparts using skeletons
|
||||
skeletons.forEach { skeletonName ->
|
||||
val prop = get(skeletonName)
|
||||
|
||||
if (prop == null) throw Error("The skeleton definition for $skeletonName does not exist")
|
||||
|
||||
prop.forEach { bodypart ->
|
||||
bodyparts.add(bodypart.variable)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.bodyparts = bodyparts.toList().sorted()
|
||||
this.skeletons = skeletons.toList().sorted()
|
||||
this.animations = animations.sorted()
|
||||
}
|
||||
|
||||
operator fun get(identifier: String) = propTable[identifier.capitalize()]
|
||||
@@ -35,6 +79,7 @@ class ADProperties {
|
||||
get() = propTable.keys
|
||||
fun containsKey(key: String) = propTable.containsKey(key)
|
||||
fun forEach(predicate: (String, List<ADPropertyObject>) -> Unit) = propTable.forEach(predicate)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,6 +102,7 @@ class ADPropertyObject(propertyRaw: String) {
|
||||
}
|
||||
val type: ADPropertyType
|
||||
|
||||
|
||||
init {
|
||||
val propPair = propertyRaw.split(variableInputSepRegex)
|
||||
|
||||
@@ -115,6 +161,6 @@ class ADPropertyObject(propertyRaw: String) {
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$variable ${input ?: ""}: $type"
|
||||
return "$variable ${input ?: ""}: ${type.toString().toLowerCase()}"
|
||||
}
|
||||
}
|
||||
@@ -76,14 +76,26 @@ If a field is recognised as an animation (in this case ANIM_RUN), the assembler
|
||||
|
||||
If the animation specifies a "body part" (in this example LEG_LEFT and LEG_RIGHT), the assembler will look for a file ```sprites/test_leg_left.tga.gz``` and ```sprites/test_leg_right.tga.gz``` respectively. Filenames are advised to be kept all lowercase.
|
||||
|
||||
#### Reserved keywords
|
||||
### Reserved keywords
|
||||
|
||||
These values must exist so that the file can be parsed successfully.
|
||||
|
||||
#### Root
|
||||
|
||||
|Name|Type|Meaning|
|
||||
|---|---|---|
|
||||
|SPRITESHEET|property/string|base file name of the images|
|
||||
|EXTENSION|property/string|extension of the base file|
|
||||
|DELAY|variable: float|delay between frames, in seconds|
|
||||
|ROW|vareable: float|which row the animation goes in the spritesheet|
|
||||
|SPRITESHEET|properties: NAME_ONLY|Base file name of the images|
|
||||
|EXTENSION|properties: NAME_ONLY|Extension of the base file|
|
||||
|
||||
#### Animation
|
||||
|
||||
Remember that 'variables' are contained within 'properties'
|
||||
|
||||
|Name|Type|Meaning|
|
||||
|---|---|---|
|
||||
|DELAY|variable: float|Delay between frames, in seconds|
|
||||
|ROW|variable: float|which row the animation goes in the spritesheet|
|
||||
|SKELETON|variable: string_pair|Which skeleton this animation uses
|
||||
|
||||
### Notes
|
||||
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
package net.torvald.spriteassembler;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2019-01-05.
|
||||
*/
|
||||
public class SpriteAssemblerApp extends JFrame {
|
||||
|
||||
private ImagePanel panelPreview = new ImagePanel();
|
||||
private JTree panelProperties = new JTree();
|
||||
private JList<String> panelBodypartsList = new JList<String>();
|
||||
private JTextPane panelCode = new JTextPane();
|
||||
private JTextArea statBar = new JTextArea("Null.");
|
||||
|
||||
|
||||
|
||||
public SpriteAssemblerApp() {
|
||||
JSplitPane panelDataView = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(panelProperties), new JScrollPane(panelBodypartsList));
|
||||
|
||||
JSplitPane panelTop = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(panelPreview), panelDataView);
|
||||
JSplitPane panelMain = new JSplitPane(JSplitPane.VERTICAL_SPLIT, panelTop, new JScrollPane(panelCode));
|
||||
|
||||
JMenuBar menu = new JMenuBar();
|
||||
menu.add(new JMenu("File"));
|
||||
menu.add(new JMenu("Parse")).addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
System.out.println("Hello");
|
||||
}
|
||||
});
|
||||
menu.add(new JMenu("Run"));
|
||||
|
||||
this.setLayout(new BorderLayout());
|
||||
this.add(menu, BorderLayout.NORTH);
|
||||
this.add(panelMain, BorderLayout.CENTER);
|
||||
this.add(statBar, BorderLayout.SOUTH);
|
||||
this.setTitle("Terrarum Sprite Assembler and Viewer");
|
||||
this.setVisible(true);
|
||||
this.setSize(1154, 768);
|
||||
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpriteAssemblerApp();
|
||||
}
|
||||
}
|
||||
|
||||
class ImagePanel extends JPanel {
|
||||
|
||||
private BufferedImage image;
|
||||
|
||||
public ImagePanel() {
|
||||
try {
|
||||
image = ImageIO.read(new File("image name and path"));
|
||||
} catch (IOException ex) {
|
||||
// handle exception...
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
g.drawImage(image, 0, 0, this); // see javadoc for more info on the parameters
|
||||
}
|
||||
|
||||
}
|
||||
210
src/net/torvald/spriteassembler/SpriteAssemblerApp.kt
Normal file
210
src/net/torvald/spriteassembler/SpriteAssemblerApp.kt
Normal file
@@ -0,0 +1,210 @@
|
||||
package net.torvald.spriteassembler
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Graphics
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.StringReader
|
||||
import java.util.*
|
||||
import javax.imageio.ImageIO
|
||||
import javax.swing.*
|
||||
import javax.swing.tree.DefaultMutableTreeNode
|
||||
import javax.swing.tree.DefaultTreeModel
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2019-01-05.
|
||||
*/
|
||||
class SpriteAssemblerApp : JFrame() {
|
||||
|
||||
private val panelPreview = ImagePanel()
|
||||
private val panelProperties = JTree()
|
||||
private val panelAnimationsList = JList<String>()
|
||||
private val panelBodypartsList = JList<String>()
|
||||
private val panelSkeletonsList = JList<String>()
|
||||
private val panelCode = JTextPane()
|
||||
private val statBar = JTextArea("Null.")
|
||||
|
||||
private var adProperties: ADProperties? = null
|
||||
|
||||
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 <strong>notes</strong>\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."
|
||||
|
||||
init {
|
||||
// setup application properties //
|
||||
try {
|
||||
props.load(StringReader(captionProperties))
|
||||
lang.load(StringReader(translations))
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
panelAnimationsList.model = DefaultListModel()
|
||||
panelBodypartsList.model = DefaultListModel()
|
||||
panelSkeletonsList.model = DefaultListModel()
|
||||
|
||||
val panelPartsList = JTabbedPane(JTabbedPane.TOP)
|
||||
panelPartsList.add("Animations", JScrollPane(panelAnimationsList))
|
||||
panelPartsList.add("Bodyparts", JScrollPane(panelBodypartsList))
|
||||
panelPartsList.add("Skeletons", JScrollPane(panelSkeletonsList))
|
||||
|
||||
|
||||
val panelDataView = JSplitPane(JSplitPane.VERTICAL_SPLIT, JScrollPane(panelProperties), panelPartsList)
|
||||
|
||||
val panelTop = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, JScrollPane(panelPreview), panelDataView)
|
||||
val panelMain = JSplitPane(JSplitPane.VERTICAL_SPLIT, panelTop, JScrollPane(panelCode))
|
||||
|
||||
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()
|
||||
panelSkeletonsList.model = DefaultListModel()
|
||||
|
||||
// populate animations view
|
||||
adProperties!!.animations.forEach {
|
||||
(panelAnimationsList.model as DefaultListModel).addElement(it)
|
||||
}
|
||||
// populate bodyparts view
|
||||
adProperties!!.bodyparts.forEach {
|
||||
(panelBodypartsList.model as DefaultListModel).addElement(it)
|
||||
}
|
||||
// populate skeletons view
|
||||
adProperties!!.skeletons.forEach {
|
||||
(panelSkeletonsList.model as DefaultListModel).addElement(it)
|
||||
}
|
||||
}
|
||||
catch (fehler: Throwable) {
|
||||
displayError("ERROR_PARSE_FAIL", fehler)
|
||||
fehler.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
menu.add(JMenu("Run"))
|
||||
|
||||
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"
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
SpriteAssemblerApp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ImagePanel : JPanel() {
|
||||
|
||||
private var image: BufferedImage? = null
|
||||
|
||||
init {
|
||||
try {
|
||||
image = ImageIO.read(File("image name and path"))
|
||||
}
|
||||
catch (ex: IOException) {
|
||||
// handle exception...
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun paintComponent(g: Graphics) {
|
||||
super.paintComponent(g)
|
||||
g.drawImage(image, 0, 0, this) // see javadoc for more info on the parameters
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user