diff --git a/.idea/artifacts/SpriteAssemblerApp.xml b/.idea/artifacts/SpriteAssemblerApp.xml
index db463884b..ecaa3f1da 100644
--- a/.idea/artifacts/SpriteAssemblerApp.xml
+++ b/.idea/artifacts/SpriteAssemblerApp.xml
@@ -1,7 +1,7 @@
$PROJECT_DIR$/out
-
+
@@ -31,56 +31,58 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/mods/basegame/sprites/fofu.properties b/assets/mods/basegame/sprites/fofu.properties
new file mode 100644
index 000000000..7b6227f07
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu.properties
@@ -0,0 +1,31 @@
+SPRITESHEET=mods/basegame/sprites/fofu/fofu_
+EXTENSION=.tga
+CONFIG=SIZE 48,56;ORIGINX 29
+BODYPARTS=HEADGEAR 11,11;\
+ HEAD 11,11;\
+ ARM_REST_RIGHT 4,2;\
+ ARM_REST_LEFT 4,2;\
+ LEG_REST_RIGHT 4,7;\
+ LEG_REST_LEFT 4,7;\
+ TORSO 10,4;\
+ TAIL_0 20,1;\
+ HELD_ITEM 0,0
+SKELETON_STAND=HEADGEAR 0,32;HAIR_FORE 0,32;\
+ ARM_REST_RIGHT -7,23;HELD_ITEM -6,11;\
+ HAIR 0,32;HEAD 0,32;\
+ TORSO 0,22;\
+ LEG_REST_RIGHT -2,7;\
+ LEG_REST_LEFT 2,7;\
+ ARM_REST_LEFT 5,24;\
+ TAIL_0 0,13
+ANIM_RUN=DELAY 0.15;ROW 2;SKELETON SKELETON_STAND
+ANIM_RUN_1=LEG_REST_RIGHT 1,1;LEG_REST_LEFT -1,0
+ANIM_RUN_2=ALL 0,1;LEG_REST_RIGHT 0,-1;LEG_REST_LEFT 0,1
+ANIM_RUN_3=LEG_REST_RIGHT -1,0;LEG_REST_LEFT 1,1
+ANIM_RUN_4=ALL 0,1;LEG_REST_RIGHT 0,1;LEG_REST_LEFT 0,-1
+
+ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND
+ANIM_IDLE_1=
+ANIM_IDLE_2=TORSO 0,-1;HEAD 0,-1;HAIR 0,-1;HELD_ITEM 0,-1;\
+ ARM_REST_LEFT 0,-1;ARM_REST_RIGHT 0,-1;\
+ HAIR_FORE 0,-1;HEADGEAR 0,-1
diff --git a/assets/mods/basegame/sprites/fofu/fofu_arm_rest_left.tga b/assets/mods/basegame/sprites/fofu/fofu_arm_rest_left.tga
new file mode 100644
index 000000000..1cfca3386
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu/fofu_arm_rest_left.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6cf19b53078ccbfaa9507432e3acbb92e7c2ab740dd8e9fc4089618e46744471
+size 494
diff --git a/assets/mods/basegame/sprites/fofu/fofu_arm_rest_right.tga b/assets/mods/basegame/sprites/fofu/fofu_arm_rest_right.tga
new file mode 100644
index 000000000..784b1fe71
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu/fofu_arm_rest_right.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ef7de78785900388c985bda74e7c098eb3bc1db372e9ffe88a2015049fe4d10
+size 522
diff --git a/assets/mods/basegame/sprites/fofu/fofu_head.tga b/assets/mods/basegame/sprites/fofu/fofu_head.tga
new file mode 100644
index 000000000..5848f0ec3
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu/fofu_head.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a2ed69862aa0e9b18944b9dda88f9963f86742fb8dc4e657dd414947303a87c5
+size 1386
diff --git a/assets/mods/basegame/sprites/fofu/fofu_leg_rest_left.tga b/assets/mods/basegame/sprites/fofu/fofu_leg_rest_left.tga
new file mode 100644
index 000000000..37b592c41
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu/fofu_leg_rest_left.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d222c44c4e1513dbb2cd45ffc2d7d4863b8d908e5ecfd430976de7c477a8d606
+size 498
diff --git a/assets/mods/basegame/sprites/fofu/fofu_leg_rest_right.tga b/assets/mods/basegame/sprites/fofu/fofu_leg_rest_right.tga
new file mode 100644
index 000000000..2980acd16
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu/fofu_leg_rest_right.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b007d035a24ef38e2c4be1f8b6b5df3e600668ebad6af197d02d2475fc6df165
+size 498
diff --git a/assets/mods/basegame/sprites/fofu/fofu_tail_0.tga b/assets/mods/basegame/sprites/fofu/fofu_tail_0.tga
new file mode 100644
index 000000000..7c846c78d
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu/fofu_tail_0.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58091d6a61f3a3760ad4b3e9d9ce2f42efe1c2b93ef937879c7bcd073ed2ff63
+size 930
diff --git a/assets/mods/basegame/sprites/fofu/fofu_torso.tga b/assets/mods/basegame/sprites/fofu/fofu_torso.tga
new file mode 100644
index 000000000..a94cbe647
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu/fofu_torso.tga
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2cd67722cef6eacdb17f86528bf65ca7c5ecde58edf77cb7d37fdb5e9da8caaf
+size 914
diff --git a/assets/mods/basegame/sprites/fofu_emsv.properties b/assets/mods/basegame/sprites/fofu_emsv.properties
new file mode 100644
index 000000000..66b444ded
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu_emsv.properties
@@ -0,0 +1,31 @@
+SPRITESHEET=mods/basegame/sprites/fofu/fofuemsv_
+EXTENSION=.tga
+CONFIG=SIZE 48,56;ORIGINX 29
+BODYPARTS=HEADGEAR 11,11;\
+ HEAD 11,11;\
+ ARM_REST_RIGHT 4,2;\
+ ARM_REST_LEFT 4,2;\
+ LEG_REST_RIGHT 4,7;\
+ LEG_REST_LEFT 4,7;\
+ TORSO 10,4;\
+ TAIL_0 20,1;\
+ HELD_ITEM 0,0
+SKELETON_STAND=HEADGEAR 0,32;HAIR_FORE 0,32;\
+ ARM_REST_RIGHT -7,23;HELD_ITEM -6,11;\
+ HAIR 0,32;HEAD 0,32;\
+ TORSO 0,22;\
+ LEG_REST_RIGHT -2,7;\
+ LEG_REST_LEFT 2,7;\
+ ARM_REST_LEFT 5,24;\
+ TAIL_0 0,13
+ANIM_RUN=DELAY 0.15;ROW 2;SKELETON SKELETON_STAND
+ANIM_RUN_1=LEG_REST_RIGHT 1,1;LEG_REST_LEFT -1,0
+ANIM_RUN_2=ALL 0,1;LEG_REST_RIGHT 0,-1;LEG_REST_LEFT 0,1
+ANIM_RUN_3=LEG_REST_RIGHT -1,0;LEG_REST_LEFT 1,1
+ANIM_RUN_4=ALL 0,1;LEG_REST_RIGHT 0,1;LEG_REST_LEFT 0,-1
+
+ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND
+ANIM_IDLE_1=
+ANIM_IDLE_2=TORSO 0,-1;HEAD 0,-1;HAIR 0,-1;HELD_ITEM 0,-1;\
+ ARM_REST_LEFT 0,-1;ARM_REST_RIGHT 0,-1;\
+ HAIR_FORE 0,-1;HEADGEAR 0,-1
diff --git a/assets/mods/basegame/sprites/fofu_glow.properties b/assets/mods/basegame/sprites/fofu_glow.properties
new file mode 100644
index 000000000..629b44acd
--- /dev/null
+++ b/assets/mods/basegame/sprites/fofu_glow.properties
@@ -0,0 +1,31 @@
+SPRITESHEET=mods/basegame/sprites/fofu/fofuglow_
+EXTENSION=.tga
+CONFIG=SIZE 48,56;ORIGINX 29
+BODYPARTS=HEADGEAR 11,11;\
+ HEAD 11,11;\
+ ARM_REST_RIGHT 4,2;\
+ ARM_REST_LEFT 4,2;\
+ LEG_REST_RIGHT 4,7;\
+ LEG_REST_LEFT 4,7;\
+ TORSO 10,4;\
+ TAIL_0 20,1;\
+ HELD_ITEM 0,0
+SKELETON_STAND=HEADGEAR 0,32;HAIR_FORE 0,32;\
+ ARM_REST_RIGHT -7,23;HELD_ITEM -6,11;\
+ HAIR 0,32;HEAD 0,32;\
+ TORSO 0,22;\
+ LEG_REST_RIGHT -2,7;\
+ LEG_REST_LEFT 2,7;\
+ ARM_REST_LEFT 5,24;\
+ TAIL_0 0,13
+ANIM_RUN=DELAY 0.15;ROW 2;SKELETON SKELETON_STAND
+ANIM_RUN_1=LEG_REST_RIGHT 1,1;LEG_REST_LEFT -1,0
+ANIM_RUN_2=ALL 0,1;LEG_REST_RIGHT 0,-1;LEG_REST_LEFT 0,1
+ANIM_RUN_3=LEG_REST_RIGHT -1,0;LEG_REST_LEFT 1,1
+ANIM_RUN_4=ALL 0,1;LEG_REST_RIGHT 0,1;LEG_REST_LEFT 0,-1
+
+ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND
+ANIM_IDLE_1=
+ANIM_IDLE_2=TORSO 0,-1;HEAD 0,-1;HAIR 0,-1;HELD_ITEM 0,-1;\
+ ARM_REST_LEFT 0,-1;ARM_REST_RIGHT 0,-1;\
+ HAIR_FORE 0,-1;HEADGEAR 0,-1
diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt
index f23b831bd..edaf0ca1d 100644
--- a/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt
+++ b/src/net/torvald/terrarum/modulebasegame/gameactors/PlayerBuilderTestSubject1.kt
@@ -10,9 +10,9 @@ import net.torvald.terrarum.gameactors.AVKey
object PlayerBuilderTestSubject1 {
operator fun invoke(): IngamePlayer {
val p: IngamePlayer = IngamePlayer(
- ModMgr.getGdxFile("basegame", "sprites/test_sprite.properties").path(),
- ModMgr.getGdxFile("basegame", "sprites/test_sprite_glow.properties").path(),
- ModMgr.getGdxFile("basegame", "sprites/test_sprite_emsv.properties").path(),
+ ModMgr.getGdxFile("basegame", "sprites/fofu.properties").path(),
+ ModMgr.getGdxFile("basegame", "sprites/fofu_glow.properties").path(),
+ ModMgr.getGdxFile("basegame", "sprites/fofu_emsv.properties").path(),
0L // random value thrown
)
InjectCreatureRaw(p.actorValue, "basegame", "CreaturePlayer.json")
diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt
index 292d85719..a3c77c7e2 100644
--- a/src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt
+++ b/src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt
@@ -65,8 +65,9 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
it.clickOnceListener = { _,_ ->
if (filenameInput.getText().isNotBlank()) {
- importReturnCode = doImport()
- if (importReturnCode == 0) remoCon.openUI(UILoadSavegame(remoCon))
+ val (importReturnCode, avatarDisk) = doImport()
+ this.importReturnCode = importReturnCode
+ if (importReturnCode == 0) remoCon.openUI(UINewWorld(remoCon, avatarDisk!!))
}
}
}
@@ -143,14 +144,16 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
override fun advanceMode(button: UIItem) {
}
- private fun doImport(): Int {
+ private fun doImport(): Pair {
val file = File("${App.importDir}/${filenameInput.getText().trim()}")
// check file's existence
if (!file.exists()) {
- return 1
+ return 1 to null
}
+ var retDisk: DiskSkimmer? = null
+
// try to mount the TEVd
try {
val dom = VDUtil.readDiskArchive(file)
@@ -164,7 +167,7 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
printdbg(this, "Avatar uuid: $uuid")
- if (newFile.exists()) return 2
+ if (newFile.exists()) return 2 to null
// update playerinfo so that:
// totalPlayTime to zero
@@ -212,6 +215,8 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
App.sortedPlayers.add(0, playerUUID)
App.savegamePlayersName[playerUUID] = it.getDiskName(Common.CHARSET)
}
+
+ retDisk = it
}
catch (e: Throwable) {
printdbgerr(this, e.stackTraceToString())
@@ -220,10 +225,10 @@ class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() {
catch (e: Throwable) {
// format error
e.printStackTrace()
- return -1
+ return -1 to null
}
- return 0
+ return 0 to retDisk
}
}
diff --git a/src/net/torvald/terrarum/savegame/VirtualDisk.kt b/src/net/torvald/terrarum/savegame/VirtualDisk.kt
index 6b961096c..ae0b44b26 100644
--- a/src/net/torvald/terrarum/savegame/VirtualDisk.kt
+++ b/src/net/torvald/terrarum/savegame/VirtualDisk.kt
@@ -313,6 +313,7 @@ object VDFileID {
const val LOADORDER = -4L
const val PLAYER_SCREENSHOT = -5L
const val SPRITEDEF_EMISSIVE = -6L
+ const val SPRITEDEF_EMSV = -7L
const val BODYPART_TO_ENTRY_MAP = -1025L
const val BODYPARTGLOW_TO_ENTRY_MAP = -1026L
const val BODYPARTEMISSIVE_TO_ENTRY_MAP = -1027L
@@ -333,6 +334,11 @@ fun diskIDtoReadableFilename(id: EntryID, saveKind: Int?): String = when (id) {
"spritedef-glow"
else
"file #$id"
+ VDFileID.SPRITEDEF_EMSV ->
+ if (saveKind == PLAYER_DATA)
+ "spritedef-glow"
+ else
+ "file #$id"
VDFileID.PLAYER_SCREENSHOT ->
if (saveKind == PLAYER_DATA)
"screenshot.tga.gz"
@@ -347,7 +353,7 @@ fun diskIDtoReadableFilename(id: EntryID, saveKind: Int?): String = when (id) {
// -1024L -> "apocryphas.json.gz"
VDFileID.BODYPART_TO_ENTRY_MAP -> "bodypart-to-entry.map"
VDFileID.BODYPARTGLOW_TO_ENTRY_MAP -> "bodypartglow-to-entry.map"
- VDFileID.BODYPARTEMISSIVE_TO_ENTRY_MAP -> "bodypartemissive-to-entry.map"
+ VDFileID.BODYPARTEMISSIVE_TO_ENTRY_MAP -> "bodypartemsv-to-entry.map"
in 1..65535 ->
if (saveKind == PLAYER_DATA)
"bodypart #$id.tga.gz"
diff --git a/src/net/torvald/terrarum/spriteassembler/AvatarBuilder.kt b/src/net/torvald/terrarum/spriteassembler/AvatarBuilder.kt
new file mode 100644
index 000000000..dd7989cfc
--- /dev/null
+++ b/src/net/torvald/terrarum/spriteassembler/AvatarBuilder.kt
@@ -0,0 +1,166 @@
+package net.torvald.terrarum.spriteassembler
+
+import com.badlogic.gdx.Gdx
+import net.torvald.terrarum.App
+import net.torvald.terrarum.savegame.*
+import net.torvald.terrarum.savegame.VDFileID.BODYPARTEMISSIVE_TO_ENTRY_MAP
+import net.torvald.terrarum.savegame.VDFileID.BODYPARTGLOW_TO_ENTRY_MAP
+import net.torvald.terrarum.savegame.VDFileID.BODYPART_TO_ENTRY_MAP
+import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT
+import net.torvald.terrarum.savegame.VDFileID.ROOT
+import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
+import net.torvald.terrarum.savegame.VDFileID.SPRITEDEF
+import net.torvald.terrarum.savegame.VDFileID.SPRITEDEF_EMSV
+import net.torvald.terrarum.savegame.VDFileID.SPRITEDEF_GLOW
+import net.torvald.terrarum.serialise.Common
+import java.awt.BorderLayout
+import java.awt.Dimension
+import java.awt.GridLayout
+import java.awt.event.MouseAdapter
+import java.awt.event.MouseEvent
+import java.io.File
+import javax.swing.*
+
+/**
+ * Created by minjaesong on 2024-09-25.
+ */
+class AvatarBuilder : JFrame() {
+
+ private val infilePath = JTextField().also { it.preferredSize = Dimension(400, 24) }
+ private val outfilePath = JTextField().also { it.preferredSize = Dimension(400, 24) }
+ private val buttonGo = JButton("Export!")
+
+ init {
+ val panelPlayer = JPanel().also {
+ it.add(JLabel("Player File"))
+ it.add(infilePath)
+ }
+
+ val panelOutput = JPanel().also {
+ it.add(JLabel("Output File"))
+ it.add(outfilePath)
+ }
+
+ val controlPanel = JPanel().also {
+ it.add(buttonGo)
+ }
+
+ val panelMain = JPanel().also {
+ it.layout = GridLayout(2, 1)
+ it.add(panelPlayer, 0)
+ it.add(panelOutput, 1)
+ }
+
+ this.layout = BorderLayout()
+ this.add(panelMain, BorderLayout.CENTER)
+ this.add(controlPanel, BorderLayout.SOUTH)
+ this.title = "Terrarum Avatar Generator"
+ this.isVisible = true
+ this.setSize(480, 120)
+ this.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
+
+ buttonGo.addMouseListener(object : MouseAdapter() {
+ override fun mouseClicked(e: MouseEvent?) {
+ if (infilePath.text.isNotBlank() && outfilePath.text.isNotBlank()) {
+ try {
+ invoke(File(infilePath.text), File(outfilePath.text))
+ popupMessage("Exported successfully!")
+ }
+ catch (e: Throwable) {
+ e.printStackTrace()
+ popupError(e.message ?: "null")
+ }
+ }
+ }
+ })
+ }
+
+ private fun popupMessage(message: String, title: String = "") {
+ JOptionPane.showOptionDialog(
+ null,
+ message,
+ title,
+ JOptionPane.DEFAULT_OPTION,
+ JOptionPane.INFORMATION_MESSAGE,
+ null, null, null
+ )
+ }
+
+ private fun popupError(message: String, title: String = "Uh oh…") {
+ JOptionPane.showOptionDialog(
+ null,
+ message,
+ title,
+ JOptionPane.DEFAULT_OPTION,
+ JOptionPane.ERROR_MESSAGE,
+ null, null, null
+ )
+ }
+
+ private var lastBodypartIndex = 0L
+
+ operator fun invoke(playerSavegameFile: File, outFile: File) {
+ val dom = VDUtil.readDiskArchive(playerSavegameFile)
+
+ val playerInfoFile = dom.getEntry(SAVEGAMEINFO)!!
+ val spritedefFile = dom.getEntry(SPRITEDEF)!!
+ val spritedefGlowFile = dom.getEntry(SPRITEDEF_GLOW)
+ val spritedefEmsvFile = dom.getEntry(SPRITEDEF_EMSV)
+ val screencap = dom.getEntry(PLAYER_SCREENSHOT)
+ val playerName = dom.diskName.toCanonicalString(Common.CHARSET)
+ val outDisk = VDUtil.createNewDisk(4294967295L, playerName, Common.CHARSET)
+
+ val timeNow = App.getTIME_T()
+
+ // initialise outDisk DOM
+ VDUtil.addFile(outDisk, playerInfoFile)
+ VDUtil.addFile(outDisk, spritedefFile)
+ spritedefGlowFile?.let { VDUtil.addFile(outDisk, it) }
+ spritedefEmsvFile?.let { VDUtil.addFile(outDisk, it) }
+ screencap?.let {VDUtil.addFile(outDisk, it) }
+
+
+
+ putBodyparts(timeNow, outDisk, spritedefFile, BODYPART_TO_ENTRY_MAP)
+ spritedefGlowFile?.let { putBodyparts(timeNow, outDisk, it, BODYPARTGLOW_TO_ENTRY_MAP) }
+ spritedefEmsvFile?.let { putBodyparts(timeNow, outDisk, it, BODYPARTEMISSIVE_TO_ENTRY_MAP) }
+
+ outDisk.saveKind = VDSaveKind.PLAYER_DATA
+
+ VDUtil.dumpToRealMachine(outDisk, outFile)
+ }
+
+ // will write: bodypart images, spritedef, to the disk
+ private fun putBodyparts(timeNow: Long, outDisk: VirtualDisk, spritedefFile: DiskEntry, bodypartsToEntryFileID: Long) {
+ val bodypartsToEntry = StringBuilder()
+ val adp = ADProperties(ByteArray64Reader(spritedefFile.contents.getContent() as ByteArray64, Common.CHARSET))
+ val images = adp.bodyparts.map { // Pair: "HEAD" to "mods/basegame/sprites/***/prefix_HEAD.tga"
+ it.uppercase() to adp.toFilename(it)
+ }
+
+ images.forEach { (name, path) ->
+
+ val bodypartFileNative = File("assets/$path")
+ if (bodypartFileNative.exists()) {
+ lastBodypartIndex += 1L
+
+ // write to bodypartsToEntry file content
+ bodypartsToEntry.append("$name=$lastBodypartIndex\n")
+
+ // copy image into new disk
+ val bodypartFile = VDUtil.importFile(bodypartFileNative, lastBodypartIndex, Common.CHARSET)
+ VDUtil.addFile(outDisk, bodypartFile)
+ }
+ }
+
+ // write bodypartsToEntry as a file
+ val bodypartsToEntryFileContent = EntryFile(ByteArray64.fromByteArray(bodypartsToEntry.toString().toByteArray(Common.CHARSET)))
+ val bodypartsToEntryFile = DiskEntry(bodypartsToEntryFileID, ROOT, timeNow, timeNow, bodypartsToEntryFileContent)
+ VDUtil.addFile(outDisk, bodypartsToEntryFile)
+ }
+
+}
+
+fun main(args: Array) {
+ AvatarBuilder()
+}
diff --git a/work_files/graphics/sprites/furbud3_jointed.psd b/work_files/graphics/sprites/furbud3_jointed.psd
new file mode 100644
index 000000000..98e04e233
--- /dev/null
+++ b/work_files/graphics/sprites/furbud3_jointed.psd
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7f57b09ec96ef20d3c584e58d3e8ccb929ebed5b6dcf897ef0f15503b3961026
+size 70054