package mekanism.common.tile; import io.netty.buffer.ByteBuf; import java.util.ArrayList; import mekanism.api.Coord4D; import mekanism.api.IConfigurable; import mekanism.api.Range4D; import mekanism.api.util.CapabilityUtils; import mekanism.api.util.StackUtils; import mekanism.common.Mekanism; import mekanism.common.PacketHandler; import mekanism.common.Tier.BaseTier; import mekanism.common.Tier.BinTier; import mekanism.common.base.IActiveState; import mekanism.common.base.ILogisticalTransporter; import mekanism.common.base.ITierUpgradeable; import mekanism.common.capabilities.Capabilities; import mekanism.common.content.transporter.TransporterManager; import mekanism.common.item.ItemBlockBasic; import mekanism.common.network.PacketTileEntity.TileEntityMessage; import mekanism.common.util.InventoryUtils; import mekanism.common.util.LangUtils; import mekanism.common.util.MekanismUtils; import mekanism.common.util.TransporterUtils; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.SoundCategory; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.Optional.Interface; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.CapabilityItemHandler; import powercrystals.minefactoryreloaded.api.IDeepStorageUnit; @Interface(iface = "powercrystals.minefactoryreloaded.api.IDeepStorageUnit", modid = "MineFactoryReloaded") public class TileEntityBin extends TileEntityBasicBlock implements ISidedInventory, IActiveState, IDeepStorageUnit, IConfigurable, ITierUpgradeable { public boolean isActive; public boolean clientActive; public final int MAX_DELAY = 10; public int addTicks = 0; public int delayTicks; public int cacheCount; public BinTier tier = BinTier.BASIC; public ItemStack itemType; public ItemStack topStack; public ItemStack bottomStack; public int prevCount; public int clientAmount; private BinItemHandler myItemHandler; public TileEntityBin(){ this.myItemHandler = new BinItemHandler(this); } @Override public boolean upgrade(BaseTier upgradeTier) { if(upgradeTier.ordinal() != tier.ordinal()+1) { return false; } tier = BinTier.values()[upgradeTier.ordinal()]; Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(Coord4D.get(this), getNetworkedData(new ArrayList())), new Range4D(Coord4D.get(this))); markDirty(); return true; } public void sortStacks() { if(getItemCount() == 0 || itemType == null) { itemType = null; topStack = null; bottomStack = null; cacheCount = 0; return; } int count = getItemCount(); int remain = tier.storage-count; if(remain >= itemType.getMaxStackSize()) { topStack = null; } else { topStack = itemType.copy(); topStack.stackSize = itemType.getMaxStackSize()-remain; } count -= StackUtils.getSize(topStack); bottomStack = itemType.copy(); bottomStack.stackSize = Math.min(itemType.getMaxStackSize(), count); count -= StackUtils.getSize(bottomStack); cacheCount = count; } public boolean isValid(ItemStack stack) { if(stack == null || stack.stackSize <= 0) { return false; } if(stack.getItem() instanceof ItemBlockBasic && stack.getItemDamage() == 6) { return false; } if(itemType == null) { return true; } if(!stack.isItemEqual(itemType) || !ItemStack.areItemStackTagsEqual(stack, itemType)) { return false; } return true; } public ItemStack add(ItemStack stack, boolean simulate) { if(isValid(stack) && (tier == BinTier.CREATIVE || getItemCount() != tier.storage)) { if(itemType == null && !simulate) { setItemType(stack); } if(tier != BinTier.CREATIVE) { if(getItemCount() + stack.stackSize <= tier.storage) { if (!simulate) { setItemCount(getItemCount() + stack.stackSize); } return null; } else { ItemStack rejects = stack.copy(); rejects.stackSize = (getItemCount()+stack.stackSize) - tier.storage; if (!simulate) { setItemCount(tier.storage); } return rejects; } } else { if (!simulate) { setItemCount(Integer.MAX_VALUE); } } } return stack; } public ItemStack add(ItemStack stack) { return add(stack, false); } public ItemStack removeStack() { if(getItemCount() == 0) { return null; } return remove(bottomStack.stackSize); } public ItemStack remove(int amount, boolean simulate) { if(getItemCount() == 0) { return null; } ItemStack ret = itemType.copy(); ret.stackSize = Math.min(Math.min(amount, itemType.getMaxStackSize()), getItemCount()); if(tier != BinTier.CREATIVE && !simulate) { setItemCount(getItemCount() - ret.stackSize); } return ret; } public ItemStack remove(int amount) { return remove(amount, false); } public int getItemCount() { return StackUtils.getSize(bottomStack) + cacheCount + StackUtils.getSize(topStack); } @Override public void onUpdate() { if(!worldObj.isRemote) { addTicks = Math.max(0, addTicks-1); delayTicks = Math.max(0, delayTicks-1); sortStacks(); if(getItemCount() != prevCount) { markDirty(); MekanismUtils.saveChunk(this); } if(delayTicks == 0) { if(bottomStack != null && isActive) { TileEntity tile = Coord4D.get(this).offset(EnumFacing.DOWN).getTileEntity(worldObj); if(CapabilityUtils.hasCapability(tile, Capabilities.LOGISTICAL_TRANSPORTER_CAPABILITY, EnumFacing.UP)) { ILogisticalTransporter transporter = CapabilityUtils.getCapability(tile, Capabilities.LOGISTICAL_TRANSPORTER_CAPABILITY, EnumFacing.UP); ItemStack rejects = TransporterUtils.insert(this, transporter, bottomStack, null, true, 0); if(TransporterManager.didEmit(bottomStack, rejects)) { setInventorySlotContents(0, rejects); } } else if(tile instanceof IInventory) { setInventorySlotContents(0, InventoryUtils.putStackInInventory((IInventory)tile, bottomStack, EnumFacing.DOWN, false)); } delayTicks = 10; } } else { delayTicks--; } } } @Override public NBTTagCompound writeToNBT(NBTTagCompound nbtTags) { super.writeToNBT(nbtTags); nbtTags.setBoolean("isActive", isActive); nbtTags.setInteger("itemCount", cacheCount); nbtTags.setInteger("tier", tier.ordinal()); if(bottomStack != null) { nbtTags.setTag("bottomStack", bottomStack.writeToNBT(new NBTTagCompound())); } if(topStack != null) { nbtTags.setTag("topStack", topStack.writeToNBT(new NBTTagCompound())); } if(getItemCount() > 0) { nbtTags.setTag("itemType", itemType.writeToNBT(new NBTTagCompound())); } return nbtTags; } @Override public void readFromNBT(NBTTagCompound nbtTags) { super.readFromNBT(nbtTags); isActive = nbtTags.getBoolean("isActive"); cacheCount = nbtTags.getInteger("itemCount"); tier = BinTier.values()[nbtTags.getInteger("tier")]; bottomStack = ItemStack.loadItemStackFromNBT(nbtTags.getCompoundTag("bottomStack")); topStack = ItemStack.loadItemStackFromNBT(nbtTags.getCompoundTag("topStack")); if(getItemCount() > 0) { itemType = ItemStack.loadItemStackFromNBT(nbtTags.getCompoundTag("itemType")); } } @Override public ArrayList<Object> getNetworkedData(ArrayList<Object> data) { super.getNetworkedData(data); data.add(isActive); data.add(getItemCount()); data.add(tier.ordinal()); if(getItemCount() > 0) { data.add(itemType); } return data; } @Override public void handlePacketData(ByteBuf dataStream) { super.handlePacketData(dataStream); if(FMLCommonHandler.instance().getEffectiveSide().isClient()) { isActive = dataStream.readBoolean(); clientAmount = dataStream.readInt(); tier = BinTier.values()[dataStream.readInt()]; if(clientAmount > 0) { itemType = PacketHandler.readStack(dataStream); } else { itemType = null; } MekanismUtils.updateBlock(worldObj, getPos()); } } @Override public ItemStack getStackInSlot(int slotID) { if(slotID == 1) { return topStack; } else { return bottomStack; } } @Override public ItemStack decrStackSize(int slotID, int amount) { if(slotID == 1) { return null; } else if(slotID == 0) { int toRemove = Math.min(getItemCount(), amount); if(toRemove > 0) { ItemStack ret = itemType.copy(); ret.stackSize = toRemove; setItemCount(getItemCount()-toRemove); return ret; } } return null; } @Override public ItemStack removeStackFromSlot(int slotID) { return getStackInSlot(slotID); } @Override public int getSizeInventory() { return 2; } @Override public void setInventorySlotContents(int i, ItemStack itemstack) { if(i == 0) { if(getItemCount() == 0) { return; } if(tier != BinTier.CREATIVE) { if(itemstack == null) { setItemCount(getItemCount() - bottomStack.stackSize); } else { setItemCount(getItemCount() - (bottomStack.stackSize-itemstack.stackSize)); } } } else if(i == 1) { if(itemstack == null) { topStack = null; } else { if(isValid(itemstack) && itemstack.stackSize > StackUtils.getSize(topStack) && tier != BinTier.CREATIVE) { add(StackUtils.size(itemstack, itemstack.stackSize-StackUtils.getSize(topStack))); } } } } @Override public void markDirty() { super.markDirty(); if(!worldObj.isRemote) { MekanismUtils.saveChunk(this); Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(Coord4D.get(this), getNetworkedData(new ArrayList<Object>())), new Range4D(Coord4D.get(this))); prevCount = getItemCount(); sortStacks(); } } public void setItemType(ItemStack stack) { if(stack == null) { itemType = null; cacheCount = 0; topStack = null; bottomStack = null; return; } ItemStack ret = stack.copy(); ret.stackSize = 1; itemType = ret; } public void setItemCount(int count) { cacheCount = Math.max(0, count); topStack = null; bottomStack = null; if(count == 0) { setItemType(null); } markDirty(); } @Override public String getName() { return LangUtils.localize(getBlockType().getUnlocalizedName() + ".Bin" + tier.getBaseTier().getSimpleName() + ".name"); } @Override public boolean hasCustomName() { return true; } @Override public ITextComponent getDisplayName() { return new TextComponentString(getName()); } @Override public int getInventoryStackLimit() { return 64; } @Override public boolean isUseableByPlayer(EntityPlayer entityplayer) { return true; } @Override public void openInventory(EntityPlayer player) {} @Override public void closeInventory(EntityPlayer player) {} @Override public boolean isItemValidForSlot(int i, ItemStack itemstack) { return i == 1 && isValid(itemstack); } @Override public int getField(int id) { return 0; } @Override public void setField(int id, int value) { } @Override public int getFieldCount() { return 0; } @Override public void clear() {} @Override public int[] getSlotsForFace(EnumFacing side) { if(side == EnumFacing.UP) { return new int[] {1}; } else if(side == EnumFacing.DOWN) { return new int[] {0}; } return InventoryUtils.EMPTY; } @Override public boolean canInsertItem(int i, ItemStack itemstack, EnumFacing side) { return isItemValidForSlot(i, itemstack); } @Override public boolean canExtractItem(int i, ItemStack itemstack, EnumFacing side) { return i == 0 && isValid(itemstack); } @Override public boolean canSetFacing(int facing) { return facing != 0 && facing != 1; } @Override public void setActive(boolean active) { isActive = active; if(clientActive != active) { Mekanism.packetHandler.sendToReceivers(new TileEntityMessage(Coord4D.get(this), getNetworkedData(new ArrayList<Object>())), new Range4D(Coord4D.get(this))); clientActive = active; } } @Override public boolean getActive() { return isActive; } @Override public boolean renderUpdate() { return true; } @Override public boolean lightUpdate() { return true; } @Override public ItemStack getStoredItemType() { if(itemType == null) { return null; } return MekanismUtils.size(itemType, getItemCount()); } @Override public void setStoredItemCount(int amount) { if(amount == 0) { setStoredItemType(null, 0); } setItemCount(amount); } @Override public void setStoredItemType(ItemStack type, int amount) { itemType = type; cacheCount = amount; topStack = null; bottomStack = null; markDirty(); } @Override public int getMaxStoredCount() { return tier.storage; } @Override public EnumActionResult onSneakRightClick(EntityPlayer player, EnumFacing side) { setActive(!getActive()); worldObj.playSound(null, getPos().getX(), getPos().getY(), getPos().getZ(), SoundEvents.UI_BUTTON_CLICK, SoundCategory.BLOCKS, 0.3F, 1); return EnumActionResult.SUCCESS; } @Override public EnumActionResult onRightClick(EntityPlayer player, EnumFacing side) { return EnumActionResult.PASS; } @Override public boolean hasCapability(Capability<?> capability, EnumFacing side) { return capability == Capabilities.CONFIGURABLE_CAPABILITY || capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY || super.hasCapability(capability, side); } @Override public <T> T getCapability(Capability<T> capability, EnumFacing side) { if(capability == Capabilities.CONFIGURABLE_CAPABILITY) { return (T)this; } else if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { return (T)myItemHandler; } return super.getCapability(capability, side); } private class BinItemHandler implements IItemHandler { private TileEntityBin tileEntityBin; public BinItemHandler(TileEntityBin tileEntityBin) { this.tileEntityBin = tileEntityBin; } public int getSlots(){return 1;} public ItemStack getStackInSlot(int slot) { if (slot != 0 || tileEntityBin.itemType == null) { return null; } return MekanismUtils.size(tileEntityBin.itemType, tileEntityBin.getItemCount()); } public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { if (slot != 0) { return null; } return tileEntityBin.add(stack, simulate); } public ItemStack extractItem(int slot, int amount, boolean simulate) { if (slot != 0) { return null; } return tileEntityBin.remove(amount, simulate); } } }