package blusunrize.immersiveengineering.common.blocks.metal; import blusunrize.immersiveengineering.api.Lib; import blusunrize.immersiveengineering.api.crafting.IMultiblockRecipe; import blusunrize.immersiveengineering.api.crafting.IngredientStack; import blusunrize.immersiveengineering.api.tool.AssemblerHandler; import blusunrize.immersiveengineering.api.tool.ConveyorHandler.IConveyorAttachable; import blusunrize.immersiveengineering.common.Config.IEConfig; import blusunrize.immersiveengineering.common.blocks.IEBlockInterfaces.IGuiTile; import blusunrize.immersiveengineering.common.blocks.multiblocks.MultiblockAssembler; import blusunrize.immersiveengineering.common.util.Utils; import blusunrize.immersiveengineering.common.util.inventory.IEInventoryHandler; import com.google.common.base.Optional; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTank; import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.oredict.OreDictionary; import java.util.ArrayList; import java.util.Iterator; public class TileEntityAssembler extends TileEntityMultiblockMetal<TileEntityAssembler,IMultiblockRecipe> implements IGuiTile, IConveyorAttachable// IAdvancedSelectionBounds,IAdvancedCollisionBounds { public boolean[] computerOn = new boolean[3]; public TileEntityAssembler() { super(MultiblockAssembler.instance, new int[]{3,3,3}, 32000, true); } public FluidTank[] tanks = {new FluidTank(8000),new FluidTank(8000),new FluidTank(8000)}; public ItemStack[] inventory = new ItemStack[18+3]; public CrafterPatternInventory[] patterns = {new CrafterPatternInventory(this),new CrafterPatternInventory(this),new CrafterPatternInventory(this)}; public boolean recursiveIngredients = false; @Override public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) { super.readCustomNBT(nbt, descPacket); tanks[0].readFromNBT(nbt.getCompoundTag("tank0")); tanks[1].readFromNBT(nbt.getCompoundTag("tank1")); tanks[2].readFromNBT(nbt.getCompoundTag("tank2")); recursiveIngredients = nbt.getBoolean("recursiveIngredients"); if(!descPacket) { inventory = Utils.readInventory(nbt.getTagList("inventory", 10), 18+3); for(int iPattern=0; iPattern<patterns.length; iPattern++) { NBTTagList patternList = nbt.getTagList("pattern"+iPattern, 10); patterns[iPattern] = new CrafterPatternInventory(this); patterns[iPattern].readFromNBT(patternList); } } } @Override public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) { super.writeCustomNBT(nbt, descPacket); nbt.setTag("tank0", tanks[0].writeToNBT(new NBTTagCompound())); nbt.setTag("tank1", tanks[1].writeToNBT(new NBTTagCompound())); nbt.setTag("tank2", tanks[2].writeToNBT(new NBTTagCompound())); nbt.setBoolean("recursiveIngredients", recursiveIngredients); if(!descPacket) { nbt.setTag("inventory", Utils.writeInventory(inventory)); for(int iPattern=0; iPattern<patterns.length; iPattern++) { NBTTagList patternList = new NBTTagList(); patterns[iPattern].writeToNBT(patternList); nbt.setTag("pattern"+iPattern, patternList); } } } @Override public void receiveMessageFromClient(NBTTagCompound message) { if(message.hasKey("buttonID")) { int id = message.getInteger("buttonID"); if(id >= 0 && id < patterns.length) { CrafterPatternInventory pattern = patterns[id]; for(int i = 0; i < pattern.inv.length; i++) pattern.inv[i] = null; } else if(id==3) { recursiveIngredients=!recursiveIngredients; } } else if(message.hasKey("patternSync")) { int r = message.getInteger("recipe"); NBTTagList list = message.getTagList("patternSync", 10); CrafterPatternInventory pattern = patterns[r]; for(int i = 0; i < list.tagCount(); i++) { NBTTagCompound itemTag = list.getCompoundTagAt(i); pattern.inv[itemTag.getInteger("slot")] = ItemStack.loadItemStackFromNBT(itemTag); } } } @Override public void update() { super.update(); if(isDummy() || isRSDisabled() || worldObj.isRemote || worldObj.getTotalWorldTime()%16!=((getPos().getX()^getPos().getZ())&15)) return; boolean update = false; ItemStack[][] outputBuffer = new ItemStack[patterns.length][0]; for(int p=0; p<patterns.length; p++) { CrafterPatternInventory pattern = patterns[p]; if ((controllingComputers != 0) && !computerOn[p]) return; if(pattern.inv[9] != null && canOutput(pattern.inv[9], p)) { ItemStack output = pattern.inv[9].copy(); ArrayList<ItemStack> queryList = new ArrayList<>();//List of all available inputs in the inventory for(ItemStack[] bufferedStacks : outputBuffer) for(ItemStack stack : bufferedStacks) if(stack!=null) queryList.add(stack.copy()); for(ItemStack stack : this.inventory) if(stack!=null) queryList.add(stack.copy()); int consumed = IEConfig.Machines.assembler_consumption; if(this.energyStorage.extractEnergy(consumed, true)==consumed && this.hasIngredients(pattern, queryList)) { this.energyStorage.extractEnergy(consumed, false); ArrayList<ItemStack> outputList = new ArrayList<ItemStack>();//List of all outputs for the current recipe. This includes discarded containers outputList.add(output); AssemblerHandler.IRecipeAdapter adapter = AssemblerHandler.findAdapter(pattern.recipe); AssemblerHandler.RecipeQuery[] queries = adapter.getQueriedInputs(pattern.recipe, pattern.inv); ItemStack[] gridItems = new ItemStack[9]; for(int i = 0; i < queries.length; i++) if(queries[i] != null) { AssemblerHandler.RecipeQuery recipeQuery = queries[i]; Optional<ItemStack> taken = null; for(int j = 0; j < outputBuffer.length; j++) { taken = consumeItem(recipeQuery.query, recipeQuery.querySize, outputBuffer[j], outputList); if(taken != null) break; } if(taken == null) taken = this.consumeItem(recipeQuery.query, recipeQuery.querySize, inventory, outputList); if(taken != null) gridItems[i] = taken.orNull(); } ItemStack[] remainingItems = pattern.recipe.getRemainingItems(Utils.InventoryCraftingFalse.createFilledCraftingInventory(3, 3, gridItems)); for(ItemStack rem : remainingItems) if(rem != null) outputList.add(rem); outputBuffer[p]=outputList.toArray(new ItemStack[outputList.size()]); update = true; } } } TileEntity inventoryTile = this.worldObj.getTileEntity(getPos().offset(facing,2)); for(int buffer=0; buffer<outputBuffer.length; buffer++) if(outputBuffer[buffer] != null && outputBuffer[buffer].length > 0) for(int iOutput = 0; iOutput < outputBuffer[buffer].length; iOutput++) { ItemStack output = outputBuffer[buffer][iOutput]; if(output != null && output.stackSize > 0) { if(!isRecipeIngredient(output, buffer) && inventoryTile != null) { output = Utils.insertStackIntoInventory(inventoryTile, output, facing.getOpposite()); if(output == null || output.stackSize <= 0) continue; } int free = -1; if(iOutput == 0)//Main recipe output { if(this.inventory[18 + buffer] == null && free < 0) free = 18 + buffer; else if(this.inventory[18 + buffer] != null && OreDictionary.itemMatches(output, this.inventory[18 + buffer], true) && this.inventory[18 + buffer].stackSize + output.stackSize <= this.inventory[18 + buffer].getMaxStackSize()) { this.inventory[18 + buffer].stackSize += output.stackSize; free = -1; continue; } } else for(int i = 0; i < this.inventory.length; i++) { if(this.inventory[i] == null && free < 0) free = i; else if(this.inventory[i] != null && OreDictionary.itemMatches(output, this.inventory[i], true) && this.inventory[i].stackSize + output.stackSize <= this.inventory[i].getMaxStackSize()) { this.inventory[i].stackSize += output.stackSize; free = -1; break; } } if(free >= 0) this.inventory[free] = output.copy(); } } for (int i=0;i<3;i++) if(!isRecipeIngredient(this.inventory[18+i], i) && inventoryTile!=null) this.inventory[18+i] = Utils.insertStackIntoInventory(inventoryTile, this.inventory[18+i], facing.getOpposite()); if(update) { this.markDirty(); this.markContainingBlockForUpdate(null); } } public Optional<ItemStack> consumeItem(Object query, int querySize, ItemStack[] inventory, ArrayList<ItemStack> containerItems) { FluidStack fs = query instanceof FluidStack ? (FluidStack)query : (query instanceof IngredientStack && ((IngredientStack)query).fluid != null) ? ((IngredientStack)query).fluid : null; if(fs != null) for(FluidTank tank : tanks) if(tank.getFluid() != null && tank.getFluid().containsFluid(fs)) { tank.drain(fs.amount, true); markDirty(); this.markContainingBlockForUpdate(null); return Optional.absent(); } Optional<ItemStack> ret = null; for(int i=0; i<inventory.length; i++) if(inventory[i] != null && Utils.stackMatchesObject(inventory[i], query, true)) { int taken = Math.min(querySize, inventory[i].stackSize); boolean doTake = true; if(doTake) { ret = Optional.of(inventory[i].splitStack(taken)); if(inventory[i].stackSize <= 0) inventory[i] = null; } querySize -= taken; if(querySize <= 0) break; } if(query == null || querySize <= 0) return ret; return null; } public boolean hasIngredients(CrafterPatternInventory pattern, ArrayList<ItemStack> queryList) { boolean match = true; AssemblerHandler.IRecipeAdapter adapter = AssemblerHandler.findAdapter(pattern.recipe); if(adapter==null) //We don't know how to handle the recipe return false; AssemblerHandler.RecipeQuery[] queries = adapter.getQueriedInputs(pattern.recipe, pattern.inv); for(AssemblerHandler.RecipeQuery recipeQuery : queries) if(recipeQuery != null && recipeQuery.query != null) { FluidStack fs = recipeQuery.query instanceof FluidStack ? (FluidStack)recipeQuery.query : (recipeQuery.query instanceof IngredientStack && ((IngredientStack)recipeQuery.query).fluid != null) ? ((IngredientStack)recipeQuery.query).fluid : null; if(fs != null) { boolean hasFluid = false; for(FluidTank tank : tanks) if(tank.getFluid() != null && tank.getFluid().containsFluid(fs)) { hasFluid = true; break; } if(hasFluid) continue; } int querySize = recipeQuery.querySize; Iterator<ItemStack> it = queryList.iterator(); while(it.hasNext()) { ItemStack next = it.next(); if(next != null && Utils.stackMatchesObject(next, recipeQuery.query, true)) { int taken = Math.min(querySize, next.stackSize); next.stackSize -= taken; if(next.stackSize <= 0) it.remove(); querySize -= taken; if(querySize <= 0) break; } } if(querySize > 0) { match = false; break; } } return match; } public boolean canOutput(ItemStack output, int iPattern) { if(this.inventory[18+iPattern]==null) return true; else if(OreDictionary.itemMatches(output, this.inventory[18+iPattern], true) && Utils.compareItemNBT(output, this.inventory[18+iPattern]) && this.inventory[18+iPattern].stackSize+output.stackSize<=this.inventory[18+iPattern].getMaxStackSize()) return true; return false; } public boolean isRecipeIngredient(ItemStack stack, int slot) { if(stack == null) return false; if(slot-1<patterns.length||recursiveIngredients) for(int p = recursiveIngredients?0:slot; p < patterns.length; p++) { CrafterPatternInventory pattern = patterns[p]; for(int i=0; i<9; i++) if(pattern.inv[i]!=null) { if(OreDictionary.itemMatches(pattern.inv[i], stack, false)) return true; else if(pattern.inv[i].getItem()==stack.getItem() && !pattern.inv[i].getHasSubtypes() && pattern.inv[i].isItemStackDamageable()) return true; } } return false; } @Override public float[] getBlockBounds() { if(pos<9 || pos==10||pos==13||pos==16 || pos==22) return new float[]{0,0,0,1,1,1}; float xMin = 0; float yMin = 0; float zMin = 0; float xMax = 1; float yMax = 1; float zMax = 1; if((pos%9<3 && facing==EnumFacing.SOUTH)||(pos%9>=6 && facing==EnumFacing.NORTH)) zMin = .25f; else if((pos%9<3 && facing==EnumFacing.NORTH)||(pos%9>=6 && facing==EnumFacing.SOUTH)) zMax = .75f; else if((pos%9<3 && facing==EnumFacing.EAST)||(pos%9>=6 && facing==EnumFacing.WEST)) xMin = .25f; else if((pos%9<3 && facing==EnumFacing.WEST)||(pos%9>=6 && facing==EnumFacing.EAST)) xMax = .75f; if((pos%3==0 && facing==EnumFacing.EAST)||(pos%3==2 && facing==EnumFacing.WEST)) zMin = .1875f; else if((pos%3==0 && facing==EnumFacing.WEST)||(pos%3==2 && facing==EnumFacing.EAST)) zMax = .8125f; else if((pos%3==0 && facing==EnumFacing.NORTH)||(pos%3==2 && facing==EnumFacing.SOUTH)) xMin = .1875f; else if((pos%3==0 && facing==EnumFacing.SOUTH)||(pos%3==2 && facing==EnumFacing.NORTH)) xMax = .8125f; return new float[]{xMin,yMin,zMin, xMax,yMax,zMax}; } @Override public int[] getEnergyPos() { return new int[]{22}; } @Override public int[] getRedstonePos() { return new int[]{3, 5}; } @Override public void replaceStructureBlock(BlockPos pos, IBlockState state, ItemStack stack, int h, int l, int w) { super.replaceStructureBlock(pos, state, stack, h, l, w); if(h==1&&w==1&&l!=1) { TileEntity tile = worldObj.getTileEntity(pos); if(tile instanceof TileEntityConveyorBelt) ((TileEntityConveyorBelt)tile).setFacing(this.facing); } } @Override public boolean isInWorldProcessingMachine() { return false; } @Override public boolean additionalCanProcessCheck(MultiblockProcess<IMultiblockRecipe> process) { return false; } @Override public void doProcessOutput(ItemStack output) { BlockPos pos = getPos().offset(facing,-1); TileEntity inventoryTile = this.worldObj.getTileEntity(pos); if(inventoryTile!=null) output = Utils.insertStackIntoInventory(inventoryTile, output, facing.getOpposite()); if(output!=null) Utils.dropStackAtPos(worldObj, pos, output, facing); } @Override public void doProcessFluidOutput(FluidStack output) { } @Override public void onProcessFinish(MultiblockProcess<IMultiblockRecipe> process) { } @Override public int getMaxProcessPerTick() { return 0; } @Override public int getProcessQueueMaxLength() { return 0; } @Override public float getMinProcessDistance(MultiblockProcess<IMultiblockRecipe> process) { return 0; } @Override public ItemStack[] getInventory() { return this.inventory; } @Override public boolean isStackValid(int slot, ItemStack stack) { return true; } @Override public int getSlotLimit(int slot) { return 64; } @Override public int[] getOutputSlots() { return new int[0]; } @Override public int[] getOutputTanks() { return new int[0]; } @Override public IFluidTank[] getInternalTanks() { return this.tanks; } @Override public void doGraphicalUpdates(int slot) { this.markDirty(); this.markContainingBlockForUpdate(null); } @Override public boolean hasCapability(Capability<?> capability, EnumFacing facing) { if((pos==10||pos==16)&&capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return (pos==10&&facing==this.facing.getOpposite())||(pos==16&&facing==this.facing); return super.hasCapability(capability, facing); } IItemHandler insertionHandler = new IEInventoryHandler(18, this, 0, true, false); IItemHandler extractionHandler = new IEInventoryHandler(3, this, 18, false, true); @Override public <T> T getCapability(Capability<T> capability, EnumFacing facing) { if((pos==10||pos==16)&&capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { TileEntityAssembler master = master(); if(master==null) return null; if(pos==10&&facing==this.facing.getOpposite()) return (T)master.insertionHandler; if(pos==16&&facing==this.facing) return (T)master.extractionHandler; return null; } return super.getCapability(capability, facing); } @Override public IMultiblockRecipe findRecipeForInsertion(ItemStack inserting) { return null; } @Override protected IMultiblockRecipe readRecipeFromNBT(NBTTagCompound tag) { return null; } @Override public boolean canOpenGui() { return formed; } @Override public int getGuiID() { return Lib.GUIID_Assembler; } @Override public TileEntity getGuiMaster() { return master(); } @Override protected IFluidTank[] getAccessibleFluidTanks(EnumFacing side) { TileEntityAssembler master = master(); if(master!=null && pos==1&&(side==null||side==facing.getOpposite())) return master.tanks; return new FluidTank[0]; } @Override protected boolean canFillTankFrom(int iTank, EnumFacing side, FluidStack resource) { return true; } @Override protected boolean canDrainTankFrom(int iTank, EnumFacing side) { return true; } @Override public EnumFacing[] sigOutputDirections() { if(pos==16) return new EnumFacing[]{this.facing}; return new EnumFacing[0]; } public static class CrafterPatternInventory implements IInventory { public ItemStack[] inv = new ItemStack[10]; public IRecipe recipe; final TileEntityAssembler tile; public CrafterPatternInventory(TileEntityAssembler tile) { this.tile = tile; } @Override public int getSizeInventory() { return 10; } @Override public ItemStack getStackInSlot(int slot) { return inv[slot]; } @Override public ItemStack decrStackSize(int slot, int amount) { ItemStack stack = getStackInSlot(slot); if(slot<9 && stack != null) if(stack.stackSize <= amount) setInventorySlotContents(slot, null); else { stack = stack.splitStack(amount); if(stack.stackSize == 0) setInventorySlotContents(slot, null); } return stack; } @Override public ItemStack removeStackFromSlot(int slot) { ItemStack stack = getStackInSlot(slot); if (stack != null) setInventorySlotContents(slot, null); return stack; } @Override public void setInventorySlotContents(int slot, ItemStack stack) { if(slot<9) { inv[slot] = stack; if (stack != null && stack.stackSize > getInventoryStackLimit()) stack.stackSize = getInventoryStackLimit(); } recalculateOutput(); } @Override public void clear() { for(int i=0; i<this.inv.length; i++) this.inv[i] = null; } public void recalculateOutput() { InventoryCrafting invC = Utils.InventoryCraftingFalse.createFilledCraftingInventory(3, 3, inv); this.recipe = Utils.findRecipe(invC, tile.getWorld()); this.inv[9] = recipe!=null?recipe.getCraftingResult(invC):null; } public ArrayList<ItemStack> getTotalPossibleOutputs() { ArrayList<ItemStack> outputList = new ArrayList<ItemStack>(); outputList.add(inv[9].copy()); for(int i=0; i<9; i++) { FluidStack fs = FluidUtil.getFluidContained(inv[i]); if(fs != null) { boolean hasFluid = false; for(FluidTank tank : tile.tanks) if(tank.getFluid()!=null && tank.getFluid().containsFluid(fs)) { hasFluid=true; break; } if(hasFluid) continue; } // ItemStack container = inv[i].getItem().getContainerItem(inv[i]); // if(container!=null && inv[i].getItem().doesContainerItemLeaveCraftingGrid(inv[i])) // outputList.add(container.copy()); } InventoryCrafting invC = Utils.InventoryCraftingFalse.createFilledCraftingInventory(3, 3, inv); for(ItemStack ss : this.recipe.getRemainingItems(invC)) if(ss!=null) outputList.add(ss); return outputList; } @Override public String getName() { return "IECrafterPattern"; } @Override public boolean hasCustomName() { return false; } @Override public int getInventoryStackLimit() { return 1; } @Override public boolean isUseableByPlayer(EntityPlayer player) { return true; } @Override public void openInventory(EntityPlayer player){} @Override public void closeInventory(EntityPlayer player){} @Override public boolean isItemValidForSlot(int slot, ItemStack stack) { return true; } @Override public void markDirty() { this.tile.markDirty(); } public void writeToNBT(NBTTagList list) { for(int i=0; i<this.inv.length; i++) if(this.inv[i] != null) { NBTTagCompound itemTag = new NBTTagCompound(); itemTag.setByte("Slot", (byte)i); this.inv[i].writeToNBT(itemTag); list.appendTag(itemTag); } } public void readFromNBT(NBTTagList list) { for (int i=0; i<list.tagCount(); i++) { NBTTagCompound itemTag = list.getCompoundTagAt(i); int slot = itemTag.getByte("Slot") & 255; if(slot>=0 && slot<getSizeInventory()) this.inv[slot] = ItemStack.loadItemStackFromNBT(itemTag); } recalculateOutput(); } @Override public ITextComponent getDisplayName() { return new TextComponentString(this.getName()); } @Override public int getField(int id) { return 0; } @Override public void setField(int id, int value) { } @Override public int getFieldCount() { return 0; } } }