fixed a long bug where item would be used twice but discounted by only one on single click

This commit is contained in:
minjaesong
2019-05-01 23:26:01 +09:00
parent 839b7e7762
commit 5816e678bf
12 changed files with 104 additions and 116 deletions

View File

@@ -205,9 +205,9 @@ public class AppLoader implements ApplicationListener {
try { processor = GetCpuName.getModelName(); } try { processor = GetCpuName.getModelName(); }
catch (IOException e1) { processor = "Unknown"; } catch (IOException e1) { processor = "Unknown CPU"; }
try { processorVendor = GetCpuName.getCPUID(); } try { processorVendor = GetCpuName.getCPUID(); }
catch (IOException e2) { processorVendor = "Unknown"; } catch (IOException e2) { processorVendor = "Unknown CPU"; }
ShaderProgram.pedantic = false; ShaderProgram.pedantic = false;

View File

@@ -126,6 +126,9 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
/** /**
* Wire version of terrainChanged() event * 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) { open fun queueWireChangedEvent(old: Int, new: Int, position: Long) {
val (x, y) = LandUtil.resolveBlockAddr(world, position) val (x, y) = LandUtil.resolveBlockAddr(world, position)

View File

@@ -6,24 +6,25 @@ package net.torvald.terrarum.blockproperties
object Wire { object Wire {
/* A mapping for World's conduitTypes bits */ /* A mapping for World's conduitTypes bits */
/* Must be aligned with the sprite sheet */
const val BIT_NONE = 0 const val BIT_NONE = 0
const val BIT_SIGNAL_RED = 1 const val BIT_SIGNAL_RED = 1
const val BIT_UTILITY_PROTOTYPE = 2 // logic gates/PCLs/Diodes/Caps/etc. const val BIT_UTILITY_PROTOTYPE = 2 // logic gates/PCLs/Diodes/Caps/etc.
const val BIT_POWER_LOW = 4 const val BIT_POWER_LOW = 4
const val BIT_POWER_HIGHT = 8 const val BIT_POWER_HIGHT = 8
const val BIT_PARALLEL_8B = 16 // uses bit-to-mantissa encoding const val BIT_THICKNET = 16 // the actual datagramme should be represented by another means than the ConduitFills
const val BIT_PARALLEL_16B = 32 // uses bit-to-mantissa encoding. 16 bit half duplex OR 8 bit full duplex const val BIT_PARALLEL_8B = 32 // uses bit-to-mantissa encoding
const val BIT_ETHERNET = 64 // the actual datagramme should be represented by another means than the ConduitFills 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 */ /* 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 const val FILL_ID_UTILITY_PROTOTYPE = 1
fun bitToConduitFillID(bit: Int) = when(bit) { fun bitToConduitFillID(bit: Int) = when(bit) {
BIT_SIGNAL_RED -> FILL_ID_SIGNAL_RED BIT_SIGNAL_RED -> FILL_ID_SIGNAL_RED
BIT_UTILITY_PROTOTYPE -> FILL_ID_UTILITY_PROTOTYPE BIT_UTILITY_PROTOTYPE -> FILL_ID_UTILITY_PROTOTYPE
else -> null else -> null
} }*/
/** /**
@@ -35,8 +36,8 @@ object Wire {
* s eeeeeeee bbbbbbbb cccccccc xxxxxxx * s eeeeeeee bbbbbbbb cccccccc xxxxxxx
* s: sign (ignored) * s: sign (ignored)
* e: binary32 exponent (non-zero and non-255) * e: binary32 exponent (non-zero and non-255)
* b: upper byte * b: upper octet
* c: lower byte (zero for Byte representation) * c: lower octet (zero for Byte representation)
* x: not used, all zero * x: not used, all zero
* ``` * ```
* *

View File

@@ -6,16 +6,12 @@ import com.badlogic.gdx.InputAdapter
import com.badlogic.gdx.controllers.Controllers import com.badlogic.gdx.controllers.Controllers
import net.torvald.terrarum.AppLoader import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.AppLoader.printdbg import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.controller.TerrarumController import net.torvald.terrarum.controller.TerrarumController
import net.torvald.terrarum.floorInt import net.torvald.terrarum.floorInt
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameworld.fmod 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.modulebasegame.Ingame
import net.torvald.terrarum.worlddrawer.CreateTileAtlas import net.torvald.terrarum.worlddrawer.CreateTileAtlas
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarum.worlddrawer.WorldCamera 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) // 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 (ingame.canPlayerControl) {
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary")) || // fire world click events; the event is defined as Ingame's (or any others') WorldClick event
Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) { 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 (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary"))) {
if (player == null) return ingame.worldPrimaryClickStart(AppLoader.UPDATE_RATE.toFloat())
}
val itemOnGrip = player.inventory.itemEquipped[GameItem.EquipPosition.HAND_GRIP] if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) {
ingame.worldSecondaryClickStart(AppLoader.UPDATE_RATE.toFloat())
itemOnGrip?.let {
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary"))) {
player.consumePrimary(ItemCodex[it]!!)
}
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) {
player.consumeSecondary(ItemCodex[it]!!)
}
} }
} }
} }
@@ -192,21 +182,6 @@ class IngameController(val ingame: Ingame) : InputAdapter() {
} }
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { 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) } ingame.uiContainer.forEach { it.touchDown(screenX, screenY, pointer, button) }
return true return true
} }

View File

@@ -10,7 +10,6 @@ import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.serialise.ReadLayerDataZip import net.torvald.terrarum.serialise.ReadLayerDataZip
import net.torvald.terrarum.toInt
import net.torvald.util.SortedArrayList import net.torvald.util.SortedArrayList
import org.dyn4j.geometry.Vector2 import org.dyn4j.geometry.Vector2
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@@ -75,20 +74,16 @@ open class GameWorld {
@TEMzPayload("FlFL", TEMzPayload.INT48_FLOAT_PAIR) @TEMzPayload("FlFL", TEMzPayload.INT48_FLOAT_PAIR)
val fluidFills: HashMap<BlockAddress, Float> val fluidFills: HashMap<BlockAddress, Float>
// 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<BlockAddress, SortedArrayList<WiringNode>>
@TEMzPayload("CtYP", TEMzPayload.INT48_INT_PAIR) /**
val conduitTypes: HashMap<BlockAddress, Int> // 1 bit = 1 conduit (pipe/wire) type * Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised.
@TEMzPayload("CfL", TEMzPayload.INT48_FLOAT_PAIR) */
val conduitFills: Array<HashMap<BlockAddress, Float>> private val wiringBlocks: HashMap<BlockAddress, Int>
val conduitFills0: HashMap<BlockAddress, Float> // size of liquid packet on the block
get() = conduitFills[0]
val conduitFills1: HashMap<BlockAddress, Float> // size of gas packet on the block
get() = conduitFills[1]
// Built from the above data; used by hypothetical updater //
private val wiringNodes = SortedArrayList<WiringNode>()
//public World physWorld = new World( new Vec2(0, -Terrarum.game.gravitationalAccel) ); //public World physWorld = new World( new Vec2(0, -Terrarum.game.gravitationalAccel) );
//physics //physics
@@ -119,13 +114,13 @@ open class GameWorld {
layerTerrainLowBits = PairedMapLayer(width, height) layerTerrainLowBits = PairedMapLayer(width, height)
layerWallLowBits = PairedMapLayer(width, height) layerWallLowBits = PairedMapLayer(width, height)
wallDamages = HashMap<BlockAddress, Float>() wallDamages = HashMap()
terrainDamages = HashMap<BlockAddress, Float>() terrainDamages = HashMap()
fluidTypes = HashMap<BlockAddress, FluidType>() fluidTypes = HashMap()
fluidFills = HashMap<BlockAddress, Float>() fluidFills = HashMap()
conduitTypes = HashMap<BlockAddress, Int>() wiringBlocks = HashMap()
conduitFills = Array(16) { HashMap<BlockAddress, Float>() } wirings = HashMap()
// temperature layer: 2x2 is one cell // temperature layer: 2x2 is one cell
//layerThermal = MapLayerHalfFloat(width, height, averageTemperature) //layerThermal = MapLayerHalfFloat(width, height, averageTemperature)
@@ -153,8 +148,8 @@ open class GameWorld {
fluidTypes = layerData.fluidTypes fluidTypes = layerData.fluidTypes
fluidFills = layerData.fluidFills fluidFills = layerData.fluidFills
conduitTypes = HashMap<BlockAddress, Int>() wiringBlocks = HashMap()
conduitFills = Array(16) { HashMap<BlockAddress, Float>() } wirings = HashMap()
spawnX = layerData.spawnX spawnX = layerData.spawnX
spawnY = layerData.spawnY spawnY = layerData.spawnY
@@ -215,8 +210,8 @@ open class GameWorld {
terrain * PairedMapLayer.RANGE + terrainDamage terrain * PairedMapLayer.RANGE + terrainDamage
} }
fun getWires(x: Int, y: Int): Int { fun getWiringBlocks(x: Int, y: Int): Int {
return conduitTypes.getOrDefault(LandUtil.getBlockAddr(this, x, y), 0) return wiringBlocks.getOrDefault(LandUtil.getBlockAddr(this, x, y), 0)
} }
fun getWallLowBits(x: Int, y: Int): Int? { 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)) Terrarum.ingame?.queueWireChangedEvent(oldWire, tile.toUint(), LandUtil.getBlockAddr(this, x, y))
}*/ }*/
/** fun getAllConduitsFrom(x: Int, y: Int): SortedArrayList<WiringNode>? {
* Overrides entire bits with given value. DO NOT USE THIS if you don't know what this means, you'll want to use setWire(). return wirings.get(LandUtil.getBlockAddr(this, x, y))
* 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
} }
/** /**
* Sets single bit for given tile. YOU'LL WANT TO USE THIS instead of setWires() * @param conduitTypeBit defined in net.torvald.terrarum.blockproperties.Wire, always power-of-two
* @param selectedWire wire-bit to modify, must be power of two
*/ */
fun setWire(x: Int, y: Int, selectedWire: Int, bitToSet: Boolean) { fun getConduitByTypeFrom(x: Int, y: Int, conduitTypeBit: Int): WiringNode? {
val oldWireBits = getWires(x, y) val conduits = getAllConduitsFrom(x, y)
val oldStatus = getWires(x, y) or selectedWire != 0 return conduits?.searchFor(conduitTypeBit) { it.typeBitMask }
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 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) return getTileFromWall(x, y)
} }
else if (mode == WIRE) { else if (mode == WIRE) {
return getWires(x, y) return getWiringBlocks(x, y)
} }
else else
throw IllegalArgumentException("illegal mode input: " + mode.toString()) throw IllegalArgumentException("illegal mode input: " + mode.toString())
@@ -487,7 +488,7 @@ open class GameWorld {
override fun toString() = "Fluid type: ${type.value}, amount: $amount" override fun toString() = "Fluid type: ${type.value}, amount: $amount"
} }
private data class WiringNode( data class WiringNode(
val position: BlockAddress, val position: BlockAddress,
/** One defined in WireCodex, always power of two */ /** One defined in WireCodex, always power of two */
val typeBitMask: Int, val typeBitMask: Int,
@@ -540,6 +541,9 @@ inline class FluidType(val value: Int) {
*/ */
annotation class TEMzPayload(val payloadName: String, val arg: Int) { annotation class TEMzPayload(val payloadName: String, val arg: Int) {
companion object { companion object {
const val EXTERNAL_JAVAPROPERTIES = -3
const val EXTERNAL_CSV = -2
const val EXTERNAL_JSON = -1
const val EIGHT_MSB = 0 const val EIGHT_MSB = 0
const val FOUR_LSB = 1 const val FOUR_LSB = 1
const val INT48_FLOAT_PAIR = 2 const val INT48_FLOAT_PAIR = 2

View File

@@ -94,7 +94,7 @@ class EntryPoint : ModuleEntryPoint() {
this.inventoryCategory == Category.WALL && this.inventoryCategory == Category.WALL &&
this.dynamicID - ItemCodex.ITEM_WALLS.start == ingame.world.getTileFromWall(Terrarum.mouseTileX, Terrarum.mouseTileY) || this.dynamicID - ItemCodex.ITEM_WALLS.start == ingame.world.getTileFromWall(Terrarum.mouseTileX, Terrarum.mouseTileY) ||
this.inventoryCategory == Category.WIRE && 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 return false

View File

@@ -394,7 +394,9 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
override fun worldPrimaryClickStart(delta: Float) { override fun worldPrimaryClickStart(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP) 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) { override fun worldPrimaryClickEnd(delta: Float) {
@@ -404,7 +406,9 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
override fun worldSecondaryClickStart(delta: Float) { override fun worldSecondaryClickStart(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP) 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) { override fun worldSecondaryClickEnd(delta: Float) {

View File

@@ -177,7 +177,9 @@ class ActorInventory(@Transient val actor: Pocketed, var maxCapacity: Int, var c
false false
fun consumeItem(actor: Actor, item: GameItem) { fun consumeItem(item: GameItem) {
val actor = this.actor as Actor
if (item.stackable && !item.isDynamic) { if (item.stackable && !item.isDynamic) {
remove(item, 1) remove(item, 1)
} }

View File

@@ -1,7 +1,6 @@
package net.torvald.terrarum.modulebasegame.gameactors package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.AppLoader import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.itemproperties.GameItem import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.ItemID import net.torvald.terrarum.itemproperties.ItemID
@@ -79,15 +78,4 @@ interface Pocketed {
fun hasItem(item: GameItem) = inventory.contains(item.dynamicID) fun hasItem(item: GameItem) = inventory.contains(item.dynamicID)
fun hasItem(id: Int) = inventory.contains(id) 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
}
} }

View File

@@ -32,7 +32,9 @@ class WirePieceSignalWire(override val originalID: ItemID) : GameItem() {
} }
override fun startPrimaryUse(delta: Float): Boolean { override fun startPrimaryUse(delta: Float): Boolean {
return super.startPrimaryUse(delta) println("Wire!")
return true
} }
override fun effectWhenEquipped(delta: Float) { override fun effectWhenEquipped(delta: Float) {

View File

@@ -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. * 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, ... * @return 0 -> null, 1 -> 0, 2 -> 1, 4 -> 2, 8 -> 3, 16 -> 4, ...
*/ */
private fun Int.toBitOrd(): Int? = private fun Int.toBitOrd(): Int? {
if (this > 0 && !FastMath.isPowerOfTwo(this)) throw IllegalArgumentException("value must be power of two: $this") //if (this > 0 && !FastMath.isPowerOfTwo(this)) throw IllegalArgumentException("value must be power of two: $this")
else { //else {
val k = FastMath.intLog2(this, -1) val k = FastMath.intLog2(this, -1)
if (k == -1) null else k return if (k == -1) null else k
} //}
}
/** /**
* Writes to buffer. Actual draw code must be called after this operation. * Writes to buffer. Actual draw code must be called after this operation.
@@ -305,7 +306,7 @@ internal object BlocksDrawer {
val thisTile = when (mode) { val thisTile = when (mode) {
WALL -> world.getTileFromWall(x, y) WALL -> world.getTileFromWall(x, y)
TERRAIN -> world.getTileFromTerrain(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() FLUID -> world.getFluid(x, y).type.abs()
else -> throw IllegalArgumentException() else -> throw IllegalArgumentException()
} }

View File

@@ -66,21 +66,25 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
return false // key not found 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. * element is stored.
* (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)
fun <R: Comparable<R>> searchForIndex(key: R, predicate: (T) -> R): Int? { *
* @param searchQuery what exactly are we looking for?
* @param searchHow and where or how can it be found?
*/
fun <R: Comparable<R>> searchForIndex(searchQuery: R, searchHow: (T) -> R): Int? {
var low = 0 var low = 0
var high = this.size - 1 var high = this.size - 1
while (low <= high) { while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows 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 low = mid + 1
else if (key < midVal) else if (searchQuery < midVal)
high = mid - 1 high = mid - 1
else else
return mid // key found return mid // key found
@@ -88,9 +92,13 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
return null // key not found return null // key not found
} }
/** Searches the element using given predicate instead of the element itself. Returns the element desired. /** 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) */ * (e.g. search the Actor by its ID rather than the actor instance)
fun <R: Comparable<R>> searchFor(key: R, predicate: (T) -> R): T? = getOrNull(searchForIndex(key, predicate)) *
* @param searchQuery what exactly are we looking for?
* @param searchHow and where or how can it be found?
*/
fun <R: Comparable<R>> searchFor(searchQuery: R, searchHow: (T) -> R): T? = getOrNull(searchForIndex(searchQuery, searchHow))
fun iterator() = arrayList.iterator() fun iterator() = arrayList.iterator()
fun forEach(action: (T) -> Unit) = arrayList.forEach(action) fun forEach(action: (T) -> Unit) = arrayList.forEach(action)