diff --git a/SAVE_FORMAT.md b/SAVE_FORMAT.md index a9ece66bd..34301f996 100644 --- a/SAVE_FORMAT.md +++ b/SAVE_FORMAT.md @@ -12,6 +12,7 @@ The main game directory is composed of following directories: [-2] spritedef, [-3] optional spritedef-glow, [-1025] sprite-bodypart-name-to-entry-number-map.properties, + [-1026] spriteglow-bodypart-name-to-entry-number-map.properties, [1+] optional bodyparts tga.gz } *if file -1025 is not there, read bodyparts from assets directory diff --git a/assets/mods/basegame/sprites/taimu_glow_old.properties b/assets/mods/basegame/sprites/taimu_glow_old.properties deleted file mode 100644 index fc450b9a4..000000000 --- a/assets/mods/basegame/sprites/taimu_glow_old.properties +++ /dev/null @@ -1,37 +0,0 @@ -# complete file name is: SPRITESHEET + bodypart name + EXTENSION -SPRITESHEET=mods/basegame/sprites/test_werebeast/taimuglow_ -EXTENSION=.tga -# defines frame size and origin point. Origin point is given as: (originx, size.y - 1) -# ORIGINY is deduced from the sprite size as shown in above; you only need to set ORIGINX -CONFIG=SIZE 64,98;ORIGINX 40 - -# note to self: don't implement skeleton hierarchy: there's too many exceptions -# besides, you have "ALL" key. - -! 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 -! 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. -! Joints' original point is defined in the document sprite_joints.psd. It also has visual representations. -# TODO right now accessory points are explicitly defined. Should they be injected in run-time? -SKELETON_STAND=HEADGEAR 0,32;\ - ARM_REST_LEFT -8,49;HELD_ITEM -6,11;\ - HEAD 2,83;\ - TORSO_0 0,55;\ - TORSO_1 0,55;\ - LEG_REST_RIGHT -4,22;\ - LEG_REST_LEFT 7,22;\ - ARM_REST_RIGHT 11,51;\ - TAIL_0 -11,27 - -# skeleton_stand is used for testing purpose -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_2=ALL 0,2;LEG_REST_RIGHT 0,-2;LEG_REST_LEFT 0,2;TAIL_0 -1,0 -ANIM_RUN_3=LEG_REST_RIGHT -2,0;LEG_REST_LEFT 2,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 - -ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND -ANIM_IDLE_1=TORSO_1 0,-999;HEAD 0,-1 -ANIM_IDLE_2=TORSO_0 0,-999;ARM_REST_LEFT 0,1;ARM_REST_RIGHT 0,1 diff --git a/assets/mods/basegame/sprites/taimu_old.properties b/assets/mods/basegame/sprites/taimu_old.properties deleted file mode 100644 index eacc02c06..000000000 --- a/assets/mods/basegame/sprites/taimu_old.properties +++ /dev/null @@ -1,37 +0,0 @@ -# complete file name is: SPRITESHEET + bodypart name + EXTENSION -SPRITESHEET=mods/basegame/sprites/test_werebeast/taimu_ -EXTENSION=.tga -# defines frame size and origin point. Origin point is given as: (originx, size.y - 1) -# ORIGINY is deduced from the sprite size as shown in above; you only need to set ORIGINX -CONFIG=SIZE 64,98;ORIGINX 40 - -# note to self: don't implement skeleton hierarchy: there's too many exceptions -# besides, you have "ALL" key. - -! 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 -! 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. -! Joints' original point is defined in the document sprite_joints.psd. It also has visual representations. -# TODO right now accessory points are explicitly defined. Should they be injected in run-time? -SKELETON_STAND=HEADGEAR 0,32;\ - ARM_REST_LEFT -8,49;HELD_ITEM -6,11;\ - HEAD 2,83;\ - TORSO_0 0,55;\ - TORSO_1 0,55;\ - LEG_REST_RIGHT -4,22;\ - LEG_REST_LEFT 7,22;\ - ARM_REST_RIGHT 11,51;\ - TAIL_0 -11,27 - -# skeleton_stand is used for testing purpose -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_2=ALL 0,2;LEG_REST_RIGHT 0,-2;LEG_REST_LEFT 0,2;TAIL_0 -1,0 -ANIM_RUN_3=LEG_REST_RIGHT -2,0;LEG_REST_LEFT 2,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 - -ANIM_IDLE=DELAY 2;ROW 1;SKELETON SKELETON_STAND -ANIM_IDLE_1=TORSO_1 0,-999;HEAD 0,-1 -ANIM_IDLE_2=TORSO_0 0,-999;ARM_REST_LEFT 0,1;ARM_REST_RIGHT 0,1 diff --git a/src/net/torvald/spriteassembler/AssembleSheetPixmap.kt b/src/net/torvald/spriteassembler/AssembleSheetPixmap.kt index 3e557edd7..b201a749a 100644 --- a/src/net/torvald/spriteassembler/AssembleSheetPixmap.kt +++ b/src/net/torvald/spriteassembler/AssembleSheetPixmap.kt @@ -3,6 +3,7 @@ package net.torvald.spriteassembler import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.utils.GdxRuntimeException +import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.linearSearch import net.torvald.terrarum.serialise.Common import net.torvald.terrarum.tvda.ByteArray64InputStream @@ -22,7 +23,7 @@ import kotlin.collections.HashMap */ object AssembleSheetPixmap { - private fun drawAndGetCanvas(properties: ADProperties, fileGetter: (String) -> InputStream): Pixmap { + private fun drawAndGetCanvas(properties: ADProperties, fileGetter: (String) -> InputStream?): Pixmap { val canvas = Pixmap(properties.cols * properties.frameWidth, properties.rows * properties.frameHeight, Pixmap.Format.RGBA8888) canvas.blending = Pixmap.Blending.SourceOver @@ -35,15 +36,21 @@ object AssembleSheetPixmap { } fun fromAssetsDir(properties: ADProperties) = drawAndGetCanvas(properties) { partName: String -> - Gdx.files.internal("assets/${properties.toFilename(partName)}").read() + val file = Gdx.files.internal("assets/${properties.toFilename(partName)}") + if (file.exists()) file.read() else null } - fun fromVirtualDisk(disk: SimpleFileSystem, properties: ADProperties): Pixmap { + fun fromVirtualDisk(disk: SimpleFileSystem, entrynum: Long, properties: ADProperties): Pixmap { val bodypartMapping = Properties() - bodypartMapping.load(ByteArray64Reader(disk.getFile(-1025L)!!.bytes, Common.CHARSET)) + bodypartMapping.load(ByteArray64Reader(disk.getFile(entrynum)!!.bytes, Common.CHARSET)) val fileGetter = { partName: String -> - ByteArray64InputStream(disk.getFile(bodypartMapping[partName] as Long)!!.bytes) + bodypartMapping.getProperty(partName).let { + if (it != null) + ByteArray64InputStream(disk.getFile(bodypartMapping.getProperty(partName).toLong())!!.bytes) + else + null + } } return drawAndGetCanvas(properties, fileGetter) @@ -52,19 +59,24 @@ object AssembleSheetPixmap { private fun drawThisFrame(frameName: String, canvas: Pixmap, properties: ADProperties, - fileGetter: (String) -> InputStream + fileGetter: (String) -> InputStream? ) { val theAnim = properties.getAnimByFrameName(frameName) val skeleton = theAnim.skeleton.joints.reversed() val transforms = properties.getTransform(frameName) val bodypartOrigins = properties.bodyparts - val bodypartImages = properties.bodyparts.keys.map { - try { - val bytes = fileGetter(it).readAllBytes() - it to Pixmap(bytes, 0, bytes.size) - } - catch (e: GdxRuntimeException) { - it to null + val bodypartImages = properties.bodyparts.keys.map { partname -> + fileGetter(partname).let { file -> + if (file == null) partname to null + else { + try { + val bytes = file.readAllBytes() + partname to Pixmap(bytes, 0, bytes.size) + } + catch (e: GdxRuntimeException) { + partname to null + } + } } }.toMap() val transformList = AssembleFrameBase.makeTransformList(skeleton, transforms) diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index 9a8f74848..4a2b84218 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -701,7 +701,7 @@ fun AppUpdateListOfSavegames() { e.printStackTrace() null } - }.filter { it != null }.sortedByDescending { it!!.diskFile.lastModified() } as List).forEach { + }.filter { it != null }.sortedByDescending { it!!.getLastModifiedOfFirstFile() } as List).forEach { println(it.diskFile.absolutePath) it.rebuild() // disk skimmer was created without initialisation, so do it now @@ -724,7 +724,7 @@ fun AppUpdateListOfSavegames() { e.printStackTrace() null } - }.filter { it != null }.sortedByDescending { it!!.diskFile.lastModified() } as List).forEach { + }.filter { it != null }.sortedByDescending { it!!.getLastModifiedOfFirstFile() } as List).forEach { println(it.diskFile.absolutePath) it.rebuild() // disk skimmer was created without initialisation, so do it now diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt index 9fb7f4440..09d1f7a97 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt @@ -87,9 +87,9 @@ class IngamePlayer : ActorHumanoid { fun reassembleSprite(disk: SimpleFileSystem, sprite: SpriteAnimation?, spriteGlow: SpriteAnimation? = null) { if (animDesc != null && sprite != null) - _rebuild(disk, animDesc!!, sprite) + _rebuild(disk, -1025L, animDesc!!, sprite) if (animDescGlow != null && spriteGlow != null) - _rebuild(disk, animDescGlow!!, spriteGlow) + _rebuild(disk, -1026L, animDescGlow!!, spriteGlow) } private fun _rebuild(ad: ADProperties, sprite: SpriteAnimation) { @@ -116,10 +116,10 @@ class IngamePlayer : ActorHumanoid { sprite.nRows = newAnimDelays.size } - private fun _rebuild(disk: SimpleFileSystem, ad: ADProperties, sprite: SpriteAnimation) { + private fun _rebuild(disk: SimpleFileSystem, entrynum: Long, ad: ADProperties, sprite: SpriteAnimation) { // TODO injecting held item/armour pictures? Would it be AssembleSheetPixmap's job? - val pixmap = if (disk.getEntry(-1025) != null) AssembleSheetPixmap.fromVirtualDisk(disk, ad) else AssembleSheetPixmap.fromAssetsDir(ad) + val pixmap = if (disk.getEntry(entrynum) != null) AssembleSheetPixmap.fromVirtualDisk(disk, entrynum, ad) else AssembleSheetPixmap.fromAssetsDir(ad) val texture = Texture(pixmap) texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) pixmap.dispose() diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt b/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt index 51c5a49ed..6c0a50347 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UILoadDemoSavefiles.kt @@ -222,8 +222,16 @@ class UILoadDemoSavefiles : UICanvas() { if (mode == 2) { loadFired += 1 // to hide the "flipped skybox" artefact + batch.end() + gdxClearAndSetBlend(.094f, .094f, .094f, 0f) + batch.begin() + + batch.color = Color.WHITE + val txt = Lang["MENU_IO_LOADING"] + App.fontGame.draw(batch, txt, (App.scr.width - App.fontGame.getWidth(txt)) / 2f, (App.scr.height - App.fontGame.lineHeight) / 2f) + if (loadFired == 2) { LoadSavegame(playerDisk!!, worldDisk) } diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt index 7e8aa66b3..f7d4bf5a1 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIProxyNewRandomGame.kt @@ -10,6 +10,7 @@ import net.torvald.terrarum.Terrarum import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.WorldgenLoadScreen import net.torvald.terrarum.modulebasegame.gameactors.PlayerBuilderTestSubject1 +import net.torvald.terrarum.modulebasegame.gameactors.PlayerBuilderWerebeastTest import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.utils.RandomWordsName @@ -39,7 +40,8 @@ class UIProxyNewRandomGame : UICanvas() { val ingame = TerrarumIngame(App.batch) val worldParam = TerrarumIngame.NewGameParams( - PlayerBuilderTestSubject1(), +// PlayerBuilderTestSubject1(), + PlayerBuilderWerebeastTest(), TerrarumIngame.NewWorldParameters(2880, 1350, HQRNG().nextLong(), RandomWordsName(4)) ) // val worldParam = TerrarumIngame.NewWorldParameters(2880, 1350, 0x51621D) diff --git a/src/net/torvald/terrarum/tvda/DiskSkimmer.kt b/src/net/torvald/terrarum/tvda/DiskSkimmer.kt index 46b8a3151..80f89e226 100644 --- a/src/net/torvald/terrarum/tvda/DiskSkimmer.kt +++ b/src/net/torvald/terrarum/tvda/DiskSkimmer.kt @@ -69,7 +69,7 @@ removefile: */ private var entryToOffsetTable = HashMap() - lateinit var fa: RandomAccessFile//RandomAccessFile(diskFile, "rw") + val fa: RandomAccessFile = RandomAccessFile(diskFile, "rw") private fun debugPrintln(s: Any) { if (false) println(s.toString()) @@ -87,7 +87,7 @@ removefile: fun rebuild() { checkFileSanity() // state of the file may have been changed (e.g. file deleted) so we check again - fa = RandomAccessFile(diskFile, "rw") +// fa = RandomAccessFile(diskFile, "rw") val fis = FileInputStream(diskFile) var currentPosition = fis.skip(VirtualDisk.HEADER_SIZE) // skip disk header @@ -353,6 +353,13 @@ removefile: return bytes.toCanonicalString(charset) } + fun getLastModifiedOfFirstFile(): Long { + val bytes = ByteArray(6) + fa.seek(326L) + fa.read(bytes) + return bytes.toInt48() + } + /////////////////////////////////////////////////////// // THESE ARE METHODS TO SUPPORT ON-LINE MODIFICATION // ///////////////////////////////////////////////////////