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

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

View File

@@ -66,6 +66,7 @@ object Terrarum : Disposable {
var blockCodex = BlockCodex(); internal set 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 itemCodex = ItemCodex(); internal set
var wireCodex = WireCodex(); internal set var wireCodex = WireCodex(); internal set
var materialCodex = MaterialCodex(); 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.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.gameitems.GameItem 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.UIItemInventoryCellBase
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes.toItemCountText import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryCellCommonRes.toItemCountText
@@ -97,23 +96,13 @@ class UIItemInventoryElemWide(
) )
// draw name of the item // draw name of the item
if (INVEN_DEBUG_MODE) { App.fontGame.draw(batch,
App.fontGame.draw(batch, // print name and amount in parens
// print static id, dynamic id, and count item!!.name + (if (amount > 0 && item!!.stackable) "$fwsp($amountString)" else if (amount != 1L) "$fwsp!!$amountString!!" else ""),
"${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 ""),
posX + textOffsetX, posX + textOffsetX,
posY + textOffsetY posY + textOffsetY
) )
}
// durability metre // durability metre

View File

@@ -24,6 +24,8 @@ typealias ItemID = String
*/ */
abstract class GameItem(val originalID: ItemID) : Comparable<GameItem>, Cloneable { abstract class GameItem(val originalID: ItemID) : Comparable<GameItem>, Cloneable {
constructor() : this("-uninitialised-")
open var dynamicID: ItemID = originalID open var dynamicID: ItemID = originalID
/** /**
* if the ID is a Actor range, it's an actor contained in a pocket. * 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.ActorID
import net.torvald.terrarum.gameactors.WireActor import net.torvald.terrarum.gameactors.WireActor
import net.torvald.terrarum.gameitems.ItemID 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.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.utils.* 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 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 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")) @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 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.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.CanBeAnItem import net.torvald.terrarum.modulebasegame.gameactors.CanBeAnItem
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
import net.torvald.terrarum.serialise.SaveLoadError
import net.torvald.terrarum.worlddrawer.BlocksDrawer 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 * 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> * <ItemID or RefID for Actor, TheItem>
* Will return corresponding Actor if ID >= ACTORID_MIN * Will return corresponding Actor if ID >= ACTORID_MIN
*/ */
@Transient val itemCodex = HashMap<ItemID, GameItem>() @Transient val itemCodex = ItemTable()
@Transient var dynamicItemDescription = HashMap<ItemID, GameItem>(); private set val dynamicItemInventory = ItemTable()
var dynamicToStaticTable = HashMap<ItemID, ItemID>(); private set val dynamicToStaticTable = ItemRemapTable()
@Transient val ACTORID_MIN = ReferencingRanges.ACTORS.first @Transient val ACTORID_MIN = ReferencingRanges.ACTORS.first
@@ -41,18 +47,33 @@ class ItemCodex {
fun clear() { fun clear() {
itemCodex.clear() itemCodex.clear()
dynamicItemDescription.clear() dynamicItemInventory.clear()
dynamicToStaticTable.clear() dynamicToStaticTable.clear()
} }
/** /**
* This method does not alter already-been-loaded itemCodex; only filles up dynamicitem-related fields * 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) { fun loadFromSave(savefile: File?, otherDynamicToStaticTable: ItemRemapTable, otherDynamicItemInventory: ItemTable) {
this.dynamicToStaticTable = other.dynamicToStaticTable otherDynamicToStaticTable.forEach { dynid, itemid ->
dynamicToStaticTable.forEach { dynid, itemid ->
printdbg(this, "Loadfromsave dynid $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) { fun registerNewDynamicItem(dynamicID: ItemID, item: GameItem) {
printdbg(this, "Registering new dynamic item $dynamicID (from ${item.originalID})") printdbg(this, "Registering new dynamic item $dynamicID (from ${item.originalID})")
dynamicItemDescription[dynamicID] = item dynamicItemInventory[dynamicID] = item
dynamicToStaticTable[dynamicID] = item.originalID dynamicToStaticTable[dynamicID] = item.originalID
} }
@@ -76,7 +97,7 @@ class ItemCodex {
if (code == null) return null if (code == null) return null
if (code.startsWith("$PREFIX_DYNAMICITEM:")) 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:")) { else if (code.startsWith("$PREFIX_ACTORITEM:")) {
val a = (Terrarum.ingame!! as TerrarumIngame).getActorByID(code.substring(6).toInt()) // actor item val a = (Terrarum.ingame!! as TerrarumIngame).getActorByID(code.substring(6).toInt()) // actor item
if (a is CanBeAnItem) return a.itemData 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 { 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) //ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader)
val world = ReadWorld.readLayerFormat(reader) val world = ReadWorld.readLayerFormat(reader, file)
demoWorld = world demoWorld = world
printdbg(this, "Demo world loaded") 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.modulebasegame.TerrarumIngame
import net.torvald.terrarum.serialise.ReadActor import net.torvald.terrarum.serialise.ReadActor
import net.torvald.terrarum.serialise.ReadWorld import net.torvald.terrarum.serialise.ReadWorld
import java.io.File
import java.io.IOException import java.io.IOException
/** /**
@@ -16,8 +17,9 @@ object ImportWorld : ConsoleCommand {
override fun execute(args: Array<String>) { override fun execute(args: Array<String>) {
if (args.size == 2) { if (args.size == 2) {
try { try {
val reader = java.io.FileReader(App.defaultDir + "/Exports/${args[1]}.json") val file = File(App.defaultDir + "/Exports/${args[1]}.json")
ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader) val reader = java.io.FileReader(file)
ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader, file)
Echo("Importworld: imported a world from ${args[1]}.json") Echo("Importworld: imported a world from ${args[1]}.json")
} }
catch (e: IOException) { catch (e: IOException) {

View File

@@ -28,6 +28,8 @@ class ActorInventory() : FixtureInventory() {
/** /**
* List of all equipped items (tools, armours, rings, necklaces, etc.) * 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` * The ItemID must be `dynamicID`
*/ */
val itemEquipped = Array<ItemID?>(GameItem.EquipPosition.INDEX_MAX) { null } 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) { fun consumeItem(item: GameItem) {
val actor = this.actor as Actor 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.Terrarum
import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.lock
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.util.SortedArrayList
import java.math.BigInteger import java.math.BigInteger
import java.util.concurrent.locks.ReentrantLock
/** /**
* Created by minjaesong on 2021-03-16. * Created by minjaesong on 2021-03-16.
@@ -32,7 +31,7 @@ open class FixtureInventory() {
/** /**
* Sorted by referenceID. * 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 var wallet = BigInteger("0") // unified currency for whole civs; Dwarf Fortress approach seems too complicated
fun isEmpty() = getTotalCount() == 0L fun isEmpty() = getTotalCount() == 0L
@@ -66,7 +65,7 @@ open class FixtureInventory() {
// If we already have the item, increment the amount // If we already have the item, increment the amount
// If not, add item with specified amount // If not, add item with specified amount
val existingItem = invSearchByDynamicID(item.dynamicID) val existingItem = searchByID(item.dynamicID)
// if the item already exists // if the item already exists
if (existingItem != null) { if (existingItem != null) {
@@ -80,7 +79,7 @@ open class FixtureInventory() {
else { else {
itemList.add(InventoryPair(item.dynamicID, count)) itemList.add(InventoryPair(item.dynamicID, count))
} }
insertionSortLastElem(itemList) // insertionSortLastElem(itemList)
} }
open fun remove(itemID: ItemID, count: Long) = remove(ItemCodex[itemID]!!, count) {} 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 if (existingItem != null) { // if the item already exists
val newCount = existingItem.qty - count val newCount = existingItem.qty - count
@@ -171,28 +170,14 @@ open class FixtureInventory() {
if (itemList.size == 0) if (itemList.size == 0)
false false
else else
itemList.binarySearch(id, DYNAMIC_ID) >= 0 itemList.contains(InventoryPair(id, 1))
fun invSearchByDynamicID(id: ItemID?): InventoryPair? { fun searchByID(id: ItemID?): InventoryPair? {
if (itemList.size == 0 || id == null) if (itemList.size == 0 || id == null)
return null return null
val index = itemList.binarySearch(id, DYNAMIC_ID) return itemList.searchFor(id) { it.itm }
if (index < 0)
return null
else
return itemList[index]
} }
protected fun invSearchByStaticID(id: ItemID?): InventoryPair? { /*protected fun insertionSortLastElem(arr: ArrayList<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>) {
ReentrantLock().lock { ReentrantLock().lock {
var j = arr.lastIndex - 1 var j = arr.lastIndex - 1
val x = arr.last() val x = arr.last()
@@ -202,7 +187,7 @@ open class FixtureInventory() {
} }
arr[j + 1] = x arr[j + 1] = x
} }
} }*/
@Transient private val STATIC_ID = 41324534 @Transient private val STATIC_ID = 41324534
@Transient private val DYNAMIC_ID = 181643953 @Transient private val DYNAMIC_ID = 181643953
protected fun ArrayList<InventoryPair>.binarySearch(ID: ItemID, searchMode: Int): Int { 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 itm: ItemID = ""; private set
var qty: Long = 0 var qty: Long = 0
@@ -244,4 +229,5 @@ class InventoryPair {
operator fun component1() = itm operator fun component1() = itm
operator fun component2() = qty 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.Terrarum
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameitems.GameItem 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.DiskSkimmer
import net.torvald.terrarum.savegame.SimpleFileSystem import net.torvald.terrarum.savegame.SimpleFileSystem
import net.torvald.terrarum.utils.PlayerLastStatus import net.torvald.terrarum.utils.PlayerLastStatus
@@ -33,6 +36,9 @@ class IngamePlayer : ActorHumanoid, HasAssembledSprite {
val uuid = UUID.randomUUID() val uuid = UUID.randomUUID()
var worldCurrentlyPlaying: UUID = UUID(0L,0L) // only filled up on save and load; DO NOT USE THIS 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 @Transient override var spriteHeadTexture: TextureRegion? = null

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ object AmmoMeterProxy {
else { else {
meter.vitalGetterVal = { meter.vitalGetterVal = {
if (currentItem.stackable && currentItem.maxDurability == GameItem.DURABILITY_NA) { if (currentItem.stackable && currentItem.maxDurability == GameItem.DURABILITY_NA) {
actor.inventory.invSearchByDynamicID(currentItem.dynamicID)!!.qty.toFloat() actor.inventory.searchByID(currentItem.dynamicID)!!.qty.toFloat()
} }
else else
currentItem.durability 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.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_X
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y 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.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.UIInventoryFull.Companion.internalWidth
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.createInvCellGenericKeyDownFun import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.createInvCellGenericKeyDownFun
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.createInvCellGenericTouchDownFun import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.createInvCellGenericTouchDownFun
@@ -136,7 +134,7 @@ internal class UIInventoryCells(
) )
// debug text // debug text
batch.color = Color.LIGHT_GRAY batch.color = Color.LIGHT_GRAY
if (INVEN_DEBUG_MODE) { if (App.IS_DEVELOPMENT_BUILD) {
App.fontSmallNumbers.draw(batch, App.fontSmallNumbers.draw(batch,
"${full.actor.inventory.capacity}/${full.actor.inventory.maxCapacity}", "${full.actor.inventory.capacity}/${full.actor.inventory.maxCapacity}",
encumbBarTextXPos, encumbBarTextXPos,

View File

@@ -38,8 +38,6 @@ class UIInventoryFull(
val CELL_COL = Toolkit.Theme.COL_CELL_FILL 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 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 const val CELLS_HOR = 10
val CELLS_VRT: Int; get() = (App.scr.height - REQUIRED_MARGIN - 134 + UIItemInventoryItemGrid.listGap) / // 134 is another magic number 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 itemGrid[k].equippedSlot = null
} }
else { else {
val itemRecord = it.invSearchByDynamicID(item)!! val itemRecord = it.searchByID(item)!!
itemGrid[k].item = ItemCodex[item] itemGrid[k].item = ItemCodex[item]
itemGrid[k].amount = itemRecord.qty 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.ActorInventory
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
import net.torvald.terrarum.modulebasegame.gameactors.InventoryPair 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.Toolkit
import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItem import net.torvald.terrarum.ui.UIItem
@@ -162,6 +161,9 @@ class UIItemInventoryItemGrid(
listRebuildFun() 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) { private val itemGrid = Array<UIItemInventoryCellBase>(horizontalCells * verticalCells) {
@@ -347,8 +349,8 @@ class UIItemInventoryItemGrid(
override fun update(delta: Float) { override fun update(delta: Float) {
super.update(delta) super.update(delta)
var tooltipSet = false
tooltipShowing[this] = false
items.forEach { items.forEach {
@@ -356,20 +358,21 @@ class UIItemInventoryItemGrid(
// set tooltip accordingly // 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( INGAME.setTooltipMessage(
if (INVEN_DEBUG_MODE) { if (App.IS_DEVELOPMENT_BUILD) {
it.item?.name + " (${it.item?.originalID}${if (it.item?.originalID == it.item?.dynamicID) "" else "/${it.item?.dynamicID}"})" it.item?.name + "\n(${it.item?.originalID}${if (it.item?.originalID == it.item?.dynamicID) "" else "/${it.item?.dynamicID}"})"
} }
else { else {
it.item?.name it.item?.name
} }
) )
tooltipSet = true
tooltipShowing[this] = true
} }
} }
if (!tooltipSet) { if (tooltipShowing.values.all { !it }) {
INGAME.setTooltipMessage(null) INGAME.setTooltipMessage(null)
} }
@@ -461,7 +464,11 @@ class UIItemInventoryItemGrid(
} }
override fun dispose() { override fun dispose() {
tooltipShowing.remove(this)
}
override fun hide() {
tooltipShowing.remove(this)
} }
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {

View File

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

View File

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

View File

@@ -63,7 +63,7 @@ object VDUtil {
if (diskSpecVersion != specversion) if (diskSpecVersion != specversion)
throw RuntimeException("Unsupported disk format version: current internal version is $specversion; the file's version is $diskSpecVersion") 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) vdisk.__internalSetFooter__(footers)

View File

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

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.savegame.finder package net.torvald.terrarum.savegame.finder
import net.torvald.terrarum.savegame.* import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.serialise.Common
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.Dimension import java.awt.Dimension
import java.awt.event.KeyEvent 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>) { 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.gdx.graphics.PixmapIO2
import net.torvald.terrarum.App.printdbg 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.IngameRenderer
import net.torvald.terrarum.modulebasegame.TerrarumIngame 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.IngamePlayer
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.toInt import net.torvald.terrarum.toInt
import net.torvald.terrarum.savegame.* import net.torvald.terrarum.savegame.*
import java.io.File import java.io.File
import java.util.HashMap
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
/** /**
@@ -61,14 +68,14 @@ class WorldSavingThread(
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList() 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 actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) }
val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) } val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) }
val cw = ingame.world.width / LandUtil.CHUNK_W val cw = ingame.world.width / LandUtil.CHUNK_W
val ch = ingame.world.height / LandUtil.CHUNK_H val ch = ingame.world.height / LandUtil.CHUNK_H
WriteSavegame.saveProgress = 0f WriteSavegame.saveProgress = 0f
WriteSavegame.saveProgressMax = 2f + (cw * ch * layers.size) + actorsList.size WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size
val tgaout = ByteArray64GrowableOutputStream() val tgaout = ByteArray64GrowableOutputStream()
@@ -79,6 +86,39 @@ class WorldSavingThread(
val creation_t = ingame.world.creationTime 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) { if (hasThumbnail) {
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true) PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
IngameRenderer.fboRGBexport.dispose() IngameRenderer.fboRGBexport.dispose()

View File

@@ -4,9 +4,12 @@ import net.torvald.spriteanimation.HasAssembledSprite
import net.torvald.spriteanimation.SpriteAnimation import net.torvald.spriteanimation.SpriteAnimation
import net.torvald.terrarum.spriteassembler.ADProperties import net.torvald.terrarum.spriteassembler.ADProperties
import net.torvald.terrarum.ItemCodex import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameitems.GameItem 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.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.savegame.* import net.torvald.terrarum.savegame.*
@@ -74,6 +77,17 @@ object WritePlayer {
player.worldCurrentlyPlaying = ingame?.world?.worldIndex ?: UUID(0L,0L) 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 actorJson = WriteActor.encodeToByteArray64(player)
val adl = player.animDesc!!.getRawADL() val adl = player.animDesc!!.getRawADL()
@@ -142,6 +156,8 @@ object ReadActor {
actor.animDescGlow = ADProperties(ByteArray64Reader(animFileGlow.bytes, Common.CHARSET)) 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]] val heldItem = ItemCodex[actor.inventory.itemEquipped[GameItem.EquipPosition.HAND_GRIP]]
if (bodypartsFile != null) if (bodypartsFile != null)

View File

@@ -125,7 +125,7 @@ object LoadSavegame {
val currentWorldId = player.worldCurrentlyPlaying val currentWorldId = player.worldCurrentlyPlaying
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!! 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.layerTerrain = BlockLayer(world.width, world.height)
world.layerWall = BlockLayer(world.width, world.height) world.layerWall = BlockLayer(world.width, world.height)

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.serialise package net.torvald.terrarum.serialise
import net.torvald.terrarum.CommonResourcePool import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.ReferencingRanges import net.torvald.terrarum.ReferencingRanges
import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.BlockMarkerActor 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.savegame.ByteArray64Writer
import net.torvald.terrarum.utils.PlayerLastStatus import net.torvald.terrarum.utils.PlayerLastStatus
import net.torvald.terrarum.weather.WeatherMixer import net.torvald.terrarum.weather.WeatherMixer
import java.io.File
import java.io.Reader import java.io.Reader
/** /**
@@ -90,22 +92,24 @@ object WriteWorld {
*/ */
object ReadWorld { object ReadWorld {
fun readLayerFormat(worldDataStream: Reader): GameWorld = fun readLayerFormat(worldDataStream: Reader, origin: File?): GameWorld =
fillInDetails(Common.jsoner.fromJson(GameWorldTitleScreen::class.java, worldDataStream)) fillInDetails(Common.jsoner.fromJson(GameWorldTitleScreen::class.java, worldDataStream), origin)
operator fun invoke(worldDataStream: Reader): GameWorld = operator fun invoke(worldDataStream: Reader, origin: File?): GameWorld =
fillInDetails(Common.jsoner.fromJson(GameWorld::class.java, worldDataStream)) 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.tileNumberToNameMap.forEach { l, s ->
world.tileNameToNumberMap[s] = l.toInt() world.tileNameToNumberMap[s] = l.toInt()
} }
ItemCodex.loadFromSave(origin, world.dynamicToStaticTable, world.dynamicItemInventory)
return world return world
} }
fun readWorldAndSetNewWorld(ingame: TerrarumIngame, worldDataStream: Reader): GameWorld { fun readWorldAndSetNewWorld(ingame: TerrarumIngame, worldDataStream: Reader, origin: File?): GameWorld {
val world = readLayerFormat(worldDataStream) val world = readLayerFormat(worldDataStream, origin)
ingame.world = world ingame.world = world
return 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) val second = Math.min(low, this.size - 1)
return Pair(first, second) 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) * (e.g. search the Actor by its ID rather than the actor instance)
* *
* @param searchQuery what exactly are we looking for? * @param searchQuery what exactly are we looking for?
* @param searchHow and where or how can it be found? * @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)) fun <R: Comparable<R>> searchFor(searchQuery: R, searchHow: (T) -> R = { it as R }): T? = getOrNull(searchForIndex(searchQuery, searchHow))