Files
Terrarum/src/net/torvald/terrarum/serialise/WriteActor.kt
2022-08-31 02:40:46 +09:00

185 lines
7.3 KiB
Kotlin

package net.torvald.terrarum.serialise
import net.torvald.spriteanimation.AssembledSpriteAnimation
import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.spriteassembler.ADProperties
import java.io.Reader
import java.util.*
/**
* Created by minjaesong on 2021-08-24.
*/
object WriteActor {
// genver must be found on fixed location of the JSON string
operator fun invoke(actor: Actor): String {
val s = Common.jsoner.toJson(actor, actor.javaClass)
return """{"genver":${Common.GENVER},"class":"${actor.javaClass.canonicalName}",${s.substring(1)}"""
}
fun encodeToByteArray64(actor: Actor): ByteArray64 {
val baw = ByteArray64Writer(Common.CHARSET)
val header = """{"genver":${Common.GENVER},"class":"${actor.javaClass.canonicalName}""""
baw.write(header)
Common.jsoner.toJson(actor, actor.javaClass, baw)
baw.flush(); baw.close()
// by this moment, contents of the baw will be:
// {"class":"some.class.Name"{"actorValue":{},......}
// (note that first bracket is not closed, and another open bracket after "class" property)
// and we want to turn it into this:
// {"class":"some.class.Name","actorValue":{},......}
val ba = baw.toByteArray64()
ba[header.toByteArray(Common.CHARSET).size.toLong()] = ','.code.toByte()
return ba
}
}
/**
* Player-specific [WriteActor]. Will write JSON and Animation Description Languages
*
* Created by minjaesong on 2021-10-07.
*/
object WritePlayer {
/**
* Will happily overwrite existing entry
*/
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
disk.entries[file.entryID] = file
file.parentEntryID = 0
val dir = VDUtil.getAsDirectory(disk, 0)
if (!dir.contains(file.entryID)) dir.add(file.entryID)
}
operator fun invoke(player: IngamePlayer, playerDisk: VirtualDisk, ingame: TerrarumIngame?, time_t: Long) {
player.lastPlayTime = time_t
player.totalPlayTime += time_t - (ingame?.loadedTime_t ?: time_t)
// restore player prop backup created on load-time for multiplayer
if (ingame?.isMultiplayer == true) {
player.setPosition(player.unauthorisedPlayerProps.physics.position)
player.actorValue = player.unauthorisedPlayerProps.actorValue!!
player.inventory = player.unauthorisedPlayerProps.inventory!!
}
player.worldCurrentlyPlaying = ingame?.world?.worldIndex ?: UUID(0L,0L)
// Write subset of Ingame.ItemCodex
// The existing ItemCodex must be rewritten to clear out obsolete records
player.dynamicToStaticTable.clear()
player.dynamicItemInventory.clear()
player.inventory.forEach { (itemid, _) ->
if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) {
player.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
player.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
}
}
val actorJson = WriteActor.encodeToByteArray64(player)
val adl = player.animDesc!!.getRawADL()
val adlGlow = player.animDescGlow?.getRawADL() // NULLABLE!
val jsonContents = EntryFile(actorJson)
val jsonCreationDate = playerDisk.getEntry(-1)?.creationDate ?: time_t
addFile(playerDisk, DiskEntry(-1L, 0L, jsonCreationDate, time_t, jsonContents))
val adlContents = EntryFile(ByteArray64.fromByteArray(adl.toByteArray(Common.CHARSET)))
val adlCreationDate = playerDisk.getEntry(-2)?.creationDate ?: time_t
addFile(playerDisk, DiskEntry(-2L, 0L, adlCreationDate, time_t, adlContents))
if (adlGlow != null) {
val adlGlowContents = EntryFile(ByteArray64.fromByteArray(adlGlow.toByteArray(Common.CHARSET)))
val adlGlowCreationDate = playerDisk.getEntry(-3)?.creationDate ?: time_t
addFile(playerDisk, DiskEntry(-3L, 0L, adlGlowCreationDate, time_t, adlGlowContents))
}
// write loadorder //
val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET)
loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n"))
loadOrderBa64Writer.flush(); loadOrderBa64Writer.close()
val loadOrderText = loadOrderBa64Writer.toByteArray64()
val loadOrderContents = EntryFile(loadOrderText)
addFile(playerDisk, DiskEntry(-4L, 0L, jsonCreationDate, time_t, loadOrderContents))
}
}
/**
* Player-specific [ReadActor].
*
* @param disk disk
* @param dataStream Reader containing JSON file
*
* Created by minjaesong on 2021-10-07.
*/
object ReadPlayer {
operator fun invoke(disk: SimpleFileSystem, dataStream: Reader): IngamePlayer =
ReadActor(disk, dataStream) as IngamePlayer
}
/**
* Actor's JSON representation is expected to have "class" property on the root object, such as:
* ```
* "class":"net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer"
* ```
*
* Created by minjaesong on 2021-08-27.
*/
object ReadActor {
operator fun invoke(disk: SimpleFileSystem, dataStream: Reader): Actor =
fillInDetails(disk, Common.jsoner.fromJson(null, dataStream))
private fun fillInDetails(disk: SimpleFileSystem, actor: Actor): Actor {
actor.reload()
if (actor is ActorWithBody && actor is IngamePlayer) {
val animFile = disk.getFile(-2L)
val animFileGlow = disk.getFile(-3L)
val bodypartsFile = disk.getFile(-1025)
actor.animDesc = ADProperties(ByteArray64Reader(animFile!!.bytes, Common.CHARSET))
actor.sprite = AssembledSpriteAnimation(actor.animDesc!!, actor, if (bodypartsFile != null) disk else null, if (bodypartsFile != null) -1025 else null)
if (animFileGlow != null) {
actor.animDescGlow = ADProperties(ByteArray64Reader(animFileGlow.bytes, Common.CHARSET))
actor.spriteGlow = AssembledSpriteAnimation(actor.animDescGlow!!, actor, if (bodypartsFile != null) disk else null, if (bodypartsFile != null) -1025 else null)
}
ItemCodex.loadFromSave(disk.getBackingFile(), actor.dynamicToStaticTable, actor.dynamicItemInventory)
// val heldItem = ItemCodex[actor.inventory.itemEquipped[GameItem.EquipPosition.HAND_GRIP]]
/*if (bodypartsFile != null)
actor.reassembleSpriteFromDisk(disk, actor.sprite!!, actor.spriteGlow, heldItem)
else
actor.reassembleSprite(actor.sprite!!, actor.spriteGlow, heldItem)*/
}
else if (actor is ActorWithBody && actor is HasAssembledSprite) {
if (actor.animDesc != null) actor.sprite = AssembledSpriteAnimation(actor.animDesc!!, actor)
if (actor.animDescGlow != null) actor.spriteGlow = AssembledSpriteAnimation(actor.animDescGlow!!, actor)
//actor.reassembleSprite(actor.sprite, actor.spriteGlow, null)
}
return actor
}
}