From df6950c0b85881740ca366ebc9f124cbd23b60dd Mon Sep 17 00:00:00 2001 From: minjaesong Date: Tue, 22 Feb 2022 17:12:49 +0900 Subject: [PATCH] fixed a bug where a dynamic item would not get saved/loaded at all --- assets/mods/basegame/commands.csv | 3 +- src/net/torvald/terrarum/Terrarum.kt | 1 + .../terrarum/UIItemInventoryElemWide.kt | 23 +++------- .../torvald/terrarum/gameitems/GameItem.kt | 2 + .../torvald/terrarum/gameworld/GameWorld.kt | 5 +++ .../terrarum/itemproperties/ItemCodex.kt | 43 +++++++++++++----- .../terrarum/modulebasegame/TitleScreen.kt | 5 ++- .../modulebasegame/console/DynToStatic.kt | 20 +++++++++ .../modulebasegame/console/ImportWorld.kt | 6 ++- .../gameactors/ActorInventory.kt | 4 +- .../gameactors/FixtureInventory.kt | 38 +++++----------- .../modulebasegame/gameactors/IngamePlayer.kt | 6 +++ .../gameitems/PickaxeGeneric.kt | 3 ++ .../gameitems/WeaponMeleeCore.kt | 3 ++ .../modulebasegame/ui/AmmoMeterProxy.kt | 2 +- .../modulebasegame/ui/UIInventoryCells.kt | 4 +- .../modulebasegame/ui/UIInventoryFull.kt | 2 - .../ui/UIItemInventoryEquippedView.kt | 2 +- .../ui/UIItemInventoryItemGrid.kt | 21 ++++++--- .../torvald/terrarum/savegame/DiskSkimmer.kt | 2 + .../terrarum/savegame/SimpleFileSystem.kt | 2 + src/net/torvald/terrarum/savegame/VDUtil.kt | 2 +- .../torvald/terrarum/savegame/VirtualDisk.kt | 7 ++- .../savegame/finder/VirtualDiskCracker.kt | 3 +- src/net/torvald/terrarum/serialise/Common.kt | 2 +- .../terrarum/serialise/GameSavingThread.kt | 44 ++++++++++++++++++- .../torvald/terrarum/serialise/WriteActor.kt | 16 +++++++ .../terrarum/serialise/WriteSavegame.kt | 2 +- .../torvald/terrarum/serialise/WriteWorld.kt | 18 +++++--- src/net/torvald/util/SortedArrayList.kt | 4 +- 30 files changed, 206 insertions(+), 89 deletions(-) create mode 100644 src/net/torvald/terrarum/modulebasegame/console/DynToStatic.kt diff --git a/assets/mods/basegame/commands.csv b/assets/mods/basegame/commands.csv index bc0a3a4f2..7edb84b5b 100644 --- a/assets/mods/basegame/commands.csv +++ b/assets/mods/basegame/commands.csv @@ -28,4 +28,5 @@ SpawnPhysTestBall StreamerMode Teleport ToggleNoClip -Zoom \ No newline at end of file +Zoom +DynToStatic \ No newline at end of file diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index 7d4196b13..751fc51cd 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -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 diff --git a/src/net/torvald/terrarum/UIItemInventoryElemWide.kt b/src/net/torvald/terrarum/UIItemInventoryElemWide.kt index cfe2ae9ef..ce436bb4d 100644 --- a/src/net/torvald/terrarum/UIItemInventoryElemWide.kt +++ b/src/net/torvald/terrarum/UIItemInventoryElemWide.kt @@ -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 diff --git a/src/net/torvald/terrarum/gameitems/GameItem.kt b/src/net/torvald/terrarum/gameitems/GameItem.kt index 7ca48fa1c..8063f7ccc 100644 --- a/src/net/torvald/terrarum/gameitems/GameItem.kt +++ b/src/net/torvald/terrarum/gameitems/GameItem.kt @@ -24,6 +24,8 @@ typealias ItemID = String */ abstract class GameItem(val originalID: ItemID) : Comparable, Cloneable { + constructor() : this("-uninitialised-") + open var dynamicID: ItemID = originalID /** * if the ID is a Actor range, it's an actor contained in a pocket. diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index d1970af4a..60a4a9ca8 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -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() // only filled up on save and load; DO NOT USE THIS diff --git a/src/net/torvald/terrarum/itemproperties/ItemCodex.kt b/src/net/torvald/terrarum/itemproperties/ItemCodex.kt index 5f25a4733..2c0399540 100644 --- a/src/net/torvald/terrarum/itemproperties/ItemCodex.kt +++ b/src/net/torvald/terrarum/itemproperties/ItemCodex.kt @@ -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 +typealias ItemTable = java.util.HashMap /** * ItemCodex holds information of every item in the game, including blocks despite the 'item' naming @@ -26,9 +32,9 @@ class ItemCodex { * * Will return corresponding Actor if ID >= ACTORID_MIN */ - @Transient val itemCodex = HashMap() - @Transient var dynamicItemDescription = HashMap(); private set - var dynamicToStaticTable = HashMap(); 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) } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt index f667e1bd5..1d8e7c1e0 100644 --- a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt @@ -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") } diff --git a/src/net/torvald/terrarum/modulebasegame/console/DynToStatic.kt b/src/net/torvald/terrarum/modulebasegame/console/DynToStatic.kt new file mode 100644 index 000000000..4b8796bb0 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/console/DynToStatic.kt @@ -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) { + ItemCodex.dynamicToStaticTable.forEach { (d,s) -> + Echo("$ccG$d$ccW → $ccY$s") + } + } + + override fun printUsage() { + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt b/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt index 448a2be80..ff386cd85 100644 --- a/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt +++ b/src/net/torvald/terrarum/modulebasegame/console/ImportWorld.kt @@ -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) { 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) { diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt index 602328ca2..20be163d5 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt @@ -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(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 diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureInventory.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureInventory.kt index 3c875f9ab..08e6d58e5 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureInventory.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureInventory.kt @@ -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() + val itemList = SortedArrayList() 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) { + /*protected fun insertionSortLastElem(arr: ArrayList) { 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.binarySearch(ID: ItemID, searchMode: Int): Int { @@ -229,7 +214,7 @@ open class FixtureInventory() { } } -class InventoryPair { +class InventoryPair : Comparable { 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) } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt index 1ec7f568b..ab42ef6fc 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/IngamePlayer.kt @@ -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 diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/PickaxeGeneric.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/PickaxeGeneric.kt index 9072fad34..d9c3f533d 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/PickaxeGeneric.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/PickaxeGeneric.kt @@ -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 diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/WeaponMeleeCore.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/WeaponMeleeCore.kt index 8357b1307..bfe078604 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/WeaponMeleeCore.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/WeaponMeleeCore.kt @@ -26,5 +26,8 @@ object WeaponMeleeCore { } abstract class WeaponMeleeBase(originalID: ItemID) : GameItem(originalID) { + + internal constructor() : this("-uninitialised-") + abstract val velocityMod: Double } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/ui/AmmoMeterProxy.kt b/src/net/torvald/terrarum/modulebasegame/ui/AmmoMeterProxy.kt index 327b891fe..ff2b7122f 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/AmmoMeterProxy.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/AmmoMeterProxy.kt @@ -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 diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryCells.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryCells.kt index 8ca988315..278c3203f 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryCells.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryCells.kt @@ -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, diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt index d57209348..de0ac2002 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIInventoryFull.kt @@ -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 diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryEquippedView.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryEquippedView.kt index f4a7e7d05..31a43b93a 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryEquippedView.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryEquippedView.kt @@ -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 diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryItemGrid.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryItemGrid.kt index 91ce294d9..da3d4dfd1 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryItemGrid.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIItemInventoryItemGrid.kt @@ -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() } private val itemGrid = Array(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 { diff --git a/src/net/torvald/terrarum/savegame/DiskSkimmer.kt b/src/net/torvald/terrarum/savegame/DiskSkimmer.kt index d4ceaab11..37e5257f7 100644 --- a/src/net/torvald/terrarum/savegame/DiskSkimmer.kt +++ b/src/net/torvald/terrarum/savegame/DiskSkimmer.kt @@ -23,6 +23,8 @@ class DiskSkimmer( noInit: Boolean = false ): SimpleFileSystem { + override fun getBackingFile() = diskFile + /* init: diff --git a/src/net/torvald/terrarum/savegame/SimpleFileSystem.kt b/src/net/torvald/terrarum/savegame/SimpleFileSystem.kt index 7cab86a61..43717cd3c 100644 --- a/src/net/torvald/terrarum/savegame/SimpleFileSystem.kt +++ b/src/net/torvald/terrarum/savegame/SimpleFileSystem.kt @@ -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? } \ No newline at end of file diff --git a/src/net/torvald/terrarum/savegame/VDUtil.kt b/src/net/torvald/terrarum/savegame/VDUtil.kt index 677b9649c..ad44c3709 100644 --- a/src/net/torvald/terrarum/savegame/VDUtil.kt +++ b/src/net/torvald/terrarum/savegame/VDUtil.kt @@ -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) diff --git a/src/net/torvald/terrarum/savegame/VirtualDisk.kt b/src/net/torvald/terrarum/savegame/VirtualDisk.kt index f3b69f431..6a9f6f452 100644 --- a/src/net/torvald/terrarum/savegame/VirtualDisk.kt +++ b/src/net/torvald/terrarum/savegame/VirtualDisk.kt @@ -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() var isReadOnly: Boolean diff --git a/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt b/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt index ec956486b..9f9a8d789 100644 --- a/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt +++ b/src/net/torvald/terrarum/savegame/finder/VirtualDiskCracker.kt @@ -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) { - VirtualDiskCracker(Charset.forName("CP437")) + VirtualDiskCracker(Common.CHARSET) } \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/Common.kt b/src/net/torvald/terrarum/serialise/Common.kt index edda9b395..7bb0c388d 100644 --- a/src/net/torvald/terrarum/serialise/Common.kt +++ b/src/net/torvald/terrarum/serialise/Common.kt @@ -329,4 +329,4 @@ object Common { } -class SaveLoadError(file: File, cause: Throwable) : RuntimeException("An error occured while loading save file '${file.absolutePath}'", cause) \ No newline at end of file +class SaveLoadError(file: File?, cause: Throwable) : RuntimeException("An error occured while loading save file '${file?.absolutePath}'", cause) \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/GameSavingThread.kt b/src/net/torvald/terrarum/serialise/GameSavingThread.kt index 0b2909fc7..649bbd560 100644 --- a/src/net/torvald/terrarum/serialise/GameSavingThread.kt +++ b/src/net/torvald/terrarum/serialise/GameSavingThread.kt @@ -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 = allTheActors.filter{ it is IngamePlayer } as List + val playersList: List = allTheActors.filterIsInstance() 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().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().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() diff --git a/src/net/torvald/terrarum/serialise/WriteActor.kt b/src/net/torvald/terrarum/serialise/WriteActor.kt index 4228936f1..d4742ab58 100644 --- a/src/net/torvald/terrarum/serialise/WriteActor.kt +++ b/src/net/torvald/terrarum/serialise/WriteActor.kt @@ -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) diff --git a/src/net/torvald/terrarum/serialise/WriteSavegame.kt b/src/net/torvald/terrarum/serialise/WriteSavegame.kt index fc78760ab..1e6b20b1a 100644 --- a/src/net/torvald/terrarum/serialise/WriteSavegame.kt +++ b/src/net/torvald/terrarum/serialise/WriteSavegame.kt @@ -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) diff --git a/src/net/torvald/terrarum/serialise/WriteWorld.kt b/src/net/torvald/terrarum/serialise/WriteWorld.kt index 2080575da..1fbc48f04 100644 --- a/src/net/torvald/terrarum/serialise/WriteWorld.kt +++ b/src/net/torvald/terrarum/serialise/WriteWorld.kt @@ -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 } diff --git a/src/net/torvald/util/SortedArrayList.kt b/src/net/torvald/util/SortedArrayList.kt index f4377f7c2..c55ed0477 100644 --- a/src/net/torvald/util/SortedArrayList.kt +++ b/src/net/torvald/util/SortedArrayList.kt @@ -142,11 +142,13 @@ class SortedArrayList>(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 > searchFor(searchQuery: R, searchHow: (T) -> R = { it as R }): T? = getOrNull(searchForIndex(searchQuery, searchHow))