diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 91eff1b10..3104e8e61 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -373,7 +373,7 @@ oc:tooltip.UpgradeExperience=This upgrade allows robots to accumulate experience oc:tooltip.UpgradeGenerator=Can be used to generate energy from fuel on the go. Burns items to generate energy over time, based on their fuel value.[nl] §fEfficiency§7: §a%s%%§7 oc:tooltip.UpgradeHover=This upgrade allows robots to fly higher above the ground without having to climb walls.[nl] Maximum height: §f%s§7 oc:tooltip.UpgradeInventory=This upgrade provides inventory space to a robot or drone. Without one of these, they will not be able to store items internally. -oc:tooltip.UpgradeInventoryController=This upgrade allows robots and drones more control in how it interacts with external inventories, and allows robots to swap their equipped tool with an item in their inventory. +oc:tooltip.UpgradeInventoryController=This upgrade allows robots and drones more control in how it interact with external inventories, and allows robots to swap their equipped tool with an item in their inventory. oc:tooltip.UpgradeMF=Allows adapters to access blocks they are not adjacent to. oc:tooltip.UpgradeMF.Linked=§fConnection established§7 oc:tooltip.UpgradeMF.Unlinked=§fNo connection§7 diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua index ca2f8e199..cec5ad009 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/edit.lua @@ -59,7 +59,9 @@ local function loadConfig() save = {{"control", "s"}}, close = {{"control", "w"}}, find = {{"control", "f"}}, - findnext = {{"control", "g"}, {"control", "n"}, {"f3"}} + findnext = {{"control", "g"}, {"control", "n"}, {"f3"}}, + cut = {{"control", "k"}}, + uncut = {{"control", "u"}} } -- Generate config file if it didn't exist. if not config then @@ -87,6 +89,11 @@ local buffer = {} local scrollX, scrollY = 0, 0 local config = loadConfig() +local cutBuffer = {} +-- cutting is true while we're in a cutting operation and set to false when cursor changes lines +-- basically, whenever you change lines, the cutting operation ends, so the next time you cut a new buffer will be created +local cutting = false + local getKeyBindHandler -- forward declaration for refind() local function helpStatusText() @@ -110,7 +117,9 @@ local function helpStatusText() end return prettifyKeybind("Save", "save") .. prettifyKeybind("Close", "close") .. - prettifyKeybind("Find", "find") + prettifyKeybind("Find", "find") .. + prettifyKeybind("Cut", "cut") .. + prettifyKeybind("Uncut", "uncut") end ------------------------------------------------------------------------------- @@ -240,7 +249,12 @@ local function setCursor(nbx, nby) term.setCursor(nbx - scrollX, nby - scrollY) --update with term lib nbx, nby = getCursor() - gpu.set(x + w - 10, y + h, text.padLeft(string.format("%d,%d", nby, nbx), 10)) + local locstring = string.format("%d,%d", nby, nbx) + if #cutBuffer > 0 then + locstring = string.format("(#%d) %s", #cutBuffer, locstring) + end + locstring = text.padLeft(locstring, 10) + gpu.set(x + w - #locstring, y + h, locstring) end local function highlight(bx, by, length, enabled) @@ -316,6 +330,7 @@ local function up(n) if cby > 1 then setCursor(cbx, cby - n) end + cutting = false end local function down(n) @@ -324,6 +339,7 @@ local function down(n) if cby < #buffer then setCursor(cbx, cby + n) end + cutting = false end local function delete(fullRow) @@ -398,6 +414,7 @@ local function enter() end setCursor(1, cby + 1) setStatus(helpStatusText()) + cutting = false end local findText = "" @@ -452,6 +469,25 @@ local function find() setStatus(helpStatusText()) end +local function cut() + if not cutting then + cutBuffer = {} + end + local cbx, cby = getCursor() + table.insert(cutBuffer, buffer[cby]) + delete(true) + cutting = true + home() +end + +local function uncut() + home() + for _, line in ipairs(cutBuffer) do + insert(line) + enter() + end +end + ------------------------------------------------------------------------------- local keyBindHandlers = { @@ -542,7 +578,9 @@ local keyBindHandlers = { findText = "" find() end, - findnext = find + findnext = find, + cut = cut, + uncut = uncut } getKeyBindHandler = function(code) diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index 124842877..2c6960da5 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -499,9 +499,9 @@ object PacketHandler extends CommonPacketHandler { def onRedstoneState(p: PacketParser) = p.readTileEntity[RedstoneAware]() match { case Some(t) => - t.isOutputEnabled = p.readBoolean() + t.setOutputEnabled(p.readBoolean()) for (d <- EnumFacing.values) { - t.output(d, p.readByte()) + t.setOutput(d, p.readByte()) } case _ => // Invalid packet. } diff --git a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala index 21fa8aeb2..b480b4b51 100644 --- a/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/block/RedstoneAware.scala @@ -30,7 +30,7 @@ abstract class RedstoneAware extends SimpleBlock /* with IRedNetOmniNode TODO MF override def getWeakPower(state: IBlockState, world: IBlockAccess, pos: BlockPos, side: EnumFacing) = world.getTileEntity(pos) match { - case redstone: tileentity.traits.RedstoneAware if side != null => redstone.output(side.getOpposite) max 0 + case redstone: tileentity.traits.RedstoneAware if side != null => redstone.getOutput(side.getOpposite) max 0 case _ => super.getWeakPower(state, world, pos, side) } @@ -44,9 +44,9 @@ abstract class RedstoneAware extends SimpleBlock /* with IRedNetOmniNode TODO MF case t: tileentity.traits.BundledRedstoneAware => for (side <- EnumFacing.values) { world.getBlock(position.offset(side)) match { case block: IRedNetNetworkContainer => - case _ => for (color <- 0 until 16) { - t.rednetInput(side, color, 0) - } + case _ => for (color <- 0 until 16) { + t.setRednetInput(side, color, 0) + } } } case _ => @@ -66,13 +66,13 @@ abstract class RedstoneAware extends SimpleBlock /* with IRedNetOmniNode TODO MF override def getOutputValue(world: World, x: Int, y: Int, z: Int, side: EnumFacing, color: Int) = world.getTileEntity(x, y, z) match { - case t: tileentity.traits.BundledRedstoneAware => t.bundledOutput(side, color) + case t: tileentity.traits.BundledRedstoneAware => t.getBundledOutput(side, color) case _ => 0 } override def getOutputValues(world: World, x: Int, y: Int, z: Int, side: EnumFacing) = world.getTileEntity(x, y, z) match { - case t: tileentity.traits.BundledRedstoneAware => t.bundledOutput(side) + case t: tileentity.traits.BundledRedstoneAware => t.getBundledOutput(side) case _ => Array.fill(16)(0) } @@ -81,7 +81,7 @@ abstract class RedstoneAware extends SimpleBlock /* with IRedNetOmniNode TODO MF override def onInputsChanged(world: World, x: Int, y: Int, z: Int, side: EnumFacing, inputValues: Array[Int]) = world.getTileEntity(x, y, z) match { case t: tileentity.traits.BundledRedstoneAware => for (color <- 0 until 16) { - t.rednetInput(side, color, inputValues(color)) + t.setRednetInput(side, color, inputValues(color)) } case _ => } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala index a9d1a3998..20026223b 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Charger.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Charger.scala @@ -205,7 +205,7 @@ class Charger extends traits.Environment with traits.PowerAcceptor with traits.R override def updateRedstoneInput(side: EnumFacing) { super.updateRedstoneInput(side) - val signal = math.max(0, math.min(15, EnumFacing.values.map(input).max)) + val signal = getInput.max min 15 if (invertSignal) chargeSpeed = (15 - signal) / 15.0 else chargeSpeed = signal / 15.0 diff --git a/src/main/scala/li/cil/oc/common/tileentity/Print.scala b/src/main/scala/li/cil/oc/common/tileentity/Print.scala index f25f3bada..db7032672 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Print.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Print.scala @@ -20,6 +20,7 @@ import net.minecraft.util.math.RayTraceResult import net.minecraft.util.math.Vec3d import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly +import scala.collection.convert.WrapAsJava._ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int => Unit], val onStateChange: Option[() => Unit]) extends traits.TileEntity with traits.RedstoneAware with traits.RotatableTile { def this() = this(None, None, None) @@ -113,6 +114,14 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int false } + private def buildValueSet(value: Int): util.Map[AnyRef, AnyRef] = { + val map: util.Map[AnyRef, AnyRef] = new util.HashMap[AnyRef, AnyRef]() + EnumFacing.values.foreach { + side => map.put(new java.lang.Integer(side.ordinal), new java.lang.Integer(value)) + } + map + } + def toggleState(): Unit = { if (canToggle.fold(true)(_.apply())) { state = !state @@ -142,7 +151,7 @@ class Print(val canToggle: Option[() => Boolean], val scheduleUpdate: Option[Int def updateRedstone(): Unit = { if (data.emitRedstone) { - EnumFacing.values().foreach(output(_, if (data.emitRedstone(state)) data.redstoneLevel else 0)) + setOutput(buildValueSet(if (data.emitRedstone(state)) data.redstoneLevel else 0)) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala index bff17b2b5..cb7bbb295 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Rack.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Rack.scala @@ -265,7 +265,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance override def markChanged(slot: Int): Unit = { hasChanged.synchronized(hasChanged(slot) = true) - isOutputEnabled = hasRedstoneCard + setOutputEnabled(hasRedstoneCard) } // ----------------------------------------------------------------------- // @@ -313,7 +313,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance override def markDirty() { super.markDirty() if (isServer) { - isOutputEnabled = hasRedstoneCard + setOutputEnabled(hasRedstoneCard) ServerPacketSender.sendRackInventory(this) } else { @@ -368,7 +368,7 @@ class Rack extends traits.PowerAcceptor with traits.Hub with traits.PowerBalance ServerPacketSender.sendRackMountableData(this, slot) world.notifyNeighborsOfStateChange(getPos, getBlockType) // These are working state dependent, so recompute them. - isOutputEnabled = hasRedstoneCard + setOutputEnabled(hasRedstoneCard) } // Power mountables without requiring them to be connected to the outside. diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index 6925b2b64..2a9c729cd 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -190,9 +190,9 @@ class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInfo override protected[tileentity] val _bundledOutput = robot._bundledOutput - override def isOutputEnabled = robot.isOutputEnabled + override def isOutputEnabled: Boolean = robot.isOutputEnabled - override def isOutputEnabled_=(value: Boolean) = robot.isOutputEnabled_=(value) + override def setOutputEnabled(value: Boolean): Unit = robot.setOutputEnabled(value) override def checkRedstoneInputChanged() = robot.checkRedstoneInputChanged() diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala index aed58d007..fe2971b9a 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala @@ -6,6 +6,7 @@ import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.util.ExtendedNBT._ import mrtjp.projectred.api.IBundledTile import net.minecraftforge.fml.common.Optional +import java.util /* TODO RedLogic import mods.immibis.redlogic.api.wiring.IBundledEmitter @@ -36,8 +37,8 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile /* with IBund // ----------------------------------------------------------------------- // - override def isOutputEnabled_=(value: Boolean) = { - if (value != isOutputEnabled) { + override def setOutputEnabled(value: Boolean): Unit = { + if (value != _isOutputEnabled) { if (!value) { for (i <- _bundledOutput.indices) { for (j <- _bundledOutput(i).indices) { @@ -46,40 +47,71 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile /* with IBund } } } - super.isOutputEnabled_=(value) + super.setOutputEnabled(value) } - def bundledInput(side: EnumFacing) = - (_bundledInput(side.ordinal()), _rednetInput(side.ordinal())).zipped.map(math.max) + def getBundledInput: Array[Array[Int]] = { + (0 until 6).map(side => (0 until 16).map(color => _bundledInput(side)(color) max _rednetInput(side)(color) max 0).toArray).toArray + } - def bundledInput(side: EnumFacing, newBundledInput: Array[Int]): Unit = { + private def checkSide(side: EnumFacing): Int = { + val index = side.ordinal + if (index >= 6) throw new IndexOutOfBoundsException(s"Bad side $side") + index + } + + private def checkColor(color: Int): Int = { + if (color < 0 || color >= 16) throw new IndexOutOfBoundsException(s"Bad color $color") + color + } + + def getBundledInput(side: EnumFacing): Array[Int] = { + val sideIndex = checkSide(side) + val bundled = _bundledInput(sideIndex) + val rednet = _rednetInput(sideIndex) + (bundled, rednet).zipped.map((a, b) => a max b max 0) + } + + def getBundledInput(side: EnumFacing, color: Int): Int = { + val sideIndex = checkSide(side) + val colorIndex = checkColor(color) + val bundled = _bundledInput(sideIndex)(colorIndex) + val rednet = _rednetInput(sideIndex)(colorIndex) + bundled max rednet max 0 + } + + def setBundledInput(side: EnumFacing, color: Int, newValue: Int): Unit = { + updateInput(_bundledInput, side, color, newValue) + } + + def setBundledInput(side: EnumFacing, newBundledInput: Array[Int]): Unit = { for (color <- 0 until 16) { - updateInput(_bundledInput, side, color, if (newBundledInput == null) 0 else newBundledInput(color)) + val value = if (newBundledInput == null || color >= newBundledInput.length) 0 else newBundledInput(color) + setBundledInput(side, color, value) } } - def rednetInput(side: EnumFacing, color: Int, value: Int): Unit = updateInput(_rednetInput, side, color, value) + def setRednetInput(side: EnumFacing, color: Int, value: Int): Unit = updateInput(_rednetInput, side, color, value) def updateInput(inputs: Array[Array[Int]], side: EnumFacing, color: Int, newValue: Int): Unit = { - val oldValue = inputs(side.ordinal())(color) + val sideIndex = checkSide(side) + val colorIndex = checkColor(color) + val oldValue = inputs(sideIndex)(colorIndex) if (oldValue != newValue) { + inputs(sideIndex)(colorIndex) = newValue if (oldValue != -1) { - onRedstoneInputChanged(RedstoneChangedEventArgs(side, oldValue, newValue, color)) + onRedstoneInputChanged(RedstoneChangedEventArgs(side, oldValue, newValue, colorIndex)) } - inputs(side.ordinal())(color) = newValue } } - def bundledInput(side: EnumFacing, color: Int) = - math.max(_bundledInput(side.ordinal())(color), _rednetInput(side.ordinal())(color)) + def getBundledOutput: Array[Array[Int]] = _bundledInput - def bundledOutput(side: EnumFacing) = _bundledOutput(toLocal(side).ordinal()) + def getBundledOutput(side: EnumFacing): Array[Int] = _bundledOutput(checkSide(toLocal(side))) - def bundledOutput(side: EnumFacing, color: Int): Int = bundledOutput(side)(color) - - def bundledOutput(side: EnumFacing, color: Int, value: Int): Unit = if (value != bundledOutput(side, color)) { - _bundledOutput(toLocal(side).ordinal())(color) = value + def getBundledOutput(side: EnumFacing, color: Int): Int = getBundledOutput(side)(checkColor(color)) + def notifyChangedSide(side: EnumFacing): Unit = { /* TODO MFR if (Mods.MineFactoryReloaded.isAvailable) { val blockPos = BlockPosition(x, y, z).offset(side) @@ -93,11 +125,50 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile /* with IBund onRedstoneOutputChanged(side) } + def setBundledOutput(side: EnumFacing, color: Int, value: Int): Boolean = if (value != getBundledOutput(side, color)) { + _bundledOutput(checkSide(toLocal(side)))(checkColor(color)) = value + notifyChangedSide(side) + true + } else false + + def setBundledOutput(side: EnumFacing, values: util.Map[_, _]): Boolean = { + val sideIndex = toLocal(side).ordinal + var changed: Boolean = false + (0 until 16).foreach(color => { + // due to a bug in our jnlua layer, I cannot loop the map + valueToInt(getObjectFuzzy(values, color)) match { + case Some(newValue: Int) => + if (newValue != getBundledOutput(side, color)) { + _bundledOutput(sideIndex)(color) = newValue + changed = true + } + case _ => + } + }) + if (changed) { + notifyChangedSide(side) + } + changed + } + + def setBundledOutput(values: util.Map[_, _]): Boolean = { + var changed: Boolean = false + EnumFacing.values.foreach(side => { + val sideIndex = toLocal(side).ordinal + // due to a bug in our jnlua layer, I cannot loop the map + getObjectFuzzy(values, sideIndex) match { + case Some(child: util.Map[_, _]) if setBundledOutput(side, child) => changed = true + case _ => + } + }) + changed + } + // ----------------------------------------------------------------------- // override def updateRedstoneInput(side: EnumFacing) { super.updateRedstoneInput(side) - bundledInput(side, BundledRedstone.computeBundledInput(position, side)) + setBundledInput(side, BundledRedstone.computeBundledInput(position, side)) } // ----------------------------------------------------------------------- // @@ -162,7 +233,7 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile /* with IBund // ----------------------------------------------------------------------- // /* TODO RedLogic @Optional.Method(modid = Mods.IDs.RedLogic) - def getBundledCableStrength(blockFace: Int, toDirection: Int): Array[Byte] = bundledOutput(EnumFacing.getOrientation(toDirection)).map(value => math.min(math.max(value, 0), 255).toByte) + def getBundledCableStrength(blockFace: Int, toDirection: Int): Array[Byte] = getBundledOutput(EnumFacing.getOrientation(toDirection)).map(value => math.min(math.max(value, 0), 255).toByte) @Optional.Method(modid = Mods.IDs.RedLogic) def onBundledInputChanged() = checkRedstoneInputChanged() @@ -170,9 +241,8 @@ trait BundledRedstoneAware extends RedstoneAware with IBundledTile /* with IBund // ----------------------------------------------------------------------- // @Optional.Method(modid = Mods.IDs.ProjectRedTransmission) - override def canConnectBundled(side: Int): Boolean = isOutputEnabled + override def canConnectBundled(side: Int): Boolean = _isOutputEnabled @Optional.Method(modid = Mods.IDs.ProjectRedTransmission) - override def getBundledSignal(side: Int): Array[Byte] = bundledOutput(EnumFacing.getFront(side)).map(value => math.min(math.max(value, 0), 255).toByte) - + override def getBundledSignal(side: Int): Array[Byte] = getBundledOutput(EnumFacing.getFront(side)).map(value => math.min(math.max(value, 0), 255).toByte) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala index 65a4a18ef..36e9bc060 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Computer.scala @@ -188,7 +188,7 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B super.markDirty() if (isServer) { machine.onHostChanged() - isOutputEnabled = hasRedstoneCard + setOutputEnabled(hasRedstoneCard) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala index 19a8258be..094f130bc 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala @@ -1,5 +1,6 @@ package li.cil.oc.common.tileentity.traits +import java.util import li.cil.oc.Settings import li.cil.oc.common.EventHandler import li.cil.oc.integration.Mods @@ -27,18 +28,18 @@ case class RedstoneChangedEventArgs (side: EnumFacing, oldValue: Int, newValue: new Optional.Interface(iface = "mods.immibis.redlogic.api.wiring.IRedstoneUpdatable", modid = Mods.IDs.RedLogic) )) trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmitter with IRedstoneUpdatable TODO RedLogic */ { - protected[tileentity] val _input = Array.fill(6)(-1) + protected[tileentity] val _input: Array[Int] = Array.fill(6)(-1) - protected[tileentity] val _output = Array.fill(6)(0) + protected[tileentity] val _output: Array[Int] = Array.fill(6)(0) - protected var _isOutputEnabled = false + protected var _isOutputEnabled: Boolean = false - protected var shouldUpdateInput = true + def isOutputEnabled: Boolean = _isOutputEnabled - def isOutputEnabled = _isOutputEnabled + protected var shouldUpdateInput: Boolean = true - def isOutputEnabled_=(value: Boolean) = { - if (value != isOutputEnabled) { + def setOutputEnabled(value: Boolean): Unit = { + if (value != _isOutputEnabled) { _isOutputEnabled = value if (!value) { for (i <- _output.indices) { @@ -47,12 +48,34 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi } onRedstoneOutputEnabledChanged() } - this } - def input(side: EnumFacing) = _input(side.ordinal()) max 0 + protected def getObjectFuzzy(map: util.Map[_, _], key: Int): Option[AnyRef] = { + val refMap: util.Map[AnyRef, AnyRef] = map.asInstanceOf[util.Map[AnyRef, AnyRef]] + if (refMap.containsKey(key)) + Option(refMap.get(key)) + else if (refMap.containsKey(new Integer(key))) + Option(refMap.get(new Integer(key))) + else if (refMap.containsKey(new Integer(key) * 1.0)) + Option(refMap.get(new Integer(key) * 1.0)) + else if (refMap.containsKey(key * 1.0)) + Option(refMap.get(key * 1.0)) + else + None + } - def input(side: EnumFacing, newInput: Int): Unit = { + protected def valueToInt(value: AnyRef): Option[Int] = { + value match { + case Some(num: Number) => Option(num.intValue) + case _ => None + } + } + + def getInput: Array[Int] = _input.map(math.max(_, 0)) + + def getInput(side: EnumFacing): Int = _input(side.ordinal) max 0 + + def setInput(side: EnumFacing, newInput: Int): Unit = { val oldInput = _input(side.ordinal()) _input(side.ordinal()) = newInput if (oldInput >= 0 && newInput != oldInput) { @@ -60,14 +83,37 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi } } - def maxInput = EnumFacing.values.map(input).max + def setInput(values: Array[Int]): Unit = { + for (side <- EnumFacing.values) { + val value = if (side.ordinal <= values.length) values(side.ordinal) else 0 + setInput(side, value) + } + } - def output(side: EnumFacing) = _output(toLocal(side).ordinal()) + def maxInput: Int = _input.map(math.max(_, 0)).max - def output(side: EnumFacing, value: Int): Unit = if (value != output(side)) { + def getOutput: Array[Int] = EnumFacing.values.map{ side: EnumFacing => _output(toLocal(side).ordinal) } + + def getOutput(side: EnumFacing) = _output(toLocal(side).ordinal()) + + def setOutput(side: EnumFacing, value: Int): Boolean = { + if (value == getOutput(side)) return false _output(toLocal(side).ordinal()) = value - onRedstoneOutputChanged(side) + true + } + + def setOutput(values: util.Map[_, _]): Boolean = { + var changed: Boolean = false + EnumFacing.values.foreach(side => { + val sideIndex = toLocal(side).ordinal + // due to a bug in our jnlua layer, I cannot loop the map + valueToInt(getObjectFuzzy(values, sideIndex)) match { + case Some(num: Int) if setOutput(side, num) => changed = true + case _ => + } + }) + changed } def checkRedstoneInputChanged() { @@ -97,9 +143,7 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi } } - def updateRedstoneInput(side: EnumFacing) { - input(side, BundledRedstone.computeInput(position, side)) - } + def updateRedstoneInput(side: EnumFacing): Unit = setInput(side, BundledRedstone.computeInput(position, side)) // ----------------------------------------------------------------------- // @@ -122,13 +166,13 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi @SideOnly(Side.CLIENT) override def readFromNBTForClient(nbt: NBTTagCompound) { super.readFromNBTForClient(nbt) - isOutputEnabled = nbt.getBoolean("isOutputEnabled") + _isOutputEnabled = nbt.getBoolean("isOutputEnabled") nbt.getIntArray("output").copyToArray(_output) } override def writeToNBTForClient(nbt: NBTTagCompound) { super.writeToNBTForClient(nbt) - nbt.setBoolean("isOutputEnabled", isOutputEnabled) + nbt.setBoolean("isOutputEnabled", _isOutputEnabled) nbt.setIntArray("output", _output) } @@ -156,7 +200,7 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi // ----------------------------------------------------------------------- // /* TODO RedLogic @Optional.Method(modid = Mods.IDs.RedLogic) - override def connects(wire: IWire, blockFace: Int, fromDirection: Int) = isOutputEnabled + override def connects(wire: IWire, blockFace: Int, fromDirection: Int) = _isOutputEnabled @Optional.Method(modid = Mods.IDs.RedLogic) override def connectsAroundCorner(wire: IWire, blockFace: Int, fromDirection: Int) = false diff --git a/src/main/scala/li/cil/oc/integration/bluepower/BundledRedstoneDevice.scala b/src/main/scala/li/cil/oc/integration/bluepower/BundledRedstoneDevice.scala index 2e20a0610..cda9070e9 100644 --- a/src/main/scala/li/cil/oc/integration/bluepower/BundledRedstoneDevice.scala +++ b/src/main/scala/li/cil/oc/integration/bluepower/BundledRedstoneDevice.scala @@ -28,11 +28,11 @@ class BundledRedstoneDevice(val tileEntity: BundledRedstoneAware) extends IBundl override def getBundledColor(side: ForgeDirection): MinecraftColor = MinecraftColor.ANY - override def getBundledOutput(side: ForgeDirection): Array[Byte] = tileEntity.bundledOutput(side).map(_.toByte) + override def getBundledOutput(side: ForgeDirection): Array[Byte] = tileEntity.getBundledOutput(side).map(_.toByte) - override def getBundledPower(side: ForgeDirection): Array[Byte] = tileEntity.bundledInput(side).map(_.toByte) + override def getBundledPower(side: ForgeDirection): Array[Byte] = tileEntity.getBundledInput(side).map(_.toByte) - override def setBundledPower(side: ForgeDirection, power: Array[Byte]): Unit = tileEntity.bundledInput(side, power.map(_ & 0xFF)) + override def setBundledPower(side: ForgeDirection, power: Array[Byte]): Unit = tileEntity.setBundledInput(side, power.map(_ & 0xFF)) override def onBundledUpdate(): Unit = tileEntity.checkRedstoneInputChanged() } diff --git a/src/main/scala/li/cil/oc/integration/bluepower/RedstoneDevice.scala b/src/main/scala/li/cil/oc/integration/bluepower/RedstoneDevice.scala index f97193757..ee7e91edd 100644 --- a/src/main/scala/li/cil/oc/integration/bluepower/RedstoneDevice.scala +++ b/src/main/scala/li/cil/oc/integration/bluepower/RedstoneDevice.scala @@ -25,9 +25,9 @@ class RedstoneDevice(val tileEntity: RedstoneAware) extends IRedstoneDevice { override def getRedstoneConnectionCache: IConnectionCache[_ <: IRedstoneDevice] = cache - override def getRedstonePower(side: ForgeDirection): Byte = tileEntity.output(side).toByte + override def getRedstonePower(side: ForgeDirection): Byte = tileEntity.getOutput(side).toByte - override def setRedstonePower(side: ForgeDirection, power: Byte): Unit = tileEntity.input(side, power & 0xFF) + override def setRedstonePower(side: ForgeDirection, power: Byte): Unit = tileEntity.setInput(side, power & 0xFF) override def onRedstoneUpdate(): Unit = tileEntity.checkRedstoneInputChanged() } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/BundledRedstoneProvider.scala b/src/main/scala/li/cil/oc/integration/computercraft/BundledRedstoneProvider.scala index a3d7f9945..63a313e5b 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/BundledRedstoneProvider.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/BundledRedstoneProvider.scala @@ -20,9 +20,9 @@ object BundledRedstoneProvider extends IBundledRedstoneProvider with RedstonePro world.getTileEntity(blockPos) match { case tile: BundledRedstoneAware => var result = 0 - val colours = tile.bundledOutput(enumFacing) - for (colour <- 0 to 15) { - if (colours(colour) > 0) result |= 1 << colour + val colors = tile.getBundledOutput(enumFacing) + for (color <- 0 to 15) { + if (colors(color) > 0) result |= 1 << color } result case _ => -1 diff --git a/src/main/scala/li/cil/oc/integration/enderio/ProviderEnderIO.scala b/src/main/scala/li/cil/oc/integration/enderio/ProviderEnderIO.scala index 4adcc159e..ede306ff4 100644 --- a/src/main/scala/li/cil/oc/integration/enderio/ProviderEnderIO.scala +++ b/src/main/scala/li/cil/oc/integration/enderio/ProviderEnderIO.scala @@ -58,7 +58,7 @@ object ProviderEnderIO extends RedstoneProvider with ISignalProvider { override def getNetworkInputs(world: World, pos: BlockPos, side: EnumFacing): util.Set[Signal] = { world.getTileEntity(pos) match { case tile: BundledRedstoneAware => - tile.bundledOutput(side).zipWithIndex.map { + tile.getBundledOutput(side).zipWithIndex.map { case (strength, i) => new Signal(pos, side.getOpposite, strength, DyeColor.fromIndex(15 - i)) }.toSet[Signal] case _ => null diff --git a/src/main/scala/li/cil/oc/integration/mcmp/PartPrint.scala b/src/main/scala/li/cil/oc/integration/mcmp/PartPrint.scala index 17ef1c709..ebefcc8d5 100644 --- a/src/main/scala/li/cil/oc/integration/mcmp/PartPrint.scala +++ b/src/main/scala/li/cil/oc/integration/mcmp/PartPrint.scala @@ -64,7 +64,7 @@ class PartPrint extends Multipart with INormallyOccludingPart with IRedstonePart override def getStrongSignal(side: EnumFacing): Int = getWeakSignal(side) - override def getWeakSignal(side: EnumFacing): Int = wrapped.output(side) + override def getWeakSignal(side: EnumFacing): Int = wrapped.getOutput(side) // ----------------------------------------------------------------------- // // IOccludingPart diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index 12766bb68..a03a73c0a 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -479,7 +479,7 @@ object PacketSender { pb.writeTileEntity(t) pb.writeBoolean(t.isOutputEnabled) for (d <- EnumFacing.values) { - pb.writeByte(t.output(d)) + pb.writeByte(t.getOutput(d)) } pb.sendToPlayersNearTileEntity(t) diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala index 616da070e..61132bf46 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala @@ -11,6 +11,7 @@ import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.common.tileentity.traits.BundledRedstoneAware +import net.minecraft.util.EnumFacing import scala.collection.convert.WrapAsJava._ @@ -26,56 +27,103 @@ trait RedstoneBundled extends RedstoneVanilla { override def getDeviceInfo: util.Map[String, String] = deviceInfo + private val COLOR_RANGE = 0 until 16 + // ----------------------------------------------------------------------- // override def redstone: EnvironmentHost with BundledRedstoneAware - @Callback(direct = true, doc = """function(side:number[, color:number]):number or table -- Get the bundled redstone input on the specified side and with the specified color.""") - def getBundledInput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - if (args.optAny(1, null) == null) - result(redstone.bundledInput(side).zipWithIndex.map(_.swap).toMap) - else - result(redstone.bundledInput(side, checkColor(args, 1))) + private def getBundleKey(args: Arguments): (Option[EnumFacing], Option[Int]) = { + args.count match { + case 2 => (Option(checkSide(args, 0)), Option(checkColor(args, 1))) + case 1 => (Option(checkSide(args, 0)), None) + case 0 => (None, None) + case _ => throw new Exception("too many arguments, expected 0, 1, or 2") + } } - @Callback(direct = true, doc = """function(side:number[, color:number]):number or table -- Get the bundled redstone output on the specified side and with the specified color.""") - def getBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - if (args.optAny(1, null) == null) - result(redstone.bundledOutput(side).zipWithIndex.map(_.swap).toMap) - else - result(redstone.bundledOutput(side, checkColor(args, 1))) - } - - @Callback(doc = """function(side:number, color:number, value:number):number -- Set the bundled redstone output on the specified side and with the specified color.""") - def setBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - if (args.isTable(1)) { - val table = args.checkTable(1) - (0 to 15).map(color => (color, table.get(color))).foreach { - case (color, number: Number) => redstone.bundledOutput(side, color, number.intValue()) - case _ => + private def tableToColorValues(table: util.Map[_, _]): Array[Int] = { + COLOR_RANGE.collect { + case color: Int if table.containsKey(color) => { + table.get(color) match { + case value: Integer => value.toInt + } } + }.toArray + } + + private def colorsToMap(ar: Array[Int]): Map[Int, Int] = { + COLOR_RANGE.map{ + case color if color < ar.length => color -> ar(color) + }.toMap + } + + private def sidesToMap(ar: Array[Array[Int]]): Map[Int, Map[Int, Int]] = { + SIDE_RANGE.map { + case side if side.ordinal < ar.length && ar(side.ordinal).length > 0 => side.ordinal -> colorsToMap(ar(side.ordinal)) + }.toMap + } + + private def getBundleAssignment(args: Arguments): (Any, Any, Any) = { + args.count match { + case 3 => (checkSide(args, 0), checkColor(args, 1), args.checkInteger(2)) + case 2 => (checkSide(args, 0), args.checkTable(1), null) + case 1 => (args.checkTable(0), null, null) + case _ => throw new Exception("invalid number of arguments, expected 1, 2, or 3") + } + } + + @Callback(direct = true, doc = "function([side:number[, color:number]]):number or table -- Fewer params returns set of inputs") + def getBundledInput(context: Context, args: Arguments): Array[AnyRef] = { + val (side, color) = getBundleKey(args) + + if (color.isDefined) { + result(redstone.getBundledInput(side.get, color.get)) + } else if (side.isDefined) { + result(colorsToMap(redstone.getBundledInput(side.get))) + } else { + result(sidesToMap(redstone.getBundledInput)) + } + } + + @Callback(direct = true, doc = "function([side:number[, color:number]]):number or table -- Fewer params returns set of outputs") + def getBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { + val (side, color) = getBundleKey(args) + + if (color.isDefined) { + result(redstone.getBundledOutput(side.get, color.get)) + } else if (side.isDefined) { + result(colorsToMap(redstone.getBundledOutput(side.get))) + } else { + result(sidesToMap(redstone.getBundledOutput)) + } + } + + @Callback(doc = "function([side:number[, color:number,]] value:number or table):number or table -- Fewer params to assign set of outputs. Returns previous values") + def setBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { + var ret: AnyRef = null + if (getBundleAssignment(args) match { + case (side: EnumFacing, color: Int, value: Int) => + ret = new java.lang.Integer(redstone.getBundledOutput(side, color)) + redstone.setBundledOutput(side, color, value) + case (side: EnumFacing, value: util.Map[_, _], _) => + ret = redstone.getBundledOutput(side) + redstone.setBundledOutput(side, value) + case (value: util.Map[_, _], _, _) => + ret = redstone.getBundledOutput + redstone.setBundledOutput(value) + }) { if (Settings.get.redstoneDelay > 0) context.pause(Settings.get.redstoneDelay) - result(true) - } - else { - val color = checkColor(args, 1) - val value = args.checkInteger(2) - redstone.bundledOutput(side, color, value) - if (Settings.get.redstoneDelay > 0) - context.pause(Settings.get.redstoneDelay) - result(redstone.bundledOutput(side, color)) } + result(ret) } // ----------------------------------------------------------------------- // private def checkColor(args: Arguments, index: Int): Int = { val color = args.checkInteger(index) - if (color < 0 || color > 15) + if (!COLOR_RANGE.contains(color)) throw new IllegalArgumentException("invalid color") color } diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala index 0c3be8047..ad5ff3238 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala @@ -36,31 +36,43 @@ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo { override def getDeviceInfo: util.Map[String, String] = deviceInfo + protected val SIDE_RANGE: Array[EnumFacing] = EnumFacing.values + // ----------------------------------------------------------------------- // - - @Callback(direct = true, doc = """function(side:number):number -- Get the redstone input on the specified side.""") + @Callback(direct = true, doc = "function([side:number]):number or table -- Get the redstone input (all sides, or optionally on the specified side)") def getInput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - result(redstone.input(side)) + getOptionalSide(args) match { + case Some(side: Int) => result(redstone.getInput(side)) + case _ => result(valuesToMap(redstone.getInput)) + } } - @Callback(direct = true, doc = """function(side:number):number -- Get the redstone output on the specified side.""") + @Callback(direct = true, doc = "function([side:number]):number or table -- Get the redstone output (all sides, or optionally on the specified side)") def getOutput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - result(redstone.output(side)) + getOptionalSide(args) match { + case Some(side: Int) => result(redstone.getOutput(side)) + case _ => result(valuesToMap(redstone.getOutput)) + } } - @Callback(doc = """function(side:number, value:number):number -- Set the redstone output on the specified side.""") + @Callback(doc = "function([side:number, ]value:number or table):number or table -- Set the redstone output (all sides, or optionally on the specified side). Returns previous values") def setOutput(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSide(args, 0) - val value = args.checkInteger(1) - redstone.output(side, value) - if (Settings.get.redstoneDelay > 0) - context.pause(Settings.get.redstoneDelay) - result(redstone.output(side)) + var ret: AnyRef = null + if (getAssignment(args) match { + case (side: EnumFacing, value: Int) => + ret = new java.lang.Integer(redstone.getOutput(side)) + redstone.setOutput(side, value) + case (value: util.Map[_, _], _) => + ret = valuesToMap(redstone.getOutput) + redstone.setOutput(value) + }) { + if (Settings.get.redstoneDelay > 0) + context.pause(Settings.get.redstoneDelay) + } + result(ret) } - @Callback(direct = true, doc = """function(side:number):number -- Get the comparator input on the specified side.""") + @Callback(direct = true, doc = "function(side:number):number -- Get the comparator input on the specified side.") def getComparatorInput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) val blockPos = BlockPosition(redstone).offset(side) @@ -87,10 +99,27 @@ trait RedstoneVanilla extends RedstoneSignaller with DeviceInfo { // ----------------------------------------------------------------------- // - protected def checkSide(args: Arguments, index: Int) = { + private def getOptionalSide(args: Arguments): Option[Int] = { + if (args.count == 1) + Option(checkSide(args, 0).ordinal) + else + None + } + + private def getAssignment(args: Arguments): (Any, Any) = { + args.count match { + case 2 => (checkSide(args, 0), args.checkInteger(1)) + case 1 => (args.checkTable(0), null) + case _ => throw new Exception("invalid number of arguments, expected 1 or 2") + } + } + + protected def checkSide(args: Arguments, index: Int): EnumFacing = { val side = args.checkInteger(index) if (side < 0 || side > 5) throw new IllegalArgumentException("invalid side") redstone.toGlobal(EnumFacing.getFront(side)) } + + private def valuesToMap(ar: Array[Int]): Map[Int, Int] = SIDE_RANGE.map(_.ordinal).map{ case side if side < ar.length => side -> ar(side) }.toMap }