package electroblob.wizardry.inventory; import electroblob.wizardry.tileentity.TileEntityBookshelf; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; /** * A {@code VirtualSlot} represents a slot in an inventory other than the one that is currently open. Like regular slots, * they hold a single {@link net.minecraft.item.ItemStack}, but unlike regular slots, they are not drawn to the screen * and cannot be interacted with directly. A virtual slot is effectively a reference to a specific slot in some other * inventory that allows the details of that inventory to be abstracted away from the current container. *

* N.B. For normal slots, {@code slotIndex == slotNumber} (index is for the inventory, number is for the container). * Importantly, for virtual slots this is not the case, since the {@code Container} they belong to is not the * one associated with the virtual slot's {@code IInventory}. */ public class VirtualSlot extends Slot { private final TileEntity tileEntity; /** Allows the virtual slot to remember what was last stored in it, so items can be put back in the same place. */ private ItemStack prevStack; // For now this doesn't persist over GUI close public VirtualSlot(IInventory inventory, int index){ super(inventory, index, -999, -999); if(!(inventory instanceof TileEntity)) throw new IllegalArgumentException("Inventory must be a tile entity!"); this.tileEntity = (TileEntity)inventory; this.prevStack = getStack().copy(); // We MUST copy the stack or it will get changed from elsewhere later! } @Override public boolean isEnabled(){ return false; // Virtual slots are never displayed } // We don't really want to be updating the bookshelves every single tick (let alone every frame!), so we're going // to have to do some 'assuming things stay the same until told otherwise'. This means it's possible that virtual // slots will remain even when their containers are gone, so we need a failsafe for when that happens. /** Returns true if this slot is valid, i.e. the tile entity still exists. */ public boolean isValid(){ return !tileEntity.isInvalid(); } // Normally the container decides if a stack is valid, but that's not going to work here @Override public boolean isItemValid(ItemStack stack){ // getSlotIndex() is required here, NOT slotNumber (which is only populated when the slot is added to a // container - the javadoc is wrong, it has nothing to do with inventories) return isValid() && inventory.isItemValidForSlot(getSlotIndex(), stack); } @Override public boolean canTakeStack(EntityPlayer playerIn){ return isValid() && super.canTakeStack(playerIn); } @Override public ItemStack onTake(EntityPlayer player, ItemStack stack){ return isValid() ? super.onTake(player, stack) : ItemStack.EMPTY; } @Override public void onSlotChanged(){ super.onSlotChanged(); if(this.getHasStack()) this.prevStack = this.getStack().copy(); // Ignore stack removal (insertion of empty stacks) } @Override public ItemStack getStack(){ return isValid() ? super.getStack() : ItemStack.EMPTY; } /** Returns the stack that was last in this slot. */ public ItemStack getPrevStack(){ return prevStack; } @Override public void putStack(ItemStack stack){ if(isValid()){ if(inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync(); super.putStack(stack); } } @Override public ItemStack decrStackSize(int amount){ if(isValid() && inventory instanceof TileEntityBookshelf) ((TileEntityBookshelf)inventory).sync(); return isValid() ? super.decrStackSize(amount) : ItemStack.EMPTY; } }