diff --git a/src/net/torvald/terrarum/Point2d.kt b/src/net/torvald/terrarum/Point2d.kt index 261d687a0..f4f9a7478 100644 --- a/src/net/torvald/terrarum/Point2d.kt +++ b/src/net/torvald/terrarum/Point2d.kt @@ -21,6 +21,11 @@ data class Point2d(var x: Double, var y: Double) : Cloneable { this.y = other.y } + fun setCoerceIn(start: Point2d, endInclusive: Point2d) { + x = x.coerceIn(start.x, endInclusive.x) + y = y.coerceIn(start.y, endInclusive.y) + } + /** * Rotate transform this point, with pivot (0, 0) * @return new Point2d that is rotated diff --git a/src/net/torvald/terrarum/gameactors/Hitbox.kt b/src/net/torvald/terrarum/gameactors/Hitbox.kt index e57f7b64d..53197c0ba 100644 --- a/src/net/torvald/terrarum/gameactors/Hitbox.kt +++ b/src/net/torvald/terrarum/gameactors/Hitbox.kt @@ -45,13 +45,13 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double, var suppress /** * @return bottom-centered point of hitbox. */ - val canonicalY: Double - get() = hitboxEnd.y + inline val canonicalY: Double + get() = endY val endX: Double - get() = hitboxEnd.x + get() = hitboxStart.x + width val endY: Double - get() = hitboxEnd.y + get() = hitboxStart.y + height /** * Set to the point top left @@ -131,10 +131,10 @@ class Hitbox(x1: Double, y1: Double, width: Double, height: Double, var suppress get() = hitboxStart.y val centeredX: Double - get() = (hitboxStart.x + hitboxEnd.x) * 0.5 + get() = hitboxStart.x + width * 0.5 val centeredY: Double - get() = (hitboxStart.y + hitboxEnd.y) * 0.5 + get() = hitboxStart.y + height * 0.5 infix fun intersects(position: Point2d) = (position.x >= startX && position.x <= startX + width) && diff --git a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt index da83903a7..d3971a298 100644 --- a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt +++ b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt @@ -17,12 +17,21 @@ import net.torvald.terrarum.modulebasegame.gameworld.WorldTime import net.torvald.terrarum.modulebasegame.ui.Notification import net.torvald.terrarum.modulebasegame.ui.UIPaletteSelector import net.torvald.terrarum.modulebasegame.weather.WeatherMixer +import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.serialise.WriteLayerDataZip.FILE_FOOTER +import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_FOOTER +import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_HEADER +import net.torvald.terrarum.serialise.toLittle +import net.torvald.terrarum.serialise.toLittleShort +import net.torvald.terrarum.serialise.toULittle48 import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarum.ui.UINSMenu import net.torvald.terrarum.worlddrawer.FeaturesDrawer.TILE_SIZE import net.torvald.terrarum.worlddrawer.LightmapRenderer import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack +import java.io.File +import java.io.FileOutputStream /** * Created by minjaesong on 2018-07-06. @@ -74,8 +83,15 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) { println("[BuildingMaker] Generating builder world...") + for (y in 0 until gameWorld.height) { + gameWorld.setTileWall(0, y, Block.ILLUMINATOR_RED) + gameWorld.setTileWall(gameWorld.width - 1, y, Block.ILLUMINATOR_RED) + gameWorld.setTileTerrain(0, y, Block.ILLUMINATOR_RED_OFF) + gameWorld.setTileTerrain(gameWorld.width - 1, y, Block.ILLUMINATOR_RED_OFF) + } + for (y in 150 until gameWorld.height) { - for (x in 0 until gameWorld.width) { + for (x in 1 until gameWorld.width - 1) { // wall layer gameWorld.setTileWall(x, y, Block.DIRT) @@ -91,7 +107,7 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) { } - override var actorNowPlaying: ActorHumanoid? = MovableWorldCamera() + override var actorNowPlaying: ActorHumanoid? = MovableWorldCamera(this) val uiToolbox = UINSMenu("Menu", 100, menuYaml) val notifier = Notification() @@ -439,7 +455,7 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) { return selection.last() - selection.first() } - private fun serialiseSelection() { + private fun serialiseSelection(outfile: File) { // save format: sparse list encoded in following binary format: /* Header: TEaT0bLD -- magic: Terrarum Attachment @@ -451,12 +467,37 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) { The rest: payloads defined in the map data format Payloads: array of (Int48 tileAddress, UInt16 blockID) + Payload names: TerL, WalL, WirL for Terrain, Wall and Wire respectively - Footer: EndAtC \xFF\xFE -- magic: end of attachment with BOM + Footer: EndTEM \xFF\xFE -- magic: end of attachment with BOM + + Endian: LITTLE */ // proc: // translate boxes so that leftmost point is (0,0) // write to the list using translated coords + + val payloads = arrayOf("WalL", "TerL", "WirL") + + val selectionDim = getSelectionTotalDimension() + val fos = FileOutputStream(outfile) + // write header + fos.write("TEaT0bLD".toByteArray()) + fos.write(byteArrayOf(1,3,3,1)) + fos.write(selectionDim.x.toLittleShort()) + // write wall -> terrain -> wire (order defined in GameWorld.TERRAIN/WALL/WIRE) + payloads.forEachIndexed { index, it -> + fos.write(PAYLOAD_HEADER); fos.write(it.toByteArray()) + selection.forEach { + val tile = world.getTileFrom(index, it.x, it.y)!! + val addr = LandUtil.getBlockAddr(world, it.x - selectionDim.x, it.y - selectionDim.y) + fos.write(addr.toULittle48()) + fos.write(tile.toLittle()) + } + fos.write(PAYLOAD_FOOTER) + } + fos.write(FILE_FOOTER) + fos.close() } } @@ -503,7 +544,7 @@ class BuildingMakerController(val screen: BuildingMaker) : InputAdapter() { } } -class MovableWorldCamera : ActorHumanoid(0, usePhysics = false) { +class MovableWorldCamera(val parent: BuildingMaker) : ActorHumanoid(0, usePhysics = false) { init { referenceID = Terrarum.PLAYER_REF_ID @@ -520,6 +561,22 @@ class MovableWorldCamera : ActorHumanoid(0, usePhysics = false) { actorValue[AVKey.FRICTIONMULT] = 4.0 } + // TODO resize-aware + private var coerceInStart = Point2d( + (Terrarum.WIDTH - hitbox.width) / 2.0, + (Terrarum.HEIGHT - hitbox.height) / 2.0 + ) + private var coerceInEnd = Point2d( + parent.world.width * TILE_SIZE - (Terrarum.WIDTH - hitbox.width) / 2.0, + parent.world.height * TILE_SIZE - (Terrarum.HEIGHT - hitbox.height) / 2.0 + ) + + override fun update(delta: Float) { + super.update(delta) + + // confine the camera so it won't wrap + this.hitbox.hitboxStart.setCoerceIn(coerceInStart, coerceInEnd) + } override fun drawBody(batch: SpriteBatch) { } diff --git a/src/net/torvald/terrarum/serialise/PayloadUtil.kt b/src/net/torvald/terrarum/serialise/PayloadUtil.kt new file mode 100644 index 000000000..523e7374e --- /dev/null +++ b/src/net/torvald/terrarum/serialise/PayloadUtil.kt @@ -0,0 +1,114 @@ +package net.torvald.terrarum.serialise + +import net.torvald.terrarum.AppLoader +import net.torvald.terrarum.serialise.WriteLayerDataZip.FILE_FOOTER +import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_FOOTER +import net.torvald.terrarum.toHex +import java.nio.charset.Charset +import java.util.* + +/** + * Created by minjaesong on 2019-02-20. + */ + +object PayloadUtil { + /** + * InputStream must be located manually at the payload begin + * + * For the actual use case, take a look at the source of the [ReadLayerDataZip]. + */ + fun readAll(inputStream: MarkableFileInputStream, footer: ByteArray = FILE_FOOTER): HashMap { + val pldBuffer4 = ByteArray(4) + val pldBuffer6 = ByteArray(6) + val pldBuffer8 = ByteArray(8) + + val payloads = HashMap() + + var pldCnt = 1 + + while (true) { + // read header and get payload's name + inputStream.read(pldBuffer8) + + // check if end of payload reached + if (pldBuffer8.contentEquals(footer)) { + break + } + + val payloadName = pldBuffer8.copyOfRange(4, 8).toString(Charset.forName("US-ASCII")) + + AppLoader.printdbg(this, "Payload $pldCnt name: $payloadName") // maybe maybe related with buffer things? + + // get uncompressed size + inputStream.read(pldBuffer6) + val uncompressedSize = pldBuffer6.toLittleInt48() + + // get deflated size + inputStream.mark(2147483647) // FIXME deflated stream cannot be larger than 2 GB + // creep forward until we hit the PAYLOAD_FOOTER + var compressedSize: Int = 0 // FIXME deflated stream cannot be larger than 2 GB + // loop init + inputStream.read(pldBuffer8) + // loop main + while (!pldBuffer8.contentEquals(PAYLOAD_FOOTER)) { + val aByte = inputStream.read(); compressedSize += 1 + if (aByte == -1) throw InternalError("Unexpected end-of-file at payload $pldCnt") + pldBuffer8.shiftLeftBy(1, aByte.toByte()) + } + + // at this point, we should have correct size of deflated bytestream + + AppLoader.printdbg(this, "Payload $pldCnt compressed size: $compressedSize") + + val compressedBytes = ByteArray(compressedSize) // FIXME deflated stream cannot be larger than 2 GB + inputStream.reset() // go back to marked spot + inputStream.read(compressedBytes) + + // PRO Debug tip: every deflated bytes must begin with 0x789C or 0x78DA + // Thus, \0pLd + [10] must be either of these. + + // put constructed payload into a container + payloads.put(payloadName, TEMzPayload(uncompressedSize, compressedBytes)) + + // skip over to be aligned with the next payload + inputStream.skip(8) + + pldCnt += 1 + } + + return payloads + } + + private fun ByteArray.shiftLeftBy(size: Int, fill: Byte = 0.toByte()) { + if (size == 0) { + return + } + else if (size < 0) { + throw IllegalArgumentException("This won't shift to right (size = $size)") + } + else if (size >= this.size) { + Arrays.fill(this, 0.toByte()) + } + else { + for (c in size..this.lastIndex) { + this[c - size] = this[c] + } + for (c in (this.size - size)..this.lastIndex) { + this[c] = fill + } + } + } + + private fun ByteArray.toByteString(): String { + val sb = StringBuilder() + this.forEach { + sb.append(it.toUint().toHex().takeLast(2)) + sb.append(' ') + } + sb.deleteCharAt(sb.lastIndex) + return sb.toString() + } + + data class TEMzPayload(val uncompressedSize: Long, val bytes: ByteArray) // FIXME deflated stream cannot be larger than 2 GB + +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt b/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt index 46e632f95..38fbcb562 100644 --- a/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt +++ b/src/net/torvald/terrarum/serialise/ReadLayerDataLzma.kt @@ -8,9 +8,7 @@ import net.torvald.terrarum.gameworld.MapLayer import net.torvald.terrarum.gameworld.PairedMapLayer import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read import net.torvald.terrarum.realestate.LandUtil -import net.torvald.terrarum.toHex import java.io.* -import java.nio.charset.Charset import java.util.* import kotlin.collections.HashMap @@ -60,7 +58,8 @@ internal object ReadLayerDataLzma { // read payloads - val pldBuffer4 = ByteArray(4) + val payloads = PayloadUtil.readAll(inputStream) + /*val pldBuffer4 = ByteArray(4) val pldBuffer6 = ByteArray(6) val pldBuffer8 = ByteArray(8) @@ -121,7 +120,7 @@ internal object ReadLayerDataLzma { // test for EOF inputStream.read(pldBuffer8) if (!pldBuffer8.contentEquals(WriteLayerDataZip.FILE_FOOTER)) - throw InternalError("Expected end-of-file, got not-so-end-of-file") + throw InternalError("Expected end-of-file, got not-so-end-of-file")*/ ////////////////////// @@ -199,8 +198,6 @@ internal object ReadLayerDataLzma { ) } - private data class TEMzPayload(val uncompressedSize: Long, val bytes: ByteArray) // FIXME deflated stream cannot be larger than 2 GB - /** * Immediately deployable, a part of the gameworld */ @@ -221,27 +218,6 @@ internal object ReadLayerDataLzma { val fluidFills: HashMap ) - private fun ByteArray.shiftLeftBy(size: Int, fill: Byte = 0.toByte()) { - if (size == 0) { - return - } - else if (size < 0) { - throw IllegalArgumentException("This won't shift to right (size = $size)") - } - else if (size >= this.size) { - Arrays.fill(this, 0.toByte()) - } - else { - for (c in size..this.lastIndex) { - this[c - size] = this[c] - } - for (c in (this.size - size)..this.lastIndex) { - this[c] = fill - } - } - } - - internal fun InputStream.readRelative(b: ByteArray, off: Int, len: Int): Int { if (b == null) { throw NullPointerException() @@ -272,14 +248,4 @@ internal object ReadLayerDataLzma { return i } - - fun ByteArray.toByteString(): String { - val sb = StringBuilder() - this.forEach { - sb.append(it.toUint().toHex().takeLast(2)) - sb.append(' ') - } - sb.deleteCharAt(sb.lastIndex) - return sb.toString() - } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt b/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt index 9041c5daf..fbabbe8b1 100644 --- a/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt +++ b/src/net/torvald/terrarum/serialise/ReadLayerDataZip.kt @@ -6,12 +6,10 @@ import net.torvald.terrarum.gameworld.MapLayer import net.torvald.terrarum.gameworld.PairedMapLayer import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.DiskSkimmer.Companion.read import net.torvald.terrarum.realestate.LandUtil -import net.torvald.terrarum.toHex import java.io.File import java.io.FileInputStream import java.io.IOException import java.io.InputStream -import java.nio.charset.Charset import java.util.* import java.util.zip.Inflater import kotlin.collections.HashMap @@ -62,7 +60,8 @@ internal object ReadLayerDataZip { // read payloads - val pldBuffer4 = ByteArray(4) + val payloads = PayloadUtil.readAll(inputStream) + /*val pldBuffer4 = ByteArray(4) val pldBuffer6 = ByteArray(6) val pldBuffer8 = ByteArray(8) @@ -123,7 +122,7 @@ internal object ReadLayerDataZip { // test for EOF inputStream.read(pldBuffer8) if (!pldBuffer8.contentEquals(WriteLayerDataZip.FILE_FOOTER)) - throw InternalError("Expected end-of-file, got not-so-end-of-file") + throw InternalError("Expected end-of-file, got not-so-end-of-file")*/ ////////////////////// @@ -194,8 +193,6 @@ internal object ReadLayerDataZip { ) } - private data class TEMzPayload(val uncompressedSize: Long, val bytes: ByteArray) // FIXME deflated stream cannot be larger than 2 GB - /** * Immediately deployable, a part of the gameworld */ @@ -214,27 +211,6 @@ internal object ReadLayerDataZip { val terrainDamages: HashMap ) - private fun ByteArray.shiftLeftBy(size: Int, fill: Byte = 0.toByte()) { - if (size == 0) { - return - } - else if (size < 0) { - throw IllegalArgumentException("This won't shift to right (size = $size)") - } - else if (size >= this.size) { - Arrays.fill(this, 0.toByte()) - } - else { - for (c in size..this.lastIndex) { - this[c - size] = this[c] - } - for (c in (this.size - size)..this.lastIndex) { - this[c] = fill - } - } - } - - internal fun InputStream.readRelative(b: ByteArray, off: Int, len: Int): Int { if (b == null) { throw NullPointerException() @@ -265,14 +241,4 @@ internal object ReadLayerDataZip { return i } - - fun ByteArray.toByteString(): String { - val sb = StringBuilder() - this.forEach { - sb.append(it.toUint().toHex().takeLast(2)) - sb.append(' ') - } - sb.deleteCharAt(sb.lastIndex) - return sb.toString() - } -} \ No newline at end of file +} diff --git a/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt b/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt index 1afd0addb..a0aa4c467 100644 --- a/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt +++ b/src/net/torvald/terrarum/serialise/WriteLayerDataLzma.kt @@ -4,6 +4,9 @@ import com.badlogic.gdx.utils.compression.Lzma import net.torvald.terrarum.AppLoader import net.torvald.terrarum.Terrarum import net.torvald.terrarum.realestate.LandUtil +import net.torvald.terrarum.serialise.WriteLayerDataZip.FILE_FOOTER +import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_FOOTER +import net.torvald.terrarum.serialise.WriteLayerDataZip.PAYLOAD_HEADER import java.io.* import java.util.zip.DeflaterOutputStream @@ -35,9 +38,6 @@ internal object WriteLayerDataLzma { val NUMBER_OF_PAYLOADS = 5.toByte() val COMPRESSION_ALGORITHM = 2.toByte() val GENERATOR_VERSION = WORLD_GENERATOR_VERSION.toULittleShort() - val PAYLOAD_HEADER = byteArrayOf(0, 0x70, 0x4C, 0x64) - val PAYLOAD_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x50, 0x59, 0x4C, 0x64, -1) - val FILE_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x54, 0x45, 0x4D, -1, -2) //val NULL: Byte = 0 diff --git a/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt b/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt index 12c35ef46..73a0c565b 100644 --- a/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt +++ b/src/net/torvald/terrarum/serialise/WriteLayerDataZip.kt @@ -40,9 +40,9 @@ internal object WriteLayerDataZip { val NUMBER_OF_PAYLOADS = 5.toByte() val COMPRESSION_ALGORITHM = 1.toByte() val GENERATOR_VERSION = WORLD_GENERATOR_VERSION.toULittleShort() - val PAYLOAD_HEADER = byteArrayOf(0, 0x70, 0x4C, 0x64) - val PAYLOAD_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x50, 0x59, 0x4C, 0x64, -1) - val FILE_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x54, 0x45, 0x4D, -1, -2) + val PAYLOAD_HEADER = byteArrayOf(0, 0x70, 0x4C, 0x64) // \0pLd + val PAYLOAD_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x50, 0x59, 0x4C, 0x64, -1) // EndPYLd\xFF + val FILE_FOOTER = byteArrayOf(0x45, 0x6E, 0x64, 0x54, 0x45, 0x4D, -1, -2) // EndTEM with BOM //val NULL: Byte = 0