mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-14 17:56:34 -04:00
suck it. robots can now pick up items lying around on the ground, and there's a fake player class for robots (with a fake inventory, which is used for that and just dispatches to the robot tile entity's inventory - allows emulating actual player behavior pretty nicely)
This commit is contained in:
parent
b1d96a3c06
commit
50f09c80dd
@ -10,18 +10,12 @@ import li.cil.oc.common
|
|||||||
import li.cil.oc.server.component
|
import li.cil.oc.server.component
|
||||||
import li.cil.oc.server.component.GraphicsCard
|
import li.cil.oc.server.component.GraphicsCard
|
||||||
import li.cil.oc.server.driver.Registry
|
import li.cil.oc.server.driver.Registry
|
||||||
import li.cil.oc.server.{PacketSender => ServerPacketSender}
|
|
||||||
import li.cil.oc.util.ExtendedNBT._
|
import li.cil.oc.util.ExtendedNBT._
|
||||||
import net.minecraft.block.Block
|
import li.cil.oc.util.RobotPlayer
|
||||||
import net.minecraft.client.Minecraft
|
import net.minecraft.client.Minecraft
|
||||||
import net.minecraft.entity.EntityLivingBase
|
import net.minecraft.item.ItemStack
|
||||||
import net.minecraft.item.{ItemBlock, ItemStack}
|
|
||||||
import net.minecraft.nbt.NBTTagCompound
|
import net.minecraft.nbt.NBTTagCompound
|
||||||
import net.minecraft.util.AxisAlignedBB
|
|
||||||
import net.minecraftforge.common.ForgeDirection
|
|
||||||
import net.minecraftforge.fluids.FluidRegistry
|
|
||||||
import scala.Some
|
import scala.Some
|
||||||
import scala.collection.convert.WrapAsScala._
|
|
||||||
|
|
||||||
class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with PowerInformation {
|
class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with PowerInformation {
|
||||||
def this() = this(false)
|
def this() = this(false)
|
||||||
@ -32,243 +26,11 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
|
|||||||
withComponent("computer", Visibility.Neighbors).
|
withComponent("computer", Visibility.Neighbors).
|
||||||
create()
|
create()
|
||||||
|
|
||||||
|
lazy val player = new RobotPlayer(this)
|
||||||
override val buffer = new common.component.Buffer(this) {
|
override val buffer = new common.component.Buffer(this) {
|
||||||
override def maxResolution = (48, 14)
|
override def maxResolution = (48, 14)
|
||||||
}
|
}
|
||||||
override val computer = if (isRemote) null
|
override val computer = if (isRemote) null else new component.Robot(this)
|
||||||
else new component.Computer(this) {
|
|
||||||
override def isRobot(context: Context, args: Arguments): Array[AnyRef] =
|
|
||||||
Array(java.lang.Boolean.TRUE)
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
|
||||||
|
|
||||||
@LuaCallback("select")
|
|
||||||
def select(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
// Get or set selected inventory slot.
|
|
||||||
if (args.count > 0 && args.checkAny(0) != null) {
|
|
||||||
val slot = checkSlot(args, 0)
|
|
||||||
if (slot != selectedSlot) {
|
|
||||||
selectedSlot = slot
|
|
||||||
ServerPacketSender.sendRobotSelectedSlotState(Robot.this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result(selectedSlot)
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("count")
|
|
||||||
def count(context: Context, args: Arguments): Array[AnyRef] =
|
|
||||||
result(stackInSlot(selectedSlot) match {
|
|
||||||
case Some(stack) => stack.stackSize
|
|
||||||
case _ => 0
|
|
||||||
})
|
|
||||||
|
|
||||||
@LuaCallback("space")
|
|
||||||
def space(context: Context, args: Arguments): Array[AnyRef] =
|
|
||||||
result(stackInSlot(selectedSlot) match {
|
|
||||||
case Some(stack) => getInventoryStackLimit - stack.stackSize
|
|
||||||
case _ => getInventoryStackLimit
|
|
||||||
})
|
|
||||||
|
|
||||||
@LuaCallback("compareTo")
|
|
||||||
def compareTo(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
val slot = checkSlot(args, 0)
|
|
||||||
result((stackInSlot(selectedSlot), stackInSlot(slot)) match {
|
|
||||||
case (Some(stackA), Some(stackB)) => haveSameItemType(stackA, stackB)
|
|
||||||
case (None, None) => true
|
|
||||||
case _ => false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("transferTo")
|
|
||||||
def transferTo(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
val slot = checkSlot(args, 0)
|
|
||||||
val count = checkOptionalItemCount(args, 1)
|
|
||||||
if (slot == selectedSlot || count == 0) {
|
|
||||||
result(true)
|
|
||||||
}
|
|
||||||
else result((stackInSlot(selectedSlot), stackInSlot(slot)) match {
|
|
||||||
case (Some(from), Some(to)) =>
|
|
||||||
if (haveSameItemType(from, to)) {
|
|
||||||
val space = (getInventoryStackLimit min to.getMaxStackSize) - to.stackSize
|
|
||||||
val amount = count min space min from.stackSize
|
|
||||||
if (amount > 0) {
|
|
||||||
from.stackSize -= amount
|
|
||||||
to.stackSize += amount
|
|
||||||
assert(from.stackSize >= 0)
|
|
||||||
if (from.stackSize == 0) {
|
|
||||||
setInventorySlotContents(actualSlot(selectedSlot), null)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setInventorySlotContents(actualSlot(slot), from)
|
|
||||||
setInventorySlotContents(actualSlot(selectedSlot), to)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
case (Some(from), None) =>
|
|
||||||
setInventorySlotContents(actualSlot(slot), decrStackSize(actualSlot(selectedSlot), count))
|
|
||||||
true
|
|
||||||
case _ => false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("drop")
|
|
||||||
def drop(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
val side = checkSideForAction(args, 0)
|
|
||||||
val count = checkOptionalItemCount(args, 1)
|
|
||||||
result(dropSlot(actualSlot(selectedSlot), count, side))
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("place")
|
|
||||||
def place(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
// Place block item selected in inventory.
|
|
||||||
val side = checkSideForAction(args, 0)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("suck")
|
|
||||||
def suck(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
// Pick up items lying around.
|
|
||||||
val side = checkSideForAction(args, 0)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
|
||||||
|
|
||||||
@LuaCallback("compare")
|
|
||||||
def compare(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
val side = checkSideForAction(args, 0)
|
|
||||||
stackInSlot(selectedSlot) match {
|
|
||||||
case Some(stack) => Option(stack.getItem) match {
|
|
||||||
case Some(item: ItemBlock) =>
|
|
||||||
val (bx, by, bz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ)
|
|
||||||
val idMatches = item.getBlockID == world.getBlockId(bx, by, bz)
|
|
||||||
val subTypeMatches = !item.getHasSubtypes || item.getMetadata(stack.getItemDamage) == world.getBlockMetadata(bx, by, bz)
|
|
||||||
return result(idMatches && subTypeMatches)
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
case _ =>
|
|
||||||
}
|
|
||||||
result(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("detect")
|
|
||||||
def detect(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
val side = checkSideForAction(args, 0)
|
|
||||||
val (bx, by, bz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ)
|
|
||||||
val id = world.getBlockId(bx, by, bz)
|
|
||||||
val block = Block.blocksList(id)
|
|
||||||
if (id == 0 || block == null || block.isAirBlock(world, bx, by, bz)) {
|
|
||||||
closestEntity(side) match {
|
|
||||||
case Some(entity) => result(true, "entity")
|
|
||||||
case _ => result(false, "air")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (FluidRegistry.lookupFluidForBlock(block) != null) {
|
|
||||||
result(false, "liquid")
|
|
||||||
}
|
|
||||||
else if (block.isBlockReplaceable(world, bx, by, bz)) {
|
|
||||||
result(false, "replaceable")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result(true, "solid")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
|
||||||
|
|
||||||
@LuaCallback("attack")
|
|
||||||
def attack(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
// Attack with equipped tool.
|
|
||||||
val side = checkSideForAction(args, 0)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("use")
|
|
||||||
def use(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
// Use equipped tool (e.g. dig, chop, till).
|
|
||||||
val side = checkSideForAction(args, 0)
|
|
||||||
val sneaky = args.checkBoolean(1)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
|
||||||
|
|
||||||
@LuaCallback("move")
|
|
||||||
def move(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
// Try to move in the specified direction.
|
|
||||||
val side = checkSideForMovement(args, 0)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
@LuaCallback("turn")
|
|
||||||
def turn(context: Context, args: Arguments): Array[AnyRef] = {
|
|
||||||
// Turn in the specified direction.
|
|
||||||
val clockwise = args.checkBoolean(0)
|
|
||||||
if (clockwise) rotate(ForgeDirection.UP)
|
|
||||||
else rotate(ForgeDirection.DOWN)
|
|
||||||
result(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
|
||||||
|
|
||||||
private def closestEntity(side: ForgeDirection) = {
|
|
||||||
val (bx, by, bz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ)
|
|
||||||
val id = world.getBlockId(bx, by, bz)
|
|
||||||
val block = Block.blocksList(id)
|
|
||||||
if (id == 0 || block == null || block.isAirBlock(world, bx, by, bz)) {
|
|
||||||
val bounds = AxisAlignedBB.getAABBPool.getAABB(bx, by, bz, bx + 1, by + 1, bz + 1)
|
|
||||||
val entities = world.getEntitiesWithinAABB(classOf[EntityLivingBase], bounds)
|
|
||||||
entities.foldLeft((Double.PositiveInfinity, None: Option[EntityLivingBase])) {
|
|
||||||
case ((bestDistance, bestEntity), entity: EntityLivingBase) =>
|
|
||||||
val distance = entity.getDistanceSq(x + 0.5, y + 0.5, z + 0.5)
|
|
||||||
if (distance < bestDistance) (distance, Some(entity))
|
|
||||||
else (bestDistance, bestEntity)
|
|
||||||
case (best, _) => best
|
|
||||||
} match {
|
|
||||||
case (_, Some(entity)) => Some(entity)
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else None
|
|
||||||
}
|
|
||||||
|
|
||||||
private def haveSameItemType(stackA: ItemStack, stackB: ItemStack) =
|
|
||||||
stackA.itemID == stackB.itemID &&
|
|
||||||
(!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage)
|
|
||||||
|
|
||||||
private def stackInSlot(slot: Int) = items(actualSlot(slot))
|
|
||||||
|
|
||||||
private def checkOptionalItemCount(args: Arguments, n: Int) =
|
|
||||||
if (args.count > n && args.checkAny(n) != null) {
|
|
||||||
args.checkInteger(n) max 0 min getInventoryStackLimit
|
|
||||||
}
|
|
||||||
else getInventoryStackLimit
|
|
||||||
|
|
||||||
private def checkSlot(args: Arguments, n: Int) = {
|
|
||||||
val slot = args.checkInteger(n) - 1
|
|
||||||
if (slot < 0 || slot > 15) {
|
|
||||||
throw new IllegalArgumentException("invalid slot")
|
|
||||||
}
|
|
||||||
slot
|
|
||||||
}
|
|
||||||
|
|
||||||
private def checkSideForAction(args: Arguments, n: Int) = checkSide(args, n, ForgeDirection.SOUTH, ForgeDirection.UP, ForgeDirection.DOWN)
|
|
||||||
|
|
||||||
private def checkSideForMovement(args: Arguments, n: Int) = checkSide(args, n, ForgeDirection.SOUTH, ForgeDirection.NORTH)
|
|
||||||
|
|
||||||
private def checkSide(args: Arguments, n: Int, allowed: ForgeDirection*) = {
|
|
||||||
val side = args.checkInteger(n)
|
|
||||||
if (side < 0 || side > 5) {
|
|
||||||
throw new IllegalArgumentException("invalid side")
|
|
||||||
}
|
|
||||||
val direction = ForgeDirection.getOrientation(side)
|
|
||||||
if (allowed contains direction) toGlobal(direction)
|
|
||||||
else throw new IllegalArgumentException("unsupported side")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val (battery, distributor, gpu, keyboard) = if (isServer) {
|
val (battery, distributor, gpu, keyboard) = if (isServer) {
|
||||||
val battery = api.Network.newNode(this, Visibility.Network).withConnector(10000).create()
|
val battery = api.Network.newNode(this, Visibility.Network).withConnector(10000).create()
|
||||||
val distributor = new component.PowerDistributor(this)
|
val distributor = new component.PowerDistributor(this)
|
||||||
@ -282,8 +44,6 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
|
|||||||
|
|
||||||
var selectedSlot = 0
|
var selectedSlot = 0
|
||||||
|
|
||||||
def actualSlot(n: Int) = n + 3
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
def tier = 0
|
def tier = 0
|
||||||
@ -292,6 +52,8 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with Buffer with Power
|
|||||||
|
|
||||||
override def installedMemory = 64 * 1024
|
override def installedMemory = 64 * 1024
|
||||||
|
|
||||||
|
def actualSlot(n: Int) = n + 3
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- //
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
@LuaCallback("start")
|
@LuaCallback("start")
|
||||||
|
262
li/cil/oc/server/component/Robot.scala
Normal file
262
li/cil/oc/server/component/Robot.scala
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
package li.cil.oc.server.component
|
||||||
|
|
||||||
|
import li.cil.oc.api.network.{LuaCallback, Arguments, Context}
|
||||||
|
import li.cil.oc.common.tileentity
|
||||||
|
import li.cil.oc.server.{PacketSender => ServerPacketSender}
|
||||||
|
import net.minecraft.block.Block
|
||||||
|
import net.minecraft.entity.item.EntityItem
|
||||||
|
import net.minecraft.entity.{Entity, EntityLivingBase}
|
||||||
|
import net.minecraft.item.{ItemStack, ItemBlock}
|
||||||
|
import net.minecraft.util.AxisAlignedBB
|
||||||
|
import net.minecraftforge.common.ForgeDirection
|
||||||
|
import net.minecraftforge.fluids.FluidRegistry
|
||||||
|
import scala.Some
|
||||||
|
import scala.collection.convert.WrapAsScala._
|
||||||
|
import scala.reflect._
|
||||||
|
|
||||||
|
class Robot(val robot: tileentity.Robot) extends Computer(robot) {
|
||||||
|
|
||||||
|
override def isRobot(context: Context, args: Arguments): Array[AnyRef] =
|
||||||
|
Array(java.lang.Boolean.TRUE)
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
@LuaCallback("select")
|
||||||
|
def select(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
if (args.count > 0 && args.checkAny(0) != null) {
|
||||||
|
val slot = checkSlot(args, 0)
|
||||||
|
if (slot != robot.selectedSlot) {
|
||||||
|
robot.selectedSlot = slot
|
||||||
|
ServerPacketSender.sendRobotSelectedSlotState(robot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result(robot.selectedSlot)
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("count")
|
||||||
|
def count(context: Context, args: Arguments): Array[AnyRef] =
|
||||||
|
result(stackInSlot(robot.selectedSlot) match {
|
||||||
|
case Some(stack) => stack.stackSize
|
||||||
|
case _ => 0
|
||||||
|
})
|
||||||
|
|
||||||
|
@LuaCallback("space")
|
||||||
|
def space(context: Context, args: Arguments): Array[AnyRef] =
|
||||||
|
result(stackInSlot(robot.selectedSlot) match {
|
||||||
|
case Some(stack) => robot.getInventoryStackLimit - stack.stackSize
|
||||||
|
case _ => robot.getInventoryStackLimit
|
||||||
|
})
|
||||||
|
|
||||||
|
@LuaCallback("compareTo")
|
||||||
|
def compareTo(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val slot = checkSlot(args, 0)
|
||||||
|
result((stackInSlot(robot.selectedSlot), stackInSlot(slot)) match {
|
||||||
|
case (Some(stackA), Some(stackB)) => haveSameItemType(stackA, stackB)
|
||||||
|
case (None, None) => true
|
||||||
|
case _ => false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("transferTo")
|
||||||
|
def transferTo(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val slot = checkSlot(args, 0)
|
||||||
|
val count = checkOptionalItemCount(args, 1)
|
||||||
|
if (slot == robot.selectedSlot || count == 0) {
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
else result((stackInSlot(robot.selectedSlot), stackInSlot(slot)) match {
|
||||||
|
case (Some(from), Some(to)) =>
|
||||||
|
if (haveSameItemType(from, to)) {
|
||||||
|
val space = (robot.getInventoryStackLimit min to.getMaxStackSize) - to.stackSize
|
||||||
|
val amount = count min space min from.stackSize
|
||||||
|
if (amount > 0) {
|
||||||
|
from.stackSize -= amount
|
||||||
|
to.stackSize += amount
|
||||||
|
assert(from.stackSize >= 0)
|
||||||
|
if (from.stackSize == 0) {
|
||||||
|
robot.setInventorySlotContents(robot.actualSlot(robot.selectedSlot), null)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
robot.setInventorySlotContents(robot.actualSlot(slot), from)
|
||||||
|
robot.setInventorySlotContents(robot.actualSlot(robot.selectedSlot), to)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
case (Some(from), None) =>
|
||||||
|
robot.setInventorySlotContents(robot.actualSlot(slot), robot.decrStackSize(robot.actualSlot(robot.selectedSlot), count))
|
||||||
|
true
|
||||||
|
case _ => false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("drop")
|
||||||
|
def drop(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val side = checkSideForAction(args, 0)
|
||||||
|
val count = checkOptionalItemCount(args, 1)
|
||||||
|
// TODO inventory, if available
|
||||||
|
result(robot.dropSlot(robot.actualSlot(robot.selectedSlot), count, side))
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("place")
|
||||||
|
def place(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
// Place block item selected in inventory.
|
||||||
|
val side = checkSideForAction(args, 0)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("suck")
|
||||||
|
def suck(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
// Pick up items lying around.
|
||||||
|
val side = checkSideForAction(args, 0)
|
||||||
|
val count = checkOptionalItemCount(args, 1)
|
||||||
|
// TODO inventory, if available
|
||||||
|
for (entity <- entitiesOnSide[EntityItem](side) if !entity.isDead && entity.delayBeforeCanPickup <= 0) {
|
||||||
|
val stack = entity.getEntityItem
|
||||||
|
val size = stack.stackSize
|
||||||
|
entity.onCollideWithPlayer(robot.player)
|
||||||
|
if (stack.stackSize < size || entity.isDead) return result(true)
|
||||||
|
}
|
||||||
|
result(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
@LuaCallback("compare")
|
||||||
|
def compare(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val side = checkSideForAction(args, 0)
|
||||||
|
stackInSlot(robot.selectedSlot) match {
|
||||||
|
case Some(stack) => Option(stack.getItem) match {
|
||||||
|
case Some(item: ItemBlock) =>
|
||||||
|
val (bx, by, bz) = (robot.x + side.offsetX, robot.y + side.offsetY, robot.z + side.offsetZ)
|
||||||
|
val idMatches = item.getBlockID == robot.world.getBlockId(bx, by, bz)
|
||||||
|
val subTypeMatches = !item.getHasSubtypes || item.getMetadata(stack.getItemDamage) == robot.world.getBlockMetadata(bx, by, bz)
|
||||||
|
return result(idMatches && subTypeMatches)
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
case _ =>
|
||||||
|
}
|
||||||
|
result(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("detect")
|
||||||
|
def detect(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
val side = checkSideForAction(args, 0)
|
||||||
|
val (bx, by, bz) = (robot.x + side.offsetX, robot.y + side.offsetY, robot.z + side.offsetZ)
|
||||||
|
val id = robot.world.getBlockId(bx, by, bz)
|
||||||
|
val block = Block.blocksList(id)
|
||||||
|
if (id == 0 || block == null || block.isAirBlock(robot.world, bx, by, bz)) {
|
||||||
|
closestLivingEntity(side) match {
|
||||||
|
case Some(entity) => result(true, "entity")
|
||||||
|
case _ => result(false, "air")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (FluidRegistry.lookupFluidForBlock(block) != null) {
|
||||||
|
result(false, "liquid")
|
||||||
|
}
|
||||||
|
else if (block.isBlockReplaceable(robot.world, bx, by, bz)) {
|
||||||
|
result(false, "replaceable")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result(true, "solid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
@LuaCallback("attack")
|
||||||
|
def attack(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
// Attack with equipped tool.
|
||||||
|
val side = checkSideForAction(args, 0)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("use")
|
||||||
|
def use(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
// Use equipped tool (e.g. dig, chop, till).
|
||||||
|
val side = checkSideForAction(args, 0)
|
||||||
|
val sneaky = args.checkBoolean(1)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
@LuaCallback("move")
|
||||||
|
def move(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
// Try to move in the specified direction.
|
||||||
|
val side = checkSideForMovement(args, 0)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaCallback("turn")
|
||||||
|
def turn(context: Context, args: Arguments): Array[AnyRef] = {
|
||||||
|
// Turn in the specified direction.
|
||||||
|
val clockwise = args.checkBoolean(0)
|
||||||
|
if (clockwise) robot.rotate(ForgeDirection.UP)
|
||||||
|
else robot.rotate(ForgeDirection.DOWN)
|
||||||
|
result(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
private def closestLivingEntity(side: ForgeDirection) = {
|
||||||
|
entitiesOnSide[EntityLivingBase](side).
|
||||||
|
foldLeft((Double.PositiveInfinity, None: Option[EntityLivingBase])) {
|
||||||
|
case ((bestDistance, bestEntity), entity: EntityLivingBase) =>
|
||||||
|
val distance = entity.getDistanceSq(robot.x + 0.5, robot.y + 0.5, robot.z + 0.5)
|
||||||
|
if (distance < bestDistance) (distance, Some(entity))
|
||||||
|
else (bestDistance, bestEntity)
|
||||||
|
case (best, _) => best
|
||||||
|
} match {
|
||||||
|
case (_, Some(entity)) => Some(entity)
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def entitiesOnSide[Type <: Entity : ClassTag](side: ForgeDirection) = {
|
||||||
|
val (bx, by, bz) = (robot.x + side.offsetX, robot.y + side.offsetY, robot.z + side.offsetZ)
|
||||||
|
val id = robot.world.getBlockId(bx, by, bz)
|
||||||
|
val block = Block.blocksList(id)
|
||||||
|
if (id == 0 || block == null || block.isAirBlock(robot.world, bx, by, bz)) {
|
||||||
|
val bounds = AxisAlignedBB.getAABBPool.getAABB(bx, by, bz, bx + 1, by + 1, bz + 1)
|
||||||
|
robot.world.getEntitiesWithinAABB(classTag[Type].runtimeClass, bounds).map(_.asInstanceOf[Type])
|
||||||
|
}
|
||||||
|
else Iterable.empty
|
||||||
|
}
|
||||||
|
|
||||||
|
private def haveSameItemType(stackA: ItemStack, stackB: ItemStack) =
|
||||||
|
stackA.itemID == stackB.itemID &&
|
||||||
|
(!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage)
|
||||||
|
|
||||||
|
private def stackInSlot(slot: Int) = Option(robot.getStackInSlot(robot.actualSlot(slot)))
|
||||||
|
|
||||||
|
private def checkOptionalItemCount(args: Arguments, n: Int) =
|
||||||
|
if (args.count > n && args.checkAny(n) != null) {
|
||||||
|
args.checkInteger(n) max 0 min robot.getInventoryStackLimit
|
||||||
|
}
|
||||||
|
else robot.getInventoryStackLimit
|
||||||
|
|
||||||
|
private def checkSlot(args: Arguments, n: Int) = {
|
||||||
|
val slot = args.checkInteger(n) - 1
|
||||||
|
if (slot < 0 || slot > 15) {
|
||||||
|
throw new IllegalArgumentException("invalid slot")
|
||||||
|
}
|
||||||
|
slot
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkSideForAction(args: Arguments, n: Int) = checkSide(args, n, ForgeDirection.SOUTH, ForgeDirection.UP, ForgeDirection.DOWN)
|
||||||
|
|
||||||
|
private def checkSideForMovement(args: Arguments, n: Int) = checkSide(args, n, ForgeDirection.SOUTH, ForgeDirection.NORTH)
|
||||||
|
|
||||||
|
private def checkSide(args: Arguments, n: Int, allowed: ForgeDirection*) = {
|
||||||
|
val side = args.checkInteger(n)
|
||||||
|
if (side < 0 || side > 5) {
|
||||||
|
throw new IllegalArgumentException("invalid side")
|
||||||
|
}
|
||||||
|
val direction = ForgeDirection.getOrientation(side)
|
||||||
|
if (allowed contains direction) robot.toGlobal(direction)
|
||||||
|
else throw new IllegalArgumentException("unsupported side")
|
||||||
|
}
|
||||||
|
}
|
136
li/cil/oc/util/InventoryRobot.scala
Normal file
136
li/cil/oc/util/InventoryRobot.scala
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package li.cil.oc.util
|
||||||
|
|
||||||
|
import net.minecraft.block.Block
|
||||||
|
import net.minecraft.entity.player.InventoryPlayer
|
||||||
|
import net.minecraft.item.{Item, ItemStack}
|
||||||
|
import net.minecraft.nbt.NBTTagList
|
||||||
|
import scala.util.control.Breaks._
|
||||||
|
|
||||||
|
class InventoryRobot(player: RobotPlayer) extends InventoryPlayer(player) {
|
||||||
|
val robot = player.robot
|
||||||
|
|
||||||
|
def selectedSlot = robot.actualSlot(robot.selectedSlot)
|
||||||
|
|
||||||
|
def selectedItemStack = robot.getStackInSlot(selectedSlot)
|
||||||
|
|
||||||
|
def inventorySlots = (robot.actualSlot(robot.selectedSlot) until getSizeInventory) ++ (0 until robot.actualSlot(robot.selectedSlot))
|
||||||
|
|
||||||
|
override def getCurrentItem = getStackInSlot(0)
|
||||||
|
|
||||||
|
override def getFirstEmptyStack = {
|
||||||
|
if (selectedItemStack == null) selectedSlot
|
||||||
|
else inventorySlots.find(getStackInSlot(_) == null).getOrElse(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
def getFirstEmptyStackAccepting(stack: ItemStack) = {
|
||||||
|
if (selectedItemStack == null && isItemValidForSlot(selectedSlot, stack)) selectedSlot
|
||||||
|
else inventorySlots.find(slot => getStackInSlot(slot) == null && isItemValidForSlot(slot, stack)).getOrElse(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def setCurrentItem(itemId: Int, itemDamage: Int, checkDamage: Boolean, create: Boolean) {}
|
||||||
|
|
||||||
|
override def changeCurrentItem(direction: Int) {}
|
||||||
|
|
||||||
|
override def clearInventory(itemId: Int, itemDamage: Int) = 0
|
||||||
|
|
||||||
|
override def func_70439_a(item: Item, itemDamage: Int) {}
|
||||||
|
|
||||||
|
override def decrementAnimations() {}
|
||||||
|
|
||||||
|
override def consumeInventoryItem(itemId: Int) = false
|
||||||
|
|
||||||
|
override def hasItem(itemId: Int) = (0 until getSizeInventory).map(getStackInSlot).filter(_ != null).exists(_.itemID == itemId)
|
||||||
|
|
||||||
|
override def addItemStackToInventory(stack: ItemStack) = {
|
||||||
|
if (stack == null || stack.stackSize == 0) false
|
||||||
|
else if (stack.isItemDamaged || (stack.stackSize == 1 && stack.getMaxStackSize == 1)) {
|
||||||
|
val slot = getFirstEmptyStackAccepting(stack)
|
||||||
|
if (slot >= 0) {
|
||||||
|
setInventorySlotContents(slot, stack.splitStack(1))
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val originalSize = stack.stackSize
|
||||||
|
breakable {
|
||||||
|
while (stack.stackSize > 0) {
|
||||||
|
if (stack.getMaxStackSize == 1) {
|
||||||
|
val slot = getFirstEmptyStackAccepting(stack)
|
||||||
|
if (slot >= 0) {
|
||||||
|
setInventorySlotContents(slot, stack.splitStack(1))
|
||||||
|
}
|
||||||
|
else break()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val slot =
|
||||||
|
if (selectedItemStack == null) selectedSlot
|
||||||
|
else inventorySlots.find(slot => {
|
||||||
|
val existing = getStackInSlot(slot)
|
||||||
|
existing != null && existing.isItemEqual(stack) &&
|
||||||
|
(!existing.getHasSubtypes || existing.getItemDamage == stack.getItemDamage) &&
|
||||||
|
(existing.stackSize < (existing.getMaxStackSize min getInventoryStackLimit))
|
||||||
|
}).getOrElse(getFirstEmptyStackAccepting(stack))
|
||||||
|
if (slot >= 0) {
|
||||||
|
if (getStackInSlot(slot) == null) {
|
||||||
|
val amount = stack.stackSize min (getInventoryStackLimit min stack.getMaxStackSize)
|
||||||
|
setInventorySlotContents(slot, stack.splitStack(amount))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val existing = getStackInSlot(slot)
|
||||||
|
val space = (getInventoryStackLimit min existing.getMaxStackSize) - existing.stackSize
|
||||||
|
val amount = stack.stackSize min space
|
||||||
|
existing.stackSize += amount
|
||||||
|
stack.stackSize -= amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else break()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stack.stackSize < originalSize) {
|
||||||
|
// This emulates behavior of the player's inventory, where surplus
|
||||||
|
// items from a collected stack vanish (yes, they do, yes, it sucks).
|
||||||
|
stack.stackSize = 0
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def decrStackSize(slot: Int, amount: Int) = robot.decrStackSize(slot, amount)
|
||||||
|
|
||||||
|
override def getStackInSlotOnClosing(slot: Int) = robot.getStackInSlotOnClosing(slot)
|
||||||
|
|
||||||
|
override def setInventorySlotContents(slot: Int, stack: ItemStack) = robot.setInventorySlotContents(slot, stack)
|
||||||
|
|
||||||
|
override def getStrVsBlock(block: Block) = Option(getCurrentItem).fold(1f)(_.getStrVsBlock(block))
|
||||||
|
|
||||||
|
override def writeToNBT(nbt: NBTTagList) = nbt
|
||||||
|
|
||||||
|
override def readFromNBT(nbt: NBTTagList) {}
|
||||||
|
|
||||||
|
override def getSizeInventory = robot.getSizeInventory
|
||||||
|
|
||||||
|
override def getStackInSlot(slot: Int) = robot.getStackInSlot(slot)
|
||||||
|
|
||||||
|
override def getInvName = robot.getInvName
|
||||||
|
|
||||||
|
override def getInventoryStackLimit = robot.getInventoryStackLimit
|
||||||
|
|
||||||
|
override def armorItemInSlot(slot: Int) = null
|
||||||
|
|
||||||
|
override def getTotalArmorValue = 0
|
||||||
|
|
||||||
|
override def damageArmor(damage: Float) {}
|
||||||
|
|
||||||
|
override def dropAllItems() = robot.dropAllSlots()
|
||||||
|
|
||||||
|
override def onInventoryChanged() = robot.onInventoryChanged()
|
||||||
|
|
||||||
|
override def hasItemStack(stack: ItemStack) = (0 until getSizeInventory).map(getStackInSlot).filter(_ != null).exists(_.isItemEqual(stack))
|
||||||
|
|
||||||
|
override def isItemValidForSlot(slot: Int, item: ItemStack) = robot.isItemValidForSlot(slot, item)
|
||||||
|
|
||||||
|
override def copyInventory(from: InventoryPlayer) {}
|
||||||
|
}
|
16
li/cil/oc/util/RobotPlayer.scala
Normal file
16
li/cil/oc/util/RobotPlayer.scala
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package li.cil.oc.util
|
||||||
|
|
||||||
|
import li.cil.oc.common.tileentity.Robot
|
||||||
|
import net.minecraft.util.ChunkCoordinates
|
||||||
|
import net.minecraftforge.common.FakePlayer
|
||||||
|
|
||||||
|
class RobotPlayer(val robot: Robot) extends FakePlayer(robot.world, "OpenComputers") {
|
||||||
|
capabilities.allowFlying = true
|
||||||
|
capabilities.disableDamage = true
|
||||||
|
capabilities.isFlying = true
|
||||||
|
inventory = new InventoryRobot(this)
|
||||||
|
|
||||||
|
// TODO override def getBoundingBox = super.getBoundingBox
|
||||||
|
|
||||||
|
override def getPlayerCoordinates = new ChunkCoordinates(robot.x, robot.y, robot.z)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user