mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-12 11:34:05 +09:00
parsing ADL
a road to auto-gen'd spriteanimation spritesheet
This commit is contained in:
115
src/net/torvald/spriteassembler/ADPropertyObject.kt
Normal file
115
src/net/torvald/spriteassembler/ADPropertyObject.kt
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package net.torvald.spriteassembler
|
||||||
|
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.Reader
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
|
class ADProperties {
|
||||||
|
private val javaProp = Properties()
|
||||||
|
|
||||||
|
/** Every key is CAPITALISED */
|
||||||
|
private val propTable = HashMap<String, List<ADPropertyObject>>()
|
||||||
|
|
||||||
|
constructor(reader: Reader) {
|
||||||
|
javaProp.load(reader)
|
||||||
|
continueLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(inputStream: InputStream) {
|
||||||
|
javaProp.load(inputStream)
|
||||||
|
continueLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun continueLoad() {
|
||||||
|
javaProp.keys.forEach { propName ->
|
||||||
|
val propsStr = javaProp.getProperty(propName as String)
|
||||||
|
val propsList = propsStr.split(';').map { ADPropertyObject(it) }
|
||||||
|
|
||||||
|
propTable[propName.capitalize()] = propsList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun get(identifier: String) = propTable[identifier.capitalize()]
|
||||||
|
val keys
|
||||||
|
get() = propTable.keys
|
||||||
|
fun containsKey(key: String) = propTable.containsKey(key)
|
||||||
|
fun forEach(predicate: (String, List<ADPropertyObject>) -> Unit) = propTable.forEach(predicate)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param propertyRaw example inputs:
|
||||||
|
* - ```DELAY 0.15```
|
||||||
|
* - ```LEG_RIGHT 0,-1```
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2019-01-05.
|
||||||
|
*/
|
||||||
|
class ADPropertyObject(propertyRaw: String) {
|
||||||
|
|
||||||
|
/** If the input is like ```UPPER_TORSO``` (that is, not a variable-input pair), this holds the string UPPER_TORSO. */
|
||||||
|
val variable: String
|
||||||
|
val input: Any?
|
||||||
|
get() = when (type) {
|
||||||
|
ADPropertyType.IVEC2 -> field!! as Vector2i
|
||||||
|
ADPropertyType.FLOAT -> field!! as Float
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
val type: ADPropertyType
|
||||||
|
|
||||||
|
init {
|
||||||
|
val propPair = propertyRaw.split(variableInputSepRegex)
|
||||||
|
|
||||||
|
if (isADvariable(propertyRaw)) {
|
||||||
|
variable = propPair[0]
|
||||||
|
val inputStr = propPair[1]!!
|
||||||
|
|
||||||
|
if (isADivec2(inputStr)) {
|
||||||
|
type = ADPropertyType.IVEC2
|
||||||
|
input = toADivec2(inputStr)
|
||||||
|
}
|
||||||
|
else if (isADfloat(inputStr)) {
|
||||||
|
type = ADPropertyType.FLOAT
|
||||||
|
input = toADfloat(inputStr)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw IllegalArgumentException("Malformed input, input property seems not ivec2, float nor string: $propertyRaw")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
variable = propertyRaw
|
||||||
|
input = null
|
||||||
|
type = ADPropertyType.STRING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val floatRegex = Regex("""-?[0-9]+(\.[0-9]*)?""")
|
||||||
|
private val ivec2Regex = Regex("""-?[0-9]+,-?[0-9]+""")
|
||||||
|
private val variableInputSepRegex = Regex(""" +""")
|
||||||
|
|
||||||
|
fun isADivec2(s: String) = ivec2Regex.matches(s)
|
||||||
|
fun isADfloat(s: String) = floatRegex.matches(s) && !ivec2Regex.containsMatchIn(s)
|
||||||
|
|
||||||
|
fun toADivec2(s: String) = if (isADivec2(s))
|
||||||
|
Vector2i(s.substringBefore(',').toInt(), s.substringAfter(',').toInt())
|
||||||
|
else throw IllegalArgumentException("Input not in ivec2 format: $s")
|
||||||
|
fun toADfloat(s: String) = if (isADfloat(s))
|
||||||
|
s.toFloat()
|
||||||
|
else throw IllegalArgumentException("Input not in ivec2 format: $s")
|
||||||
|
|
||||||
|
/** example valid input: ```LEG_RIGHT 0,1``` */
|
||||||
|
fun isADvariable(property: String) = variableInputSepRegex.containsMatchIn(property)
|
||||||
|
/** example valid input: ```sprites/test.tga``` */
|
||||||
|
fun isADstring(property: String) = !isADvariable(property)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Vector2i(var x: Int, var y: Int)
|
||||||
|
|
||||||
|
enum class ADPropertyType {
|
||||||
|
STRING, IVEC2, FLOAT
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "$variable ${input ?: ""}: $type"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
## Animation Description Language
|
||||||
|
|
||||||
|
This is a text version of my drawing of same name. 2018-01-04 CuriousTorvald
|
||||||
|
|
||||||
|
Author's node: yet another non-JSON domain-specific language because why not?
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
* Java .properties-compatible
|
||||||
|
* Case insensitive
|
||||||
|
|
||||||
|
## Example code
|
||||||
|
|
||||||
|
```
|
||||||
|
SPRITESHEET=sprites/test
|
||||||
|
EXTENSION=.tga.gz
|
||||||
|
|
||||||
|
ANIM_RUN=DELAY 0.15;ROW 2
|
||||||
|
ANIM_RUN_BODYPARTS=HEAD;UPPER_TORSO;LOWER_TORSO;ARM_FWD_LEFT;ARM_FWD_RIGHT;LEG_LEFT;LEG_RIGHT
|
||||||
|
ANIM_RUN_1=LEG_RIGHT 1,-1;LEG_LEFT -1,0
|
||||||
|
ANIM_RUN_2=ALL 0,-1;LEG_RIGHT 0,1;LEG_LEFT 0,-1
|
||||||
|
ANIM_RUN_3=LEG_RIGHT -1,0;LEG_LEFT 1,-1
|
||||||
|
ANIM_RUN_4=ALL 0,-1;LEG_RIGHT 0,-1;LEG_LEFT 0,1
|
||||||
|
|
||||||
|
ANIM_IDLE=DELAY 2;ROW 1
|
||||||
|
ANIM_IDLE_BODYPARTS=HEAD;UPPER_TORSO;LOWER_TORSO;ARM_REST_LEFT;ARM_REST_RIGHT;LEG_LEFT;LEG_RIGHT
|
||||||
|
ANIM_IDLE_1=
|
||||||
|
! ANIM_IDLE_1 will not make any transformation
|
||||||
|
ANIM_IDLE_2=UPPER_TORSO 0,-1
|
||||||
|
|
||||||
|
ANIM_CROUCH=DELAY 1;ROW 3
|
||||||
|
ANIM_CROUCH_BODYPARTS=HEAD;UPPER_TORSO;LOWER_TORSO;ARM_FWD_LEFT;ARM_FWD_RIGHT;LEG_CROUCH_LEFT;LEG_CROUCH_RIGHT
|
||||||
|
ANIM_CROUCH_1=
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### In-detail
|
||||||
|
|
||||||
|
```
|
||||||
|
ANIM_RUN=DELAY 0.15;ROW 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line defines one property. A property is a field-value pair. In this code, field is ```ANIM_RUN```, and the value is ```DELAY 0.15;ROW 2```
|
||||||
|
|
||||||
|
The values are further parsed using ```;``` (semicolon with NO spaces attached) as a separator.
|
||||||
|
|
||||||
|
```
|
||||||
|
In this example, ANIM_RUN contains two variables:
|
||||||
|
|
||||||
|
DELAY = 0.15
|
||||||
|
ROW = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
A value of the field is consisted of zero or more variable-input pairs. Variable and the input are separated with one or more connected spaces.
|
||||||
|
|
||||||
|
#### Variables
|
||||||
|
|
||||||
|
Variables can have only one of the two types: ```float``` and ```ivec2```. Single integer value ('2' in the ROW) are regarded as a float.
|
||||||
|
|
||||||
|
Float and Ivec2 are determined by:
|
||||||
|
|
||||||
|
* Ivec2: inputs that are matched by the regex ```-?[0-9]+,-?[0-9]+``` (we call this "ivec2 regex")
|
||||||
|
* Float: inputs that are matched by the regex ```-?[0-9]+(\.[0-9]*)?```, but not even partially matched by the ivec2 regex.
|
||||||
|
|
||||||
|
Any argument to the body parts takes ivec2, to move the parts accordingly.
|
||||||
|
|
||||||
|
#### Just one exception: SPRITESHEET and EXTENSION
|
||||||
|
|
||||||
|
SPRITESHEET and EXTENSION property is not parsed as a property, it's just a single string like the original .properties
|
||||||
|
|
||||||
|
### Naming convention of properties
|
||||||
|
|
||||||
|
If a field is recognised as an animation (in this case ANIM_RUN), the assembler will look for the fields named like ANIM_RUN_1, ANIM_RUN_2, ... , ANIM_RUN_9, ANIM_RUN_10 and beyond.
|
||||||
|
|
||||||
|
### Naming convention of files
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Reserved keywords
|
||||||
|
|
||||||
|
|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|
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
* All indices are one-based
|
||||||
|
|
||||||
|
## Operation
|
||||||
|
|
||||||
|
* Each line describes transformation
|
||||||
|
* Transformation are applied sequentially from left to right. In other words, their order matters. Be wary of the clipping that may occur!
|
||||||
|
* The Field is an identifier the game code -- sprite assembler -- recognises.
|
||||||
|
* The Field of animation's name is the name the game code looks for. Example: ```this.setAnim("ANIM_RUN")```
|
||||||
49
src/net/torvald/terrarum/tests/ADLParsingTest.kt
Normal file
49
src/net/torvald/terrarum/tests/ADLParsingTest.kt
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package net.torvald.terrarum.tests
|
||||||
|
|
||||||
|
import net.torvald.spriteassembler.ADProperties
|
||||||
|
import java.io.StringReader
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2019-01-05.
|
||||||
|
*/
|
||||||
|
class ADLParsingTest {
|
||||||
|
|
||||||
|
val TEST_STR = """
|
||||||
|
SPRITESHEET=sprites/test
|
||||||
|
EXTENSION=.tga.gz
|
||||||
|
|
||||||
|
ANIM_RUN=DELAY 0.15;ROW 2
|
||||||
|
ANIM_RUN_BODYPARTS=HEAD;UPPER_TORSO;LOWER_TORSO;ARM_FWD_LEFT;ARM_FWD_RIGHT;LEG_LEFT;LEG_RIGHT
|
||||||
|
ANIM_RUN_1=LEG_RIGHT 1,-1;LEG_LEFT -1,0
|
||||||
|
ANIM_RUN_2=ALL 0,-1;LEG_RIGHT 0,1;LEG_LEFT 0,-1
|
||||||
|
ANIM_RUN_3=LEG_RIGHT -1,0;LEG_LEFT 1,-1
|
||||||
|
ANIM_RUN_4=ALL 0,-1;LEG_RIGHT 0,-1;LEG_LEFT 0,1
|
||||||
|
|
||||||
|
ANIM_IDLE=DELAY 2;ROW 1
|
||||||
|
ANIM_IDLE_BODYPARTS=HEAD;UPPER_TORSO;LOWER_TORSO;ARM_REST_LEFT;ARM_REST_RIGHT;LEG_LEFT;LEG_RIGHT
|
||||||
|
ANIM_IDLE_1=
|
||||||
|
! ANIM_IDLE_1 will not make any transformation
|
||||||
|
ANIM_IDLE_2=UPPER_TORSO 0,-1
|
||||||
|
|
||||||
|
ANIM_CROUCH=DELAY 1;ROW 3
|
||||||
|
ANIM_CROUCH_BODYPARTS=HEAD;UPPER_TORSO;LOWER_TORSO;ARM_FWD_LEFT;ARM_FWD_RIGHT;LEG_CROUCH_LEFT;LEG_CROUCH_RIGHT
|
||||||
|
ANIM_CROUCH_1=
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
operator fun invoke() {
|
||||||
|
val prop = ADProperties(StringReader(TEST_STR))
|
||||||
|
|
||||||
|
prop.forEach { s, list ->
|
||||||
|
println(s)
|
||||||
|
|
||||||
|
list.forEach {
|
||||||
|
println("\t$it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
ADLParsingTest().invoke()
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ import org.dyn4j.Epsilon
|
|||||||
* |a rotate th|Vector2(a).rotate(th)|
|
* |a rotate th|Vector2(a).rotate(th)|
|
||||||
* |a to b |Vector2(a).to(b) |
|
* |a to b |Vector2(a).to(b) |
|
||||||
*
|
*
|
||||||
* @author William Bittle
|
* @author William Bittle and Minjaesong (Torvald)
|
||||||
* *
|
* *
|
||||||
* @version 3.1.11
|
* @version 3.1.11
|
||||||
* *
|
* *
|
||||||
|
|||||||
Reference in New Issue
Block a user