diff --git a/src/net/torvald/terrarum/AppLoader.java b/src/net/torvald/terrarum/AppLoader.java index 9af051cf5..826f0e41a 100644 --- a/src/net/torvald/terrarum/AppLoader.java +++ b/src/net/torvald/terrarum/AppLoader.java @@ -205,9 +205,9 @@ public class AppLoader implements ApplicationListener { try { processor = GetCpuName.getModelName(); } - catch (IOException e1) { processor = "Unknown"; } + catch (IOException e1) { processor = "Unknown CPU"; } try { processorVendor = GetCpuName.getCPUID(); } - catch (IOException e2) { processorVendor = "Unknown"; } + catch (IOException e2) { processorVendor = "Unknown CPU"; } ShaderProgram.pedantic = false; diff --git a/src/net/torvald/terrarum/IngameInstance.kt b/src/net/torvald/terrarum/IngameInstance.kt index 8b6fd1faf..8b6e85771 100644 --- a/src/net/torvald/terrarum/IngameInstance.kt +++ b/src/net/torvald/terrarum/IngameInstance.kt @@ -126,6 +126,9 @@ open class IngameInstance(val batch: SpriteBatch) : Screen { /** * Wire version of terrainChanged() event + * + * @param old previous settings of conduits in bit set format. + * @param new current settings of conduits in bit set format. */ open fun queueWireChangedEvent(old: Int, new: Int, position: Long) { val (x, y) = LandUtil.resolveBlockAddr(world, position) diff --git a/src/net/torvald/terrarum/blockproperties/Wire.kt b/src/net/torvald/terrarum/blockproperties/Wire.kt index 7005afc08..dc1d59c77 100644 --- a/src/net/torvald/terrarum/blockproperties/Wire.kt +++ b/src/net/torvald/terrarum/blockproperties/Wire.kt @@ -6,24 +6,25 @@ package net.torvald.terrarum.blockproperties object Wire { /* A mapping for World's conduitTypes bits */ + /* Must be aligned with the sprite sheet */ const val BIT_NONE = 0 const val BIT_SIGNAL_RED = 1 const val BIT_UTILITY_PROTOTYPE = 2 // logic gates/PCLs/Diodes/Caps/etc. const val BIT_POWER_LOW = 4 const val BIT_POWER_HIGHT = 8 - const val BIT_PARALLEL_8B = 16 // uses bit-to-mantissa encoding - const val BIT_PARALLEL_16B = 32 // uses bit-to-mantissa encoding. 16 bit half duplex OR 8 bit full duplex - const val BIT_ETHERNET = 64 // the actual datagramme should be represented by another means than the ConduitFills + const val BIT_THICKNET = 16 // the actual datagramme should be represented by another means than the ConduitFills + const val BIT_PARALLEL_8B = 32 // uses bit-to-mantissa encoding + const val BIT_PARALLEL_16B = 64 // uses bit-to-mantissa encoding. 16 bit half duplex OR 8 bit full duplex /* A mapping for World's WiringNode.fills[] index */ - const val FILL_ID_SIGNAL_RED = 0 + /*const val FILL_ID_SIGNAL_RED = 0 const val FILL_ID_UTILITY_PROTOTYPE = 1 fun bitToConduitFillID(bit: Int) = when(bit) { BIT_SIGNAL_RED -> FILL_ID_SIGNAL_RED BIT_UTILITY_PROTOTYPE -> FILL_ID_UTILITY_PROTOTYPE else -> null - } + }*/ /** @@ -35,8 +36,8 @@ object Wire { * s eeeeeeee bbbbbbbb cccccccc xxxxxxx * s: sign (ignored) * e: binary32 exponent (non-zero and non-255) - * b: upper byte - * c: lower byte (zero for Byte representation) + * b: upper octet + * c: lower octet (zero for Byte representation) * x: not used, all zero * ``` * diff --git a/src/net/torvald/terrarum/gamecontroller/IngameController.kt b/src/net/torvald/terrarum/gamecontroller/IngameController.kt index aa9a4ec69..d5e94528d 100644 --- a/src/net/torvald/terrarum/gamecontroller/IngameController.kt +++ b/src/net/torvald/terrarum/gamecontroller/IngameController.kt @@ -6,16 +6,12 @@ import com.badlogic.gdx.InputAdapter import com.badlogic.gdx.controllers.Controllers import net.torvald.terrarum.AppLoader import net.torvald.terrarum.AppLoader.printdbg -import net.torvald.terrarum.Terrarum import net.torvald.terrarum.controller.TerrarumController import net.torvald.terrarum.floorInt import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameworld.fmod -import net.torvald.terrarum.itemproperties.GameItem -import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.modulebasegame.Ingame import net.torvald.terrarum.worlddrawer.CreateTileAtlas -import net.torvald.terrarum.worlddrawer.FeaturesDrawer import net.torvald.terrarum.worlddrawer.WorldCamera /** @@ -57,22 +53,16 @@ class IngameController(val ingame: Ingame) : InputAdapter() { /////////////////// // Use item: assuming the player has only one effective grip (EquipPosition.HAND_GRIP) + // don't separate Player from this! Physics will break, esp. airborne manoeuvre if (ingame.canPlayerControl) { - if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary")) || - Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) { + // fire world click events; the event is defined as Ingame's (or any others') WorldClick event + if (ingame.uiContainer.map { if ((it.isOpening || it.isOpened) && it.mouseUp) 1 else 0 }.sum() == 0) { // no UI on the mouse, right? - val player = (Terrarum.ingame!! as Ingame).actorNowPlaying - if (player == null) return - - val itemOnGrip = player.inventory.itemEquipped[GameItem.EquipPosition.HAND_GRIP] - - itemOnGrip?.let { - if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary"))) { - player.consumePrimary(ItemCodex[it]!!) - } - if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) { - player.consumeSecondary(ItemCodex[it]!!) - } + if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary"))) { + ingame.worldPrimaryClickStart(AppLoader.UPDATE_RATE.toFloat()) + } + if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) { + ingame.worldSecondaryClickStart(AppLoader.UPDATE_RATE.toFloat()) } } } @@ -192,21 +182,6 @@ class IngameController(val ingame: Ingame) : InputAdapter() { } override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { - // don't separate Player from this! Physics will break, esp. airborne manoeuvre - if (ingame.canPlayerControl) { - // fire world click events; the event is defined as Ingame's (or any others') WorldClick event - if (ingame.uiContainer.map { if ((it.isOpening || it.isOpened) && it.mouseUp) 1 else 0 }.sum() == 0) { // no UI on the mouse, right? - - if (button == AppLoader.getConfigInt("mouseprimary")) { - ingame.worldPrimaryClickStart(AppLoader.UPDATE_RATE.toFloat()) - } - if (button == AppLoader.getConfigInt("mousesecondary")) { - ingame.worldSecondaryClickStart(AppLoader.UPDATE_RATE.toFloat()) - } - } - } - - ingame.uiContainer.forEach { it.touchDown(screenX, screenY, pointer, button) } return true } diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index 87aa42f4b..a094689a2 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -10,7 +10,6 @@ import net.torvald.terrarum.blockproperties.Fluid import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.serialise.ReadLayerDataZip -import net.torvald.terrarum.toInt import net.torvald.util.SortedArrayList import org.dyn4j.geometry.Vector2 import kotlin.math.absoluteValue @@ -75,20 +74,16 @@ open class GameWorld { @TEMzPayload("FlFL", TEMzPayload.INT48_FLOAT_PAIR) val fluidFills: HashMap - // Actually stores the wiring data; used by renderers // + /** + * Single block can have multiple conduits, different types of conduits are stored separately. + */ + @TEMzPayload("WiNt", TEMzPayload.EXTERNAL_JSON) + private val wirings: HashMap> - @TEMzPayload("CtYP", TEMzPayload.INT48_INT_PAIR) - val conduitTypes: HashMap // 1 bit = 1 conduit (pipe/wire) type - @TEMzPayload("CfL", TEMzPayload.INT48_FLOAT_PAIR) - val conduitFills: Array> - val conduitFills0: HashMap // size of liquid packet on the block - get() = conduitFills[0] - val conduitFills1: HashMap // size of gas packet on the block - get() = conduitFills[1] - - // Built from the above data; used by hypothetical updater // - - private val wiringNodes = SortedArrayList() + /** + * Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised. + */ + private val wiringBlocks: HashMap //public World physWorld = new World( new Vec2(0, -Terrarum.game.gravitationalAccel) ); //physics @@ -119,13 +114,13 @@ open class GameWorld { layerTerrainLowBits = PairedMapLayer(width, height) layerWallLowBits = PairedMapLayer(width, height) - wallDamages = HashMap() - terrainDamages = HashMap() - fluidTypes = HashMap() - fluidFills = HashMap() + wallDamages = HashMap() + terrainDamages = HashMap() + fluidTypes = HashMap() + fluidFills = HashMap() - conduitTypes = HashMap() - conduitFills = Array(16) { HashMap() } + wiringBlocks = HashMap() + wirings = HashMap() // temperature layer: 2x2 is one cell //layerThermal = MapLayerHalfFloat(width, height, averageTemperature) @@ -153,8 +148,8 @@ open class GameWorld { fluidTypes = layerData.fluidTypes fluidFills = layerData.fluidFills - conduitTypes = HashMap() - conduitFills = Array(16) { HashMap() } + wiringBlocks = HashMap() + wirings = HashMap() spawnX = layerData.spawnX spawnY = layerData.spawnY @@ -215,8 +210,8 @@ open class GameWorld { terrain * PairedMapLayer.RANGE + terrainDamage } - fun getWires(x: Int, y: Int): Int { - return conduitTypes.getOrDefault(LandUtil.getBlockAddr(this, x, y), 0) + fun getWiringBlocks(x: Int, y: Int): Int { + return wiringBlocks.getOrDefault(LandUtil.getBlockAddr(this, x, y), 0) } fun getWallLowBits(x: Int, y: Int): Int? { @@ -296,25 +291,31 @@ open class GameWorld { Terrarum.ingame?.queueWireChangedEvent(oldWire, tile.toUint(), LandUtil.getBlockAddr(this, x, y)) }*/ - /** - * Overrides entire bits with given value. DO NOT USE THIS if you don't know what this means, you'll want to use setWire(). - * Besides, this function won't fire WireChangedEvent - */ - fun setWires(x: Int, y: Int, wireBits: Int) { - val (x, y) = coerceXY(x, y) - conduitTypes[LandUtil.getBlockAddr(this, x, y)] = wireBits + fun getAllConduitsFrom(x: Int, y: Int): SortedArrayList? { + return wirings.get(LandUtil.getBlockAddr(this, x, y)) } /** - * Sets single bit for given tile. YOU'LL WANT TO USE THIS instead of setWires() - * @param selectedWire wire-bit to modify, must be power of two + * @param conduitTypeBit defined in net.torvald.terrarum.blockproperties.Wire, always power-of-two */ - fun setWire(x: Int, y: Int, selectedWire: Int, bitToSet: Boolean) { - val oldWireBits = getWires(x, y) - val oldStatus = getWires(x, y) or selectedWire != 0 - if (oldStatus != bitToSet) { - setWires(x, y, (oldWireBits and selectedWire.inv()) or (selectedWire * oldStatus.toInt())) - Terrarum.ingame?.queueWireChangedEvent(selectedWire * oldStatus.toInt(), selectedWire * bitToSet.toInt(), LandUtil.getBlockAddr(this, x, y)) + fun getConduitByTypeFrom(x: Int, y: Int, conduitTypeBit: Int): WiringNode? { + val conduits = getAllConduitsFrom(x, y) + return conduits?.searchFor(conduitTypeBit) { it.typeBitMask } + } + + fun addNewConduitTo(x: Int, y: Int, node: WiringNode) { + val blockAddr = LandUtil.getBlockAddr(this, x, y) + + // check for existing type of conduit + // if there's no duplicate... + if (getWiringBlocks(x, y) and node.typeBitMask == 0) { + // store as-is + wirings.getOrPut(blockAddr) { SortedArrayList() }.add(node) + // synchronise wiringBlocks + wiringBlocks[blockAddr] = (wiringBlocks[blockAddr] ?: 0) or node.typeBitMask + } + else { + TODO("need overwriting policy for existing conduit node") } } @@ -326,7 +327,7 @@ open class GameWorld { return getTileFromWall(x, y) } else if (mode == WIRE) { - return getWires(x, y) + return getWiringBlocks(x, y) } else throw IllegalArgumentException("illegal mode input: " + mode.toString()) @@ -487,7 +488,7 @@ open class GameWorld { override fun toString() = "Fluid type: ${type.value}, amount: $amount" } - private data class WiringNode( + data class WiringNode( val position: BlockAddress, /** One defined in WireCodex, always power of two */ val typeBitMask: Int, @@ -540,6 +541,9 @@ inline class FluidType(val value: Int) { */ annotation class TEMzPayload(val payloadName: String, val arg: Int) { companion object { + const val EXTERNAL_JAVAPROPERTIES = -3 + const val EXTERNAL_CSV = -2 + const val EXTERNAL_JSON = -1 const val EIGHT_MSB = 0 const val FOUR_LSB = 1 const val INT48_FLOAT_PAIR = 2 diff --git a/src/net/torvald/terrarum/modulebasegame/EntryPoint.kt b/src/net/torvald/terrarum/modulebasegame/EntryPoint.kt index 0536cf0b9..1299cbd89 100644 --- a/src/net/torvald/terrarum/modulebasegame/EntryPoint.kt +++ b/src/net/torvald/terrarum/modulebasegame/EntryPoint.kt @@ -94,7 +94,7 @@ class EntryPoint : ModuleEntryPoint() { this.inventoryCategory == Category.WALL && this.dynamicID - ItemCodex.ITEM_WALLS.start == ingame.world.getTileFromWall(Terrarum.mouseTileX, Terrarum.mouseTileY) || this.inventoryCategory == Category.WIRE && - 1.shl(this.dynamicID - ItemCodex.ITEM_WIRES.start) and (ingame.world.getWires(Terrarum.mouseTileX, Terrarum.mouseTileY) ?: 0) != 0 + 1.shl(this.dynamicID - ItemCodex.ITEM_WIRES.start) and (ingame.world.getWiringBlocks(Terrarum.mouseTileX, Terrarum.mouseTileY) ?: 0) != 0 ) return false diff --git a/src/net/torvald/terrarum/modulebasegame/Ingame.kt b/src/net/torvald/terrarum/modulebasegame/Ingame.kt index 827b2ead3..33058a828 100644 --- a/src/net/torvald/terrarum/modulebasegame/Ingame.kt +++ b/src/net/torvald/terrarum/modulebasegame/Ingame.kt @@ -394,7 +394,9 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) { override fun worldPrimaryClickStart(delta: Float) { val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP) - ItemCodex[itemOnGrip]?.startPrimaryUse(delta) + val consumptionSuccessful = ItemCodex[itemOnGrip]?.startPrimaryUse(delta) ?: false + if (consumptionSuccessful) + actorNowPlaying?.inventory?.consumeItem(ItemCodex[itemOnGrip]!!) } override fun worldPrimaryClickEnd(delta: Float) { @@ -404,7 +406,9 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) { override fun worldSecondaryClickStart(delta: Float) { val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP) - ItemCodex[itemOnGrip]?.startSecondaryUse(delta) + val consumptionSuccessful = ItemCodex[itemOnGrip]?.startSecondaryUse(delta) ?: false + if (consumptionSuccessful) + actorNowPlaying?.inventory?.consumeItem(ItemCodex[itemOnGrip]!!) } override fun worldSecondaryClickEnd(delta: Float) { diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt index 19262e5a9..0b972159b 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/ActorInventory.kt @@ -177,7 +177,9 @@ class ActorInventory(@Transient val actor: Pocketed, var maxCapacity: Int, var c false - fun consumeItem(actor: Actor, item: GameItem) { + fun consumeItem(item: GameItem) { + val actor = this.actor as Actor + if (item.stackable && !item.isDynamic) { remove(item, 1) } diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/Pocketed.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/Pocketed.kt index 30f28a372..99c052c93 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/Pocketed.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/Pocketed.kt @@ -1,7 +1,6 @@ package net.torvald.terrarum.modulebasegame.gameactors import net.torvald.terrarum.AppLoader -import net.torvald.terrarum.gameactors.Actor import net.torvald.terrarum.itemproperties.GameItem import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.ItemID @@ -79,15 +78,4 @@ interface Pocketed { fun hasItem(item: GameItem) = inventory.contains(item.dynamicID) fun hasItem(id: Int) = inventory.contains(id) - - fun consumePrimary(item: GameItem) { - if (item.startPrimaryUse(AppLoader.UPDATE_RATE.toFloat())) { - inventory.consumeItem(this as Actor, item) // consume on successful - } - } - - fun consumeSecondary(item: GameItem) { - if (item.startSecondaryUse(AppLoader.UPDATE_RATE.toFloat())) - inventory.consumeItem(this as Actor, item) // consume on successful - } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/items/WirePieceSignalWire.kt b/src/net/torvald/terrarum/modulebasegame/items/WirePieceSignalWire.kt index 18e24f1f3..eec295a1b 100644 --- a/src/net/torvald/terrarum/modulebasegame/items/WirePieceSignalWire.kt +++ b/src/net/torvald/terrarum/modulebasegame/items/WirePieceSignalWire.kt @@ -32,7 +32,9 @@ class WirePieceSignalWire(override val originalID: ItemID) : GameItem() { } override fun startPrimaryUse(delta: Float): Boolean { - return super.startPrimaryUse(delta) + println("Wire!") + + return true } override fun effectWhenEquipped(delta: Float) { diff --git a/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt b/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt index baa2d9ee7..9b54b4e76 100644 --- a/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt +++ b/src/net/torvald/terrarum/worlddrawer/BlocksDrawerNew.kt @@ -263,12 +263,13 @@ internal object BlocksDrawer { * Turns bitmask-with-single-bit-set into its bit index. The LSB is counted as 1, and thus the index starts at one. * @return 0 -> null, 1 -> 0, 2 -> 1, 4 -> 2, 8 -> 3, 16 -> 4, ... */ - private fun Int.toBitOrd(): Int? = - if (this > 0 && !FastMath.isPowerOfTwo(this)) throw IllegalArgumentException("value must be power of two: $this") - else { - val k = FastMath.intLog2(this, -1) - if (k == -1) null else k - } + private fun Int.toBitOrd(): Int? { + //if (this > 0 && !FastMath.isPowerOfTwo(this)) throw IllegalArgumentException("value must be power of two: $this") + //else { + val k = FastMath.intLog2(this, -1) + return if (k == -1) null else k + //} + } /** * Writes to buffer. Actual draw code must be called after this operation. @@ -305,7 +306,7 @@ internal object BlocksDrawer { val thisTile = when (mode) { WALL -> world.getTileFromWall(x, y) TERRAIN -> world.getTileFromTerrain(x, y) - WIRE -> world.getWires(x, y).and(wireBit).toBitOrd() + WIRE -> world.getWiringBlocks(x, y).and(wireBit).toBitOrd() FLUID -> world.getFluid(x, y).type.abs() else -> throw IllegalArgumentException() } diff --git a/src/net/torvald/util/SortedArrayList.kt b/src/net/torvald/util/SortedArrayList.kt index f583c2b60..3177fa6cb 100644 --- a/src/net/torvald/util/SortedArrayList.kt +++ b/src/net/torvald/util/SortedArrayList.kt @@ -66,21 +66,25 @@ class SortedArrayList>(initialSize: Int = 10) { return false // key not found } - /** Searches the element using given predicate instead of the element itself. Returns index in the array where desired + /** Searches the element using given predicate instead of the element itself. Returns index in the array where desired, null when there is no such element. * element is stored. - * (e.g. search the Actor by its ID rather than the actor instance) */ - fun > searchForIndex(key: R, predicate: (T) -> R): Int? { + * (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? + */ + fun > searchForIndex(searchQuery: R, searchHow: (T) -> R): Int? { var low = 0 var high = this.size - 1 while (low <= high) { val mid = (low + high).ushr(1) // safe from overflows - val midVal = predicate(get(mid)) + val midVal = searchHow(get(mid)) - if (key > midVal) + if (searchQuery > midVal) low = mid + 1 - else if (key < midVal) + else if (searchQuery < midVal) high = mid - 1 else return mid // key found @@ -88,9 +92,13 @@ class SortedArrayList>(initialSize: Int = 10) { return null // key not found } - /** Searches the element using given predicate instead of the element itself. Returns the element desired. - * (e.g. search the Actor by its ID rather than the actor instance) */ - fun > searchFor(key: R, predicate: (T) -> R): T? = getOrNull(searchForIndex(key, predicate)) + /** Searches the element using given predicate instead of the element itself. Returns the element desired, null when there is no such element. + * (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? + */ + fun > searchFor(searchQuery: R, searchHow: (T) -> R): T? = getOrNull(searchForIndex(searchQuery, searchHow)) fun iterator() = arrayList.iterator() fun forEach(action: (T) -> Unit) = arrayList.forEach(action)