sprite and spriteassembler update

This commit is contained in:
minjaesong
2021-10-03 16:35:25 +09:00
parent abb46f410a
commit ea2525e4df
17 changed files with 181 additions and 128 deletions

View File

@@ -1,37 +1,45 @@
# complete file name is: SPRITESHEET + bodypart name + EXTENSION # complete file name is: SPRITESHEET + bodypart name + EXTENSION
SPRITESHEET=mods/basegame/sprites/test_werebeastf/taimu_ SPRITESHEET=mods/basegame/sprites/test_werebeastf/taimu_
EXTENSION=.tga EXTENSION=.tga
# defines frame size and origin point. Origin point is given as: (originx, size.y - 1) # defines frame size and origin point. Origin point is given as: (originx, 0)
# ORIGINY is deduced from the sprite size as shown in above; you only need to set ORIGINX
CONFIG=SIZE 64,98;ORIGINX 40 CONFIG=SIZE 64,98;ORIGINX 40
# note to self: don't implement skeleton hierarchy: there's too many exceptions ! A skeleton also defines what body parts (images) be used.
# besides, you have "ALL" key. ! You can also write multiline text using reverse solidus; this is a feature of .properties
! a skeleton also defines what body parts (images) be used.
! you can also write multiline text using reverse solidus; this is a feature of .properties
! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST ! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST
! are painted first, and any object that comes before it will paint over it. In other words, this list is ! are painted first, and any object that comes before it will paint over it. In other words, this list is
! first reversed then being iterated. ! first reversed then being iterated.
! Joints' original point is defined in the document sprite_joints.psd. It also has visual representations. ! Some keywords are considered special within the game, they are:
# TODO right now accessory points are explicitly defined. Should they be injected in run-time? ! HEADGEAR, HELD_ITEM, BOOT_L, BOOT_R, GAUNTLET_L, GAUNTLET_R and ARMOUR_* (star means any number starting from zero)
SKELETON_STAND=HEADGEAR 0,32;\ BODYPARTS=HEADGEAR 13,14;\
ARM_REST_LEFT -9,49;HELD_ITEM -6,11;\ HEAD 13,14;\
HEAD 2,83;\ ARM_REST_RIGHT 5,3;\
BUST_0 4,60;\ ARM_REST_LEFT 6,3;\
TORSO_0 0,55;\ LEG_REST_RIGHT 12,6;\
TORSO_1 0,55;\ LEG_REST_LEFT 8,6;\
LEG_REST_RIGHT -6,22;\ TORSO_0 18,19;\
LEG_REST_LEFT 7,22;\ TORSO_1 18,19;\
ARM_REST_RIGHT 12,51;\ TAIL_0 30,2;\
TAIL_0 -11,27 BUST_0 11,2;\
HELD_ITEM 0,0
SKELETON_STAND=HEADGEAR 0,78;\
ARM_REST_RIGHT -15,66;\
HELD_ITEM -11,33;\
HEAD 0,78;\
BUST_0 0,63;\
TORSO_0 0,54;\
TORSO_1 0,54;\
LEG_REST_RIGHT -5,41;\
LEG_REST_LEFT 3,41;\
ARM_REST_LEFT 8,66;\
TAIL_0 2,40
# skeleton_stand is used for testing purpose # skeleton_stand is used for testing purpose
ANIM_RUN=DELAY 0.3;ROW 2;SKELETON SKELETON_STAND ANIM_RUN=DELAY 0.3;ROW 2;SKELETON SKELETON_STAND
ANIM_RUN_1=LEG_REST_RIGHT 2,2;LEG_REST_LEFT -2,0;TAIL_0 1,0 ANIM_RUN_1=LEG_REST_RIGHT 2,2;LEG_REST_LEFT -2,0;TAIL_0 1,0;TORSO_1 0,-999
ANIM_RUN_2=ALL 0,2;LEG_REST_RIGHT 0,-2;LEG_REST_LEFT 0,2;TAIL_0 -1,0 ANIM_RUN_2=ALL 0,2;LEG_REST_RIGHT 0,-2;LEG_REST_LEFT 0,2;TAIL_0 -1,0;TORSO_1 0,-999
ANIM_RUN_3=LEG_REST_RIGHT -2,0;LEG_REST_LEFT 2,2;TAIL_0 -1,0 ANIM_RUN_3=LEG_REST_RIGHT -2,0;LEG_REST_LEFT 2,2;TAIL_0 -1,0;TORSO_1 0,-999
ANIM_RUN_4=ALL 0,2;LEG_REST_RIGHT 0,2;LEG_REST_LEFT 0,-2;TAIL_0 1,0 ANIM_RUN_4=ALL 0,2;LEG_REST_RIGHT 0,2;LEG_REST_LEFT 0,-2;TAIL_0 1,0;TORSO_1 0,-999
ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND
ANIM_IDLE_1=TORSO_1 0,-999;HEAD 0,-1 ANIM_IDLE_1=TORSO_1 0,-999;HEAD 0,-1

View File

@@ -1,37 +1,45 @@
# complete file name is: SPRITESHEET + bodypart name + EXTENSION # complete file name is: SPRITESHEET + bodypart name + EXTENSION
SPRITESHEET=mods/basegame/sprites/test_werebeastf/taimuglow_ SPRITESHEET=mods/basegame/sprites/test_werebeastf/taimuglow_
EXTENSION=.tga EXTENSION=.tga
# defines frame size and origin point. Origin point is given as: (originx, size.y - 1) # defines frame size and origin point. Origin point is given as: (originx, 0)
# ORIGINY is deduced from the sprite size as shown in above; you only need to set ORIGINX
CONFIG=SIZE 64,98;ORIGINX 40 CONFIG=SIZE 64,98;ORIGINX 40
# note to self: don't implement skeleton hierarchy: there's too many exceptions ! A skeleton also defines what body parts (images) be used.
# besides, you have "ALL" key. ! You can also write multiline text using reverse solidus; this is a feature of .properties
! a skeleton also defines what body parts (images) be used.
! you can also write multiline text using reverse solidus; this is a feature of .properties
! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST ! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST
! are painted first, and any object that comes before it will paint over it. In other words, this list is ! are painted first, and any object that comes before it will paint over it. In other words, this list is
! first reversed then being iterated. ! first reversed then being iterated.
! Joints' original point is defined in the document sprite_joints.psd. It also has visual representations. ! Some keywords are considered special within the game, they are:
# TODO right now accessory points are explicitly defined. Should they be injected in run-time? ! HEADGEAR, HELD_ITEM, BOOT_L, BOOT_R, GAUNTLET_L, GAUNTLET_R and ARMOUR_* (star means any number starting from zero)
SKELETON_STAND=HEADGEAR 0,32;\ BODYPARTS=HEADGEAR 13,14;\
ARM_REST_LEFT -8,49;HELD_ITEM -6,11;\ HEAD 13,14;\
HEAD 2,83;\ ARM_REST_RIGHT 5,3;\
BUST_0 4,60;\ ARM_REST_LEFT 6,3;\
TORSO_0 0,55;\ LEG_REST_RIGHT 12,6;\
TORSO_1 0,55;\ LEG_REST_LEFT 8,6;\
LEG_REST_RIGHT -5,22;\ TORSO_0 18,19;\
LEG_REST_LEFT 6,22;\ TORSO_1 18,19;\
ARM_REST_RIGHT 11,51;\ TAIL_0 30,2;\
TAIL_0 -11,27 BUST_0 11,2;\
HELD_ITEM 0,0
SKELETON_STAND=HEADGEAR 0,78;\
ARM_REST_RIGHT -15,66;\
HELD_ITEM -11,33;\
HEAD 0,78;\
BUST_0 0,63;\
TORSO_0 0,54;\
TORSO_1 0,54;\
LEG_REST_RIGHT -5,41;\
LEG_REST_LEFT 3,41;\
ARM_REST_LEFT 8,66;\
TAIL_0 2,40
# skeleton_stand is used for testing purpose # skeleton_stand is used for testing purpose
ANIM_RUN=DELAY 0.3;ROW 2;SKELETON SKELETON_STAND ANIM_RUN=DELAY 0.3;ROW 2;SKELETON SKELETON_STAND
ANIM_RUN_1=LEG_REST_RIGHT 2,2;LEG_REST_LEFT -2,0;TAIL_0 1,0 ANIM_RUN_1=LEG_REST_RIGHT 2,2;LEG_REST_LEFT -2,0;TAIL_0 1,0;TORSO_1 0,-999
ANIM_RUN_2=ALL 0,2;LEG_REST_RIGHT 0,-2;LEG_REST_LEFT 0,2;TAIL_0 -1,0 ANIM_RUN_2=ALL 0,2;LEG_REST_RIGHT 0,-2;LEG_REST_LEFT 0,2;TAIL_0 -1,0;TORSO_1 0,-999
ANIM_RUN_3=LEG_REST_RIGHT -2,0;LEG_REST_LEFT 2,2;TAIL_0 -1,0 ANIM_RUN_3=LEG_REST_RIGHT -2,0;LEG_REST_LEFT 2,2;TAIL_0 -1,0;TORSO_1 0,-999
ANIM_RUN_4=ALL 0,2;LEG_REST_RIGHT 0,2;LEG_REST_LEFT 0,-2;TAIL_0 1,0 ANIM_RUN_4=ALL 0,2;LEG_REST_RIGHT 0,2;LEG_REST_LEFT 0,-2;TAIL_0 1,0;TORSO_1 0,-999
ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND
ANIM_IDLE_1=TORSO_1 0,-999;HEAD 0,-1 ANIM_IDLE_1=TORSO_1 0,-999;HEAD 0,-1

View File

@@ -1,20 +1,29 @@
# complete file name is: SPRITESHEET + bodypart name + EXTENSION # complete file name is: SPRITESHEET + bodypart name + EXTENSION
SPRITESHEET=mods/basegame/sprites/sprite_assembler_test_assets/test_ SPRITESHEET=mods/basegame/sprites/sprite_assembler_test_assets/test_
EXTENSION=.tga EXTENSION=.tga
# defines frame size and origin point. Origin point is given as: (originx, size.y - 1) # defines frame size and origin point. Origin point is given as: (originx, 0)
# ORIGINY is deduced from the sprite size as shown in above; you only need to set ORIGINX
CONFIG=SIZE 48,56;ORIGINX 29 CONFIG=SIZE 48,56;ORIGINX 29
# note to self: don't implement skeleton hierarchy: there's too many exceptions ! A skeleton also defines what body parts (images) be used.
# besides, you have "ALL" key. ! You can also write multiline text using reverse solidus; this is a feature of .properties
! a skeleton also defines what body parts (images) be used.
! you can also write multiline text using reverse solidus; this is a feature of .properties
! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST ! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST
! are painted first, and any object that comes before it will paint over it. In other words, this list is ! are painted first, and any object that comes before it will paint over it. In other words, this list is
! first reversed then being iterated. ! first reversed then being iterated.
! Joints' original point is defined in the document sprite_joints.psd. It also has visual representations. ! Some keywords are considered special within the game, they are:
# TODO right now accessory points are explicitly defined. Should they be injected in run-time? In that case, certain names (e.g. headgear, held_item) will act as an anchor. ! HEADGEAR, HELD_ITEM, BOOT_L, BOOT_R, GAUNTLET_L, GAUNTLET_R and ARMOUR_* (star means any number starting from zero)
BODYPARTS=ARM_REST_RIGHT 3,8;\
ARM_REST_LEFT 3,8;\
FOOT_LEFT 4,2;\
FOOT_RIGHT 4,2;\
UPPER_TORSO 9,4;\
LOWER_TORSO 7,4;\
LEG_REST_RIGHT 3,7;\
LEG_REST_LEFT 3,7;\
HEAD 8,7;\
HAND_REST_RIGHT 3,3;\
HAND_REST_LEFT 3,3;\
HAIR 20,12;\
HAIR_FORE 20,12
SKELETON_STAND=HEADGEAR 0,32;HAIR_FORE 0,32;\ SKELETON_STAND=HEADGEAR 0,32;HAIR_FORE 0,32;\
ARM_REST_RIGHT -7,23;HAND_REST_RIGHT -6,11;HELD_ITEM -6,11;\ ARM_REST_RIGHT -7,23;HAND_REST_RIGHT -6,11;HELD_ITEM -6,11;\
HAIR 0,32;HEAD 0,32;\ HAIR 0,32;HEAD 0,32;\

View File

@@ -1,20 +1,29 @@
# complete file name is: SPRITESHEET + bodypart name + EXTENSION # complete file name is: SPRITESHEET + bodypart name + EXTENSION
SPRITESHEET=mods/basegame/sprites/sprite_assembler_test_assets/testglow_ SPRITESHEET=mods/basegame/sprites/sprite_assembler_test_assets/testglow_
EXTENSION=.tga EXTENSION=.tga
# defines frame size and origin point. Origin point is given as: (originx, size.y - 1) # defines frame size and origin point. Origin point is given as: (originx, 0)
# ORIGINY is deduced from the sprite size as shown in above; you only need to set ORIGINX
CONFIG=SIZE 48,56;ORIGINX 29 CONFIG=SIZE 48,56;ORIGINX 29
# note to self: don't implement skeleton hierarchy: there's too many exceptions ! A skeleton also defines what body parts (images) be used.
# besides, you have "ALL" key. ! You can also write multiline text using reverse solidus; this is a feature of .properties
! a skeleton also defines what body parts (images) be used.
! you can also write multiline text using reverse solidus; this is a feature of .properties
! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST ! skeleton joints are ordered: foremost-drawn object comes first, which means lowermost object IN THIS LIST
! are painted first, and any object that comes before it will paint over it. In other words, this list is ! are painted first, and any object that comes before it will paint over it. In other words, this list is
! first reversed then being iterated. ! first reversed then being iterated.
! Joints' original point is defined in the document sprite_joints.psd. It also has visual representations. ! Some keywords are considered special within the game, they are:
# TODO right now accessory points are explicitly defined. Should they be injected in run-time? In that case, certain names (e.g. headgear, held_item) will act as an anchor. ! HEADGEAR, HELD_ITEM, BOOT_L, BOOT_R, GAUNTLET_L, GAUNTLET_R and ARMOUR_* (star means any number starting from zero)
BODYPARTS=ARM_REST_RIGHT 3,8;\
ARM_REST_LEFT 3,8;\
FOOT_LEFT 4,2;\
FOOT_RIGHT 4,2;\
UPPER_TORSO 9,4;\
LOWER_TORSO 7,4;\
LEG_REST_RIGHT 3,7;\
LEG_REST_LEFT 3,7;\
HEAD 8,7;\
HAND_REST_RIGHT 3,3;\
HAND_REST_LEFT 3,3;\
HAIR 20,12;\
HAIR_FORE 20,12
SKELETON_STAND=HEADGEAR 0,32;HAIR_FORE 0,32;\ SKELETON_STAND=HEADGEAR 0,32;HAIR_FORE 0,32;\
ARM_REST_RIGHT -7,23;HAND_REST_RIGHT -6,11;HELD_ITEM -6,11;\ ARM_REST_RIGHT -7,23;HAND_REST_RIGHT -6,11;HELD_ITEM -6,11;\
HAIR 0,32;HEAD 0,32;\ HAIR 0,32;HEAD 0,32;\

View File

@@ -29,9 +29,9 @@ interface HasAssembledSprite {
* ``` * ```
*/ */
fun reassembleSprite(sprite: SpriteAnimation, spriteGlow: SpriteAnimation? = null) { fun reassembleSprite(sprite: SpriteAnimation, spriteGlow: SpriteAnimation? = null) {
_rebuild(ADProperties(Gdx.files.internal(animDescPath).read()), sprite) _rebuild(ADProperties(Gdx.files.internal(animDescPath)), sprite)
if (animDescPathGlow != null && spriteGlow != null) if (animDescPathGlow != null && spriteGlow != null)
_rebuild(ADProperties(Gdx.files.internal(animDescPathGlow).read()), spriteGlow) _rebuild(ADProperties(Gdx.files.internal(animDescPathGlow)), spriteGlow)
} }
/*fun rebuild(animDescPath: String, spriteAnimation: SpriteAnimation) { /*fun rebuild(animDescPath: String, spriteAnimation: SpriteAnimation) {

View File

@@ -1,11 +1,10 @@
package net.torvald.spriteassembler package net.torvald.spriteassembler
import com.badlogic.gdx.files.FileHandle
import net.torvald.terrarum.linearSearchBy import net.torvald.terrarum.linearSearchBy
import java.io.InputStream import java.io.InputStream
import java.io.Reader import java.io.Reader
import java.util.* import java.util.*
import kotlin.collections.HashMap
import kotlin.collections.HashSet
internal data class Joint(val name: String, val position: ADPropertyObject.Vector2i) { internal data class Joint(val name: String, val position: ADPropertyObject.Vector2i) {
override fun toString() = "$name $position" override fun toString() = "$name $position"
@@ -15,6 +14,7 @@ internal data class Skeleton(val name: String, val joints: List<Joint>) {
override fun toString() = "$name=$joints" override fun toString() = "$name=$joints"
} }
/** /**
* @param name You know it * @param name You know it
* @param delay Delay between each frame in seconds * @param delay Delay between each frame in seconds
@@ -32,22 +32,25 @@ internal data class Transform(val joint: Joint, val translate: ADPropertyObject.
} }
class ADProperties { class ADProperties {
private var fileFrom = ""
private val javaProp = Properties() private val javaProp = Properties()
/** Every key is CAPITALISED */ /** Every key is CAPITALISED */
private val propTable = HashMap<String, List<ADPropertyObject>>() private val propTable = HashMap<String, List<ADPropertyObject>>()
/** list of bodyparts used by all the skeletons (HEAD, UPPER_TORSO, LOWER_TORSO) */ /** list of bodyparts used by all the skeletons (HEAD, UPPER_TORSO, LOWER_TORSO) */
lateinit var bodyparts: List<String>; private set // lateinit var bodyparts: List<String>; private set
lateinit var bodypartFiles: List<String>; private set lateinit var bodypartFiles: List<String>; private set
/** properties that are being used as skeletons (SKELETON_STAND) */ /** properties that are being used as skeletons (SKELETON_STAND) */
internal lateinit var skeletons: HashMap<String, Skeleton>; private set internal lateinit var skeletons: HashMap<String, Skeleton>; private set
/** properties that defines position of joint of the bodypart */
internal val bodyparts = HashMap<String, ADPropertyObject.Vector2i>()
/** properties that are recognised as animations (ANIM_RUN, ANIM)IDLE) */ /** properties that are recognised as animations (ANIM_RUN, ANIM)IDLE) */
internal lateinit var animations: HashMap<String, Animation>; private set internal lateinit var animations: HashMap<String, Animation>; private set
/** an "animation frame" property (ANIM_RUN_1, ANIM_RUN_2) */ /** an "animation frame" property (ANIM_RUN_1, ANIM_RUN_2) */
internal lateinit var transforms: HashMap<String, List<Transform>>; private set internal lateinit var transforms: HashMap<String, List<Transform>>; private set
private val reservedProps = listOf("SPRITESHEET", "EXTENSION") private val reservedProps = listOf("SPRITESHEET", "EXTENSION", "CONFIG", "BODYPARTS")
private val animMustContain = listOf("DELAY", "ROW", "SKELETON") private val animMustContain = listOf("DELAY", "ROW", "SKELETON")
lateinit var baseFilename: String; private set lateinit var baseFilename: String; private set
@@ -55,9 +58,8 @@ class ADProperties {
var frameWidth: Int = -1; private set var frameWidth: Int = -1; private set
var frameHeight: Int = -1; private set var frameHeight: Int = -1; private set
var originX: Int = -1; private set var originX: Int = -1; private set
var originY: Int = -1; private set
internal val origin: ADPropertyObject.Vector2i internal val origin: ADPropertyObject.Vector2i
get() = ADPropertyObject.Vector2i(originX, originY) get() = ADPropertyObject.Vector2i(originX, 0)
private val animFrameSuffixRegex = Regex("""_[0-9]+""") private val animFrameSuffixRegex = Regex("""_[0-9]+""")
@@ -70,6 +72,12 @@ class ADProperties {
const val ALL_JOINT_SELECT_KEY = "ALL" const val ALL_JOINT_SELECT_KEY = "ALL"
} }
constructor(gdxFile: FileHandle) {
fileFrom = gdxFile.path()
javaProp.load(gdxFile.read())
continueLoad()
}
constructor(reader: Reader) { constructor(reader: Reader) {
javaProp.load(reader) javaProp.load(reader)
continueLoad() continueLoad()
@@ -85,6 +93,16 @@ class ADProperties {
} }
private fun continueLoad() { private fun continueLoad() {
// sanity check
reservedProps.forEach {
try {
javaProp[it]!!
}
catch (e: NullPointerException) {
throw IllegalArgumentException("Prop '$it' not found from ${fileFrom.ifBlank { "(path unavailable)" }}", e)
}
}
javaProp.keys.forEach { propName -> javaProp.keys.forEach { propName ->
val propsStr = javaProp.getProperty(propName as String) val propsStr = javaProp.getProperty(propName as String)
val propsList = propsStr.split(';').map { ADPropertyObject(it) } val propsList = propsStr.split(';').map { ADPropertyObject(it) }
@@ -99,11 +117,9 @@ class ADProperties {
frameWidth = frameSizeVec.x frameWidth = frameSizeVec.x
frameHeight = frameSizeVec.y frameHeight = frameSizeVec.y
originX = (get("CONFIG").linearSearchBy { it.name == "ORIGINX" }!!.input as Float).toInt() originX = (get("CONFIG").linearSearchBy { it.name == "ORIGINX" }!!.input as Float).toInt()
originY = frameHeight - 1
var maxColFinder = -1 var maxColFinder = -1
var maxRowFinder = -1 var maxRowFinder = -1
val bodyparts = HashSet<String>()
val skeletons = HashMap<String, Skeleton>() val skeletons = HashMap<String, Skeleton>()
val animations = HashMap<String, Animation>() val animations = HashMap<String, Animation>()
val animFrames = HashMap<String, Int>() val animFrames = HashMap<String, Int>()
@@ -153,13 +169,6 @@ class ADProperties {
} }
} }
// populate the bodyparts using skeletons
skeletons.forEach { (_, prop: Skeleton) ->
prop.joints.forEach {
bodyparts.add(it.name)
}
}
// populate transforms // populate transforms
animations.forEach { t, u -> animations.forEach { t, u ->
for (fc in 1..u.frames) { for (fc in 1..u.frames) {
@@ -190,17 +199,28 @@ class ADProperties {
} }
} }
this.bodyparts = bodyparts.toList().sorted() get("BODYPARTS").forEach {
try {
this.bodyparts[it.name] = (it.input as ADPropertyObject.Vector2i)
}
catch (e: NullPointerException) {
if (it.name.isBlank())
throw IllegalArgumentException("Empty Bodypart name on BODYPARTS; try removing trailing semicolon (';')?")
else
throw IllegalArgumentException("Bodyparts definition for '${it.name}' not found from ${fileFrom.ifBlank { "(path unavailable)" }}", e)
}
}
this.skeletons = skeletons this.skeletons = skeletons
this.animations = animations this.animations = animations
this.bodypartFiles = this.bodyparts.map { toFilename(it) } this.bodypartFiles = this.bodyparts.keys.map { toFilename(it) }
this.transforms = transforms this.transforms = transforms
cols = maxColFinder cols = maxColFinder
rows = maxRowFinder rows = maxRowFinder
} }
operator fun get(identifier: String) = propTable[identifier.toUpperCase()]!! operator fun get(identifier: String): List<ADPropertyObject> = propTable[identifier.toUpperCase()]!!
val keys val keys
get() = propTable.keys get() = propTable.keys
fun containsKey(key: String) = propTable.containsKey(key) fun containsKey(key: String) = propTable.containsKey(key)
@@ -299,6 +319,8 @@ class ADPropertyObject(propertyRaw: String) {
operator fun minus(other: Vector2i) = Vector2i(this.x - other.x, this.y - other.y) operator fun minus(other: Vector2i) = Vector2i(this.x - other.x, this.y - other.y)
fun invertY() = Vector2i(this.x, -this.y) fun invertY() = Vector2i(this.x, -this.y)
fun invertX() = Vector2i(-this.x, this.y)
fun invertXY() = Vector2i(-this.x, -this.y)
} }
enum class ADPropertyType { enum class ADPropertyType {

View File

@@ -3,7 +3,6 @@ package net.torvald.spriteassembler
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Pixmap
import net.torvald.terrarum.linearSearch import net.torvald.terrarum.linearSearch
import java.io.File
/** /**
* Assembles the single frame of the animation, outputs GDX Pixmap. * Assembles the single frame of the animation, outputs GDX Pixmap.
@@ -34,19 +33,11 @@ object AssembleSheetPixmap {
val theAnim = properties.getAnimByFrameName(frameName) val theAnim = properties.getAnimByFrameName(frameName)
val skeleton = theAnim.skeleton.joints.reversed() val skeleton = theAnim.skeleton.joints.reversed()
val transforms = properties.getTransform(frameName) val transforms = properties.getTransform(frameName)
val bodyparts = Array<Pixmap?>(skeleton.size) { val bodypartOrigins = properties.bodyparts
// if file does not exist, null it val bodypartImages = properties.bodyparts.keys.map {
val file = File("assets/" + properties.toFilename(skeleton[it].name)) val file = Gdx.files.internal("assets/${properties.toFilename(it)}")
it to (if (file.exists()) Pixmap(file) else null)
//printdbg(this, "Loading file ${file.absolutePath}, exists: ${file.exists()}") }.toMap()
/*return*/if (file.exists()) {
Pixmap(Gdx.files.internal(file.path))
}
else {
null
}
}
val transformList = AssembleFrameBase.makeTransformList(skeleton, transforms) val transformList = AssembleFrameBase.makeTransformList(skeleton, transforms)
val animRow = theAnim.row val animRow = theAnim.row
@@ -54,25 +45,26 @@ object AssembleSheetPixmap {
// AppLoader.printdbg(this, "Frame to draw: $frameName (R$animRow C$animFrame)") // AppLoader.printdbg(this, "Frame to draw: $frameName (R$animRow C$animFrame)")
drawFrame(animRow, animFrame, canvas, properties, bodyparts, transformList) drawFrame(animRow, animFrame, canvas, properties, bodypartOrigins, bodypartImages, transformList)
bodyparts.forEach { it?.dispose() } bodypartImages.values.forEach { it?.dispose() }
} }
private fun drawFrame(row: Int, column: Int, private fun drawFrame(row: Int, column: Int,
canvas: Pixmap, canvas: Pixmap,
props: ADProperties, props: ADProperties,
bodyparts: Array<Pixmap?>, bodypartOrigins: HashMap<String, ADPropertyObject.Vector2i>,
bodypartImages: Map<String, Pixmap?>,
transformList: List<Pair<String, ADPropertyObject.Vector2i>> transformList: List<Pair<String, ADPropertyObject.Vector2i>>
) { ) {
val tmpFrame = Pixmap(props.frameWidth, props.frameHeight, Pixmap.Format.RGBA8888) val tmpFrame = Pixmap(props.frameWidth, props.frameHeight, Pixmap.Format.RGBA8888)
bodyparts.forEachIndexed { index, image -> transformList.forEach { (name, pos) ->
if (image != null) { bodypartImages[name]?.let { image ->
val imgCentre = AssembleFrameBase.getCentreOf(image) val imgCentre = bodypartOrigins[name]!!.invertX()
val drawPos = transformList[index].second.invertY() + props.origin - imgCentre val drawPos = props.origin + pos + imgCentre
tmpFrame.drawPixmap(image, drawPos.x, drawPos.y) tmpFrame.drawPixmap(image, drawPos.x, props.frameHeight - drawPos.y - 1)
} }
} }

View File

@@ -89,7 +89,9 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
|GNU General Public License for more details. |GNU General Public License for more details.
| |
|You should have received a copy of the GNU General Public License |You should have received a copy of the GNU General Public License
|along with this program. If not, see <https://www.gnu.org/licenses/>.""".trimMargin() |along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|Paste your Animation Description here and press 'Update'!""".trimMargin()
panelCode.addMouseListener(object : MouseAdapter() { panelCode.addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent?) { override fun mousePressed(e: MouseEvent?) {
if (panelCodeInit) { if (panelCodeInit) {
@@ -116,14 +118,14 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
panelProperties.model = DefaultTreeModel(DefaultMutableTreeNode(lang.getProperty("PROPERTIES_GO_HERE"))) panelProperties.model = DefaultTreeModel(DefaultMutableTreeNode(lang.getProperty("PROPERTIES_GO_HERE")))
val panelDataView = JSplitPane(JSplitPane.VERTICAL_SPLIT, JScrollPane(panelProperties), panelPartsList) val panelDataView = JSplitPane(JSplitPane.VERTICAL_SPLIT, JScrollPane(panelProperties), panelPartsList)
panelDataView.resizeWeight = 0.333 panelDataView.resizeWeight = 0.4
// to disable text wrap // to disable text wrap
//val panelCodeNoWrap = JPanel(BorderLayout()) //val panelCodeNoWrap = JPanel(BorderLayout())
//panelCodeNoWrap.add(panelCode) //panelCodeNoWrap.add(panelCode)
val panelMain = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, JScrollPane(panelCode), panelDataView) val panelMain = JSplitPane(JSplitPane.HORIZONTAL_SPLIT, JScrollPane(panelCode), panelDataView)
panelMain.resizeWeight = 0.666 panelMain.resizeWeight = 0.6
val menu = JMenuBar() val menu = JMenuBar()
menu.add(JMenu("Update")).addMouseListener(object : MouseAdapter() { menu.add(JMenu("Update")).addMouseListener(object : MouseAdapter() {
@@ -158,8 +160,8 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
(panelAnimationsList.model as DefaultListModel).addElement("${it.value}") (panelAnimationsList.model as DefaultListModel).addElement("${it.value}")
} }
// populate bodyparts view // populate bodyparts view
adProperties.bodyparts.forEach { partName -> adProperties.bodyparts.toSortedMap().forEach { part ->
(panelBodypartsList.model as DefaultListModel).addElement(partName) (panelBodypartsList.model as DefaultListModel).addElement("${part.key}: ${part.value}")
} }
// populate image file list view // populate image file list view
adProperties.bodypartFiles.forEach { partName -> adProperties.bodypartFiles.forEach { partName ->
@@ -177,7 +179,7 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
(panelStatList.model as DefaultListModel).addElement("Spritesheet rows: ${adProperties.rows}") (panelStatList.model as DefaultListModel).addElement("Spritesheet rows: ${adProperties.rows}")
(panelStatList.model as DefaultListModel).addElement("Spritesheet columns: ${adProperties.cols}") (panelStatList.model as DefaultListModel).addElement("Spritesheet columns: ${adProperties.cols}")
(panelStatList.model as DefaultListModel).addElement("Frame size: ${adProperties.frameWidth}, ${adProperties.frameHeight}") (panelStatList.model as DefaultListModel).addElement("Frame size: ${adProperties.frameWidth}, ${adProperties.frameHeight}")
(panelStatList.model as DefaultListModel).addElement("Origin position: ${adProperties.originX}, ${adProperties.originY}") (panelStatList.model as DefaultListModel).addElement("Origin position: ${adProperties.origin}")
@@ -218,7 +220,7 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
this.add(statBar, BorderLayout.SOUTH) this.add(statBar, BorderLayout.SOUTH)
this.title = "Terrarum Sprite Assembler and Viewer" this.title = "Terrarum Sprite Assembler and Viewer"
this.isVisible = true this.isVisible = true
this.setSize(1154, 768) this.setSize(1120, 768)
this.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE this.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
} }
@@ -317,7 +319,7 @@ class SpriteAssemblerPreview: Game() {
fun main(args: Array<String>) { fun main(args: Array<String>) {
val appConfig = Lwjgl3ApplicationConfiguration() val appConfig = Lwjgl3ApplicationConfiguration()
appConfig.setWindowedMode(800, 800) appConfig.setWindowedMode(1024, 1024)
appConfig.setIdleFPS(5) appConfig.setIdleFPS(5)
appConfig.setForegroundFPS(5) appConfig.setForegroundFPS(5)
appConfig.setResizable(false) appConfig.setResizable(false)

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.EMDASH import net.torvald.EMDASH
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.* import net.torvald.terrarum.App.*
import net.torvald.terrarum.Terrarum.PLAYER_REF_ID
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.blockproperties.BlockPropUtil import net.torvald.terrarum.blockproperties.BlockPropUtil
@@ -293,7 +294,9 @@ open class TerrarumIngame(batch: SpriteBatch) : IngameInstance(batch) {
} }
catch (e: NullPointerException) { catch (e: NullPointerException) {
System.err.println("Could not read the actor ${it} from the disk") System.err.println("Could not read the actor ${it} from the disk")
// throw e // TODO don't rethrow -- let players play the corrupted world if it loads, they'll be able to cope with their losses even though there will be buncha lone actorblocks lying around... e.printStackTrace()
if (it == PLAYER_REF_ID) throw e
// throw e // if not player, don't rethrow -- let players play the corrupted world if it loads, they'll be able to cope with their losses even though there will be buncha lone actorblocks lying around...
} }
} }

View File

@@ -69,7 +69,7 @@ object ReadActor {
if (actor is ActorWithBody && actor is HasAssembledSprite) { if (actor is ActorWithBody && actor is HasAssembledSprite) {
actor.sprite = SpriteAnimation(actor) actor.sprite = SpriteAnimation(actor)
if (actor.animDescPathGlow != null) actor.spriteGlow = SpriteAnimation(actor) if (actor.animDescPathGlow != null) actor.spriteGlow = SpriteAnimation(actor)
actor.reassembleSprite(actor.sprite!!, actor.spriteGlow) actor.reassembleSprite(actor.sprite ?: throw InternalError("actor.sprite (type: SpriteAnimation) is null"), actor.spriteGlow)
} }
return actor return actor