crafting addon, allows robots to craft in the 3x3 grid in the top left of their inventory; added extended context interface for robot interaction, allowing access to selected slot index and fake player for general stuff and in particular for inventory interaction; todo: maybe a nicer check in component if method is interface compatible (context/robotcontext) each call?

This commit is contained in:
Florian Nücke 2013-11-29 06:14:55 +01:00
parent 608383bf8a
commit d23a6534d9
16 changed files with 234 additions and 107 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 B

After

Width:  |  Height:  |  Size: 250 B

View File

@ -6,16 +6,33 @@ import li.cil.oc.common.item
object Items {
var multi: item.Delegator = null
// ----------------------------------------------------------------------- //
// Tools
var analyzer: item.Analyzer = null
var disk: item.Disk = null
var generator: item.Generator = null
var gpu1, gpu2, gpu3: item.GraphicsCard = null
var hdd1, hdd2, hdd3: item.HardDiskDrive = null
var lan: item.NetworkCard = null
// ----------------------------------------------------------------------- //
// Memory
var ram1, ram2, ram3: item.Memory = null
// ----------------------------------------------------------------------- //
// Storage
var disk: item.Disk = null
var hdd1, hdd2, hdd3: item.HardDiskDrive = null
// ----------------------------------------------------------------------- //
// Cards
var gpu1, gpu2, gpu3: item.GraphicsCard = null
var lan: item.NetworkCard = null
var rs: item.RedstoneCard = null
var wlan: item.WirelessNetworkCard = null
// ----------------------------------------------------------------------- //
// Upgrades
var crafting: item.Crafting = null
var generator: item.Generator = null
// ----------------------------------------------------------------------- //
// Crafting
var card: item.Card = null
var circuitBoardBody: item.PlatineBody = null
var circuitBoard: item.Platine = null
@ -41,6 +58,7 @@ object Items {
ram3 = new item.Memory(multi, 2)
rs = new item.RedstoneCard(multi)
wlan = new item.WirelessNetworkCard(multi)
crafting = new item.Crafting(multi)
card = new item.Card(multi)
circuitBoardBody = new item.PlatineBody(multi)

View File

@ -1,7 +1,5 @@
package li.cil.oc.api.network;
import net.minecraft.item.ItemStack;
/**
* This is used to provide some context to {@link LuaCallback}s, i.e. the
* computer from which the callback was called.
@ -161,37 +159,4 @@ public interface Context {
* @return <tt>true</tt> if the signal was queued; <tt>false</tt> otherwise.
*/
boolean signal(String name, Object... args);
/**
* Get the item stack that is currently in the robot's selected slot.
* <p/>
* For normal computers this will always return <tt>null</tt>.
* <p/>
* Note that this returns the actual <tt>ItemStack</tt> instance that is in
* the robot's inventory, so if you manipulate the stack's size, make sure
* to always call {@link #setStackInSelectedSlot} with the stack itself
* afterwards, to trigger an `onInventoryUpdate` in the robot.
*
* @return the item stack in the robot's currently selected inventory slot.
*/
ItemStack getStackInSelectedSlot();
/**
* Set the item stack in the robot's selected inventory slot.
* <p/>
* For computers this will always do nothing and return <tt>false</tt>.
* <p/>
* This will store a copy / split portion of the passed stack in the robot's
* inventory. How many items of the stack were stored can be seen from the
* stack size of the passed stack after the function returns (it will be
* set to the number of remaining items, or zero if the stack was completely
* stored).
* <p/>
* If the slot is not empty, and the stack cannot be - at least partially -
* merged into the existing item stack this will return <tt>false</tt>.
*
* @param stack the stack to store in the currently selected slot.
* @return <tt>true</tt> if the stack was completely or partially stored.
*/
boolean setStackInSelectedSlot(ItemStack stack);
}

View File

@ -0,0 +1,25 @@
package li.cil.oc.api.network;
import net.minecraft.entity.player.EntityPlayer;
public interface RobotContext extends Context {
/**
* Gets the index of the currently selected slot in the robot's inventory.
*
* @return the index of the currently selected slot.
*/
int selectedSlot();
/**
* Returns the fake player used to represent the robot as an entity for
* certain actions that require one.
* <p/>
* This will automatically be positioned and rotated to represent the
* robot's current position and rotation in the world. Use this to trigger
* events involving the robot that require a player entity, and for
* interacting with the robots' inventory.
*
* @return the fake player for the robot.
*/
EntityPlayer player();
}

View File

@ -119,8 +119,9 @@ class Robot(playerInventory: InventoryPlayer, val robot: tileentity.Robot) exten
Minecraft.getMinecraft.renderEngine.bindTexture(selection)
val now = System.currentTimeMillis() / 1000.0
val offsetV = ((now - now.toInt) * selectionsStates).toInt * selectionStepV
val x = guiLeft + inventoryX + (robot.selectedSlot % 4) * (selectionSize - 2)
val y = guiTop + inventoryY + (robot.selectedSlot / 4) * (selectionSize - 2)
val slot = robot.selectedSlot - robot.actualSlot(0)
val x = guiLeft + inventoryX + (slot % 4) * (selectionSize - 2)
val y = guiTop + inventoryY + (slot / 4) * (selectionSize - 2)
val t = Tessellator.instance
t.startDrawingQuads()

View File

@ -26,11 +26,12 @@ class Proxy {
api.Driver.add(driver.block.Carriage)
api.Driver.add(driver.block.CommandBlock)
api.Driver.add(driver.item.Crafting)
api.Driver.add(driver.item.FileSystem)
api.Driver.add(driver.item.Generator)
api.Driver.add(driver.item.GraphicsCard)
api.Driver.add(driver.item.Memory)
api.Driver.add(driver.item.NetworkCard)
api.Driver.add(driver.item.Generator)
api.Driver.add(driver.item.RedstoneCard)
api.Driver.add(driver.item.WirelessNetworkCard)

View File

@ -0,0 +1,23 @@
package li.cil.oc.common.item
import java.util
import li.cil.oc.Settings
import li.cil.oc.util.Tooltip
import net.minecraft.client.renderer.texture.IconRegister
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
class Crafting(val parent: Delegator) extends Delegate {
val unlocalizedName = "Crafting"
override def addInformation(stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) {
tooltip.addAll(Tooltip.get(unlocalizedName))
super.addInformation(stack, player, tooltip, advanced)
}
override def registerIcons(iconRegister: IconRegister) = {
super.registerIcons(iconRegister)
icon = iconRegister.registerIcon(Settings.resourceDomain + ":crafting")
}
}

View File

@ -33,7 +33,12 @@ trait Inventory extends TileEntity with IInventory with Persistable {
onItemRemoved(slot, items(slot).get)
}
items(slot) = Option(stack)
if (stack == null || stack.stackSize <= 0) {
items(slot) = None
}
else {
items(slot) = Some(stack)
}
if (stack != null && stack.stackSize > getInventoryStackLimit) {
stack.stackSize = getInventoryStackLimit
}

View File

@ -12,7 +12,6 @@ import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.{ThreadPoolFactory, GameTimeFormatter, LuaStateFactory}
import li.cil.oc.{OpenComputers, Settings}
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt._
import net.minecraft.server.MinecraftServer
import scala.Array.canBuildFrom
@ -174,10 +173,6 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
}
})
def getStackInSelectedSlot: ItemStack = null
def setStackInSelectedSlot(stack: ItemStack) = false
// ----------------------------------------------------------------------- //
@LuaCallback("start")

View File

@ -0,0 +1,95 @@
package li.cil.oc.server.component
import cpw.mods.fml.common.registry.GameRegistry
import li.cil.oc.api
import li.cil.oc.api.network._
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.inventory.{Container, InventoryCrafting}
import net.minecraft.item.ItemStack
import net.minecraft.item.crafting.CraftingManager
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
import net.minecraftforge.common.MinecraftForge
import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent
import scala.collection.mutable
class Crafting(owner: MCTileEntity) extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Network).
withComponent("crafting").
create()
@LuaCallback("craft")
def craft(context: RobotContext, args: Arguments): Array[AnyRef] = {
val count = if (args.count > 0) args.checkInteger(0) else 64
if (count > 0 && context.player.inventory.getStackInSlot(context.selectedSlot) != null) {
throw new IllegalArgumentException("selected result slot is not empty")
}
result(CraftingInventory.craft(context, count))
}
private object CraftingInventory extends InventoryCrafting(new Container {
def canInteractWith(player: EntityPlayer) = true
}, 4, 4) {
var amountPossible = 0
def craft(context: RobotContext, wantedCount: Int): Boolean = {
CraftingInventory.load(context)
val manager = CraftingManager.getInstance
val result = manager.findMatchingRecipe(CraftingInventory, owner.getWorldObj)
if (result == null) return false
val targetStackSize = if (result.isStackable) wantedCount min result.getMaxStackSize else result.stackSize
val timesCrafted = targetStackSize / result.stackSize
if (timesCrafted <= 0) return true
val surplus = mutable.ArrayBuffer.empty[ItemStack]
for (row <- 0 until 3) for (col <- 0 until 3) {
val slot = row * 3 + col
val stack = getStackInSlot(slot)
if (stack != null) {
decrStackSize(slot, timesCrafted)
val item = stack.getItem
if (item.hasContainerItem) {
val container = item.getContainerItemStack(stack)
if (container.isItemStackDamageable && container.getItemDamage > container.getMaxDamage) {
MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(context.player, container))
}
else if (container.getItem.doesContainerItemLeaveCraftingGrid(container) || getStackInSlot(slot) != null) {
surplus += container
}
else {
container.stackSize *= timesCrafted
setInventorySlotContents(slot, container)
}
}
}
}
GameRegistry.onItemCrafted(context.player, result, this)
CraftingInventory.save(context)
result.stackSize *= timesCrafted
val inventory = context.player.inventory
inventory.addItemStackToInventory(result)
for (stack <- surplus) {
inventory.addItemStackToInventory(stack)
}
true
}
def load(context: RobotContext) {
val inventory = context.player.inventory
amountPossible = Int.MaxValue
for (slot <- 0 until 16) {
val stack = inventory.getStackInSlot(slot + 4)
setInventorySlotContents(slot, stack)
if (stack != null) {
amountPossible = amountPossible min stack.stackSize
}
}
}
def save(context: RobotContext) {
val inventory = context.player.inventory
for (slot <- 0 until 16) {
inventory.setInventorySlotContents(slot + 4, getStackInSlot(slot))
}
}
}
}

View File

@ -1,11 +1,12 @@
package li.cil.oc.server.component
import li.cil.oc.api.network.{Context, Arguments, LuaCallback, Visibility}
import li.cil.oc.api.network._
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.{Settings, api}
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntityFurnace
import scala.Some
class Generator extends ManagedComponent {
val node = api.Network.newNode(this, Visibility.Network).
@ -20,14 +21,15 @@ class Generator extends ManagedComponent {
// ----------------------------------------------------------------------- //
@LuaCallback("insert")
def insert(context: Context, args: Arguments): Array[AnyRef] = {
def insert(context: RobotContext, args: Arguments): Array[AnyRef] = {
val count = if (args.count > 0) args.checkInteger(0) else 64
val stack = context.getStackInSelectedSlot
val player = context.player
val stack = player.inventory.getStackInSlot(context.selectedSlot)
if (stack == null) throw new IllegalArgumentException("selected slot is empty")
if (!TileEntityFurnace.isItemFuel(stack)) return result(false, "selected slot does not contain fuel")
inventory match {
case Some(existingStack) =>
if (!ItemStack.areItemStacksEqual(existingStack, stack) ||
if (!existingStack.isItemEqual(stack) ||
!ItemStack.areItemStackTagsEqual(existingStack, stack)) {
return result(false, "different fuel type already queued")
}
@ -41,7 +43,7 @@ class Generator extends ManagedComponent {
case _ =>
inventory = Some(stack.splitStack(stack.getMaxStackSize min count))
}
context.setStackInSelectedSlot(stack)
player.inventory.setInventorySlotContents(context.selectedSlot, stack)
result(true)
}
@ -54,8 +56,19 @@ class Generator extends ManagedComponent {
}
@LuaCallback("remove")
def remove(context: Context, args: Arguments): Array[AnyRef] = {
null
def remove(context: RobotContext, args: Arguments): Array[AnyRef] = {
val count = if (args.count > 0) args.checkInteger(0) else Int.MaxValue
inventory match {
case Some(stack) =>
val removedStack = stack.splitStack(count min stack.stackSize)
val success = context.player.inventory.addItemStackToInventory(removedStack)
stack.stackSize += removedStack.stackSize
if (success && stack.stackSize <= 0) {
inventory = None
}
result(success)
case _ => result(false)
}
}
// ----------------------------------------------------------------------- //

View File

@ -1,13 +1,13 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.network.{LuaCallback, Arguments, Context}
import li.cil.oc.api.network.{RobotContext, LuaCallback, Arguments, Context}
import li.cil.oc.common.tileentity
import li.cil.oc.server.component.robot.{Player, ActivationType}
import li.cil.oc.server.{PacketSender => ServerPacketSender}
import net.minecraft.block.{BlockFluid, Block}
import net.minecraft.entity.{EntityLivingBase, Entity}
import net.minecraft.entity.item.EntityItem
import net.minecraft.entity.{EntityLivingBase, Entity}
import net.minecraft.inventory.{IInventory, ISidedInventory}
import net.minecraft.item.{ItemStack, ItemBlock}
import net.minecraft.util.{Vec3, MovingObjectPosition, EnumMovingObjectType}
@ -16,9 +16,7 @@ import net.minecraftforge.fluids.FluidRegistry
import scala.Some
import scala.collection.convert.WrapAsScala._
class Robot(val robot: tileentity.Robot) extends Computer(robot) {
def selectedSlot = robot.selectedSlot
class Robot(val robot: tileentity.Robot) extends Computer(robot) with RobotContext {
def actualSlot(n: Int) = robot.actualSlot(n)
@ -34,34 +32,9 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
override def isRobot = true
override def getStackInSelectedSlot = stackInSlot(selectedSlot).orNull
def selectedSlot = robot.selectedSlot
override def setStackInSelectedSlot(stack: ItemStack): Boolean = {
val existingStack = stackInSlot(selectedSlot).orNull
if (stack == existingStack) {
robot.onInventoryChanged()
return true
}
if (existingStack != null &&
!ItemStack.areItemStacksEqual(existingStack, stack) ||
!ItemStack.areItemStackTagsEqual(existingStack, stack) ||
existingStack.stackSize >= robot.getInventoryStackLimit) {
return false
}
val maxStackSize = stack.getMaxStackSize min robot.getInventoryStackLimit
if (existingStack != null) {
val space = maxStackSize - existingStack.stackSize
val moveCount = stack.stackSize min space
existingStack.stackSize += moveCount
stack.stackSize -= moveCount
robot.onInventoryChanged()
}
else {
val moveCount = stack.stackSize min maxStackSize
robot.setInventorySlotContents(selectedSlot, stack.splitStack(moveCount))
}
true
}
def player = robot.player()
// ----------------------------------------------------------------------- //
@ -118,19 +91,19 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
to.stackSize += amount
assert(from.stackSize >= 0)
if (from.stackSize == 0) {
robot.setInventorySlotContents(actualSlot(selectedSlot), null)
robot.setInventorySlotContents(selectedSlot, null)
}
true
}
else false
}
else {
robot.setInventorySlotContents(actualSlot(slot), from)
robot.setInventorySlotContents(actualSlot(selectedSlot), to)
robot.setInventorySlotContents(slot, from)
robot.setInventorySlotContents(selectedSlot, to)
true
}
case (Some(from), None) =>
robot.setInventorySlotContents(actualSlot(slot), robot.decrStackSize(actualSlot(selectedSlot), count))
robot.setInventorySlotContents(slot, robot.decrStackSize(selectedSlot, count))
true
case _ => false
})
@ -157,7 +130,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
def drop(context: Context, args: Arguments): Array[AnyRef] = {
val facing = checkSideForAction(args, 0)
val count = checkOptionalItemCount(args, 1)
val dropped = robot.decrStackSize(actualSlot(selectedSlot), count)
val dropped = robot.decrStackSize(selectedSlot, count)
if (dropped != null && dropped.stackSize > 0) {
def tryDropIntoInventory(inventory: IInventory, filter: (Int) => Boolean) = {
var success = false
@ -187,7 +160,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
if (success) {
inventory.onInventoryChanged()
}
robot.player().inventory.addItemStackToInventory(dropped)
player.inventory.addItemStackToInventory(dropped)
result(success)
}
world.getBlockTileEntity(x + facing.offsetX, y + facing.offsetY, z + facing.offsetZ) match {
@ -196,7 +169,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
case inventory: IInventory =>
tryDropIntoInventory(inventory, (slot) => true)
case _ =>
robot.player().dropPlayerItemWithRandomChoice(dropped, inPlace = false)
player.dropPlayerItemWithRandomChoice(dropped, inPlace = false)
context.pause(Settings.get.dropDelay)
result(true)
}
@ -252,7 +225,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
val maxStackSize = robot.getInventoryStackLimit min stack.getMaxStackSize
val amount = maxStackSize min stack.stackSize min count
val sucked = stack.splitStack(amount)
success = robot.player().inventory.addItemStackToInventory(sucked)
success = player.inventory.addItemStackToInventory(sucked)
stack.stackSize += sucked.stackSize
if (stack.stackSize == 0) {
inventory.setInventorySlotContents(slot, null)
@ -270,10 +243,10 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
case inventory: IInventory =>
trySuckFromInventory(inventory, (slot) => true)
case _ =>
for (entity <- robot.player().entitiesOnSide[EntityItem](facing) if !entity.isDead && entity.delayBeforeCanPickup <= 0) {
for (entity <- player.entitiesOnSide[EntityItem](facing) if !entity.isDead && entity.delayBeforeCanPickup <= 0) {
val stack = entity.getEntityItem
val size = stack.stackSize
entity.onCollideWithPlayer(robot.player())
entity.onCollideWithPlayer(player)
if (stack.stackSize < size || entity.isDead) {
context.pause(Settings.get.suckDelay)
return result(true)
@ -459,7 +432,6 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
}
private def endConsumeDrops(entity: Entity) {
val player = robot.player()
entity.captureDrops = false
for (drop <- entity.capturedDrops) {
val stack = drop.getEntityItem
@ -478,7 +450,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
val id = world.getBlockId(bx, by, bz)
val block = Block.blocksList(id)
if (id == 0 || block == null || block.isAirBlock(world, bx, by, bz)) {
robot.player().closestEntity[Entity]() match {
player.closestEntity[Entity]() match {
case Some(entity) => (true, "entity")
case _ => (false, "air")
}
@ -526,7 +498,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
stackA.getItem == stackB.getItem &&
(!stackA.getHasSubtypes || stackA.getItemDamage == stackB.getItemDamage)
private def stackInSlot(slot: Int) = Option(robot.getStackInSlot(actualSlot(slot)))
private def stackInSlot(slot: Int) = Option(robot.getStackInSlot(slot))
// ----------------------------------------------------------------------- //
@ -541,7 +513,7 @@ class Robot(val robot: tileentity.Robot) extends Computer(robot) {
if (slot < 0 || slot > 15) {
throw new IllegalArgumentException("invalid slot")
}
slot
actualSlot(slot)
}
private def checkSideForAction(args: Arguments, n: Int) = checkSide(args, n, ForgeDirection.SOUTH, ForgeDirection.UP, ForgeDirection.DOWN)

View File

@ -9,13 +9,13 @@ import scala.util.control.Breaks._
class Inventory(player: Player) extends InventoryPlayer(player) {
val robot = player.robot
def selectedSlot = robot.actualSlot(robot.selectedSlot)
def selectedSlot = robot.selectedSlot
def selectedItemStack = robot.getStackInSlot(selectedSlot)
def firstInventorySlot = robot.actualSlot(0)
def inventorySlots = (robot.actualSlot(robot.selectedSlot) until getSizeInventory) ++ (firstInventorySlot until robot.actualSlot(robot.selectedSlot))
def inventorySlots = (robot.selectedSlot until getSizeInventory) ++ (firstInventorySlot until robot.selectedSlot)
override def getCurrentItem = getStackInSlot(0)

View File

@ -0,0 +1,15 @@
package li.cil.oc.server.driver.item
import li.cil.oc.Items
import li.cil.oc.api.driver.Slot
import li.cil.oc.server.component
import net.minecraft.item.ItemStack
import net.minecraft.tileentity.{TileEntity => MCTileEntity}
object Crafting extends Item {
override def worksWith(stack: ItemStack) = isOneOf(stack, Items.crafting)
override def createEnvironment(stack: ItemStack, container: MCTileEntity) = new component.Crafting(container)
override def slot(stack: ItemStack) = Slot.Upgrade
}

View File

@ -4,7 +4,7 @@ import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.relauncher.Side
import java.lang.reflect.{Method, InvocationTargetException}
import li.cil.oc.api
import li.cil.oc.api.network.{LuaCallback, Arguments, Context, Visibility}
import li.cil.oc.api.network._
import li.cil.oc.common.tileentity
import li.cil.oc.util.Persistable
import net.minecraft.nbt.NBTTagCompound
@ -117,7 +117,7 @@ object Component {
ms.filter(_.isAnnotationPresent(classOf[LuaCallback])).foreach(m =>
if (m.getParameterTypes.size != 2 ||
m.getParameterTypes()(0) != classOf[Context] ||
(m.getParameterTypes()(0) != classOf[Context] && m.getParameterTypes()(0) != classOf[RobotContext]) ||
m.getParameterTypes()(1) != classOf[Arguments]) {
throw new IllegalArgumentException("Invalid use of LuaCallback annotation (invalid signature).")
}
@ -130,7 +130,6 @@ object Component {
throw new IllegalArgumentException("Invalid use of LuaCallback annotation (name must not be null or empty).")
}
else if (!callbacks.contains(a.value)) {
callbacks += a.value -> new Callback(m, a.direct, a.limit)
}
}