From c5e79df8b5889e9fb253b1853774cea32f521ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Mon, 15 Dec 2014 21:38:54 +0100 Subject: [PATCH] Extracted tank logic from robot component and some minor bugfixing. --- .../li/cil/oc/api/machine/MachineHost.java | 1 + .../cil/oc/common/inventory/MultiTank.scala | 10 + .../li/cil/oc/common/tileentity/Robot.scala | 5 +- .../cil/oc/common/tileentity/RobotProxy.scala | 3 +- .../common/tileentity/traits/Computer.scala | 3 +- .../tileentity/traits/Environment.scala | 2 +- .../opencomputers/ModOpenComputers.scala | 2 +- .../li/cil/oc/server/component/Drone.scala | 2 +- .../cil/oc/server/component/robot/Robot.scala | 226 ++---------------- .../component/traits/InventoryAware.scala | 2 +- ...spectable.scala => InventoryControl.scala} | 14 +- ...erop.scala => InventoryWorldControl.scala} | 2 +- .../server/component/traits/TankAware.scala | 23 ++ .../server/component/traits/TankControl.scala | 79 ++++++ .../component/traits/TankWorldControl.scala | 130 ++++++++++ ...ldInspectable.scala => WorldControl.scala} | 2 +- .../li/cil/oc/util/ExtendedArguments.scala | 5 +- .../scala/li/cil/oc/util/ExtendedWorld.scala | 9 + 18 files changed, 290 insertions(+), 230 deletions(-) create mode 100644 src/main/scala/li/cil/oc/common/inventory/MultiTank.scala rename src/main/scala/li/cil/oc/server/component/traits/{InventoryInspectable.scala => InventoryControl.scala} (77%) rename src/main/scala/li/cil/oc/server/component/traits/{InventoryWorldInterop.scala => InventoryWorldControl.scala} (98%) create mode 100644 src/main/scala/li/cil/oc/server/component/traits/TankAware.scala create mode 100644 src/main/scala/li/cil/oc/server/component/traits/TankControl.scala create mode 100644 src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala rename src/main/scala/li/cil/oc/server/component/traits/{WorldInspectable.scala => WorldControl.scala} (86%) diff --git a/src/main/java/li/cil/oc/api/machine/MachineHost.java b/src/main/java/li/cil/oc/api/machine/MachineHost.java index d38ed9fa3..c234d8e04 100644 --- a/src/main/java/li/cil/oc/api/machine/MachineHost.java +++ b/src/main/java/li/cil/oc/api/machine/MachineHost.java @@ -79,6 +79,7 @@ public interface MachineHost extends EnvironmentHost { *

* This method is called from executor threads, so it must be thread-safe. */ + // TODO Merge with {@link EnvironmentHost#markChanged} in 1.5 void markForSaving(); /** diff --git a/src/main/scala/li/cil/oc/common/inventory/MultiTank.scala b/src/main/scala/li/cil/oc/common/inventory/MultiTank.scala new file mode 100644 index 000000000..b9a83abc7 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/inventory/MultiTank.scala @@ -0,0 +1,10 @@ +package li.cil.oc.common.inventory + +import net.minecraftforge.fluids.IFluidTank + +// TODO Move to api.internal in 1.5, make internal.Robot extend this. +trait MultiTank { + def tankCount: Int + + def getFluidTank(index: Int): IFluidTank +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index eeeade7e3..200b7a60d 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -17,6 +17,7 @@ import li.cil.oc.api.network._ import li.cil.oc.client.gui import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.inventory.MultiTank import li.cil.oc.integration.opencomputers.DriverKeyboard import li.cil.oc.integration.opencomputers.DriverRedstoneCard import li.cil.oc.integration.opencomputers.DriverScreen @@ -43,7 +44,7 @@ import scala.collection.mutable // robot moves we only create a new proxy tile entity, hook the instance of this // class that was held by the old proxy to it and can then safely forget the // old proxy, which will be cleaned up by Minecraft like any other tile entity. -class Robot extends traits.Computer with traits.PowerInformation with IFluidHandler with internal.Robot { +class Robot extends traits.Computer with traits.PowerInformation with IFluidHandler with internal.Robot with MultiTank { var proxy: RobotProxy = _ val info = new ItemUtils.RobotData() @@ -651,7 +652,7 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand } override def setInventorySlotContents(slot: Int, stack: ItemStack) { - if (slot < getSizeInventory - componentCount && isItemValidForSlot(slot, stack)) { + if (slot < getSizeInventory - componentCount && (isItemValidForSlot(slot, stack) || stack == null)) { if (stack != null && stack.stackSize > 1 && isComponentSlot(slot)) { super.setInventorySlotContents(slot, stack.splitStack(1)) if (stack.stackSize > 0 && isServer) { 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 c568514cc..dd4d31bbc 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -9,6 +9,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.api.network._ +import li.cil.oc.common.inventory.MultiTank import li.cil.oc.integration.Mods import mods.immibis.redlogic.api.wiring.IWire import net.minecraft.entity.Entity @@ -21,7 +22,7 @@ import net.minecraftforge.fluids.Fluid import net.minecraftforge.fluids.FluidStack import net.minecraftforge.fluids.IFluidHandler -class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInformation with ISidedInventory with IFluidHandler with internal.Robot { +class RobotProxy(val robot: Robot) extends traits.Computer with traits.PowerInformation with ISidedInventory with IFluidHandler with internal.Robot with MultiTank { def this() = this(new Robot()) // ----------------------------------------------------------------------- // 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 00d737428..b53fb1dc3 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 @@ -118,8 +118,7 @@ trait Computer extends Environment with ComponentInventory with Rotatable with B if (_isRunning != machine.isRunning) { _isRunning = machine.isRunning - isOutputEnabled = hasRedstoneCard - isAbstractBusAvailable = hasAbstractBusCard + markDirty() ServerPacketSender.sendComputerState(this) } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala index 2fc370635..bec2408f6 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Environment.scala @@ -19,7 +19,7 @@ trait Environment extends TileEntity with network.Environment with driver.Enviro override def zPosition = z + 0.5 - override def markChanged() = if (canUpdate) isChangeScheduled = true else markDirty() + override def markChanged() = if (canUpdate) isChangeScheduled = true else world.markTileEntityChunkModified(x, y, z, this) protected def isConnected = node.address != null && node.network != null diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala index c590e7a6b..f0b0786d1 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/ModOpenComputers.scala @@ -105,7 +105,7 @@ object ModOpenComputers extends ModProxy { blacklistHost(classOf[internal.Adapter], "geolyzer", "keyboard", "screen1", "angelUpgrade", "batteryUpgrade1", "batteryUpgrade2", "batteryUpgrade3", "chunkloaderUpgrade", "craftingUpgrade", "experienceUpgrade", "generatorUpgrade", "inventoryUpgrade", "navigationUpgrade", "pistonUpgrade", "solarGeneratorUpgrade", "tankUpgrade", "tractorBeamUpgrade") blacklistHost(classOf[internal.Microcontroller], "graphicsCard1", "graphicsCard2", "graphicsCard3", "keyboard", "screen1", "angelUpgrade", "chunkloaderUpgrade", "craftingUpgrade", "databaseUpgrade1", "databaseUpgrade2", "databaseUpgrade3", "experienceUpgrade", "generatorUpgrade", "inventoryUpgrade", "inventoryControllerUpgrade", "navigationUpgrade", "tankUpgrade", "tankControllerUpgrade", "tractorBeamUpgrade") - blacklistHost(classOf[internal.Drone], "graphicsCard1", "graphicsCard2", "graphicsCard3", "keyboard", "lanCard", "redstoneCard1", "screen1", "angelUpgrade", "craftingUpgrade", "experienceUpgrade", "generatorUpgrade", "tankUpgrade", "tankControllerUpgrade") + blacklistHost(classOf[internal.Drone], "graphicsCard1", "graphicsCard2", "graphicsCard3", "keyboard", "lanCard", "redstoneCard1", "screen1", "angelUpgrade", "craftingUpgrade", "experienceUpgrade") blacklistHost(classOf[internal.Tablet], "lanCard", "redstoneCard1", "screen1", "angelUpgrade", "chunkloaderUpgrade", "craftingUpgrade", "databaseUpgrade1", "databaseUpgrade2", "databaseUpgrade3", "experienceUpgrade", "generatorUpgrade", "inventoryUpgrade", "inventoryControllerUpgrade", "tankUpgrade", "tankControllerUpgrade") if (!WirelessRedstone.isAvailable) { diff --git a/src/main/scala/li/cil/oc/server/component/Drone.scala b/src/main/scala/li/cil/oc/server/component/Drone.scala index 513209b26..10978561a 100644 --- a/src/main/scala/li/cil/oc/server/component/Drone.scala +++ b/src/main/scala/li/cil/oc/server/component/Drone.scala @@ -16,7 +16,7 @@ import net.minecraft.world.WorldServer import net.minecraftforge.common.util.FakePlayerFactory import net.minecraftforge.common.util.ForgeDirection -class Drone(val host: entity.Drone) extends prefab.ManagedEnvironment with traits.WorldInspectable with traits.InventoryInspectable with traits.InventoryWorldInterop { +class Drone(val host: entity.Drone) extends prefab.ManagedEnvironment with traits.WorldControl with traits.InventoryControl with traits.InventoryWorldControl { override val node = Network.newNode(this, Visibility.Network). withComponent("drone"). create() diff --git a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala index a43facb14..adaddb69b 100644 --- a/src/main/scala/li/cil/oc/server/component/robot/Robot.scala +++ b/src/main/scala/li/cil/oc/server/component/robot/Robot.scala @@ -10,6 +10,7 @@ import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ import li.cil.oc.api.prefab import li.cil.oc.common.ToolDurabilityProviders +import li.cil.oc.common.inventory.MultiTank import li.cil.oc.common.tileentity import li.cil.oc.server.component.traits import li.cil.oc.server.{PacketSender => ServerPacketSender} @@ -28,11 +29,10 @@ import net.minecraft.util.MovingObjectPosition.MovingObjectType import net.minecraft.util.Vec3 import net.minecraftforge.common.MinecraftForge import net.minecraftforge.common.util.ForgeDirection -import net.minecraftforge.fluids._ import scala.collection.convert.WrapAsScala._ -class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with traits.WorldInspectable with traits.InventoryInspectable with traits.InventoryWorldInterop { +class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with traits.WorldControl with traits.InventoryControl with traits.InventoryWorldControl with traits.TankControl with traits.TankWorldControl { override val node = api.Network.newNode(this, Visibility.Network). withComponent("robot"). withConnector(Settings.get.bufferRobot). @@ -90,13 +90,25 @@ class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with // ----------------------------------------------------------------------- // + val tank = new MultiTank { + override def tankCount = robot.tankCount + + override def getFluidTank(index: Int) = robot.getFluidTank(index) + } + + def selectedTank = robot.selectedTank + + override def selectedTank_=(value: Int) = robot.selectedTank = value + + // ----------------------------------------------------------------------- // + override protected def fakePlayer = robot.player() override protected def checkSideForAction(args: Arguments, n: Int) = robot.toGlobal(args.checkSideForAction(n)) - // ----------------------------------------------------------------------- // + private def checkSideForFace(args: Arguments, n: Int, facing: ForgeDirection) = robot.toGlobal(args.checkSideForFace(n, robot.toLocal(facing))) - def selectedTank = robot.selectedTank + // ----------------------------------------------------------------------- // def canPlaceInAir = { val event = new RobotPlaceInAirEvent(robot) @@ -378,195 +390,6 @@ class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with // ----------------------------------------------------------------------- // - @Callback - def tankCount(context: Context, args: Arguments): Array[AnyRef] = result(robot.tankCount) - - @Callback - def selectTank(context: Context, args: Arguments): Array[AnyRef] = { - if (args.count > 0 && args.checkAny(0) != null) { - robot.selectedTank = checkTank(args, 0) - } - result(selectedTank + 1) - } - - @Callback(direct = true) - def tankLevel(context: Context, args: Arguments): Array[AnyRef] = { - val index = - if (args.count > 0 && args.checkAny(0) != null) checkTank(args, 0) - else selectedTank - result(fluidInTank(index) match { - case Some(fluid) => fluid.amount - case _ => 0 - }) - } - - @Callback(direct = true) - def tankSpace(context: Context, args: Arguments): Array[AnyRef] = { - val index = - if (args.count > 0 && args.checkAny(0) != null) checkTank(args, 0) - else selectedTank - result(getTank(index) match { - case Some(tank) => tank.getCapacity - tank.getFluidAmount - case _ => 0 - }) - } - - @Callback - def compareFluidTo(context: Context, args: Arguments): Array[AnyRef] = { - val index = checkTank(args, 0) - result((fluidInTank(selectedTank), fluidInTank(index)) match { - case (Some(stackA), Some(stackB)) => haveSameFluidType(stackA, stackB) - case (None, None) => true - case _ => false - }) - } - - @Callback - def transferFluidTo(context: Context, args: Arguments): Array[AnyRef] = { - val index = checkTank(args, 0) - val count = args.optionalFluidCount(1) - if (index == selectedTank || count == 0) { - result(true) - } - else (getTank(selectedTank), getTank(index)) match { - case (Some(from), Some(to)) => - val drained = from.drain(count, false) - val transferred = to.fill(drained, true) - if (transferred > 0) { - from.drain(transferred, true) - robot.markDirty() - result(true) - } - else if (count >= from.getFluidAmount && to.getCapacity >= from.getFluidAmount && from.getCapacity >= to.getFluidAmount) { - // Swap. - val tmp = to.drain(to.getFluidAmount, true) - to.fill(from.drain(from.getFluidAmount, true), true) - from.fill(tmp, true) - robot.markDirty() - result(true) - } - else result(Unit, "incompatible or no fluid") - case _ => result(Unit, "invalid index") - } - } - - @Callback - def compareFluid(context: Context, args: Arguments): Array[AnyRef] = { - val side = checkSideForAction(args, 0) - fluidInTank(selectedTank) match { - case Some(stack) => - val (nx, ny, nz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ) - if (world.blockExists(nx, ny, nz)) world.getTileEntity(nx, ny, nz) match { - case handler: IFluidHandler => - result(Option(handler.getTankInfo(side.getOpposite)).exists(_.exists(other => stack.isFluidEqual(other.fluid)))) - case _ => - val block = world.getBlock(x + side.offsetX, y + side.offsetY, z + side.offsetZ) - val fluid = FluidRegistry.lookupFluidForBlock(block) - result(stack.getFluid == fluid) - } - else result(false) - case _ => result(false) - } - } - - @Callback - def drain(context: Context, args: Arguments): Array[AnyRef] = { - val facing = checkSideForAction(args, 0) - val count = args.optionalFluidCount(1) - getTank(selectedTank) match { - case Some(tank) => - val space = tank.getCapacity - tank.getFluidAmount - val amount = math.min(count, space) - if (count > 0 && amount == 0) { - result(Unit, "tank is full") - } - else { - val (nx, ny, nz) = (x + facing.offsetX, y + facing.offsetY, z + facing.offsetZ) - if (world.blockExists(nx, ny, nz)) world.getTileEntity(nx, ny, nz) match { - case handler: IFluidHandler => - tank.getFluid match { - case stack: FluidStack => - val drained = handler.drain(facing.getOpposite, new FluidStack(stack, amount), true) - if ((drained != null && drained.amount > 0) || amount == 0) { - tank.fill(drained, true) - result(true) - } - else result(Unit, "incompatible or no fluid") - case _ => - tank.fill(handler.drain(facing.getOpposite, amount, true), true) - result(true) - } - case _ => - val block = world.getBlock(x + facing.offsetX, y + facing.offsetY, z + facing.offsetZ) - val fluid = FluidRegistry.lookupFluidForBlock(block) - if (fluid == null) { - result(Unit, "incompatible or no fluid") - } - else if (tank.fill(new FluidStack(fluid, 1000), false) == 1000) { - tank.fill(new FluidStack(fluid, 1000), true) - world.setBlockToAir(x + facing.offsetX, y + facing.offsetY, z + facing.offsetZ) - result(true) - } - else result(Unit, "tank is full") - } - else result(Unit, "incompatible or no fluid") - } - case _ => result(Unit, "no tank selected") - } - } - - @Callback - def fill(context: Context, args: Arguments): Array[AnyRef] = { - val facing = checkSideForAction(args, 0) - val count = args.optionalFluidCount(1) - getTank(selectedTank) match { - case Some(tank) => - val amount = math.min(count, tank.getFluidAmount) - if (count > 0 && amount == 0) { - result(Unit, "tank is empty") - } - val (bx, by, bz) = (x + facing.offsetX, y + facing.offsetY, z + facing.offsetZ) - if (world.blockExists(bx, by, bz)) world.getTileEntity(bx, by, bz) match { - case handler: IFluidHandler => - tank.getFluid match { - case stack: FluidStack => - val filled = handler.fill(facing.getOpposite, new FluidStack(stack, amount), true) - if (filled > 0 || amount == 0) { - tank.drain(filled, true) - result(true) - } - else result(Unit, "incompatible or no fluid") - case _ => - result(Unit, "tank is empty") - } - case _ => - val block = world.getBlock(bx, by, bz) - if (block != null && !block.isAir(world, x, y, z) && !block.isReplaceable(world, x, y, z)) { - result(Unit, "no space") - } - else if (tank.getFluidAmount < 1000) { - result(Unit, "tank is empty") - } - else if (!tank.getFluid.getFluid.canBePlacedInWorld) { - result(Unit, "incompatible fluid") - } - else { - val fluidBlock = tank.getFluid.getFluid.getBlock - tank.drain(1000, true) - world.func_147480_a(bx, by, bz, true) - world.setBlock(bx, by, bz, fluidBlock) - // This fake neighbor update is required to get stills to start flowing. - world.notifyBlockOfNeighborChange(bx, by, bz, robot.block) - result(true) - } - } - else result(Unit, "no space") - case _ => result(Unit, "no tank selected") - } - } - - // ----------------------------------------------------------------------- // - override def onConnect(node: Node) { super.onConnect(node) if (node == this.node) { @@ -659,21 +482,4 @@ class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with (hit.hitVec.yCoord - hit.blockY).toFloat, (hit.hitVec.zCoord - hit.blockZ).toFloat) } - - // ----------------------------------------------------------------------- // - - private def haveSameFluidType(stackA: FluidStack, stackB: FluidStack) = stackA.isFluidEqual(stackB) - - private def getTank(index: Int) = robot.tryGetTank(index) - - private def fluidInTank(index: Int) = getTank(index) match { - case Some(tank) => Option(tank.getFluid) - case _ => None - } - - // ----------------------------------------------------------------------- // - - private def checkTank(args: Arguments, n: Int) = args.checkTank(robot, n) - - private def checkSideForFace(args: Arguments, n: Int, facing: ForgeDirection) = robot.toGlobal(args.checkSideForFace(n, robot.toLocal(facing))) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala index 39e5e42b1..70ec53ff3 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryAware.scala @@ -10,7 +10,7 @@ trait InventoryAware { def selectedSlot_=(value: Int): Unit - def insertionSlots = { + protected def insertionSlots = { val slots = (0 until inventory.getSizeInventory).toIterable slots.drop(selectedSlot) ++ slots.take(selectedSlot) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryInspectable.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala similarity index 77% rename from src/main/scala/li/cil/oc/server/component/traits/InventoryInspectable.scala rename to src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala index 3a9b9beed..07d63cb8e 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryInspectable.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala @@ -6,11 +6,11 @@ import li.cil.oc.api.machine.Context import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ResultWrapper.result -trait InventoryInspectable extends InventoryAware { - @Callback +trait InventoryControl extends InventoryAware { + @Callback(doc = "function():number -- The size of this device's internal inventory.") def inventorySize(context: Context, args: Arguments): Array[AnyRef] = result(inventory.getSizeInventory) - @Callback + @Callback(doc = "function([slot:number]):number -- Get the currently selected slot; set the selected slot if specified.") def select(context: Context, args: Arguments): Array[AnyRef] = { if (args.count > 0 && args.checkAny(0) != null) { val slot = args.checkSlot(inventory, 0) @@ -21,7 +21,7 @@ trait InventoryInspectable extends InventoryAware { result(selectedSlot + 1) } - @Callback(direct = true) + @Callback(direct = true, doc = "function([slot:number]):number -- Get the number of items in the specified slot, otherwise in the selected slot.") def count(context: Context, args: Arguments): Array[AnyRef] = { val slot = if (args.count > 0 && args.checkAny(0) != null) args.checkSlot(inventory, 0) @@ -32,7 +32,7 @@ trait InventoryInspectable extends InventoryAware { }) } - @Callback(direct = true) + @Callback(direct = true, doc = "function([slot:number]):number -- Get the remaining space in the specified slot, otherwise in the selected slot.") def space(context: Context, args: Arguments): Array[AnyRef] = { val slot = if (args.count > 0 && args.checkAny(0) != null) args.checkSlot(inventory, 0) @@ -43,7 +43,7 @@ trait InventoryInspectable extends InventoryAware { }) } - @Callback + @Callback(doc = "function(otherSlot:number):boolean -- Compare the contents of the selected slot to the contents of the specified slot.") def compareTo(context: Context, args: Arguments): Array[AnyRef] = { val slot = args.checkSlot(inventory, 0) result((stackInSlot(selectedSlot), stackInSlot(slot)) match { @@ -53,7 +53,7 @@ trait InventoryInspectable extends InventoryAware { }) } - @Callback + @Callback(doc = "function(toSlot:number[, amount:number]):boolean -- Move up to the specified amount of items from the selected slot into the specified slot.") def transferTo(context: Context, args: Arguments): Array[AnyRef] = { val slot = args.checkSlot(inventory, 0) val count = args.optionalItemCount(1) diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldInterop.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala similarity index 98% rename from src/main/scala/li/cil/oc/server/component/traits/InventoryWorldInterop.scala rename to src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala index e0db7343e..201d5a0bd 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldInterop.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala @@ -13,7 +13,7 @@ import net.minecraft.entity.item.EntityItem import net.minecraft.item.ItemBlock import net.minecraftforge.common.util.ForgeDirection -trait InventoryWorldInterop extends InventoryAware with WorldAware with SideRestricted { +trait InventoryWorldControl extends InventoryAware with WorldAware with SideRestricted { @Callback def compare(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSideForAction(args, 0) diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankAware.scala b/src/main/scala/li/cil/oc/server/component/traits/TankAware.scala new file mode 100644 index 000000000..8fb3848a0 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/traits/TankAware.scala @@ -0,0 +1,23 @@ +package li.cil.oc.server.component.traits + +import li.cil.oc.common.inventory.MultiTank +import net.minecraftforge.fluids.FluidStack + +trait TankAware { + def tank: MultiTank + + def selectedTank: Int + + def selectedTank_=(value: Int): Unit + + // ----------------------------------------------------------------------- // + + protected def getTank(index: Int) = Option(tank.getFluidTank(index)) + + protected def fluidInTank(index: Int) = getTank(index) match { + case Some(tank) => Option(tank.getFluid) + case _ => None + } + + protected def haveSameFluidType(stackA: FluidStack, stackB: FluidStack) = stackA.isFluidEqual(stackB) +} diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala new file mode 100644 index 000000000..cb4cc3ba2 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala @@ -0,0 +1,79 @@ +package li.cil.oc.server.component.traits + +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.util.ExtendedArguments._ +import li.cil.oc.util.ResultWrapper.result + +trait TankControl extends TankAware { + @Callback + def tankCount(context: Context, args: Arguments): Array[AnyRef] = result(tank.tankCount) + + @Callback + def selectTank(context: Context, args: Arguments): Array[AnyRef] = { + if (args.count > 0 && args.checkAny(0) != null) { + selectedTank = args.checkTank(tank, 0) + } + result(selectedTank + 1) + } + + @Callback(direct = true) + def tankLevel(context: Context, args: Arguments): Array[AnyRef] = { + val index = + if (args.count > 0 && args.checkAny(0) != null) args.checkTank(tank, 0) + else selectedTank + result(fluidInTank(index) match { + case Some(fluid) => fluid.amount + case _ => 0 + }) + } + + @Callback(direct = true) + def tankSpace(context: Context, args: Arguments): Array[AnyRef] = { + val index = + if (args.count > 0 && args.checkAny(0) != null) args.checkTank(tank, 0) + else selectedTank + result(getTank(index) match { + case Some(tank) => tank.getCapacity - tank.getFluidAmount + case _ => 0 + }) + } + + @Callback + def compareFluidTo(context: Context, args: Arguments): Array[AnyRef] = { + val index = args.checkTank(tank, 0) + result((fluidInTank(selectedTank), fluidInTank(index)) match { + case (Some(stackA), Some(stackB)) => haveSameFluidType(stackA, stackB) + case (None, None) => true + case _ => false + }) + } + + @Callback + def transferFluidTo(context: Context, args: Arguments): Array[AnyRef] = { + val index = args.checkTank(tank, 0) + val count = args.optionalFluidCount(1) + if (index == selectedTank || count == 0) { + result(true) + } + else (getTank(selectedTank), getTank(index)) match { + case (Some(from), Some(to)) => + val drained = from.drain(count, false) + val transferred = to.fill(drained, true) + if (transferred > 0) { + from.drain(transferred, true) + result(true) + } + else if (count >= from.getFluidAmount && to.getCapacity >= from.getFluidAmount && from.getCapacity >= to.getFluidAmount) { + // Swap. + val tmp = to.drain(to.getFluidAmount, true) + to.fill(from.drain(from.getFluidAmount, true), true) + from.fill(tmp, true) + result(true) + } + else result(Unit, "incompatible or no fluid") + case _ => result(Unit, "invalid index") + } + } +} diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala new file mode 100644 index 000000000..687ecdde1 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala @@ -0,0 +1,130 @@ +package li.cil.oc.server.component.traits + +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.util.BlockPosition +import li.cil.oc.util.ExtendedBlock._ +import li.cil.oc.util.ExtendedWorld._ +import li.cil.oc.util.ExtendedArguments._ +import li.cil.oc.util.ResultWrapper.result +import net.minecraftforge.fluids.FluidRegistry +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.IFluidHandler + +trait TankWorldControl extends TankAware with WorldAware with SideRestricted { + @Callback + def compareFluid(context: Context, args: Arguments): Array[AnyRef] = { + val side = checkSideForAction(args, 0) + fluidInTank(selectedTank) match { + case Some(stack) => + val blockPos = BlockPosition(x, y, z).offset(side) + if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match { + case handler: IFluidHandler => + result(Option(handler.getTankInfo(side.getOpposite)).exists(_.exists(other => stack.isFluidEqual(other.fluid)))) + case _ => + val block = world.getBlock(blockPos) + val fluid = FluidRegistry.lookupFluidForBlock(block) + result(stack.getFluid == fluid) + } + else result(false) + case _ => result(false) + } + } + + @Callback + def drain(context: Context, args: Arguments): Array[AnyRef] = { + val facing = checkSideForAction(args, 0) + val count = args.optionalFluidCount(1) + getTank(selectedTank) match { + case Some(tank) => + val space = tank.getCapacity - tank.getFluidAmount + val amount = math.min(count, space) + if (count > 0 && amount == 0) { + result(Unit, "tank is full") + } + else { + val blockPos = BlockPosition(x, y, z).offset(facing) + if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match { + case handler: IFluidHandler => + tank.getFluid match { + case stack: FluidStack => + val drained = handler.drain(facing.getOpposite, new FluidStack(stack, amount), true) + if ((drained != null && drained.amount > 0) || amount == 0) { + tank.fill(drained, true) + result(true) + } + else result(Unit, "incompatible or no fluid") + case _ => + val transferred = tank.fill(handler.drain(facing.getOpposite, amount, true), true) + result(transferred > 0, transferred) + } + case _ => + val block = world.getBlock(blockPos) + val fluid = FluidRegistry.lookupFluidForBlock(block) + if (fluid == null) { + result(Unit, "incompatible or no fluid") + } + else if (tank.fill(new FluidStack(fluid, 1000), false) == 1000) { + tank.fill(new FluidStack(fluid, 1000), true) + world.setBlockToAir(blockPos) + result(true) + } + else result(Unit, "tank is full") + } + else result(Unit, "incompatible or no fluid") + } + case _ => result(Unit, "no tank selected") + } + } + + @Callback + def fill(context: Context, args: Arguments): Array[AnyRef] = { + val facing = checkSideForAction(args, 0) + val count = args.optionalFluidCount(1) + getTank(selectedTank) match { + case Some(tank) => + val amount = math.min(count, tank.getFluidAmount) + if (count > 0 && amount == 0) { + result(Unit, "tank is empty") + } + val blockPos = BlockPosition(x, y, z).offset(facing) + if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match { + case handler: IFluidHandler => + tank.getFluid match { + case stack: FluidStack => + val filled = handler.fill(facing.getOpposite, new FluidStack(stack, amount), true) + if (filled > 0 || amount == 0) { + tank.drain(filled, true) + result(true) + } + else result(Unit, "incompatible or no fluid") + case _ => + result(Unit, "tank is empty") + } + case _ => + val block = world.getBlock(blockPos) + if (!block.isAir(blockPos) && !block.isReplaceable(blockPos)) { + result(Unit, "no space") + } + else if (tank.getFluidAmount < 1000) { + result(Unit, "tank is empty") + } + else if (!tank.getFluid.getFluid.canBePlacedInWorld) { + result(Unit, "incompatible fluid") + } + else { + val fluidBlock = tank.getFluid.getFluid.getBlock + tank.drain(1000, true) + world.breakBlock(blockPos) + world.setBlock(blockPos, fluidBlock) + // This fake neighbor update is required to get stills to start flowing. + world.notifyBlockOfNeighborChange(blockPos, world.getBlock(x, y, z)) + result(true) + } + } + else result(Unit, "no space") + case _ => result(Unit, "no tank selected") + } + } +} diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldInspectable.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldControl.scala similarity index 86% rename from src/main/scala/li/cil/oc/server/component/traits/WorldInspectable.scala rename to src/main/scala/li/cil/oc/server/component/traits/WorldControl.scala index 12a2ff679..2e0c657f6 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldInspectable.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldControl.scala @@ -5,7 +5,7 @@ import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.util.ResultWrapper.result -trait WorldInspectable extends WorldAware with SideRestricted { +trait WorldControl extends WorldAware with SideRestricted { @Callback def detect(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSideForAction(args, 0) diff --git a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala index 5889618cf..c7f318d4d 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala @@ -2,6 +2,7 @@ package li.cil.oc.util import li.cil.oc.api.internal.Robot import li.cil.oc.api.machine.Arguments +import li.cil.oc.common.inventory.MultiTank import net.minecraft.inventory.IInventory import net.minecraftforge.common.util.ForgeDirection @@ -45,9 +46,9 @@ object ExtendedArguments { slot + 1 + robot.containerCount } - def checkTank(robot: Robot, n: Int) = { + def checkTank(multi: MultiTank, n: Int) = { val tank = args.checkInteger(n) - 1 - if (tank < 0 || tank >= robot.tankCount) { + if (tank < 0 || tank >= multi.tankCount) { throw new IllegalArgumentException("invalid tank index") } tank diff --git a/src/main/scala/li/cil/oc/util/ExtendedWorld.scala b/src/main/scala/li/cil/oc/util/ExtendedWorld.scala index 9eb935250..219f48b67 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedWorld.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedWorld.scala @@ -1,6 +1,7 @@ package li.cil.oc.util import li.cil.oc.api.driver.EnvironmentHost +import net.minecraft.block.Block import net.minecraft.tileentity.TileEntity import net.minecraft.world.IBlockAccess import net.minecraft.world.World @@ -28,6 +29,8 @@ object ExtendedWorld { } class ExtendedWorld(override val world: World) extends ExtendedBlockAccess(world) { + def blockExists(position: BlockPosition) = world.blockExists(position.x, position.y, position.z) + def getBlockHardness(position: BlockPosition) = getBlock(position).getBlockHardness(world, position.x, position.y, position.z) def getBlockHarvestLevel(position: BlockPosition) = getBlock(position).getHarvestLevel(getBlockMetadata(position)) @@ -35,6 +38,12 @@ object ExtendedWorld { def getBlockHarvestTool(position: BlockPosition) = getBlock(position).getHarvestTool(getBlockMetadata(position)) def setBlockToAir(position: BlockPosition) = world.setBlockToAir(position.x, position.y, position.z) + + def notifyBlockOfNeighborChange(position: BlockPosition, block: Block) = world.notifyBlockOfNeighborChange(position.x, position.y, position.z, block) + + def breakBlock(position: BlockPosition, drops: Boolean = true) = world.func_147480_a(position.x, position.y, position.z, drops) + + def setBlock(position: BlockPosition, block: Block) = world.setBlock(position.x, position.y, position.z, block) } }