More extraction of logic into traits.

*BREAKING CHANGE*: Reworked inventory and tank controllers a bit. Specifically, the methods taking `sides.back` to indicate the robots own inventory now only operate on the world, internal inventory is accessed via separate methods now (e.g. getStackInSlot(sides.back, ...) -> getStackInInternalSlot(...)).
This commit is contained in:
Florian Nücke 2014-12-16 16:13:05 +01:00
parent fe9d7edc16
commit a322981dea
28 changed files with 610 additions and 599 deletions

View File

@ -0,0 +1,7 @@
package li.cil.oc.common.inventory
trait InventorySelection {
def selectedSlot: Int
def selectedSlot_=(value: Int): Unit
}

View File

@ -0,0 +1,7 @@
package li.cil.oc.common.inventory
trait TankSelection {
def selectedTank: Int
def selectedTank_=(value: Int): Unit
}

View File

@ -17,7 +17,7 @@ object DroneTemplate extends Template {
override protected val suggestedComponents = Array(
"BIOS" -> hasComponent("eeprom") _)
override protected def hostClass = classOf[internal.Microcontroller]
override protected def hostClass = classOf[internal.Drone]
def select(stack: ItemStack) = api.Items.get(stack) == api.Items.get("droneCase")
@ -45,6 +45,7 @@ object DroneTemplate extends Template {
val upgradeSlots = new NBTTagList()
upgradeSlots.appendTag(Map("tier" -> Tier.Three))
upgradeSlots.appendTag(Map("tier" -> Tier.Two))
upgradeSlots.appendTag(Map("tier" -> Tier.One))
nbt.setTag("upgradeSlots", upgradeSlots)
val componentSlots = new NBTTagList()
@ -60,7 +61,7 @@ object DroneTemplate extends Template {
FMLInterModComms.sendMessage("OpenComputers", "registerAssemblerTemplate", nbt)
}
override protected def maxComplexity(inventory: IInventory) = 6
override protected def maxComplexity(inventory: IInventory) = 8
override protected def caseTier(inventory: IInventory) = if (select(inventory.getStackInSlot(0))) Tier.One else Tier.None
}

View File

@ -17,7 +17,9 @@ 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.InventorySelection
import li.cil.oc.common.inventory.MultiTank
import li.cil.oc.common.inventory.TankSelection
import li.cil.oc.integration.opencomputers.DriverKeyboard
import li.cil.oc.integration.opencomputers.DriverRedstoneCard
import li.cil.oc.integration.opencomputers.DriverScreen
@ -47,7 +49,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 with MultiTank {
class Robot extends traits.Computer with traits.PowerInformation with IFluidHandler with internal.Robot with MultiTank with InventorySelection with TankSelection {
var proxy: RobotProxy = _
val info = new ItemUtils.RobotData()
@ -99,6 +101,12 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
var selectedSlot = actualSlot(0)
val tank = new MultiTank {
override def tankCount = Robot.this.tankCount
override def getFluidTank(index: Int) = Robot.this.getFluidTank(index)
}
var selectedTank = 0
// For client.
@ -760,42 +768,42 @@ class Robot extends traits.Computer with traits.PowerInformation with IFluidHand
override def fill(from: ForgeDirection, resource: FluidStack, doFill: Boolean) =
tryGetTank(selectedTank) match {
case Some(tank) =>
tank.fill(resource, doFill)
case Some(t) =>
t.fill(resource, doFill)
case _ => 0
}
override def drain(from: ForgeDirection, resource: FluidStack, doDrain: Boolean) =
tryGetTank(selectedTank) match {
case Some(tank) if tank.getFluid != null && tank.getFluid.isFluidEqual(resource) =>
tank.drain(resource.amount, doDrain)
case Some(t) if t.getFluid != null && t.getFluid.isFluidEqual(resource) =>
t.drain(resource.amount, doDrain)
case _ => null
}
override def drain(from: ForgeDirection, maxDrain: Int, doDrain: Boolean) = {
tryGetTank(selectedTank) match {
case Some(tank) =>
tank.drain(maxDrain, doDrain)
case Some(t) =>
t.drain(maxDrain, doDrain)
case _ => null
}
}
override def canFill(from: ForgeDirection, fluid: Fluid) = {
tryGetTank(selectedTank) match {
case Some(tank) => tank.getFluid == null || tank.getFluid.getFluid == fluid
case Some(t) => t.getFluid == null || t.getFluid.getFluid == fluid
case _ => false
}
}
override def canDrain(from: ForgeDirection, fluid: Fluid): Boolean = {
tryGetTank(selectedTank) match {
case Some(tank) => tank.getFluid != null && tank.getFluid.getFluid == fluid
case Some(t) => t.getFluid != null && t.getFluid.getFluid == fluid
case _ => false
}
}
override def getTankInfo(from: ForgeDirection) =
components.collect {
case Some(tank: IFluidTank) => tank.getInfo
case Some(t: IFluidTank) => t.getInfo
}.toArray
}

View File

@ -5,9 +5,10 @@ import li.cil.oc.api.driver.EnvironmentAware
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.driver.item.HostAware
import li.cil.oc.api.internal.Adapter
import li.cil.oc.api.internal.Robot
import li.cil.oc.common.Slot
import li.cil.oc.common.Tier
import li.cil.oc.common.entity.Drone
import li.cil.oc.common.tileentity.Robot
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
@ -16,8 +17,9 @@ object DriverUpgradeInventoryController extends Item with HostAware with Environ
isOneOf(stack, api.Items.get("inventoryControllerUpgrade"))
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match {
case robot: EnvironmentHost with Robot => new component.UpgradeInventoryControllerInRobot(robot)
case adapter: EnvironmentHost with Adapter => new component.UpgradeInventoryControllerInAdapter(adapter)
case host: EnvironmentHost with Adapter => new component.UpgradeInventoryController.Adapter(host)
case host: EnvironmentHost with Drone => new component.UpgradeInventoryController.Drone(host)
case host: EnvironmentHost with Robot => new component.UpgradeInventoryController.Robot(host)
case _ => null
}
@ -25,5 +27,5 @@ object DriverUpgradeInventoryController extends Item with HostAware with Environ
override def tier(stack: ItemStack) = Tier.Two
override def providedEnvironment(stack: ItemStack) = classOf[component.UpgradeInventoryControllerInRobot]
override def providedEnvironment(stack: ItemStack) = classOf[component.UpgradeInventoryController.Robot]
}

View File

@ -5,9 +5,10 @@ import li.cil.oc.api.driver.EnvironmentAware
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.driver.item.HostAware
import li.cil.oc.api.internal.Adapter
import li.cil.oc.api.internal.Robot
import li.cil.oc.common.Slot
import li.cil.oc.common.Tier
import li.cil.oc.common.entity.Drone
import li.cil.oc.common.tileentity.Robot
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
@ -16,8 +17,9 @@ object DriverUpgradeTankController extends Item with HostAware with EnvironmentA
isOneOf(stack, api.Items.get("tankControllerUpgrade"))
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match {
case robot: EnvironmentHost with Robot => new component.UpgradeTankControllerInRobot(robot)
case adapter: EnvironmentHost with Adapter => new component.UpgradeTankControllerInAdapter(adapter)
case host: EnvironmentHost with Adapter => new component.UpgradeTankController.Adapter(host)
case host: EnvironmentHost with Drone => new component.UpgradeTankController.Drone(host)
case host: EnvironmentHost with Robot => new component.UpgradeTankController.Robot(host)
case _ => null
}
@ -25,5 +27,5 @@ object DriverUpgradeTankController extends Item with HostAware with EnvironmentA
override def tier(stack: ItemStack) = Tier.Two
override def providedEnvironment(stack: ItemStack) = classOf[component.UpgradeTankControllerInRobot]
override def providedEnvironment(stack: ItemStack) = classOf[component.UpgradeTankController.Robot]
}

View File

@ -1,6 +1,5 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.Network
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback
@ -12,8 +11,6 @@ import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.entity.item.EntityItem
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.WorldControl with traits.InventoryControl with traits.InventoryWorldControl with traits.TankAware with traits.TankControl with traits.TankWorldControl {
@ -21,13 +18,7 @@ class Drone(val host: entity.Drone) extends prefab.ManagedEnvironment with trait
withComponent("drone").
create()
def world = host.world
def x = math.floor(host.posX).toInt
def y = math.floor(host.posY).toInt
def z = math.floor(host.posZ).toInt
override protected def position = BlockPosition(host)
override def inventory = host.inventory
@ -41,14 +32,6 @@ class Drone(val host: entity.Drone) extends prefab.ManagedEnvironment with trait
override def selectedTank_=(value: Int) = host.selectedTank = value
override protected def fakePlayer = {
val player = FakePlayerFactory.get(world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile)
player.posX = host.posX
player.posY = host.posY
player.posZ = host.posZ
player
}
override protected def checkSideForAction(args: Arguments, n: Int) =
args.checkSide(n, ForgeDirection.VALID_DIRECTIONS: _*)

View File

@ -0,0 +1,79 @@
package li.cil.oc.server.component
import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost
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.api.prefab
import li.cil.oc.common.entity
import li.cil.oc.common.tileentity
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import net.minecraftforge.common.util.ForgeDirection
object UpgradeInventoryController {
class Adapter(val host: EnvironmentHost) extends prefab.ManagedEnvironment with traits.WorldInventoryAnalytics {
override val node = Network.newNode(this, Visibility.Network).
withComponent("inventory_controller", Visibility.Network).
create()
// ----------------------------------------------------------------------- //
override protected def position = BlockPosition(host)
override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSide(n, ForgeDirection.VALID_DIRECTIONS: _*)
}
class Drone(val host: EnvironmentHost with entity.Drone) extends prefab.ManagedEnvironment with traits.InventoryAnalytics with traits.InventoryWorldControlMk2 with traits.WorldInventoryAnalytics {
override val node = Network.newNode(this, Visibility.Network).
withComponent("inventory_controller", Visibility.Neighbors).
create()
// ----------------------------------------------------------------------- //
override protected def position = BlockPosition(host)
override def inventory = host.inventory
override def selectedSlot = host.selectedSlot
override def selectedSlot_=(value: Int) = host.selectedSlot = value
override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSide(n, ForgeDirection.VALID_DIRECTIONS: _*)
}
class Robot(val host: EnvironmentHost with tileentity.Robot) extends prefab.ManagedEnvironment with traits.InventoryAnalytics with traits.InventoryWorldControlMk2 with traits.WorldInventoryAnalytics {
override val node = Network.newNode(this, Visibility.Network).
withComponent("inventory_controller", Visibility.Neighbors).
create()
// ----------------------------------------------------------------------- //
override protected def position = BlockPosition(host)
override def inventory = host.dynamicInventory
override def selectedSlot = host.selectedSlot
override def selectedSlot_=(value: Int) = host.selectedSlot = value
override protected def checkSideForAction(args: Arguments, n: Int) = host.toGlobal(args.checkSideForAction(n))
@Callback(doc = """function():boolean -- Swaps the equipped tool with the content of the currently selected inventory slot.""")
def equip(context: Context, args: Arguments): Array[AnyRef] = {
if (host.inventorySize > 0) {
val selectedSlot = host.selectedSlot
val equipped = host.getStackInSlot(0)
val selected = host.getStackInSlot(selectedSlot)
host.setInventorySlotContents(0, selected)
host.setInventorySlotContents(selectedSlot, equipped)
result(true)
}
else result(false)
}
}
}

View File

@ -1,114 +0,0 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.internal.Adapter
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.api.prefab
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraft.world.WorldServer
import net.minecraftforge.common.util.FakePlayerFactory
import net.minecraftforge.common.util.ForgeDirection
class UpgradeInventoryControllerInAdapter(val host: EnvironmentHost with Adapter) extends prefab.ManagedEnvironment {
override val node = Network.newNode(this, Visibility.Network).
withComponent("inventory_controller", Visibility.Network).
create()
private val fakePlayer = FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile)
// ----------------------------------------------------------------------- //
@Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the adapter.""")
def getInventorySize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if checkPermission(inventory) => result(inventory.getSizeInventory)
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(side:number, slot:number):number -- Get number of items in the specified slot of the inventory on the specified side of the adapter.""")
def getSlotStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if checkPermission(inventory) =>
result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.stackSize))
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(side:number, slot:number):number -- Get the maximum number of items in the specified slot of the inventory on the specified side of the adapter.""")
def getSlotMaxStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if checkPermission(inventory) =>
result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getMaxStackSize))
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(slotA:number, slotB:number):boolean -- Get whether the items in the two specified slots of the inventory on the specified side of the adapter are of the same type.""")
def compareStacks(context: Context, args: Arguments): Array[AnyRef] = {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if checkPermission(inventory) =>
val stackA = inventory.getStackInSlot(args.checkSlot(inventory, 1))
val stackB = inventory.getStackInSlot(args.checkSlot(inventory, 2))
result(haveSameItemType(stackA, stackB))
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(side:number, slot:number):table -- Get a description of the stack in the specified slot of the inventory on the specified side of the adapter.""")
def getStackInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if checkPermission(inventory) =>
result(inventory.getStackInSlot(args.checkSlot(inventory, 1)))
case _ => result(Unit, "no inventory")
}
}
else result(Unit, "not enabled in config")
@Callback(doc = """function(side:number, slot:number, dbAddress:string, dbSlot:number):boolean -- Store an item stack description in the specified slot of the database with the specified address. Returns true if something was overwritten.""")
def store(context: Context, args: Arguments): Array[AnyRef] = {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if checkPermission(inventory) =>
val stack = inventory.getStackInSlot(args.checkSlot(inventory, 1))
DatabaseAccess.withDatabase(node, args.checkString(2), database => {
val dbSlot = args.checkSlot(database.data, 3)
val nonEmpty = database.data.getStackInSlot(dbSlot) != null
database.data.setInventorySlotContents(dbSlot, stack.copy())
result(nonEmpty)
})
case _ => result(Unit, "no inventory")
}
}
private def checkPermission(inventory: IInventory) = {
fakePlayer synchronized {
fakePlayer.setPosition(host.xPosition, host.yPosition, host.zPosition)
inventory.isUseableByPlayer(fakePlayer)
}
}
private def haveSameItemType(stackA: ItemStack, stackB: ItemStack) =
(Option(stackA), Option(stackB)) match {
case (Some(a), Some(b)) =>
a.getItem == b.getItem &&
(!a.getHasSubtypes || a.getItemDamage == b.getItemDamage)
case (None, None) => true
case _ => false
}
}

View File

@ -1,188 +0,0 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.internal.Robot
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.api.prefab
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.item.ItemStack
import net.minecraftforge.common.util.ForgeDirection
class UpgradeInventoryControllerInRobot(val host: EnvironmentHost with Robot) extends prefab.ManagedEnvironment {
override val node = Network.newNode(this, Visibility.Network).
withComponent("inventory_controller", Visibility.Neighbors).
create()
// ----------------------------------------------------------------------- //
@Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the robot. Back refers to the robot's own inventory.""")
def getInventorySize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForInventory(args, 0)
if (facing == host.facing.getOpposite) result(host.inventorySize)
else InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) => result(inventory.getSizeInventory)
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(side:number, slot:number):number -- Get number of items in the specified slot of the inventory on the specified side of the robot. Back refers to the robot's own inventory.""")
def getSlotStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForInventory(args, 0)
if (facing == host.facing.getOpposite)
result(Option(host.getStackInSlot(args.checkSlot(host, 1))).fold(0)(_.stackSize))
else InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) =>
result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.stackSize))
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(side:number, slot:number):number -- Get the maximum number of items in the specified slot of the inventory on the specified side of the robot. Back refers to the robot's own inventory.""")
def getSlotMaxStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForInventory(args, 0)
if (facing == host.facing.getOpposite)
result(Option(host.getStackInSlot(args.checkSlot(host, 1))).fold(0)(_.getMaxStackSize))
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) =>
result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getMaxStackSize))
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(slotA:number, slotB:number):boolean -- Get whether the items in the two specified slots of the inventory on the specified side of the robot are of the same type. Back refers to the robot's own inventory.""")
def compareStacks(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForInventory(args, 0)
if (facing == host.facing.getOpposite) {
val stackA = host.getStackInSlot(args.checkSlot(host, 1))
val stackB = host.getStackInSlot(args.checkSlot(host, 2))
result(haveSameItemType(stackA, stackB))
}
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) =>
val stackA = inventory.getStackInSlot(args.checkSlot(inventory, 1))
val stackB = inventory.getStackInSlot(args.checkSlot(inventory, 2))
result(haveSameItemType(stackA, stackB))
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(side:number, slot:number):table -- Get a description of the stack in the the inventory on the specified side of the robot. Back refers to the robot's own inventory.""")
def getStackInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val facing = checkSideForInventory(args, 0)
if (facing == host.facing.getOpposite) {
val slot = args.checkSlot(host, 1)
result(host.getStackInSlot(slot))
}
else InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) =>
val slot = args.checkSlot(inventory, 1)
result(inventory.getStackInSlot(slot))
case _ => result(Unit, "no inventory")
}
}
else result(Unit, "not enabled in config")
@Callback(doc = """function(side:number, slot:number, dbAddress:string, dbSlot:number):boolean -- Store an item stack description in the specified slot of the database with the specified address. Returns true if something was overwritten.""")
def store(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForInventory(args, 0)
def store(stack: ItemStack) = DatabaseAccess.withDatabase(node, args.checkString(2), database => {
val dbSlot = args.checkSlot(database.data, 3)
val nonEmpty = database.data.getStackInSlot(dbSlot) != null
database.data.setInventorySlotContents(dbSlot, stack.copy())
result(nonEmpty)
})
if (facing == host.facing.getOpposite) {
val slot = args.checkSlot(host, 1)
store(host.getStackInSlot(slot))
}
else InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) =>
val slot = args.checkSlot(inventory, 1)
store(inventory.getStackInSlot(slot))
case _ => result(Unit, "no inventory")
}
}
@Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Drops the selected item stack into the specified slot of an inventory.""")
def dropIntoSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2)
val selectedSlot = host.selectedSlot
val stack = host.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) {
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) =>
val slot = args.checkSlot(inventory, 1)
if (!InventoryUtils.insertIntoInventorySlot(stack, inventory, Option(facing.getOpposite), slot, count)) {
// Cannot drop into that inventory.
return result(false, "inventory full/invalid slot")
}
else if (stack.stackSize == 0) {
// Dropped whole stack.
host.setInventorySlotContents(selectedSlot, null)
}
else {
// Dropped partial stack.
host.markDirty()
}
case _ => return result(false, "no inventory")
}
context.pause(Settings.get.dropDelay)
result(true)
}
else result(false)
}
@Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Sucks items from the specified slot of an inventory.""")
def suckFromSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2)
InventoryUtils.inventoryAt(BlockPosition(host).offset(facing)) match {
case Some(inventory) if inventory.isUseableByPlayer(host.player) =>
val slot = args.checkSlot(inventory, 1)
if (InventoryUtils.extractFromInventorySlot(host.player.inventory.addItemStackToInventory, inventory, facing.getOpposite, slot, count)) {
context.pause(Settings.get.suckDelay)
result(true)
}
else result(false)
case _ => result(false, "no inventory")
}
}
@Callback(doc = """function():boolean -- Swaps the equipped tool with the content of the currently selected inventory slot.""")
def equip(context: Context, args: Arguments): Array[AnyRef] = {
if (host.inventorySize > 0) {
val selectedSlot = host.selectedSlot
val equipped = host.getStackInSlot(0)
val selected = host.getStackInSlot(selectedSlot)
host.setInventorySlotContents(0, selected)
host.setInventorySlotContents(selectedSlot, equipped)
result(true)
}
else result(false)
}
private def haveSameItemType(stackA: ItemStack, stackB: ItemStack) =
(Option(stackA), Option(stackB)) match {
case (Some(a), Some(b)) =>
a.getItem == b.getItem &&
(!a.getHasSubtypes || a.getItemDamage == b.getItemDamage)
case (None, None) => true
case _ => false
}
private def checkSideForInventory(args: Arguments, n: Int) = host.toGlobal(args.checkSide(n, ForgeDirection.SOUTH, ForgeDirection.NORTH, ForgeDirection.UP, ForgeDirection.DOWN))
private def checkSideForAction(args: Arguments, n: Int) = host.toGlobal(args.checkSideForAction(n))
}

View File

@ -0,0 +1,72 @@
package li.cil.oc.server.component
import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.network._
import li.cil.oc.api.prefab
import li.cil.oc.common.entity
import li.cil.oc.common.tileentity
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import net.minecraftforge.common.util.ForgeDirection
object UpgradeTankController {
class Adapter(val host: EnvironmentHost) extends prefab.ManagedEnvironment with traits.WorldTankAnalytics {
override val node = Network.newNode(this, Visibility.Network).
withComponent("tank_controller", Visibility.Network).
create()
// ----------------------------------------------------------------------- //
override protected def position = BlockPosition(host)
override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSide(n, ForgeDirection.VALID_DIRECTIONS: _*)
}
class Drone(val host: EnvironmentHost with entity.Drone) extends prefab.ManagedEnvironment with traits.TankInventoryControl with traits.WorldTankAnalytics {
override val node = Network.newNode(this, Visibility.Network).
withComponent("tank_controller", Visibility.Neighbors).
create()
override protected def position = BlockPosition(host)
override def inventory = host.inventory
override def selectedSlot = host.selectedSlot
override def selectedSlot_=(value: Int) = host.selectedSlot = value
override def tank = host.tank
override def selectedTank = host.selectedTank
override def selectedTank_=(value: Int) = host.selectedTank = value
override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSide(n, ForgeDirection.VALID_DIRECTIONS: _*)
}
class Robot(val host: EnvironmentHost with tileentity.Robot) extends prefab.ManagedEnvironment with traits.TankInventoryControl with traits.WorldTankAnalytics {
override val node = Network.newNode(this, Visibility.Network).
withComponent("tank_controller", Visibility.Neighbors).
create()
override protected def position = BlockPosition(host)
override def inventory = host.dynamicInventory
override def selectedSlot = host.selectedSlot
override def selectedSlot_=(value: Int) = host.selectedSlot = value
override def tank = host.tank
override def selectedTank = host.selectedTank
override def selectedTank_=(value: Int) = host.selectedTank = value
override protected def checkSideForAction(args: Arguments, n: Int) = host.toGlobal(args.checkSideForAction(n))
}
}

View File

@ -1,54 +0,0 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.internal.Adapter
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.api.prefab
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.fluids.IFluidHandler
class UpgradeTankControllerInAdapter(val host: EnvironmentHost with Adapter) extends prefab.ManagedEnvironment {
override val node = Network.newNode(this, Visibility.Network).
withComponent("tank_controller", Visibility.Network).
create()
// ----------------------------------------------------------------------- //
@Callback(doc = """function(side:number):number -- Get the amount of fluid in the tank on the specified side of the adapter.""")
def getTankLevel(context: Context, args: Arguments): Array[AnyRef] = {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
host.world.getTileEntity(BlockPosition(host).offset(facing)) match {
case handler: IFluidHandler =>
result(handler.getTankInfo(facing.getOpposite).map(info => Option(info.fluid).fold(0)(_.amount)).sum)
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function(side:number):number -- Get the capacity of the tank on the specified side of the adapter.""")
def getTankCapacity(context: Context, args: Arguments): Array[AnyRef] = {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
host.world.getTileEntity(BlockPosition(host).offset(facing)) match {
case handler: IFluidHandler =>
result(handler.getTankInfo(facing.getOpposite).map(_.capacity).foldLeft(0)((max, capacity) => math.max(max, capacity)))
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function(side:number):table -- Get a description of the fluid in the the tank on the specified side of the adapter.""")
def getFluidInTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val facing = args.checkSide(0, ForgeDirection.VALID_DIRECTIONS: _*)
host.world.getTileEntity(BlockPosition(host).offset(facing)) match {
case handler: IFluidHandler => result(handler.getTankInfo(facing.getOpposite))
case _ => result(Unit, "no tank")
}
}
else result(Unit, "not enabled in config")
}

View File

@ -1,145 +0,0 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.Network
import li.cil.oc.api.driver.EnvironmentHost
import li.cil.oc.api.internal.Robot
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.api.prefab
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._
import net.minecraft.item.ItemStack
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.fluids.FluidContainerRegistry
import net.minecraftforge.fluids.IFluidContainerItem
import net.minecraftforge.fluids.IFluidHandler
class UpgradeTankControllerInRobot(val host: EnvironmentHost with Robot) extends prefab.ManagedEnvironment {
override val node = Network.newNode(this, Visibility.Network).
withComponent("tank_controller", Visibility.Neighbors).
create()
// ----------------------------------------------------------------------- //
@Callback(doc = """function(side:number):number -- Get the amount of fluid in the tank on the specified side of the robot. Back refers to the robot's own selected tank.""")
def getTankLevel(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForTank(args, 0)
if (facing == host.facing.getOpposite) result(Option(host.getFluidTank(host.selectedTank)).fold(0)(_.getFluidAmount))
else host.world.getTileEntity(BlockPosition(host).offset(facing)) match {
case handler: IFluidHandler =>
result((Option(host.getFluidTank(host.selectedTank)) match {
case Some(tank) => handler.getTankInfo(facing.getOpposite).filter(info => info.fluid == null || info.fluid.isFluidEqual(tank.getFluid))
case _ => handler.getTankInfo(facing.getOpposite)
}).map(info => Option(info.fluid).fold(0)(_.amount)).sum)
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function(side:number):number -- Get the capacity of the tank on the specified side of the robot. Back refers to the robot's own selected tank.""")
def getTankCapacity(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForTank(args, 0)
if (facing == host.facing.getOpposite) result(Option(host.getFluidTank(host.selectedTank)).fold(0)(_.getCapacity))
else host.world.getTileEntity(BlockPosition(host).offset(facing)) match {
case handler: IFluidHandler =>
result((Option(host.getFluidTank(host.selectedTank)) match {
case Some(tank) => handler.getTankInfo(facing.getOpposite).filter(info => info.fluid == null || info.fluid.isFluidEqual(tank.getFluid))
case _ => handler.getTankInfo(facing.getOpposite)
}).map(_.capacity).foldLeft(0)((max, capacity) => math.max(max, capacity)))
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function(side:number):table -- Get a description of the fluid in the the tank on the specified side of the robot. Back refers to the robot's own selected tank.""")
def getFluidInTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val facing = checkSideForTank(args, 0)
if (facing == host.facing.getOpposite) result(Option(host.getFluidTank(host.selectedTank)).map(_.getFluid).orNull)
else host.world.getTileEntity(BlockPosition(host).offset(facing)) match {
case handler: IFluidHandler => result(handler.getTankInfo(facing.getOpposite))
case _ => result(Unit, "no tank")
}
}
else result(Unit, "not enabled in config")
@Callback(doc = """function([amount:number]):boolean -- Transfers fluid from a tank in the selected inventory slot to the selected tank.""")
def drain(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0)
Option(host.getFluidTank(host.selectedTank)) match {
case Some(tank) =>
host.getStackInSlot(host.selectedSlot) match {
case stack: ItemStack =>
if (FluidContainerRegistry.isFilledContainer(stack)) {
val contents = FluidContainerRegistry.getFluidForFilledItem(stack)
val container = stack.getItem.getContainerItem(stack)
if (tank.getCapacity - tank.getFluidAmount < contents.amount) {
result(Unit, "tank is full")
}
else if (tank.fill(contents, false) < contents.amount) {
result(Unit, "incompatible fluid")
}
else {
tank.fill(contents, true)
host.decrStackSize(host.selectedSlot, 1)
host.player().inventory.addItemStackToInventory(container)
result(true)
}
}
else stack.getItem match {
case container: IFluidContainerItem =>
val drained = container.drain(stack, amount, false)
val transferred = tank.fill(drained, true)
if (transferred > 0) {
container.drain(stack, transferred, true)
result(true)
}
else result(Unit, "incompatible or no fluid")
case _ => result(Unit, "item is empty or not a fluid container")
}
case _ => result(Unit, "nothing selected")
}
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function([amount:number]):boolean -- Transfers fluid from the selected tank to a tank in the selected inventory slot.""")
def fill(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0)
Option(host.getFluidTank(host.selectedTank)) match {
case Some(tank) =>
host.getStackInSlot(host.selectedSlot) match {
case stack: ItemStack =>
if (FluidContainerRegistry.isEmptyContainer(stack)) {
val drained = tank.drain(amount, false)
val filled = FluidContainerRegistry.fillFluidContainer(drained, stack)
if (filled == null) {
result(Unit, "tank is empty")
}
else {
tank.drain(FluidContainerRegistry.getFluidForFilledItem(filled).amount, true)
host.decrStackSize(host.selectedSlot, 1)
host.player().inventory.addItemStackToInventory(filled)
result(true)
}
}
else stack.getItem match {
case container: IFluidContainerItem =>
val drained = tank.drain(amount, false)
val transferred = container.fill(stack, drained, true)
if (transferred > 0) {
tank.drain(transferred, true)
result(true)
}
else result(Unit, "incompatible or no fluid")
case _ => result(Unit, "item is full or not a fluid container")
}
case _ => result(Unit, "nothing selected")
}
case _ => result(Unit, "no tank")
}
}
private def checkSideForTank(args: Arguments, n: Int) = host.toGlobal(args.checkSide(n, ForgeDirection.SOUTH, ForgeDirection.NORTH, ForgeDirection.UP, ForgeDirection.DOWN))
}

View File

@ -10,19 +10,17 @@ 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}
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.ResultWrapper.result
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.item.EntityMinecart
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.MovingObjectPosition
import net.minecraft.util.MovingObjectPosition.MovingObjectType
@ -41,13 +39,7 @@ class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with
val romRobot = Option(api.FileSystem.asManagedEnvironment(api.FileSystem.
fromClass(OpenComputers.getClass, Settings.resourceDomain, "lua/component/robot"), "robot"))
def world = robot.world
def x = robot.x
def y = robot.y
def z = robot.z
override protected def position = BlockPosition(robot)
// ----------------------------------------------------------------------- //
@ -64,11 +56,7 @@ 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)
}
override def tank = robot.tank
def selectedTank = robot.selectedTank
@ -205,7 +193,7 @@ class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with
case Some(entity) =>
attack(player, entity)
case _ =>
if (world.extinguishFire(player, x, y, z, facing.ordinal)) {
if (world.extinguishFire(player, position, facing)) {
triggerDelay()
(true, "fire")
}
@ -435,16 +423,15 @@ class Robot(val robot: tileentity.Robot) extends prefab.ManagedEnvironment with
}
private def clickParamsForPlace(facing: ForgeDirection) = {
(x, y, z,
(position.x, position.y, position.z,
0.5f + facing.offsetX * 0.5f,
0.5f + facing.offsetY * 0.5f,
0.5f + facing.offsetZ * 0.5f)
}
private def clickParamsForItemUse(facing: ForgeDirection, side: ForgeDirection) = {
(x + facing.offsetX + side.offsetX,
y + facing.offsetY + side.offsetY,
z + facing.offsetZ + side.offsetZ,
val blockPos = position.offset(facing).offset(side)
(blockPos.x, blockPos.y, blockPos.z,
0.5f - side.offsetX * 0.5f,
0.5f - side.offsetY * 0.5f,
0.5f - side.offsetZ * 0.5f)

View File

@ -0,0 +1,32 @@
package li.cil.oc.server.component.traits
import li.cil.oc.Settings
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.server.component.result
import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._
import net.minecraft.item.ItemStack
trait InventoryAnalytics extends InventoryAware with NetworkAware {
@Callback(doc = """function([slot:number]):table -- Get a description of the stack in the specified slot or the selected slot.""")
def getStackInInternalSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val slot = optSlot(args, 0)
result(inventory.getStackInSlot(slot))
}
else result(Unit, "not enabled in config")
@Callback(doc = """function(slot:number, dbAddress:string, dbSlot:number):boolean -- Store an item stack description in the specified slot of the database with the specified address.""")
def storeInternal(context: Context, args: Arguments): Array[AnyRef] = {
val localSlot = args.checkSlot(inventory, 0)
val dbAddress = args.checkString(1)
def store(stack: ItemStack) = DatabaseAccess.withDatabase(node, dbAddress, database => {
val dbSlot = args.checkSlot(database.data, 2)
val nonEmpty = database.data.getStackInSlot(dbSlot) != null
database.data.setInventorySlotContents(dbSlot, stack.copy())
result(nonEmpty)
})
store(inventory.getStackInSlot(localSlot))
}
}

View File

@ -1,5 +1,7 @@
package li.cil.oc.server.component.traits
import li.cil.oc.api.machine.Arguments
import li.cil.oc.util.ExtendedArguments._
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
@ -17,6 +19,10 @@ trait InventoryAware {
// ----------------------------------------------------------------------- //
protected def optSlot(args: Arguments, n: Int) =
if (args.count > 0 && args.checkAny(0) != null) args.checkSlot(inventory, 0)
else selectedSlot
protected def stackInSlot(slot: Int) = Option(inventory.getStackInSlot(slot))
protected def haveSameItemType(stackA: ItemStack, stackB: ItemStack) =

View File

@ -12,20 +12,16 @@ trait InventoryControl extends InventoryAware {
@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)
if (slot != selectedSlot) {
selectedSlot = slot
}
val slot = optSlot(args, 0)
if (slot != selectedSlot) {
selectedSlot = slot
}
result(selectedSlot + 1)
}
@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)
else selectedSlot
val slot = optSlot(args, 0)
result(stackInSlot(slot) match {
case Some(stack) => stack.stackSize
case _ => 0
@ -34,9 +30,7 @@ trait InventoryControl extends InventoryAware {
@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)
else selectedSlot
val slot = optSlot(args, 0)
result(stackInSlot(slot) match {
case Some(stack) => math.min(inventory.getInventoryStackLimit, stack.getMaxStackSize) - stack.stackSize
case _ => inventory.getInventoryStackLimit

View File

@ -4,7 +4,6 @@ import li.cil.oc.Settings
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.ExtendedArguments._
import li.cil.oc.util.ExtendedWorld._
import li.cil.oc.util.InventoryUtils
@ -20,7 +19,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
stackInSlot(selectedSlot) match {
case Some(stack) => Option(stack.getItem) match {
case Some(item: ItemBlock) =>
val blockPos = BlockPosition(x, y, z, Option(world)).offset(side)
val blockPos = position.offset(side)
val idMatches = item.field_150939_a == world.getBlock(blockPos)
val subTypeMatches = !item.getHasSubtypes || item.getMetadata(stack.getItemDamage) == world.getBlockMetadata(blockPos)
return result(idMatches && subTypeMatches)
@ -37,7 +36,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val count = args.optionalItemCount(1)
val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) {
InventoryUtils.inventoryAt(BlockPosition(x, y, z, Option(world)).offset(facing)) match {
InventoryUtils.inventoryAt(position.offset(facing)) match {
case Some(inv) if inv.isUseableByPlayer(fakePlayer) =>
if (!InventoryUtils.insertIntoInventory(stack, inv, Option(facing.getOpposite), count)) {
// Cannot drop into that inventory.
@ -68,7 +67,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(1)
if (InventoryUtils.inventoryAt(BlockPosition(x, y, z, Option(world)).offset(facing)).exists(inventory => {
if (InventoryUtils.inventoryAt(position.offset(facing)).exists(inventory => {
inventory.isUseableByPlayer(fakePlayer) && InventoryUtils.extractFromInventory(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, count)
})) {
context.pause(Settings.get.suckDelay)

View File

@ -0,0 +1,62 @@
package li.cil.oc.server.component.traits
import li.cil.oc.Settings
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.server.component.result
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.inventory.IInventory
import net.minecraftforge.common.util.ForgeDirection
trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideRestricted {
@Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Drops the selected item stack into the specified slot of an inventory.""")
def dropIntoSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2)
val stack = inventory.getStackInSlot(selectedSlot)
if (stack != null && stack.stackSize > 0) {
withInventory(facing, inventory => {
val slot = args.checkSlot(inventory, 1)
if (!InventoryUtils.insertIntoInventorySlot(stack, inventory, Option(facing.getOpposite), slot, count)) {
// Cannot drop into that inventory.
return result(false, "inventory full/invalid slot")
}
else if (stack.stackSize == 0) {
// Dropped whole stack.
inventory.setInventorySlotContents(selectedSlot, null)
}
else {
// Dropped partial stack.
inventory.markDirty()
}
context.pause(Settings.get.dropDelay)
result(true)
})
}
else result(false)
}
@Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Sucks items from the specified slot of an inventory.""")
def suckFromSlot(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = args.optionalItemCount(2)
withInventory(facing, inventory => {
val slot = args.checkSlot(inventory, 1)
if (InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, slot, count)) {
context.pause(Settings.get.suckDelay)
result(true)
}
else result(false)
})
}
private def withInventory(side: ForgeDirection, f: IInventory => Array[AnyRef]) =
InventoryUtils.inventoryAt(position.offset(side)) match {
case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) => f(inventory)
case _ => result(Unit, "no inventory")
}
}

View File

@ -0,0 +1,7 @@
package li.cil.oc.server.component.traits
import li.cil.oc.api.network.Node
trait NetworkAware {
def node: Node
}

View File

@ -1,6 +1,8 @@
package li.cil.oc.server.component.traits
import li.cil.oc.api.machine.Arguments
import li.cil.oc.common.inventory.MultiTank
import li.cil.oc.util.ExtendedArguments._
import net.minecraftforge.fluids.FluidStack
trait TankAware {
@ -12,6 +14,10 @@ trait TankAware {
// ----------------------------------------------------------------------- //
protected def optTank(args: Arguments, n: Int) =
if (args.count > 0 && args.checkAny(0) != null) args.checkTank(tank, 0)
else selectedTank
protected def getTank(index: Int) = Option(tank.getFluidTank(index))
protected def fluidInTank(index: Int) = getTank(index) match {

View File

@ -0,0 +1,137 @@
package li.cil.oc.server.component.traits
import li.cil.oc.Settings
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.server.component.result
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.item.ItemStack
import net.minecraftforge.fluids.FluidContainerRegistry
import net.minecraftforge.fluids.FluidStack
import net.minecraftforge.fluids.IFluidContainerItem
trait TankInventoryControl extends WorldAware with InventoryAware with TankAware {
@Callback(doc = """function([slot:number]):number -- Get the amount of fluid in the tank item in the specified slot or the selected slot.""")
def getTankLevelInSlot(context: Context, args: Arguments): Array[AnyRef] =
withFluidInfo(optSlot(args, 0), (fluid, _) => result(fluid.amount))
@Callback(doc = """function([slot:number]):number -- Get the capacity of the tank item in the specified slot of the robot or the selected slot.""")
def getTankCapacityInSlot(context: Context, args: Arguments): Array[AnyRef] =
withFluidInfo(optSlot(args, 0), (_, capacity) => result(capacity))
@Callback(doc = """function([slot:number]):table -- Get a description of the fluid in the tank item in the specified slot or the selected slot.""")
def getFluidInTankInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
withFluidInfo(optSlot(args, 0), (fluid, _) => result(fluid))
}
else result(Unit, "not enabled in config")
@Callback(doc = """function([tank:number]):table -- Get a description of the fluid in the tank in the specified slot or the selected slot.""")
def getFluidInInternalTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
result(Option(tank.getFluidTank(optTank(args, 0))).map(_.getFluid).orNull)
}
else result(Unit, "not enabled in config")
@Callback(doc = """function([amount:number]):boolean -- Transfers fluid from a tank in the selected inventory slot to the selected tank.""")
def drain(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0)
Option(tank.getFluidTank(selectedTank)) match {
case Some(into) => inventory.getStackInSlot(selectedSlot) match {
case stack: ItemStack =>
if (FluidContainerRegistry.isFilledContainer(stack)) {
val contents = FluidContainerRegistry.getFluidForFilledItem(stack)
val container = stack.getItem.getContainerItem(stack)
if (into.getCapacity - into.getFluidAmount < contents.amount) {
result(Unit, "tank is full")
}
else if (into.fill(contents, false) < contents.amount) {
result(Unit, "incompatible fluid")
}
else {
into.fill(contents, true)
inventory.decrStackSize(selectedSlot, 1)
InventoryUtils.insertIntoInventory(container, inventory, slots = Option(insertionSlots))
if (container.stackSize > 0) {
InventoryUtils.spawnStackInWorld(position, container)
}
result(true)
}
}
else stack.getItem match {
case from: IFluidContainerItem =>
val drained = from.drain(stack, amount, false)
val transferred = into.fill(drained, true)
if (transferred > 0) {
from.drain(stack, transferred, true)
result(true)
}
else result(Unit, "incompatible or no fluid")
case _ => result(Unit, "item is empty or not a fluid container")
}
case _ => result(Unit, "nothing selected")
}
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function([amount:number]):boolean -- Transfers fluid from the selected tank to a tank in the selected inventory slot.""")
def fill(context: Context, args: Arguments): Array[AnyRef] = {
val amount = args.optionalFluidCount(0)
Option(tank.getFluidTank(selectedTank)) match {
case Some(from) => inventory.getStackInSlot(selectedSlot) match {
case stack: ItemStack =>
if (FluidContainerRegistry.isEmptyContainer(stack)) {
val drained = from.drain(amount, false)
val filled = FluidContainerRegistry.fillFluidContainer(drained, stack)
if (filled == null) {
result(Unit, "tank is empty")
}
else {
from.drain(FluidContainerRegistry.getFluidForFilledItem(filled).amount, true)
inventory.decrStackSize(selectedSlot, 1)
InventoryUtils.insertIntoInventory(filled, inventory, slots = Option(insertionSlots))
if (filled.stackSize > 0) {
InventoryUtils.spawnStackInWorld(position, filled)
}
result(true)
}
}
else stack.getItem match {
case into: IFluidContainerItem =>
val drained = from.drain(amount, false)
val transferred = into.fill(stack, drained, true)
if (transferred > 0) {
from.drain(transferred, true)
result(true)
}
else result(Unit, "incompatible or no fluid")
case _ => result(Unit, "item is full or not a fluid container")
}
case _ => result(Unit, "nothing selected")
}
case _ => result(Unit, "no tank")
}
}
private def withFluidInfo(slot: Int, f: (FluidStack, Int) => Array[AnyRef]) = {
def fluidInfo(stack: ItemStack) = {
if (FluidContainerRegistry.isFilledContainer(stack)) {
Option((FluidContainerRegistry.getFluidForFilledItem(stack), FluidContainerRegistry.getContainerCapacity(stack)))
}
else if (FluidContainerRegistry.isEmptyContainer(stack)) {
Option((new FluidStack(0, 0), FluidContainerRegistry.getContainerCapacity(stack)))
}
else stack.getItem match {
case from: IFluidContainerItem => Option((from.getFluid(stack), from.getCapacity(stack)))
case _ => None
}
}
inventory.getStackInSlot(slot) match {
case stack: ItemStack => fluidInfo(stack) match {
case Some((fluid, capacity)) => f(fluid, capacity)
case _ => result(Unit, "item is not a fluid container")
}
}
}
}

View File

@ -3,10 +3,9 @@ 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.ExtendedArguments._
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
@ -18,7 +17,7 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
val side = checkSideForAction(args, 0)
fluidInTank(selectedTank) match {
case Some(stack) =>
val blockPos = BlockPosition(x, y, z).offset(side)
val blockPos = position.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))))
@ -44,7 +43,7 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
result(Unit, "tank is full")
}
else {
val blockPos = BlockPosition(x, y, z).offset(facing)
val blockPos = position.offset(facing)
if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match {
case handler: IFluidHandler =>
tank.getFluid match {
@ -88,7 +87,7 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
if (count > 0 && amount == 0) {
result(Unit, "tank is empty")
}
val blockPos = BlockPosition(x, y, z, Option(world)).offset(facing)
val blockPos = position.offset(facing)
if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match {
case handler: IFluidHandler =>
tank.getFluid match {
@ -119,7 +118,7 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted {
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))
world.notifyBlockOfNeighborChange(blockPos, world.getBlock(position))
result(true)
}
}

View File

@ -1,14 +1,16 @@
package li.cil.oc.server.component.traits
import li.cil.oc.Settings
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedBlock._
import li.cil.oc.util.ExtendedWorld._
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.item.EntityMinecart
import net.minecraft.world.World
import net.minecraft.world.WorldServer
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.common.util.FakePlayer
import net.minecraftforge.common.util.FakePlayerFactory
import net.minecraftforge.common.util.ForgeDirection
import net.minecraftforge.event.world.BlockEvent
import net.minecraftforge.fluids.FluidRegistry
@ -18,26 +20,28 @@ import scala.reflect.ClassTag
import scala.reflect.classTag
trait WorldAware {
protected def world: World
protected def position: BlockPosition
protected def x: Int
protected def world = position.world.get
protected def y: Int
protected def z: Int
protected def fakePlayer: FakePlayer
protected def fakePlayer: FakePlayer = {
val player = FakePlayerFactory.get(world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile)
player.posX = position.x + 0.5
player.posY = position.y + 0.5
player.posZ = position.z + 0.5
player
}
protected def entitiesInBlock[Type <: Entity : ClassTag](blockPos: BlockPosition) = {
world.getEntitiesWithinAABB(classTag[Type].runtimeClass, blockPos.bounds).map(_.asInstanceOf[Type])
}
protected def entitiesOnSide[Type <: Entity : ClassTag](side: ForgeDirection) = {
entitiesInBlock[Type](BlockPosition(x, y, z, Option(world)).offset(side))
entitiesInBlock[Type](position.offset(side))
}
protected def closestEntity[Type <: Entity : ClassTag](side: ForgeDirection) = {
val blockPos = BlockPosition(x, y, z, Option(world)).offset(side)
val blockPos = position.offset(side)
Option(world.findNearestEntityWithinAABB(classTag[Type].runtimeClass, blockPos.bounds, fakePlayer)).map(_.asInstanceOf[Type])
}
@ -46,7 +50,7 @@ trait WorldAware {
case Some(_@(_: EntityLivingBase | _: EntityMinecart)) =>
(true, "entity")
case _ =>
val blockPos = BlockPosition(x, y, z, Option(world)).offset(side)
val blockPos = position.offset(side)
val block = world.getBlock(blockPos)
val metadata = world.getBlockMetadata(blockPos)
if (block.isAir(blockPos)) {

View File

@ -0,0 +1,72 @@
package li.cil.oc.server.component.traits
import li.cil.oc.Settings
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.server.component.result
import li.cil.oc.util.DatabaseAccess
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.InventoryUtils
import net.minecraft.inventory.IInventory
import net.minecraft.item.ItemStack
import net.minecraftforge.common.util.ForgeDirection
trait WorldInventoryAnalytics extends WorldAware with SideRestricted with NetworkAware {
@Callback(doc = """function(side:number):number -- Get the number of slots in the inventory on the specified side of the robot.""")
def getInventorySize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => result(inventory.getSizeInventory))
}
@Callback(doc = """function(side:number, slot:number):number -- Get number of items in the specified slot of the inventory on the specified side of the robot.""")
def getSlotStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.stackSize)))
}
@Callback(doc = """function(side:number, slot:number):number -- Get the maximum number of items in the specified slot of the inventory on the specified side of the robot.""")
def getSlotMaxStackSize(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => result(Option(inventory.getStackInSlot(args.checkSlot(inventory, 1))).fold(0)(_.getMaxStackSize)))
}
@Callback(doc = """function(side:number, slotA:number, slotB:number):boolean -- Get whether the items in the two specified slots of the inventory on the specified side of the robot are of the same type.""")
def compareStacks(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => {
val stackA = inventory.getStackInSlot(args.checkSlot(inventory, 1))
val stackB = inventory.getStackInSlot(args.checkSlot(inventory, 2))
result(stackA == stackB ||
(stackA != null && stackB != null &&
stackA.getItem == stackB.getItem &&
(!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage)))
})
}
@Callback(doc = """function(side:number, slot:number):table -- Get a description of the stack in the the inventory on the specified side of the robot.""")
def getStackInSlot(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val facing = checkSideForAction(args, 0)
withInventory(facing, inventory => result(inventory.getStackInSlot(args.checkSlot(inventory, 1))))
}
else result(Unit, "not enabled in config")
@Callback(doc = """function(side:number, slot:number, dbAddress:string, dbSlot:number):boolean -- Store an item stack description in the specified slot of the database with the specified address.""")
def store(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val dbAddress = args.checkString(2)
def store(stack: ItemStack) = DatabaseAccess.withDatabase(node, dbAddress, database => {
val dbSlot = args.checkSlot(database.data, 3)
val nonEmpty = database.data.getStackInSlot(dbSlot) != null
database.data.setInventorySlotContents(dbSlot, stack.copy())
result(nonEmpty)
})
withInventory(facing, inventory => store(inventory.getStackInSlot(args.checkSlot(inventory, 1))))
}
private def withInventory(side: ForgeDirection, f: IInventory => Array[AnyRef]) =
InventoryUtils.inventoryAt(position.offset(side)) match {
case Some(inventory) if inventory.isUseableByPlayer(fakePlayer) => f(inventory)
case _ => result(Unit, "no inventory")
}
}

View File

@ -0,0 +1,42 @@
package li.cil.oc.server.component.traits
import li.cil.oc.Settings
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.server.component.result
import li.cil.oc.util.ExtendedWorld._
import net.minecraftforge.fluids.IFluidHandler
trait WorldTankAnalytics extends WorldAware with SideRestricted {
@Callback(doc = """function(side:number):number -- Get the amount of fluid in the tank on the specified side.""")
def getTankLevel(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
world.getTileEntity(position.offset(facing)) match {
case handler: IFluidHandler =>
result(handler.getTankInfo(facing.getOpposite).map(info => Option(info.fluid).fold(0)(_.amount)).sum)
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function(side:number):number -- Get the capacity of the tank on the specified side.""")
def getTankCapacity(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
world.getTileEntity(position.offset(facing)) match {
case handler: IFluidHandler =>
result(handler.getTankInfo(facing.getOpposite).map(_.capacity).foldLeft(0)((max, capacity) => math.max(max, capacity)))
case _ => result(Unit, "no tank")
}
}
@Callback(doc = """function(side:number):table -- Get a description of the fluid in the the tank on the specified side.""")
def getFluidInTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) {
val facing = checkSideForAction(args, 0)
world.getTileEntity(position.offset(facing)) match {
case handler: IFluidHandler =>
result(handler.getTankInfo(facing.getOpposite))
case _ => result(Unit, "no tank")
}
}
else result(Unit, "not enabled in config")
}

View File

@ -2,9 +2,11 @@ package li.cil.oc.util
import li.cil.oc.api.driver.EnvironmentHost
import net.minecraft.block.Block
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.IBlockAccess
import net.minecraft.world.World
import net.minecraftforge.common.util.ForgeDirection
import scala.language.implicitConversions
@ -17,10 +19,10 @@ object ExtendedWorld {
class ExtendedBlockAccess(val world: IBlockAccess) {
def getBlock(position: BlockPosition) = world.getBlock(position.x, position.y, position.z)
def getBlockMetadata(position: BlockPosition) = world.getBlockMetadata(position.x, position.y, position.z)
def getBlockMapColor(position: BlockPosition) = getBlock(position).getMapColor(getBlockMetadata(position))
def getBlockMetadata(position: BlockPosition) = world.getBlockMetadata(position.x, position.y, position.z)
def getTileEntity(position: BlockPosition): TileEntity = world.getTileEntity(position.x, position.y, position.z)
def getTileEntity(host: EnvironmentHost): TileEntity = getTileEntity(BlockPosition(host))
@ -31,19 +33,21 @@ object ExtendedWorld {
class ExtendedWorld(override val world: World) extends ExtendedBlockAccess(world) {
def blockExists(position: BlockPosition) = world.blockExists(position.x, position.y, position.z)
def breakBlock(position: BlockPosition, drops: Boolean = true) = world.func_147480_a(position.x, position.y, position.z, drops)
def extinguishFire(player: EntityPlayer, position: BlockPosition, side: ForgeDirection) = world.extinguishFire(player, position.x, position.y, position.z, side.ordinal)
def getBlockHardness(position: BlockPosition) = getBlock(position).getBlockHardness(world, position.x, position.y, position.z)
def getBlockHarvestLevel(position: BlockPosition) = getBlock(position).getHarvestLevel(getBlockMetadata(position))
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)
def setBlockToAir(position: BlockPosition) = world.setBlockToAir(position.x, position.y, position.z)
}
}

View File

@ -233,7 +233,7 @@ object InventoryUtils {
/**
* Utility method for spawning an item stack in the world.
*/
def spawnStackInWorld(position: BlockPosition, stack: ItemStack, direction: ForgeDirection): EntityItem = position.world match {
def spawnStackInWorld(position: BlockPosition, stack: ItemStack, direction: ForgeDirection = ForgeDirection.UP): EntityItem = position.world match {
case Some(world) =>
val rng = world.rand
val (tx, ty, tz) = (