package com.vanhal.progressiveautomation.entities; import java.util.Random; import javax.annotation.Nonnull; import com.vanhal.progressiveautomation.PAConfig; import com.vanhal.progressiveautomation.blocks.network.PartialTileNBTUpdateMessage; import com.vanhal.progressiveautomation.items.ItemRFEngine; import com.vanhal.progressiveautomation.ref.ToolHelper; import com.vanhal.progressiveautomation.ref.WrenchModes; import com.vanhal.progressiveautomation.util.BlockHelper; import com.vanhal.progressiveautomation.util.Point2I; import cofh.api.energy.IEnergyProvider; import cofh.api.energy.IEnergyReceiver; import net.minecraft.block.Block; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityFurnace; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.energy.IEnergyStorage; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; import net.minecraftforge.items.wrapper.SidedInvWrapper; public class BaseTileEntity extends TileEntity implements ISidedInventory, IEnergyStorage, IEnergyProvider, IEnergyReceiver, ITickable { protected ItemStack[] slots; protected int progress = 0; protected int burnLevel = 0; protected boolean inventoryFull = false; protected boolean RedstonePowered = false; //first time looking in this machine, used for displaying help protected boolean firstLook = false; /** * Direction to auto output items to */ public EnumFacing extDirection = EnumFacing.UP; public EnumFacing facing = EnumFacing.EAST; public WrenchModes.Mode sides[] = new WrenchModes.Mode[6]; /** * Signalises whether the TileEntity needs to be synced with clients */ private boolean dirty; /** * Tag holding partial updates that will be sent to players upon synchronisation */ private NBTTagCompound partialUpdateTag = new NBTTagCompound(); protected Random RND = new Random(); //inventory slots variables public int SLOT_FUEL = 0; public int SLOT_PICKAXE = -1; public int SLOT_SHOVEL = -1; public int SLOT_AXE = -1; public int SLOT_SWORD = -1; public int SLOT_HOE = -1; public int SLOT_UPGRADE = -1; public int SLOT_INVENTORY_START = -1; public int SLOT_INVENTORY_END = -1; public BaseTileEntity(int numSlots) { slots = new ItemStack[numSlots+1]; if (numSlots > 9) { SLOT_INVENTORY_START = numSlots - 8; SLOT_INVENTORY_END = numSlots; //ProgressiveAutomation.logger.info("Start: "+SLOT_INVENTORY_START+" End: "+SLOT_INVENTORY_END); } else { SLOT_INVENTORY_START = SLOT_INVENTORY_END = numSlots; } for (int i =0; i < 6; i++) { sides[i] = WrenchModes.Mode.Normal; if (i==extDirection.ordinal()) sides[i] = WrenchModes.Mode.Output; } } protected void setExtDirection(EnumFacing dir) { sides[extDirection.ordinal()] = WrenchModes.Mode.Normal; extDirection = dir; sides[extDirection.ordinal()] = WrenchModes.Mode.Output; } /** * Do not extend this method, use writeSyncOnlyNBT, writeCommonNBT or writeNonSyncableNBT as needed. * @return */ @Override public final NBTTagCompound writeToNBT(NBTTagCompound nbt) { nbt = super.writeToNBT(nbt); writeCommonNBT(nbt); writeNonSyncableNBT(nbt); return nbt; } /** * Do not extend this method, use readSyncOnlyNBT, readCommonNBT or readNonSyncableNBT as needed. */ @Override public final void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); readCommonNBT(nbt); readNonSyncableNBT(nbt); } public void readFromItemStack(ItemStack itemStack) { if (itemStack == null || itemStack.getTagCompound() == null) { return; } readCommonNBT(itemStack.getTagCompound()); readNonSyncableNBT(itemStack.getTagCompound()); } public void writeToItemStack(ItemStack itemStack) { if (itemStack == null ) { return; } if (itemStack.getTagCompound() == null) { itemStack.setTagCompound(new NBTTagCompound()); } writeCommonNBT(itemStack.getTagCompound()); writeNonSyncableNBT(itemStack.getTagCompound()); } /** * This overridable method is intended for writing to NBT all data that is used only at runtime * and never saved to hdd. * @param nbt */ protected void writeSyncOnlyNBT(NBTTagCompound nbt) { } /** * This overridable method is intended for writing to NBT all data that is both saved to hdd * and can be sent to the client TileEntity through S35PacketUpdateTileEntity. * * WARNING: Do not include slot contents here. * They are automagically synced when a GUI is opened using S30PacketWindowItems * @param nbt */ public void writeCommonNBT(NBTTagCompound nbt) { nbt.setInteger("Progress", progress); nbt.setInteger("BurnLevel", burnLevel); nbt.setBoolean("inventoryFull", inventoryFull); nbt.setBoolean("firstLook", firstLook); nbt.setInteger("facing", facing.ordinal()); int ary[] = new int[6]; for (int i = 0; i < 6; i++) { ary[i] = sides[i].ordinal(); } nbt.setIntArray("sides", ary); ary = null; } /** * This overridable method is intended for writing to NBT all data that will * NOT be synced using S35PacketUpdateTileEntity packets. * * This includes, but is not limited to, slot contents. * See writeSyncableNBT for more info. * @param nbt */ public void writeNonSyncableNBT(NBTTagCompound nbt) { NBTTagList contents = new NBTTagList(); for (int i = 0; i < slots.length; i++) { if (slots[i] != null) { ItemStack stack = slots[i]; NBTTagCompound tag = new NBTTagCompound(); tag.setByte("Slot", (byte)i); stack.writeToNBT(tag); contents.appendTag(tag); } } nbt.setTag("Contents", contents); } /** * This overridable method is intended for reading from NBT all data that is used only at runtime * and never saved to hdd. * @param nbt */ public void readSyncOnlyNBT(NBTTagCompound nbt) { } /** * This overridable method is intended for reading from NBT all data that is both saved to hdd * and can be sent to the client TileEntity through S35PacketUpdateTileEntity and PartialTileNBTUpdateMessage. * * WARNING: Please check if the tag exists before reading it. Due to the nature of * PartialTileNBTUpdateMessage properties that didn't change since the last message * will not be included. * * WARNING: Do not include slot contents here. * They are automagically synced when a GUI is opened using S30PacketWindowItems * @param nbt */ public void readCommonNBT(NBTTagCompound nbt) { if (nbt.hasKey("Progress")) progress = nbt.getInteger("Progress"); if (nbt.hasKey("BurnLevel")) burnLevel = nbt.getInteger("BurnLevel"); if (nbt.hasKey("inventoryFull")) inventoryFull = nbt.getBoolean("inventoryFull"); if (nbt.hasKey("firstLook")) firstLook = nbt.getBoolean("firstLook"); if (nbt.hasKey("facing")) facing = EnumFacing.getFront(nbt.getInteger("facing")); if (nbt.hasKey("sides")) { int ary[] = nbt.getIntArray("sides"); for (int i = 0; i<6; i++) { sides[i] = WrenchModes.modes.get(ary[i]); } } } /** * This overridable method is intended for reading from NBT all data that is only saved to hdd * and never synced between client and server, or is synced using a different method (e.g. inventory * contents and S30PacketWindowItems) * @param nbt */ protected void readNonSyncableNBT(NBTTagCompound nbt) { NBTTagList contents = nbt.getTagList("Contents", 10); for (int i = 0; i < contents.tagCount(); i++) { NBTTagCompound tag = (NBTTagCompound) contents.getCompoundTagAt(i); byte slot = tag.getByte("Slot"); if (slot < slots.length) { slots[slot] = ItemStack.loadItemStackFromNBT(tag); } } } /** * This method is used to sync data when a GUI is opened. the packet will contain * all syncable data. */ @Override public SPacketUpdateTileEntity getUpdatePacket() { NBTTagCompound nbttagcompound = new NBTTagCompound(); this.writeCommonNBT(nbttagcompound); this.writeSyncOnlyNBT(nbttagcompound); return new SPacketUpdateTileEntity(new BlockPos(getPos().getX(), getPos().getY(), getPos().getZ()), -1, nbttagcompound); } /** * This method is used to load syncable data when a GUI is opened. */ @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { this.readCommonNBT(pkt.getNbtCompound()); this.readSyncOnlyNBT(pkt.getNbtCompound()); } /** * This method will generate a message with partial updates for this TileEntity. * * WARNING: Using this method resets the dirty flag and clears all pending updates up to this point. * @return */ public PartialTileNBTUpdateMessage getPartialUpdateMessage() { PartialTileNBTUpdateMessage message = new PartialTileNBTUpdateMessage(getPos().getX(), getPos().getY(), getPos().getZ(), partialUpdateTag); dirty = false; partialUpdateTag = new NBTTagCompound(); return message; } /** * Utility method, so you don't have to remember to set dirty to true */ protected void addPartialUpdate(String fieldName, Integer value) { partialUpdateTag.setInteger(fieldName, value); dirty = true; } /** * Utility method, so you don't have to remember to set dirty to true */ protected void addPartialUpdate(String fieldName, String value) { partialUpdateTag.setString(fieldName, value); dirty = true; } /** * Utility method, so you don't have to remember to set dirty to true */ protected void addPartialUpdate(String fieldName, NBTBase value) { partialUpdateTag.setTag(fieldName, value); dirty = true; } /** * Utility method, so you don't have to remember to set dirty to true */ protected void addPartialUpdate(String fieldName, Boolean value) { partialUpdateTag.setBoolean(fieldName, value); dirty = true; } protected void addPartialUpdate(String fieldName, NBTTagCompound value) { partialUpdateTag.setTag(fieldName, value); dirty = true; } protected NBTTagCompound getCompoundTagFromPartialUpdate(String fieldName) { return partialUpdateTag.getCompoundTag(fieldName); } /** * Whether the TileEntity needs syncing. * @return */ public boolean isDirty() { return dirty; } @Override public void update() { if (!worldObj.isRemote) { if (isFull()) return; if (!isBurning()) { RedstonePowered = isIndirectlyPowered(); if (!RedstonePowered) { if (readyToBurn()) { if (slots[SLOT_FUEL]!=null) { if (isFuel()) { burnLevel = progress = getBurnTime(); addPartialUpdate("Progress", progress); addPartialUpdate("BurnLevel", burnLevel); if (slots[SLOT_FUEL].getItem().hasContainerItem(slots[SLOT_FUEL])) { slots[SLOT_FUEL] = slots[SLOT_FUEL].getItem().getContainerItem(slots[SLOT_FUEL]); //if the container is no longer fuel then pump it into the internal inventory if (!isFuel()) { moveToInventoryOrDrop(SLOT_FUEL); } } else { slots[SLOT_FUEL].stackSize--; if (slots[SLOT_FUEL].stackSize==0) { slots[SLOT_FUEL] = null; } } } else if (hasEngine()) { if (useEnergy(PAConfig.rfCost, false) > 0) { if (burnLevel != 1 || progress != 1) { //consumed a tick worth of energy burnLevel = progress = 1; addPartialUpdate("Progress", progress); addPartialUpdate("BurnLevel", burnLevel); } } } } } } } else { //ProgressiveAutomation.logger.info("Ticks Left for fuel: "+progress); progress--; if (progress<=0) { burnLevel = progress = 0; addPartialUpdate("BurnLevel", burnLevel); if ( (readyToBurn()) && (hasEngine()) ) { if (useEnergy(PAConfig.rfCost, false) > 0) { //consumed a tick worth of energy burnLevel = progress = 1; } } } addPartialUpdate("Progress", progress); } checkForPowerChange(); } } public int getProgress() { return progress; } public void setProgress(int value) { progress = value; } public int getBurnLevel() { return burnLevel; } public void setBurnLevel(int value) { burnLevel = value; } private void setInventoryFull(boolean state) { if (SLOT_INVENTORY_START == SLOT_INVENTORY_END) return; if (inventoryFull != state) { inventoryFull = state; addPartialUpdate("inventoryFull", inventoryFull); } } /* Inventory methods */ @Override public int getSizeInventory() { return slots.length; } @Override public ItemStack getStackInSlot(int slot) { return slots[slot]; } @Override public ItemStack decrStackSize(int slot, int amt) { if (slots[slot] != null) { ItemStack newStack; if (slots[slot].stackSize <= amt) { newStack = slots[slot]; slots[slot] = null; } else { newStack = slots[slot].splitStack(amt); if (slots[slot].stackSize == 0) { slots[slot] = null; } } return newStack; } return null; } @Override public ItemStack removeStackFromSlot(int slot) { if (slots[slot]!=null) { ItemStack stack = slots[slot]; slots[slot] = null; return stack; } return null; } @Override public void setInventorySlotContents(int slot, ItemStack stack) { slots[slot] = stack; if (stack != null && stack.stackSize > this.getInventoryStackLimit()) { stack.stackSize = this.getInventoryStackLimit(); } } // Do we have available inventory spaces and do we care? public boolean isFull() { return PAConfig.pauseOnFullInventory && SLOT_INVENTORY_START != SLOT_INVENTORY_END && inventoryFull; } @Override public String getName() { return null; } @Override public boolean hasCustomName() { return false; } @Override public ITextComponent getDisplayName() { return null; } @Override public int getInventoryStackLimit() { return 64; } @Override public boolean isUseableByPlayer(EntityPlayer player) { return worldObj.getTileEntity(getPos()) == this && player.getDistanceSq(getPos().getX() + 0.5, getPos().getY() + 0.5, getPos().getZ() + 0.5) < 64; } @Override public void openInventory(EntityPlayer playerIn) {} @Override public void closeInventory(EntityPlayer playerIn) {} @Override public boolean isItemValidForSlot(int slot, ItemStack stack) { return this.isItemValidForSlot(slot, stack, false); } public boolean isItemValidForSlot(int slot, ItemStack stack, boolean internalStorage) { if ( (slot==SLOT_FUEL) && (getItemBurnTime(stack)>0) && (ToolHelper.getType(stack)==-1) ) { return true; } if ( ( (slot >= SLOT_INVENTORY_START) && (slot <= SLOT_INVENTORY_END) ) && (SLOT_INVENTORY_START!=SLOT_INVENTORY_END) && (internalStorage) ) { return true; } return false; } public void destroyTool(int slot) { if ((slot==-1)||(slots[slot]==null)) return; if (ToolHelper.tinkersType(slots[slot].getItem())>=0) { addToInventory(slots[slot]); } else { if (!PAConfig.destroyTools) { addToInventory(slots[slot]); } } slots[slot] = null; } //sided things public WrenchModes.Mode getSide(EnumFacing side) { return sides[side.ordinal()]; } public void setSide(EnumFacing side, WrenchModes.Mode type) { sides[side.ordinal()] = type; } @Override public int[] getSlotsForFace(EnumFacing side) { int[] output = new int[slots.length]; for (int i=0; i<slots.length; i++) { output[i] = i; } return output; } @Override public boolean canInsertItem(int slot, ItemStack stack, EnumFacing face) { if ( (slot<0) || (slot>SLOT_INVENTORY_END) ) return false; if (face!=null) { if (sides[face.ordinal()] == WrenchModes.Mode.Disabled) return false; if (sides[face.ordinal()] == WrenchModes.Mode.Output) return false; if ( (sides[face.ordinal()] == WrenchModes.Mode.FuelInput) && (slot != SLOT_FUEL) ) return false; if ( (sides[face.ordinal()] == WrenchModes.Mode.Input) && (slot == SLOT_FUEL) ) return false; } if ( (slots[slot] != null) && (slots[slot].isItemEqual(stack)) && (ItemStack.areItemStackTagsEqual(stack, slots[slot])) ) { int availSpace = this.getInventoryStackLimit() - slots[slot].stackSize; if (availSpace>0) { return true; } } else if (isItemValidForSlot(slot, stack)) { return true; } return false; } @Override public boolean canExtractItem(int slot, ItemStack stack, EnumFacing face) { if (face!=null) if (sides[face.ordinal()] == WrenchModes.Mode.Disabled) return false; if ( (slot>=SLOT_INVENTORY_START) && (slot<=SLOT_INVENTORY_END) ) { if ( (sides[face.ordinal()] == WrenchModes.Mode.Normal) || (sides[face.ordinal()] == WrenchModes.Mode.Output) ) return true; } return false; } public boolean readyToBurn() { return true; } public boolean isBurning() { return (progress>0); } public float getPercentDone() { if ( (isBurning()) && (burnLevel>0) ) { float done = (float)progress;//(burnLevel - progress); done = done/(float)burnLevel; return done; } else { return 0; } } public int getScaledDone(int scale) { return (int) Math.floor(scale * getPercentDone()); } public int getBurnTime() { return getBurnTime(slots[0]); } public int getBurnTime(ItemStack item) { if (PAConfig.fuelCost == 0) return 0; return getItemBurnTime(item) / PAConfig.fuelCost; } public static int getItemBurnTime(ItemStack item) { if (PAConfig.allowPotatos) { if (item.getItem() == Items.POTATO) return 40; else if (item.getItem() == Items.BAKED_POTATO) return 80; } if ( (item == null) || (item.getItem() == null) ) return 0; return TileEntityFurnace.getItemBurnTime(item); } public boolean isFuel() { return (getBurnTime()>0); } public boolean hasFuel() { if (slots[SLOT_FUEL]!=null) { if (hasEngine()) { if (useEnergy(PAConfig.rfCost, true) > 0) { return true; } } else { return true; } } return false; } /* do some checks for block specific items, must return -1 on failure */ public int extraSlotCheck(ItemStack item) { int targetSlot = -1; if (getBurnTime(item)>0) { if (slots[0]==null) { targetSlot = 0; } else if ( (item.isItemEqual(slots[0])) && (ItemStack.areItemStackTagsEqual(item, slots[0])) ) { targetSlot = 0; } } return targetSlot; } /* Check the inventory, move any useful items to their correct slots */ public void checkInventory() { if ( (SLOT_INVENTORY_START==-1) || (SLOT_INVENTORY_END==-1) ) return; boolean openSlots = false; for (int i = SLOT_INVENTORY_START; i <= SLOT_INVENTORY_END; i++) { if (slots[i]!=null) { int moveTo = extraSlotCheck(slots[i]); if (moveTo>=0) { slots[i] = moveItemToSlot(slots[i], moveTo); } } if (slots[i]==null && !openSlots) { openSlots = true; } } //then check if there is any inventories on any of the output sides that we can output to for(EnumFacing facing : EnumFacing.values()) { if (sides[facing.ordinal()] == WrenchModes.Mode.Output) { TileEntity tile = worldObj.getTileEntity(pos.offset(facing)); if (tile != null) { if (tile.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite())) { IItemHandler inv = tile.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite()); for (int i = SLOT_INVENTORY_START; i <= SLOT_INVENTORY_END; i++) { if (slots[i]!=null && addtoExtInventory(inv, i)) { openSlots = true; } } } else if (tile instanceof ISidedInventory) { ISidedInventory externalInv = (ISidedInventory) tile; for (int i = SLOT_INVENTORY_START; i <= SLOT_INVENTORY_END; i++) { if (slots[i]!=null && addtoSidedExtInventory(externalInv, i)) { openSlots = true; } } } else if (tile instanceof IInventory) { IInventory externalInv = (IInventory) tile; for (int i = SLOT_INVENTORY_START; i <= SLOT_INVENTORY_END; i++) { if (slots[i]!=null && addtoExtInventory(externalInv, i)) { openSlots = true; } } } } } } setInventoryFull( !openSlots ); } protected ItemStack moveItemToSlot(ItemStack item, int targetSlot) { if (slots[targetSlot]==null) { slots[targetSlot] = item; item = null; } else if ( (slots[targetSlot].stackSize < slots[targetSlot].getMaxStackSize()) && (slots[targetSlot].isItemEqual(item)) && (ItemStack.areItemStackTagsEqual(item, slots[targetSlot])) ) { int avail = slots[targetSlot].getMaxStackSize() - slots[targetSlot].stackSize; if (avail >= item.stackSize) { slots[targetSlot].stackSize += item.stackSize; item = null; } else { item.stackSize -= avail; slots[targetSlot].stackSize += avail; } } return item; } public boolean addtoExtInventory(IItemHandler inv, int fromSlot) { if (ItemHandlerHelper.insertItemStacked(inv, slots[fromSlot], true) != slots[fromSlot]) { slots[fromSlot] = ItemHandlerHelper.insertItemStacked(inv, slots[fromSlot], false); } return false; } public boolean addtoExtInventory(IInventory inv, int fromSlot) { for (int i = 0; i < inv.getSizeInventory(); i++) { if (inv.getStackInSlot(i)!=null) { if ( (inv.getStackInSlot(i).isItemEqual(slots[fromSlot])) && (inv.getStackInSlot(i).stackSize < inv.getStackInSlot(i).getMaxStackSize()) && (ItemStack.areItemStackTagsEqual(inv.getStackInSlot(i), slots[fromSlot])) ) { int avail = inv.getStackInSlot(i).getMaxStackSize() - inv.getStackInSlot(i).stackSize; if (avail >= slots[fromSlot].stackSize) { inv.getStackInSlot(i).stackSize += slots[fromSlot].stackSize; slots[fromSlot] = null; return true; } else { slots[fromSlot].stackSize -= avail; inv.getStackInSlot(i).stackSize += avail; } } } } if ( (slots[fromSlot] != null) && (slots[fromSlot].stackSize>0) ) { for (int i = 0; i < inv.getSizeInventory(); i++) { if ( (inv.getStackInSlot(i)==null) && (inv.isItemValidForSlot(i, slots[fromSlot])) ) { inv.setInventorySlotContents(i, slots[fromSlot]); slots[fromSlot] = null; return true; } } } return false; } public boolean addtoSidedExtInventory(ISidedInventory inv, int fromSlot) { int[] trySlots = inv.getSlotsForFace(extDirection.getOpposite()); int i = 0; for (int j = 0; j < trySlots.length; j++) { i = trySlots[j]; if (inv.getStackInSlot(i)!=null) { if ( (inv.getStackInSlot(i).isItemEqual(slots[fromSlot])) && (inv.getStackInSlot(i).stackSize < inv.getStackInSlot(i).getMaxStackSize()) && (ItemStack.areItemStackTagsEqual(inv.getStackInSlot(i), slots[fromSlot])) ) { int avail = inv.getStackInSlot(i).getMaxStackSize() - inv.getStackInSlot(i).stackSize; if (avail >= slots[fromSlot].stackSize) { inv.getStackInSlot(i).stackSize += slots[fromSlot].stackSize; slots[fromSlot] = null; return true; } else { slots[fromSlot].stackSize -= avail; inv.getStackInSlot(i).stackSize += avail; } } } } if ( (slots[fromSlot] != null) && (slots[fromSlot].stackSize>0) ) { for (int j = 0; j < trySlots.length; j++) { i = trySlots[j]; if (inv.canInsertItem(i, slots[fromSlot], extDirection.getOpposite())) { if ( (inv.getStackInSlot(i)==null) && (inv.isItemValidForSlot(i, slots[fromSlot])) ) { inv.setInventorySlotContents(i, slots[fromSlot]); slots[fromSlot] = null; return true; } } } } return false; } public boolean roomInInventory(ItemStack item) { if ( (SLOT_INVENTORY_START==-1) || (SLOT_INVENTORY_END==-1) ) return false; if (item == null) return false; int stackSize = item.stackSize; boolean hasRoom = false; for (int i = SLOT_INVENTORY_START; i <= SLOT_INVENTORY_END; i++) { if (slots[i]==null) { hasRoom = true; setInventoryFull( false ); } else if (stackSize > 0 && slots[i].isItemEqual(item) && (ItemStack.areItemStackTagsEqual(item, slots[i]))) { stackSize = Math.max( stackSize - (slots[i].getMaxStackSize() - slots[i].stackSize), 0 ); } if (hasRoom || stackSize == 0) return true; } setInventoryFull( true ); return false; } public boolean addToInventory(ItemStack item) { if (item == null) return false; if ( (SLOT_INVENTORY_START==-1) || (SLOT_INVENTORY_END==-1) ) return false; //check to see if this item is something that's used int extraSlot = extraSlotCheck(item); if (extraSlot>=0) { item = moveItemToSlot(item, extraSlot); if (item == null) return true; } int emptySlot = -1; //add it to the main inventory for (int i = SLOT_INVENTORY_START; i <= SLOT_INVENTORY_END; i++) { if (slots[i]==null) { // Remember this empty slot in case there aren't enough partial slots if (emptySlot == -1 && this.isItemValidForSlot(i, item, true)) { emptySlot = i; setInventoryFull( false ); } continue; } if (item == null || item.stackSize == 0) { continue; } if (slots[i].isItemEqual(item) && slots[i].stackSize < slots[i].getMaxStackSize() && ItemStack.areItemStackTagsEqual(item, slots[i]) ) { int avail = slots[i].getMaxStackSize() - slots[i].stackSize; if (avail >= item.stackSize) { slots[i].stackSize += item.stackSize; item.stackSize = 0; item = null; } else { item.stackSize -= avail; slots[i].stackSize += avail; } } } if (emptySlot == -1) setInventoryFull( true ); if (item == null || item.stackSize == 0) { item = null; return true; } // If we've got an empty slot available, drop it in there if (emptySlot != -1) { slots[emptySlot] = item; item = null; return true; } // If we still have an item, drop in on the ground, unless the config says to delete it. if (item!=null) { if (PAConfig.allowInventoryOverflow) dropItem(item); item = null; } return false; } public void moveToInventoryOrDrop(int slot) { if (!worldObj.isRemote) { //move directly to an output side first if possible for (int x = 0; x < 6; x++) { if (sides[x] == WrenchModes.Mode.Output) { EnumFacing testSide = EnumFacing.getFront(x); if (BlockHelper.getAdjacentTileEntity(this, testSide) instanceof ISidedInventory) { ISidedInventory externalInv = (ISidedInventory) BlockHelper.getAdjacentTileEntity(this, testSide); if (slots[slot]!=null) { addtoSidedExtInventory(externalInv, slot); } } else if (BlockHelper.getAdjacentTileEntity(this, testSide) instanceof IInventory) { IInventory externalInv = (IInventory) BlockHelper.getAdjacentTileEntity(this, testSide); if (slots[slot]!=null) { addtoExtInventory(externalInv, slot); } } } } //try the internal inventory //We'll need to make a copy of it so that it doesn't duplicate at this point if (slots[slot]!=null) { ItemStack item = slots[slot].copy(); slots[slot] = null; if (item!=null) { addToInventory(item); } //finally if it hasn't already been dropped, drop it if ( (item!=null) && ( (SLOT_INVENTORY_START==-1) || (SLOT_INVENTORY_END==-1) ) ) { dropItem(item); } } } } public void dropItem(ItemStack item) { if (item!=null) { EntityItem entItem = new EntityItem(worldObj, pos.getX() + 0.5f, pos.getY() + 1.5f, pos.getZ() + 0.5f, item); float f3 = 0.05F; entItem.motionX = (double)((float)worldObj.rand.nextGaussian() * f3); entItem.motionY = (double)((float)worldObj.rand.nextGaussian() * f3 + 0.2F); entItem.motionZ = (double)((float)worldObj.rand.nextGaussian() * f3); worldObj.spawnEntityInWorld(entItem); } } //the following are for handling energy public boolean hasEngine() { if (slots[SLOT_FUEL]==null) return false; if (slots[SLOT_FUEL].getItem() instanceof ItemRFEngine) { return true; } return false; } protected ItemRFEngine getEngine() { if (hasEngine()) { return (ItemRFEngine) slots[SLOT_FUEL].getItem(); } return null; } @Override public boolean hasCapability(@Nonnull Capability<?> capability, @Nonnull EnumFacing facing) { if ( capability == CapabilityEnergy.ENERGY || capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY ) return true; return super.hasCapability(capability, facing); } @Override @Nonnull public <T> T getCapability(@Nonnull Capability<T> capability, @Nonnull EnumFacing facing) { if (capability == CapabilityEnergy.ENERGY) return CapabilityEnergy.ENERGY.cast(this); if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return (T) new SidedInvWrapper(this, facing); return super.getCapability(capability, facing); } @Override public int receiveEnergy(int maxReceive, boolean simulate) { return this.receiveEnergy(EnumFacing.UP, maxReceive, simulate); } @Override public int extractEnergy(int maxExtract, boolean simulate) { return this.extractEnergy(EnumFacing.UP, maxExtract, simulate); } @Override public int getEnergyStored() { return this.getEnergyStored(EnumFacing.UP); } @Override public int getMaxEnergyStored() { return this.getMaxEnergyStored(EnumFacing.UP); } @Override public boolean canExtract() { return this.extractEnergy(1, true)>0; } @Override public boolean canReceive() { return this.receiveEnergy(1, true)>0; } @Override public boolean canConnectEnergy(EnumFacing from) { if (getEngine()==null) return false; else return true; } @Override public int receiveEnergy(EnumFacing from, int maxReceive, boolean simulate) { if (worldObj.isRemote) return 0; return addEnergy(maxReceive, simulate); } @Override public int extractEnergy(EnumFacing from, int maxExtract, boolean simulate) { return 0; } protected int useEnergy(int amount, boolean simulate) { ItemRFEngine engine = getEngine(); if (getEngine()==null) return 0; int energyExtracted = Math.min(engine.getCharge(slots[SLOT_FUEL]), amount); if (energyExtracted!=amount) return 0; if (!simulate) { engine.addCharge(slots[SLOT_FUEL], (energyExtracted * -1)); } return energyExtracted; } protected int addEnergy(int amount, boolean simulate) { ItemRFEngine engine = getEngine(); if (getEngine()==null) return 0; int energyReceived = Math.min(engine.getMaxCharge() - engine.getCharge(slots[SLOT_FUEL]), Math.min(amount, PAConfig.rfRate)); if (!simulate) { engine.addCharge(slots[SLOT_FUEL], energyReceived); } return energyReceived; } public int getEnergyStored(EnumFacing from) { if ( hasEngine() ) { return getEngine().getCharge(slots[SLOT_FUEL]); } else { return 0; } } public int getMaxEnergyStored(EnumFacing from) { if ( hasEngine() ) { return getEngine().getMaxCharge(); } else { return 0; } } //this will just update the blocks around if the rfengine is added to a machine protected boolean lastEngine = false; protected void checkForPowerChange() { if ( ( (!lastEngine) && (hasEngine()) ) || ( (lastEngine) && (!hasEngine()) ) ) { lastEngine = hasEngine(); notifyUpdate(); } } protected void notifyUpdate() { Block minerBlock = worldObj.getBlockState(pos).getBlock(); worldObj.notifyNeighborsOfStateChange(getPos(), minerBlock); } public void setLooked() { if (!firstLook) { firstLook = true; addPartialUpdate("firstLook", firstLook); } } public boolean isLooked() { return firstLook; } public Point2I spiral(int n, int x, int y) { return spiral(n, x, y, facing); } //my function to get a point on a spiral around the block public static Point2I spiral(int n, int x, int y, EnumFacing direction) { int dx, dy; int k = (int)Math.ceil( (Math.sqrt(n)-1)/2); int t = 2*k + 1; int m = t*t; t = t-1; if (n>=(m-t)) { dx = k-(m-n); dy = -k; } else { m = m-t; if (n>=(m-t)) { dx = -k; dy = -k + (m-n); } else { m = m-t; if (n>=(m-t)) { dx = -k + (m-n); dy = k; } else { dx = k; dy = k - (m-n-t); } } } if (direction == EnumFacing.NORTH) return new Point2I(x + dy, y - dx); else if (direction == EnumFacing.SOUTH) return new Point2I(x + dy, y + dx); else if (direction == EnumFacing.EAST) return new Point2I(x + dx, y + dy); else return new Point2I(x - dx, y - dy); } //clear the internal inventory (I have a feeling this is used for pooling but I can't be sure) @Override public void clear() { for (int i = 0; i < this.slots.length; ++i) { this.slots[i] = null; } setInventoryFull( false ); } //WHAT THE HELL ARE THESE?????? //They look like they're possibly used for GUI stuff? @Override public int getField(int id) { return 0; } @Override public void setField(int id, int value) { } @Override public int getFieldCount() { return 0; } //check if machine is powered protected boolean isIndirectlyPowered() { EnumFacing[] aenumfacing = EnumFacing.values(); int i = aenumfacing.length; int j; for (j = 0; j < i; ++j) { EnumFacing enumfacing1 = aenumfacing[j]; if (worldObj.isSidePowered(pos.offset(enumfacing1), enumfacing1)) { return true; } } return false; } public int getX() { return pos.getX(); } public int getY() { return pos.getY(); } public int getZ() { return pos.getZ(); } public World getWorldObj() { return this.worldObj; } }