fixed a bug where a dynamic item would not get saved/loaded at all

This commit is contained in:
minjaesong
2022-02-22 17:12:49 +09:00
parent 1787ad7cdd
commit df6950c0b8
30 changed files with 206 additions and 89 deletions

View File

@@ -28,4 +28,5 @@ SpawnPhysTestBall
StreamerMode
Teleport
ToggleNoClip
Zoom
Zoom
DynToStatic
1 CatStdout
28 StreamerMode
29 Teleport
30 ToggleNoClip
31 Zoom
32 DynToStatic

View File

@@ -66,6 +66,7 @@ object Terrarum : Disposable {
var blockCodex = BlockCodex(); internal set
/** The actual contents of the ItemCodex is sum of Player's Codex and the World's Codex */
var itemCodex = ItemCodex(); internal set
var wireCodex = WireCodex(); internal set
var materialCodex = MaterialCodex(); internal set

View File

@@ -5,7 +5,6 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVEN_DEBUG_MODE
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellBase
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes.toItemCountText
@@ -97,23 +96,13 @@ class UIItemInventoryElemWide(
)
// draw name of the item
if (INVEN_DEBUG_MODE) {
App.fontGame.draw(batch,
// print static id, dynamic id, and count
"${item!!.originalID}/${item!!.dynamicID}" + (if (amount > 0 && item!!.stackable) "$fwsp($amountString)" else if (amount != 1L) "$fwsp!!$amountString!!" else ""),
posX + textOffsetX,
posY + textOffsetY
)
}
else {
App.fontGame.draw(batch,
// print name and amount in parens
item!!.name + (if (amount > 0 && item!!.stackable) "$fwsp($amountString)" else if (amount != 1L) "$fwsp!!$amountString!!" else ""),
App.fontGame.draw(batch,
// print name and amount in parens
item!!.name + (if (amount > 0 && item!!.stackable) "$fwsp($amountString)" else if (amount != 1L) "$fwsp!!$amountString!!" else ""),
posX + textOffsetX,
posY + textOffsetY
)
}
posX + textOffsetX,
posY + textOffsetY
)
// durability metre

View File

@@ -24,6 +24,8 @@ typealias ItemID = String
*/
abstract class GameItem(val originalID: ItemID) : Comparable<GameItem>, Cloneable {
constructor() : this("-uninitialised-")
open var dynamicID: ItemID = originalID
/**
* if the ID is a Actor range, it's an actor contained in a pocket.

View File

@@ -10,6 +10,8 @@ import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameactors.WireActor
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.itemproperties.ItemRemapTable
import net.torvald.terrarum.itemproperties.ItemTable
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.utils.*
@@ -128,6 +130,9 @@ open class GameWorld() : Disposable {
internal var genver = -1 // only gets used when the game saves and loads
internal var comp = -1 // only gets used when the game saves and loads
internal val dynamicItemInventory = ItemTable()
internal val dynamicToStaticTable = ItemRemapTable()
@Deprecated("This value is only used for savegames; DO NOT USE THIS", ReplaceWith("INGAME.actorContainerActive", "net.torvald.terrarum.INGAME"))
internal val actors = ArrayList<ActorID>() // only filled up on save and load; DO NOT USE THIS

View File

@@ -13,7 +13,13 @@ import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.CanBeAnItem
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
import net.torvald.terrarum.serialise.SaveLoadError
import net.torvald.terrarum.worlddrawer.BlocksDrawer
import java.io.File
import java.io.InvalidObjectException
typealias ItemRemapTable = java.util.HashMap<ItemID, ItemID>
typealias ItemTable = java.util.HashMap<ItemID, GameItem>
/**
* ItemCodex holds information of every item in the game, including blocks despite the 'item' naming
@@ -26,9 +32,9 @@ class ItemCodex {
* <ItemID or RefID for Actor, TheItem>
* Will return corresponding Actor if ID >= ACTORID_MIN
*/
@Transient val itemCodex = HashMap<ItemID, GameItem>()
@Transient var dynamicItemDescription = HashMap<ItemID, GameItem>(); private set
var dynamicToStaticTable = HashMap<ItemID, ItemID>(); private set
@Transient val itemCodex = ItemTable()
val dynamicItemInventory = ItemTable()
val dynamicToStaticTable = ItemRemapTable()
@Transient val ACTORID_MIN = ReferencingRanges.ACTORS.first
@@ -41,18 +47,33 @@ class ItemCodex {
fun clear() {
itemCodex.clear()
dynamicItemDescription.clear()
dynamicItemInventory.clear()
dynamicToStaticTable.clear()
}
/**
* This method does not alter already-been-loaded itemCodex; only filles up dynamicitem-related fields
*
* Normally, the player's dynamicToStaticTable is what gets loaded first.
*/
fun loadFromSave(other: ItemCodex) {
this.dynamicToStaticTable = other.dynamicToStaticTable
dynamicToStaticTable.forEach { dynid, itemid ->
fun loadFromSave(savefile: File?, otherDynamicToStaticTable: ItemRemapTable, otherDynamicItemInventory: ItemTable) {
otherDynamicToStaticTable.forEach { dynid, itemid ->
printdbg(this, "Loadfromsave dynid $dynid ->> $itemid")
dynamicItemDescription[dynid] = itemCodex[itemid]!!
dynamicToStaticTable[dynid]?.let {
if (it != itemid) {
throw SaveLoadError(savefile, InvalidObjectException("Discrepancy detected -- currently loaded $dynid is mapped to $it, but ${savefile?.name} indicates it should map to $itemid"))
}
}
dynamicToStaticTable[dynid] = itemid
}
otherDynamicItemInventory.forEach { dynid, item ->
printdbg(this, "Loadfromsave dynitem $dynid ->> $item")
dynamicItemInventory[dynid]?.let {
if (it != item) {
throw SaveLoadError(savefile, InvalidObjectException("Discrepancy detected -- currently loaded $dynid is mapped to $it, but ${savefile?.name} indicates it should map to $item"))
}
}
dynamicItemInventory[dynid] = item
}
}
@@ -64,7 +85,7 @@ class ItemCodex {
*/
fun registerNewDynamicItem(dynamicID: ItemID, item: GameItem) {
printdbg(this, "Registering new dynamic item $dynamicID (from ${item.originalID})")
dynamicItemDescription[dynamicID] = item
dynamicItemInventory[dynamicID] = item
dynamicToStaticTable[dynamicID] = item.originalID
}
@@ -76,7 +97,7 @@ class ItemCodex {
if (code == null) return null
if (code.startsWith("$PREFIX_DYNAMICITEM:"))
return dynamicItemDescription[code] ?: throw NullPointerException("No ItemProp with id $code")
return dynamicItemInventory[code] ?: throw NullPointerException("No ItemProp with id $code")
else if (code.startsWith("$PREFIX_ACTORITEM:")) {
val a = (Terrarum.ingame!! as TerrarumIngame).getActorByID(code.substring(6).toInt()) // actor item
if (a is CanBeAnItem) return a.itemData
@@ -142,5 +163,5 @@ class ItemCodex {
}
fun hasItem(itemID: ItemID): Boolean = dynamicItemDescription.containsKey(itemID)
fun hasItem(itemID: ItemID): Boolean = dynamicItemInventory.containsKey(itemID)
}

View File

@@ -133,9 +133,10 @@ class TitleScreen(batch: SpriteBatch) : IngameInstance(batch) {
try {
val reader = java.io.FileReader(ModMgr.getFile("basegame", "demoworld"))
val file = ModMgr.getFile("basegame", "demoworld")
val reader = java.io.FileReader(file)
//ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader)
val world = ReadWorld.readLayerFormat(reader)
val world = ReadWorld.readLayerFormat(reader, file)
demoWorld = world
printdbg(this, "Demo world loaded")
}

View File

@@ -0,0 +1,20 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.*
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.serialise.Common
/**
* Created by minjaesong on 2022-02-22.
*/
object DynToStatic : ConsoleCommand {
override fun execute(args: Array<String>) {
ItemCodex.dynamicToStaticTable.forEach { (d,s) ->
Echo("$ccG$d$ccW$ccY$s")
}
}
override fun printUsage() {
}
}

View File

@@ -7,6 +7,7 @@ import net.torvald.terrarum.console.Echo
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.serialise.ReadActor
import net.torvald.terrarum.serialise.ReadWorld
import java.io.File
import java.io.IOException
/**
@@ -16,8 +17,9 @@ object ImportWorld : ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 2) {
try {
val reader = java.io.FileReader(App.defaultDir + "/Exports/${args[1]}.json")
ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader)
val file = File(App.defaultDir + "/Exports/${args[1]}.json")
val reader = java.io.FileReader(file)
ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader, file)
Echo("Importworld: imported a world from ${args[1]}.json")
}
catch (e: IOException) {

View File

@@ -28,6 +28,8 @@ class ActorInventory() : FixtureInventory() {
/**
* List of all equipped items (tools, armours, rings, necklaces, etc.)
*
* It's your responsibility to make sure currently equipped item also exists in the `super.itemList`
*
* The ItemID must be `dynamicID`
*/
val itemEquipped = Array<ItemID?>(GameItem.EquipPosition.INDEX_MAX) { null }
@@ -62,7 +64,7 @@ class ActorInventory() : FixtureInventory() {
}
}
fun getQuickslotItem(slot: Int): InventoryPair? = invSearchByDynamicID(quickSlot[slot])
fun getQuickslotItem(slot: Int): InventoryPair? = searchByID(quickSlot[slot])
fun consumeItem(item: GameItem) {
val actor = this.actor as Actor

View File

@@ -4,10 +4,9 @@ import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.lock
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.util.SortedArrayList
import java.math.BigInteger
import java.util.concurrent.locks.ReentrantLock
/**
* Created by minjaesong on 2021-03-16.
@@ -32,7 +31,7 @@ open class FixtureInventory() {
/**
* Sorted by referenceID.
*/
val itemList = ArrayList<InventoryPair>()
val itemList = SortedArrayList<InventoryPair>()
var wallet = BigInteger("0") // unified currency for whole civs; Dwarf Fortress approach seems too complicated
fun isEmpty() = getTotalCount() == 0L
@@ -66,7 +65,7 @@ open class FixtureInventory() {
// If we already have the item, increment the amount
// If not, add item with specified amount
val existingItem = invSearchByDynamicID(item.dynamicID)
val existingItem = searchByID(item.dynamicID)
// if the item already exists
if (existingItem != null) {
@@ -80,7 +79,7 @@ open class FixtureInventory() {
else {
itemList.add(InventoryPair(item.dynamicID, count))
}
insertionSortLastElem(itemList)
// insertionSortLastElem(itemList)
}
open fun remove(itemID: ItemID, count: Long) = remove(ItemCodex[itemID]!!, count) {}
@@ -102,7 +101,7 @@ open class FixtureInventory() {
val existingItem = invSearchByDynamicID(item.dynamicID)
val existingItem = searchByID(item.dynamicID)
if (existingItem != null) { // if the item already exists
val newCount = existingItem.qty - count
@@ -171,28 +170,14 @@ open class FixtureInventory() {
if (itemList.size == 0)
false
else
itemList.binarySearch(id, DYNAMIC_ID) >= 0
fun invSearchByDynamicID(id: ItemID?): InventoryPair? {
itemList.contains(InventoryPair(id, 1))
fun searchByID(id: ItemID?): InventoryPair? {
if (itemList.size == 0 || id == null)
return null
val index = itemList.binarySearch(id, DYNAMIC_ID)
if (index < 0)
return null
else
return itemList[index]
return itemList.searchFor(id) { it.itm }
}
protected fun invSearchByStaticID(id: ItemID?): InventoryPair? {
if (itemList.size == 0 || id == null)
return null
val index = itemList.binarySearch(id, STATIC_ID)
if (index < 0)
return null
else
return itemList[index]
}
protected fun insertionSortLastElem(arr: ArrayList<InventoryPair>) {
/*protected fun insertionSortLastElem(arr: ArrayList<InventoryPair>) {
ReentrantLock().lock {
var j = arr.lastIndex - 1
val x = arr.last()
@@ -202,7 +187,7 @@ open class FixtureInventory() {
}
arr[j + 1] = x
}
}
}*/
@Transient private val STATIC_ID = 41324534
@Transient private val DYNAMIC_ID = 181643953
protected fun ArrayList<InventoryPair>.binarySearch(ID: ItemID, searchMode: Int): Int {
@@ -229,7 +214,7 @@ open class FixtureInventory() {
}
}
class InventoryPair {
class InventoryPair : Comparable<InventoryPair> {
var itm: ItemID = ""; private set
var qty: Long = 0
@@ -244,4 +229,5 @@ class InventoryPair {
operator fun component1() = itm
operator fun component2() = qty
override fun compareTo(other: InventoryPair) = this.itm.compareTo(other.itm)
}

View File

@@ -11,6 +11,9 @@ import net.torvald.terrarum.App
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.itemproperties.ItemRemapTable
import net.torvald.terrarum.itemproperties.ItemTable
import net.torvald.terrarum.savegame.DiskSkimmer
import net.torvald.terrarum.savegame.SimpleFileSystem
import net.torvald.terrarum.utils.PlayerLastStatus
@@ -33,6 +36,9 @@ class IngamePlayer : ActorHumanoid, HasAssembledSprite {
val uuid = UUID.randomUUID()
var worldCurrentlyPlaying: UUID = UUID(0L,0L) // only filled up on save and load; DO NOT USE THIS
internal val dynamicItemInventory = ItemTable()
internal val dynamicToStaticTable = ItemRemapTable()
@Transient override var spriteHeadTexture: TextureRegion? = null

View File

@@ -111,6 +111,7 @@ object PickaxeCore {
* Created by minjaesong on 2017-07-17.
*/
class PickaxeCopper(originalID: ItemID) : GameItem(originalID) {
internal constructor() : this("-uninitialised-")
override val originalName = "PACKAGED_PICK"
override var baseToolSize: Double? = BASE_MASS_AND_SIZE
@@ -138,6 +139,7 @@ class PickaxeCopper(originalID: ItemID) : GameItem(originalID) {
* Created by minjaesong on 2019-03-10.
*/
class PickaxeIron(originalID: ItemID) : GameItem(originalID) {
internal constructor() : this("-uninitialised-")
override val originalName = "PACKAGED_PICK"
override var baseToolSize: Double? = BASE_MASS_AND_SIZE
@@ -165,6 +167,7 @@ class PickaxeIron(originalID: ItemID) : GameItem(originalID) {
* Created by minjaesong on 2019-03-10.
*/
class PickaxeSteel(originalID: ItemID) : GameItem(originalID) {
internal constructor() : this("-uninitialised-")
override val originalName = "PACKAGED_PICK"
override var baseToolSize: Double? = BASE_MASS_AND_SIZE

View File

@@ -26,5 +26,8 @@ object WeaponMeleeCore {
}
abstract class WeaponMeleeBase(originalID: ItemID) : GameItem(originalID) {
internal constructor() : this("-uninitialised-")
abstract val velocityMod: Double
}

View File

@@ -19,7 +19,7 @@ object AmmoMeterProxy {
else {
meter.vitalGetterVal = {
if (currentItem.stackable && currentItem.maxDurability == GameItem.DURABILITY_NA) {
actor.inventory.invSearchByDynamicID(currentItem.dynamicID)!!.qty.toFloat()
actor.inventory.searchByID(currentItem.dynamicID)!!.qty.toFloat()
}
else
currentItem.durability

View File

@@ -10,9 +10,7 @@ import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.CELLS_HO
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.CELLS_VRT
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_X
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVEN_DEBUG_MODE
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.controlHelpHeight
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.internalHeight
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.internalWidth
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.createInvCellGenericKeyDownFun
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.createInvCellGenericTouchDownFun
@@ -136,7 +134,7 @@ internal class UIInventoryCells(
)
// debug text
batch.color = Color.LIGHT_GRAY
if (INVEN_DEBUG_MODE) {
if (App.IS_DEVELOPMENT_BUILD) {
App.fontSmallNumbers.draw(batch,
"${full.actor.inventory.capacity}/${full.actor.inventory.maxCapacity}",
encumbBarTextXPos,

View File

@@ -38,8 +38,6 @@ class UIInventoryFull(
val CELL_COL = Toolkit.Theme.COL_CELL_FILL
const val INVEN_DEBUG_MODE = false
const val REQUIRED_MARGIN: Int = 138 // hard-coded value. Don't know the details. Range: [91-146]. I chose MAX-8 because cell gap is 8
const val CELLS_HOR = 10
val CELLS_VRT: Int; get() = (App.scr.height - REQUIRED_MARGIN - 134 + UIItemInventoryItemGrid.listGap) / // 134 is another magic number

View File

@@ -125,7 +125,7 @@ class UIItemInventoryEquippedView(
itemGrid[k].equippedSlot = null
}
else {
val itemRecord = it.invSearchByDynamicID(item)!!
val itemRecord = it.searchByID(item)!!
itemGrid[k].item = ItemCodex[item]
itemGrid[k].amount = itemRecord.qty

View File

@@ -13,7 +13,6 @@ import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
import net.torvald.terrarum.modulebasegame.gameactors.InventoryPair
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVEN_DEBUG_MODE
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItem
@@ -162,6 +161,9 @@ class UIItemInventoryItemGrid(
listRebuildFun()
}
}
// COMMON variables because more than one instance of this can be up on the screen
private val tooltipShowing = HashMap<UIItemInventoryItemGrid, Boolean>()
}
private val itemGrid = Array<UIItemInventoryCellBase>(horizontalCells * verticalCells) {
@@ -347,8 +349,8 @@ class UIItemInventoryItemGrid(
override fun update(delta: Float) {
super.update(delta)
var tooltipSet = false
tooltipShowing[this] = false
items.forEach {
@@ -356,20 +358,21 @@ class UIItemInventoryItemGrid(
// set tooltip accordingly
if (isCompactMode && it.item != null && it.mouseUp && !tooltipSet) {
if ((App.IS_DEVELOPMENT_BUILD || isCompactMode) && it.item != null && it.mouseUp && tooltipShowing[this] != true) {
INGAME.setTooltipMessage(
if (INVEN_DEBUG_MODE) {
it.item?.name + " (${it.item?.originalID}${if (it.item?.originalID == it.item?.dynamicID) "" else "/${it.item?.dynamicID}"})"
if (App.IS_DEVELOPMENT_BUILD) {
it.item?.name + "\n(${it.item?.originalID}${if (it.item?.originalID == it.item?.dynamicID) "" else "/${it.item?.dynamicID}"})"
}
else {
it.item?.name
}
)
tooltipSet = true
tooltipShowing[this] = true
}
}
if (!tooltipSet) {
if (tooltipShowing.values.all { !it }) {
INGAME.setTooltipMessage(null)
}
@@ -461,7 +464,11 @@ class UIItemInventoryItemGrid(
}
override fun dispose() {
tooltipShowing.remove(this)
}
override fun hide() {
tooltipShowing.remove(this)
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {

View File

@@ -23,6 +23,8 @@ class DiskSkimmer(
noInit: Boolean = false
): SimpleFileSystem {
override fun getBackingFile() = diskFile
/*
init:

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.savegame
import java.io.File
import java.nio.charset.Charset
/**
@@ -9,4 +10,5 @@ interface SimpleFileSystem {
fun getEntry(id: EntryID): DiskEntry?
fun getFile(id: EntryID): EntryFile?
fun getDiskName(charset: Charset): String
fun getBackingFile(): File?
}

View File

@@ -63,7 +63,7 @@ object VDUtil {
if (diskSpecVersion != specversion)
throw RuntimeException("Unsupported disk format version: current internal version is $specversion; the file's version is $diskSpecVersion")
val vdisk = VirtualDisk(diskSize, diskName)
val vdisk = VirtualDisk(diskSize, diskName, infile)
vdisk.__internalSetFooter__(footers)

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.savegame
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.serialise.Common
import java.io.File
import java.io.IOException
import java.nio.charset.Charset
import java.util.*
@@ -125,8 +126,12 @@ val specversion = 254.toByte()
class VirtualDisk(
/** capacity of 0 makes the disk read-only */
var capacity: Long,
var diskName: ByteArray = ByteArray(NAME_LENGTH)
var diskName: ByteArray = ByteArray(NAME_LENGTH),
var origin: File? = null
): SimpleFileSystem {
override fun getBackingFile() = origin
var extraInfoBytes = ByteArray(16)
val entries = HashMap<EntryID, DiskEntry>()
var isReadOnly: Boolean

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.savegame.finder
import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.serialise.Common
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.event.KeyEvent
@@ -676,5 +677,5 @@ ${String(file.contents.bytes.sliceArray64(0L..minOf(PREVIEW_MAX_BYTES, file.cont
}
fun main(args: Array<String>) {
VirtualDiskCracker(Charset.forName("CP437"))
VirtualDiskCracker(Common.CHARSET)
}

View File

@@ -329,4 +329,4 @@ object Common {
}
class SaveLoadError(file: File, cause: Throwable) : RuntimeException("An error occured while loading save file '${file.absolutePath}'", cause)
class SaveLoadError(file: File?, cause: Throwable) : RuntimeException("An error occured while loading save file '${file?.absolutePath}'", cause)

View File

@@ -2,13 +2,20 @@ package net.torvald.terrarum.serialise
import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.itemproperties.ItemRemapTable
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.toInt
import net.torvald.terrarum.savegame.*
import java.io.File
import java.util.HashMap
import java.util.zip.GZIPOutputStream
/**
@@ -61,14 +68,14 @@ class WorldSavingThread(
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
val playersList: List<IngamePlayer> = allTheActors.filter{ it is IngamePlayer } as List<IngamePlayer>
val playersList: List<IngamePlayer> = allTheActors.filterIsInstance<IngamePlayer>()
val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) }
val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) }
val cw = ingame.world.width / LandUtil.CHUNK_W
val ch = ingame.world.height / LandUtil.CHUNK_H
WriteSavegame.saveProgress = 0f
WriteSavegame.saveProgressMax = 2f + (cw * ch * layers.size) + actorsList.size
WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size
val tgaout = ByteArray64GrowableOutputStream()
@@ -79,6 +86,39 @@ class WorldSavingThread(
val creation_t = ingame.world.creationTime
// Write subset of Ingame.ItemCodex
// The existing ItemCodex must be rewritten to clear out obsolete records
// We're assuming the dynamic item generated by players does exist in the world, and it's recorded
// into the world's dynamicToStaticTable, therefore every item recorded into the world's dynamicToStaticTable
// can be found in this world without need to look up the players
ingame.world.dynamicToStaticTable.clear()
ingame.world.dynamicItemInventory.clear()
actorsList.filterIsInstance<Pocketed>().forEach { actor ->
actor.inventory.forEach { (itemid, _) ->
printdbg(this, "World side dynamicitem: $itemid contained in $actor")
if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) {
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
}
}
}
actorsList.filterIsInstance<FixtureBase>().forEach { fixture ->
fixture.inventory?.forEach { (itemid, _) ->
printdbg(this, "World side dynamicitem: $itemid contained in $fixture")
if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) {
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
}
}
}
if (hasThumbnail) {
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
IngameRenderer.fboRGBexport.dispose()

View File

@@ -4,9 +4,12 @@ import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.terrarum.spriteassembler.ADProperties
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.itemproperties.ItemRemapTable
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.savegame.*
@@ -74,6 +77,17 @@ object WritePlayer {
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()
@@ -142,6 +156,8 @@ object ReadActor {
actor.animDescGlow = ADProperties(ByteArray64Reader(animFileGlow.bytes, Common.CHARSET))
}
ItemCodex.loadFromSave(disk.getBackingFile(), actor.dynamicToStaticTable, actor.dynamicItemInventory)
val heldItem = ItemCodex[actor.inventory.itemEquipped[GameItem.EquipPosition.HAND_GRIP]]
if (bodypartsFile != null)

View File

@@ -125,7 +125,7 @@ object LoadSavegame {
val currentWorldId = player.worldCurrentlyPlaying
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!
val world = ReadWorld(ByteArray64Reader(worldDisk.getFile(-1L)!!.bytes, Common.CHARSET))
val world = ReadWorld(ByteArray64Reader(worldDisk.getFile(-1L)!!.bytes, Common.CHARSET), worldDisk.diskFile)
world.layerTerrain = BlockLayer(world.width, world.height)
world.layerWall = BlockLayer(world.width, world.height)

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.serialise
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.ReferencingRanges
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.BlockMarkerActor
@@ -15,6 +16,7 @@ import net.torvald.terrarum.savegame.ByteArray64
import net.torvald.terrarum.savegame.ByteArray64Writer
import net.torvald.terrarum.utils.PlayerLastStatus
import net.torvald.terrarum.weather.WeatherMixer
import java.io.File
import java.io.Reader
/**
@@ -90,22 +92,24 @@ object WriteWorld {
*/
object ReadWorld {
fun readLayerFormat(worldDataStream: Reader): GameWorld =
fillInDetails(Common.jsoner.fromJson(GameWorldTitleScreen::class.java, worldDataStream))
fun readLayerFormat(worldDataStream: Reader, origin: File?): GameWorld =
fillInDetails(Common.jsoner.fromJson(GameWorldTitleScreen::class.java, worldDataStream), origin)
operator fun invoke(worldDataStream: Reader): GameWorld =
fillInDetails(Common.jsoner.fromJson(GameWorld::class.java, worldDataStream))
operator fun invoke(worldDataStream: Reader, origin: File?): GameWorld =
fillInDetails(Common.jsoner.fromJson(GameWorld::class.java, worldDataStream), origin)
private fun fillInDetails(world: GameWorld): GameWorld {
private fun fillInDetails(world: GameWorld, origin: File?): GameWorld {
world.tileNumberToNameMap.forEach { l, s ->
world.tileNameToNumberMap[s] = l.toInt()
}
ItemCodex.loadFromSave(origin, world.dynamicToStaticTable, world.dynamicItemInventory)
return world
}
fun readWorldAndSetNewWorld(ingame: TerrarumIngame, worldDataStream: Reader): GameWorld {
val world = readLayerFormat(worldDataStream)
fun readWorldAndSetNewWorld(ingame: TerrarumIngame, worldDataStream: Reader, origin: File?): GameWorld {
val world = readLayerFormat(worldDataStream, origin)
ingame.world = world
return world
}

View File

@@ -142,11 +142,13 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) : MutableCollecti
val second = Math.min(low, this.size - 1)
return Pair(first, second)
}
/** Searches the element using given predicate instead of the element itself. Returns the element desired, null when there is no such element.
/** Searches the element using given predicate instead of the element itself
* (e.g. search the Actor by its ID rather than the actor instance)
*
* @param searchQuery what exactly are we looking for?
* @param searchHow and where or how can it be found?
*
* @return The element you're searching; null when there is no such element.
*/
fun <R: Comparable<R>> searchFor(searchQuery: R, searchHow: (T) -> R = { it as R }): T? = getOrNull(searchForIndex(searchQuery, searchHow))