diff --git a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt index acd2dca2e..35cac10b9 100644 --- a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt +++ b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt @@ -17,6 +17,8 @@ import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameparticles.ParticleBase import net.torvald.terrarum.gameworld.BlockLayerI16 import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN +import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.gameworld.WorldTime import net.torvald.terrarum.modulebasegame.ui.UIBuildingMakerBlockChooser @@ -717,7 +719,7 @@ class YamlCommandToolExportTest : YamlInvokable { var name = "test" // prepare POI - val poi = PointOfInterest(name, maxX - minX + 1, maxY - minY + 1, ui.world.tileNumberToNameMap) + val poi = PointOfInterest(name, maxX - minX + 1, maxY - minY + 1, ui.world.tileNumberToNameMap, ui.world.tileNameToNumberMap) val layer = POILayer(name) val terr = BlockLayerI16(poi.w, poi.h) val wall = BlockLayerI16(poi.w, poi.h) @@ -804,6 +806,31 @@ class YamlCommandToolImportTest : YamlInvokable { val json = """{"genver":67108864,"id":"test","wlen":8,"w":6,"h":7,"lut":{"0":"basegame:0","1":"basegame:19","2":"basegame:-1"},"layers":[{"name":"test1","dat":["abZy8000000rlLp0S#Gh%Q1%XFDc>fH&5.j6G~zOdGxCG","abZy8000000rlLp0S#Gh38ntBemYv>C=*G~dGxCG"]},{"name":"test2","dat":["abZy8000000rlLp0S#Gh%Q1%XFDc>fH&5.j6G~zOdGxCG","abZy8000000rlLp0S#Gh38ntBemYv>C=*G~dGxCG"]}]}""" - val poi = Common.jsoner.fromJson(PointOfInterest().javaClass, json) + val poi = Common.jsoner.fromJson(PointOfInterest().javaClass, json).also { + it.getReadyToBeUsed(ui.world.tileNameToNumberMap) + + println("==== Test print layer ====") + it.layers.forEach { layer -> + println("Layer ${layer.name}") + println(" Terrain:") + for (y in 0 until it.h) { + for (x in 0 until it.w) { + val tnum = layer.blockLayer[TERRAIN].unsafeGetTile(x, y).toLong() + print("$tnum(${ui.world.tileNumberToNameMap[tnum]?.replace("basegame:","b")})\t") + } + println() + } + println(" Wall:") + for (y in 0 until it.h) { + for (x in 0 until it.w) { + val tnum = layer.blockLayer[WALL].unsafeGetTile(x, y).toLong() + print("$tnum(${ui.world.tileNumberToNameMap[tnum]?.replace("basegame:","b")})\t") + } + println() + } + } + } + + println("Imported POI: $poi") } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/savegame/ByteArray64.kt b/src/net/torvald/terrarum/savegame/ByteArray64.kt index 0aa39b905..9d64ead7f 100644 --- a/src/net/torvald/terrarum/savegame/ByteArray64.kt +++ b/src/net/torvald/terrarum/savegame/ByteArray64.kt @@ -266,7 +266,15 @@ class ByteArray64(initialSize: Long = BANK_SIZE.toLong()) { if (this.size > Integer.MAX_VALUE - 8) // according to OpenJDK; the size itself is VM-dependent throw TypeCastException("Impossible cast; too large to fit") - return ByteArray(this.size.toInt()) { this[it.toLong()] } + // TODO make it faster using chunks and memcpy +// return ByteArray(this.size.toInt()) { this[it.toLong()] } + return ByteArray(this.size.toInt()).also { out -> + var c = 0 + this.forEachUsedBanks { usedCount, bytes -> + System.arraycopy(bytes, 0, out, c, usedCount) + c += usedCount + } + } } fun writeToFile(file: File) { diff --git a/src/net/torvald/terrarum/serialise/PointOfInterest.kt b/src/net/torvald/terrarum/serialise/PointOfInterest.kt index ebc52a19d..ab3bbaf38 100644 --- a/src/net/torvald/terrarum/serialise/PointOfInterest.kt +++ b/src/net/torvald/terrarum/serialise/PointOfInterest.kt @@ -12,6 +12,7 @@ import net.torvald.terrarum.gameworld.GameWorld.Companion.TERRAIN import net.torvald.terrarum.gameworld.GameWorld.Companion.WALL import net.torvald.terrarum.linearSearchBy import net.torvald.terrarum.utils.HashArray +import java.io.StringReader /** * Created by minjaesong on 2023-10-17. @@ -20,17 +21,22 @@ class PointOfInterest( identifier: String, width: Int, height: Int, - tileNumberToNameMap0: HashArray + tileNumberToNameMap0: HashArray, + tileNameToNumberMap0: HashMap ) : Json.Serializable { - constructor() : this("undefined", 0,0, HashArray()) + constructor() : this("undefined", 0,0, HashArray(), HashMap()) + + constructor(numberToNameMap: HashArray, nameToNumberMap: HashMap) : this("undefined", 0, 0, numberToNameMap, nameToNumberMap) @Transient var w = width; private set @Transient var h = height; private set @Transient var layers = ArrayList(); private set @Transient var id = identifier; private set @Transient var tileNumberToNameMap: HashArray = tileNumberToNameMap0; private set - + @Transient var tileNameToNumberMap: HashMap = tileNameToNumberMap0; private set + @Transient lateinit var lutFromJson: HashArray + @Transient var wlenFromJson = 0 override fun write(json: Json) { val tileSymbolToItemId = HashArray() // exported @@ -74,25 +80,33 @@ class PointOfInterest( this.id = jsonData["id"].asString() this.w = jsonData["w"].asInt() this.h = jsonData["h"].asInt() - this.tileNumberToNameMap = json.readValue(HashArray().javaClass, jsonData["lut"]) - val wlen = jsonData["wlen"].asInt() + this.lutFromJson = json.readValue(HashArray().javaClass, jsonData["lut"]) + this.wlenFromJson = jsonData["wlen"].asInt() println("read test") - println("id: $id, w: $w, h: $h") - println("lut: $tileNumberToNameMap") - println("==== layers ====") - jsonData["layers"].forEachIndexed { index, it -> + println("id: $id, w: $w, h: $h, wlen: $wlenFromJson") + println("lut: $lutFromJson") + + println("==== layers (bytes) ====") + + // decompress layers + jsonData["layers"].forEachIndexed { index, jsonData -> print("#${index+1}: ") - println(it["name"].asString()) - println("layerdata:") - it["dat"].forEachIndexed { index, value -> - print(" L${if (index == TERRAIN) "terr" else if (index == WALL) "wall" else "unk$index"}: ") - println(value.asString()) + POILayer().also { + it.w = this.w; it.h = this.h; it.id = this.id + it.read(json, jsonData) + layers.add(it) } } } + fun getReadyToBeUsed(itemIDtoTileNum: Map) { + layers.forEach { + it.getReadyToBeUsed(lutFromJson, itemIDtoTileNum, w, h, wlenFromJson / 8) + } + } + /** * Place the specified layers onto the world. The name of the layers are case-insensitive. */ @@ -114,10 +128,14 @@ class POILayer( ) : Json.Serializable { constructor() : this("undefined") - @Transient val name = name + @Transient var name = name @Transient internal lateinit var blockLayer: ArrayList @Transient internal lateinit var dat: Array + @Deprecated("Used for debug print", ReplaceWith("name")) @Transient internal var id = "" + @Deprecated("Used for debug print") @Transient internal var w = 0 + @Deprecated("Used for debug print") @Transient internal var h = 0 + /** * @return list of unique tiles, in the form of TileNums */ @@ -157,8 +175,6 @@ class POILayer( /** * Converts `dat` into `blockLayer` so the Layer can be actually utilised. - * - * `tileSymbolToItemId[255]` and `tileSymbolToItemId[65535]` should return `Block.NULL` */ fun getReadyToBeUsed(tileSymbolToItemId: HashArray, itemIDtoTileNum: Map, width: Int, height: Int, byteLength: Int) { if (::blockLayer.isInitialized) { @@ -168,13 +184,17 @@ class POILayer( dat.forEachIndexed { layerIndex, layer -> val currentBlockLayer = BlockLayerI16(width, height).also { - blockLayer[layerIndex] = it + blockLayer.add(it) } for (w in 0 until layer.size / byteLength) { - val word = if (byteLength == 1) layer[w].toUint() else if (byteLength == 2) layer.toULittleShort(2*w) else throw IllegalArgumentException() + val word = if (byteLength == 1) layer[w].toUint() else if (byteLength == 2) layer.toULittleShort(2*w) else throw IllegalArgumentException("Illegal byteLength $byteLength") val x = w % width val y = w / width - val tile = itemIDtoTileNum[tileSymbolToItemId[word.toLong()]!!]!! + val itemID = tileSymbolToItemId[word.toLong()]!! + val tile = if (itemID == Block.NULL) + -1 + else + itemIDtoTileNum[itemID]!! currentBlockLayer.unsafeSetTile(x, y, tile) } } @@ -187,7 +207,7 @@ class POILayer( blockLayer.forEachIndexed { layerIndex, layer -> for (x in 0 until layer.width) { for (y in 0 until layer.height) { val tile = layer.unsafeGetTile(x, y) - if (tile != -1) { + if (tile != -1 && tile != 65535) { val (wx, wy) = world.coerceXY(x + topLeftX, y + topLeftY) world.setTileOnLayerUnsafe(layerIndex, wx, wy, tile) } @@ -202,8 +222,24 @@ class POILayer( json.writeValue("dat", dat) } - override fun read(json: Json?, jsonData: JsonValue?) { - TODO("Not yet implemented") + override fun read(json: Json, jsonData: JsonValue) { + name = jsonData["name"].asString() + + println(name) + + + dat = jsonData["dat"].mapIndexed { index, value -> + val zipdStr = value.asString() + val ba = Common.strToBytes(StringReader(zipdStr)).toByteArray() + val lname = "L${if (index == TERRAIN) "terr" else if (index == WALL) "wall" else "unk$index"}" + if (ba.size != w * h) throw IllegalStateException("Layer size mismatch: expected ${w*h} but got ${ba.size} on POI $id Layer $name $lname") + + print(" $lname: ") + print("(${ba.size})") + println("[${ba.joinToString()}]") + + ba + }.toTypedArray() } }