@Override
  public ItemStack slotClick(int id, int button, ClickType clickType, EntityPlayer player) {
    Slot slot = id < 0 ? null : this.inventorySlots.get(id);
    if (!(slot instanceof IESlot.Ghost)) return super.slotClick(id, button, clickType, player);
    // Spooky Ghost Slots!!!!
    ItemStack stack = null;
    ItemStack stackSlot = slot.getStack();
    if (stackSlot != null) stack = stackSlot.copy();

    if (button == 2) slot.putStack(null);
    else if (button == 0 || button == 1) {
      InventoryPlayer playerInv = player.inventory;
      ItemStack stackHeld = playerInv.getItemStack();
      if (stackSlot == null) {
        if (stackHeld != null && slot.isItemValid(stackHeld)) {
          slot.putStack(Utils.copyStackWithAmount(stackHeld, 1));
        }
      } else if (stackHeld == null) {
        slot.putStack(null);
      } else if (slot.isItemValid(stackHeld)) {
        slot.putStack(Utils.copyStackWithAmount(stackHeld, 1));
      }
    } else if (button == 5) {
      InventoryPlayer playerInv = player.inventory;
      ItemStack stackHeld = playerInv.getItemStack();
      if (!slot.getHasStack()) {
        slot.putStack(Utils.copyStackWithAmount(stackHeld, 1));
      }
    }
    return stack;
  }
 @Override
 public boolean onItemUseFirst(
     ItemStack stack,
     EntityPlayer player,
     World world,
     BlockPos pos,
     EnumFacing side,
     float hitX,
     float hitY,
     float hitZ) {
   if (!world.isRemote) {
     TileEntity tileEntity = world.getTileEntity(pos);
     if (tileEntity instanceof IFluidHandler)
       return Utils.fillFluidHandlerWithPlayerItem(world, (IFluidHandler) tileEntity, player);
     else {
       FluidStack fs = this.getFluid(stack);
       if (Utils.placeFluidBlock(world, pos.offset(side), fs)) {
         if (fs.amount <= 0) fs = null;
         ItemNBTHelper.setFluidStack(stack, "fluid", fs);
         return true;
       }
     }
   }
   return false;
 }
  @Override
  public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) {
    super.readCustomNBT(nbt, descPacket);
    facing = nbt.getInteger("facing");
    energyStorage.readFromNBT(nbt);

    tank.readFromNBT(nbt.getCompoundTag("tank"));

    this.inventory = Utils.readInventory(nbt.getTagList("inventory", 10), 5);
    this.predictedOutput = Utils.readInventory(nbt.getTagList("predictedOutput", 10), 5);
    process = nbt.getIntArray("process");
  }
  @Override
  public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) {
    super.writeCustomNBT(nbt, descPacket);
    nbt.setInteger("facing", facing);
    energyStorage.writeToNBT(nbt);

    NBTTagCompound tankTag = tank.writeToNBT(new NBTTagCompound());
    nbt.setTag("tank", tankTag);

    nbt.setTag("inventory", Utils.writeInventory(inventory));

    nbt.setTag("predictedOutput", Utils.writeInventory(predictedOutput));

    nbt.setIntArray("process", process);
  }
 @Override
 public ItemStack getCraftingResult(InventoryCrafting inv) {
   ItemStack jerrycan = null;
   ItemStack container = null;
   FluidStack fs = null;
   for (int i = 0; i < inv.getSizeInventory(); i++) {
     ItemStack stackInSlot = inv.getStackInSlot(i);
     if (stackInSlot != null)
       if (jerrycan == null
           && IEContent.itemJerrycan.equals(stackInSlot.getItem())
           && FluidUtil.getFluidContained(stackInSlot) != null) {
         jerrycan = stackInSlot;
         fs = FluidUtil.getFluidContained(jerrycan);
       } else if (container == null && FluidUtil.getFluidHandler(stackInSlot) != null)
         container = stackInSlot;
   }
   if (fs != null && container != null) {
     ItemStack newContainer = Utils.copyStackWithAmount(container, 1);
     IFluidHandler handler = FluidUtil.getFluidHandler(newContainer);
     int accepted = handler.fill(fs, false);
     if (accepted > 0) {
       handler.fill(fs, true);
       //				FluidUtil.getFluidHandler(jerrycan).drain(accepted,true);
       ItemNBTHelper.setInt(jerrycan, "jerrycanDrain", accepted);
     }
     return newContainer;
   }
   return null;
 }
 @Override
 public boolean isItemValidForSlot(int slot, ItemStack stack) {
   if (!formed) return false;
   if (master() != null) return master().isItemValidForSlot(slot, stack);
   if (slot == 1 || slot == 3 || slot == 5) return false;
   if (slot == 4)
     return (tank2.getFluidAmount() <= 0
         ? FluidContainerRegistry.isEmptyContainer(stack)
         : FluidContainerRegistry.fillFluidContainer(
                 tank2.getFluid(), Utils.copyStackWithAmount(stack, 1))
             != null);
   FluidStack fs = FluidContainerRegistry.getFluidForFilledItem(stack);
   if (fs == null) return false;
   RefineryRecipe partialRecipe = DieselHandler.findIncompleteRefineryRecipe(fs, null);
   if (partialRecipe == null) return false;
   if (slot == 0)
     return (tank0.getFluidAmount() <= 0 || fs.isFluidEqual(tank0.getFluid()))
         && (tank1.getFluidAmount() <= 0
             || DieselHandler.findIncompleteRefineryRecipe(fs, tank1.getFluid()) != null);
   if (slot == 2)
     return (tank1.getFluidAmount() <= 0 || fs.isFluidEqual(tank1.getFluid()))
         && (tank0.getFluidAmount() <= 0
             || DieselHandler.findIncompleteRefineryRecipe(fs, tank0.getFluid()) != null);
   return false;
 }
 // DEEP STORAGE
 @Override
 public ItemStack getStoredItemType() {
   TileEntitySilo mast = master();
   if (mast != null) return mast.getStoredItemType();
   if (this.identStack != null) return Utils.copyStackWithAmount(identStack, storageAmount);
   return null;
 }
 @Override
 public String[] getOverlayText(EntityPlayer player, MovingObjectPosition mop, boolean hammer) {
   if (Utils.isFluidRelatedItemStack(player.getCurrentEquippedItem())) {
     FluidStack fs = master() != null ? master().tank.getFluid() : this.tank.getFluid();
     String s = null;
     if (fs != null) s = fs.getLocalizedName() + ": " + fs.amount + "mB";
     else s = StatCollector.translateToLocal(Lib.GUI + "empty");
     return new String[] {s};
   }
   return null;
 }
  @Override
  public void updateEntity() {
    if (!loaded && hasWorldObj() && !worldObj.isRemote) {
      loaded = true;
      createAELink();
      for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
        TileEntity tileEntity =
            worldObj.getTileEntity(
                xCoord + direction.offsetX, yCoord + direction.offsetY, zCoord + direction.offsetZ);
        if (tileEntity instanceof IGridHost) {
          IGridNode node = ((IGridHost) tileEntity).getGridNode(direction);
          if (node != null) {
            node.updateState();
          }
        }

        for (ImmersiveNetHandler.Connection connection :
            ImmersiveNetHandler.INSTANCE.getConnections(worldObj, Utils.toCC(this))) {
          ChunkCoordinates opposite = connection.end;
          if (connection.end.equals(Utils.toCC(this))) {
            opposite = connection.start;
          }

          TileEntity teOpposite =
              worldObj.getTileEntity(opposite.posX, opposite.posY, opposite.posZ);
          if (teOpposite instanceof TileMEWireConnector) {
            GridNode nodeA =
                (GridNode) ((TileMEWireConnector) teOpposite).getGridNode(ForgeDirection.UNKNOWN);
            GridNode nodeB = (GridNode) getGridNode(ForgeDirection.UNKNOWN);
            if (!nodeA.hasConnection(nodeB) && !nodeB.hasConnection(nodeA)) {
              try {
                gridConnections.add(AEApi.instance().createGridConnection(nodeA, nodeB));
              } catch (FailedConnection failedConnection) {
                failedConnection.printStackTrace();
              }
            }
          }
        }
      }
    }
  }
  @Override
  public void updateEntity() {
    if (pos == 4
        && !worldObj.isRemote
        && this.outputStack == null
        && storageAmount > 0
        && identStack != null) this.markDirty();

    if (pos == 4
        && !worldObj.isRemote
        && this.outputStack != null
        && worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord)
        && worldObj.getTotalWorldTime() % 8 == 0) {
      updateComparatorValuesPart1();
      for (int i = 0; i < 6; i++)
        if (i != 1) {
          TileEntity inventory =
              this.worldObj.getTileEntity(
                  xCoord + (i == 4 ? -1 : i == 5 ? 1 : 0),
                  yCoord + (i == 0 ? -1 : 0),
                  zCoord + (i == 2 ? -1 : i == 3 ? 1 : 0));
          ItemStack stack = Utils.copyStackWithAmount(identStack, 1);
          if ((inventory instanceof ISidedInventory
                  && ((ISidedInventory) inventory)
                          .getAccessibleSlotsFromSide(ForgeDirection.OPPOSITES[i])
                          .length
                      > 0)
              || (inventory instanceof IInventory
                  && ((IInventory) inventory).getSizeInventory() > 0))
            stack =
                Utils.insertStackIntoInventory(
                    (IInventory) inventory, stack, ForgeDirection.OPPOSITES[i]);
          if (stack == null) {
            outputStack.stackSize--;
            this.markDirty();
            if (outputStack == null) break;
          }
        }
      updateComparatorValuesPart2();
    }
  }
 @Override
 public void readCustomNBT(NBTTagCompound nbt, boolean descPacket) {
   super.readCustomNBT(nbt, descPacket);
   facing = nbt.getInteger("facing");
   tank0.readFromNBT(nbt.getCompoundTag("tank0"));
   tank1.readFromNBT(nbt.getCompoundTag("tank1"));
   tank2.readFromNBT(nbt.getCompoundTag("tank2"));
   energyStorage.readFromNBT(nbt);
   if (!descPacket) {
     inventory = Utils.readInventory(nbt.getTagList("inventory", 10), 6);
   }
 }
  @Override
  public void markDirty() {
    super.markDirty();

    int oldStorage = storageAmount;
    if (inputStack != null) {
      if (this.identStack == null) identStack = inputStack;

      if ((maxStorage - storageAmount) > 0) {
        if (prevInputStack == null) // inputStack is new
        storageAmount += inputStack.stackSize;
        else storageAmount += inputStack.stackSize - prevInputStack.stackSize;

        if (storageAmount > maxStorage) storageAmount = maxStorage;
      }
      // Set new fake inputs
      if ((maxStorage - storageAmount) >= identStack.getMaxStackSize()) {
        inputStack = null;
        prevInputStack = null;
      } else {
        inputStack =
            Utils.copyStackWithAmount(
                identStack, identStack.getMaxStackSize() - (maxStorage - storageAmount));
        prevInputStack = inputStack.copy();
      }
    }

    if (prevOutputStack != null) // Had fake output
    {
      if (outputStack == null) // fully depleted
      storageAmount -= prevOutputStack.stackSize;
      else storageAmount -= (prevOutputStack.stackSize - outputStack.stackSize);

      if (storageAmount < 0) storageAmount = 0;
    }

    // Handle emptying of the barrel
    if (storageAmount == 0 && !lockItem) {
      identStack = null;
      outputStack = null;
      prevOutputStack = null;
      inputStack = null;
      prevInputStack = null;
      forceUpdate = true;
    } else if (identStack != null) {
      if (outputStack == null) outputStack = identStack.copy();
      outputStack.stackSize = Math.min(outputStack.getMaxStackSize(), storageAmount);
      prevOutputStack = outputStack.copy();
    }
    if (storageAmount != oldStorage || forceUpdate)
      worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
    forceUpdate = false;
  }
 @Callback(doc = "function():table -- returns the entire input queue of the crusher")
 public Object[] getInputQueue(Context context, Arguments args) {
   TileEntityCrusher master = getTileEntity();
   Map<Integer, Object> ret = new HashMap<>();
   List<MultiblockProcess<CrusherRecipe>> queue = master.processQueue;
   for (int i = 0; i < queue.size(); i++) {
     MultiblockProcess<CrusherRecipe> currTmp = queue.get(i);
     if (currTmp instanceof MultiblockProcessInWorld) {
       MultiblockProcessInWorld<CrusherRecipe> curr =
           (MultiblockProcessInWorld<CrusherRecipe>) currTmp;
       Map<String, Object> recipe = new HashMap<>();
       recipe.put("progress", curr.processTick);
       recipe.put("maxProgress", curr.maxTicks);
       Map<String, Object> input = Utils.saveStack(curr.inputItem);
       recipe.put("input", input);
       recipe.put("output", Utils.saveStack(curr.recipe.output));
       ret.put(i + 1, recipe);
     }
   }
   return new Object[] {ret};
 }
 @Override
 public void writeCustomNBT(NBTTagCompound nbt, boolean descPacket) {
   super.writeCustomNBT(nbt, descPacket);
   nbt.setInteger("facing", facing);
   NBTTagCompound tankTag = tank0.writeToNBT(new NBTTagCompound());
   nbt.setTag("tank0", tankTag);
   tankTag = tank1.writeToNBT(new NBTTagCompound());
   nbt.setTag("tank1", tankTag);
   tankTag = tank2.writeToNBT(new NBTTagCompound());
   nbt.setTag("tank2", tankTag);
   energyStorage.writeToNBT(nbt);
   if (!descPacket) {
     nbt.setTag("inventory", Utils.writeInventory(inventory));
   }
 }
 @Override
 public int fill(ItemStack container, FluidStack resource, boolean doFill) {
   if (resource != null) {
     FluidStack fs = getFluid(container);
     if (fs == null || resource.isFluidEqual(fs)) {
       int space = fs == null ? getCapacity(container) : getCapacity(container) - fs.amount;
       int accepted = Math.min(space, resource.amount);
       if (fs == null) fs = Utils.copyFluidStackWithAmount(resource, accepted, false);
       else fs.amount += accepted;
       if (doFill) ItemNBTHelper.setFluidStack(container, "fluid", fs);
       return accepted;
     }
   }
   return 0;
 }
 @Override
 public void setStoredItemType(ItemStack type, int amount) {
   TileEntitySilo mast = master();
   if (mast != null) {
     mast.setStoredItemType(type, amount);
     return;
   }
   updateComparatorValuesPart1();
   this.identStack = Utils.copyStackWithAmount(type, 0);
   if (amount > maxStorage) amount = maxStorage;
   this.storageAmount = amount;
   this.forceUpdate = true;
   this.markDirty();
   updateComparatorValuesPart2();
 }
  @Override
  public ItemStack decrStackSize(int slot, int amount) {
    updateComparatorValuesPart1();
    if (!formed) return null;
    TileEntitySilo master = master();
    if (master != null) return master.decrStackSize(slot, amount);

    if (this.outputStack == null) return null;

    int rem = Math.min(amount, outputStack.stackSize);
    ItemStack ret = Utils.copyStackWithAmount(outputStack, rem);
    outputStack.stackSize -= rem;
    if (outputStack.stackSize <= 0) outputStack = null;
    this.markDirty();
    updateComparatorValuesPart2();
    return ret;
  }
  @Override
  public void removeCable(ImmersiveNetHandler.Connection connection) {
    if (!worldObj.isRemote) {
      ChunkCoordinates opposite = connection.end;
      if (connection.end.equals(Utils.toCC(this))) {
        opposite = connection.start;
      }

      for (IGridConnection gridConnection : gridConnections) {
        DimensionalCoord locA = gridConnection.a().getGridBlock().getLocation();
        DimensionalCoord locB = gridConnection.b().getGridBlock().getLocation();
        if ((opposite.posX == locA.x && opposite.posZ == locA.z && opposite.posY == locA.y)
            || (opposite.posX == locB.x && opposite.posZ == locB.z && opposite.posY == locB.y)) {
          gridConnection.destroy();
          gridConnections.remove(gridConnection);
          break;
        }
      }
    }

    super.removeCable(connection);
  }
  @SubscribeEvent()
  public void renderOverlay(RenderGameOverlayEvent.Post event) {
    if (ClientUtils.mc().thePlayer != null
        && ClientUtils.mc().thePlayer.getCurrentEquippedItem() != null
        && event.type == RenderGameOverlayEvent.ElementType.TEXT) {
      if (OreDictionary.itemMatches(
              new ItemStack(IEContent.itemTool, 1, 2),
              ClientUtils.mc().thePlayer.getCurrentEquippedItem(),
              false)
          || OreDictionary.itemMatches(
              new ItemStack(IEContent.itemWireCoil, 1, OreDictionary.WILDCARD_VALUE),
              ClientUtils.mc().thePlayer.getCurrentEquippedItem(),
              false)) {
        if (ItemNBTHelper.hasKey(
            ClientUtils.mc().thePlayer.getCurrentEquippedItem(), "linkingPos")) {
          int[] link =
              ItemNBTHelper.getIntArray(
                  ClientUtils.mc().thePlayer.getCurrentEquippedItem(), "linkingPos");
          if (link != null && link.length > 3) {
            String s =
                StatCollector.translateToLocalFormatted(
                    Lib.DESC_INFO + "attachedTo", link[1], link[2], link[3]);
            ClientUtils.font()
                .drawString(
                    s,
                    event.resolution.getScaledWidth() / 2
                        - ClientUtils.font().getStringWidth(s) / 2,
                    event.resolution.getScaledHeight() - GuiIngameForge.left_height - 10,
                    WireType.ELECTRUM.getColour(null),
                    true);
          }
        }

      } else if (Utils.isHammer(ClientUtils.mc().thePlayer.getCurrentEquippedItem())) {
        MovingObjectPosition mop = ClientUtils.mc().objectMouseOver;
        if (mop != null
            && ClientUtils.mc().thePlayer.worldObj.getTileEntity(mop.blockX, mop.blockY, mop.blockZ)
                instanceof IEBlockInterfaces.IBlockOverlayText) {
          IEBlockInterfaces.IBlockOverlayText overlayBlock =
              (IBlockOverlayText)
                  ClientUtils.mc()
                      .thePlayer
                      .worldObj
                      .getTileEntity(mop.blockX, mop.blockY, mop.blockZ);
          String[] s = overlayBlock.getOverlayText(mop);
          if (s != null && s.length > 0)
            for (int is = 0; is < s.length; is++)
              ClientUtils.font()
                  .drawString(
                      s[is],
                      event.resolution.getScaledWidth() / 2 + 8,
                      event.resolution.getScaledHeight() / 2
                          + 8
                          + is * ClientUtils.font().FONT_HEIGHT,
                      0xcccccc,
                      true);
        }

      } else if (ClientUtils.mc().thePlayer.getCurrentEquippedItem().getItem()
              instanceof ItemRevolver
          && ClientUtils.mc().thePlayer.getCurrentEquippedItem().getItemDamage() != 2) {
        ClientUtils.bindTexture("immersiveengineering:textures/gui/revolver.png");
        ItemStack[] bullets =
            ((ItemRevolver) ClientUtils.mc().thePlayer.getCurrentEquippedItem().getItem())
                .getBullets(ClientUtils.mc().thePlayer.getCurrentEquippedItem());
        int bulletAmount = bullets.length;
        float dx = event.resolution.getScaledWidth() - 32 - 48;
        float dy = event.resolution.getScaledHeight() - 64;
        GL11.glPushMatrix();
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glTranslated(dx, dy, 0);
        GL11.glScalef(.5f, .5f, 1);

        ClientUtils.drawTexturedRect(0, 1, 74, 74, 0 / 256f, 74 / 256f, 51 / 256f, 125 / 256f);
        if (bulletAmount >= 18)
          ClientUtils.drawTexturedRect(
              47, 1, 103, 74, 74 / 256f, 177 / 256f, 51 / 256f, 125 / 256f);
        else if (bulletAmount > 8)
          ClientUtils.drawTexturedRect(57, 1, 79, 39, 57 / 256f, 136 / 256f, 12 / 256f, 51 / 256f);

        RenderItem ir = RenderItem.getInstance();
        int[][] slots =
            ContainerRevolver.slotPositions[bulletAmount >= 18 ? 2 : bulletAmount > 8 ? 1 : 0];
        for (int i = 0; i < bulletAmount; i++) {
          if (bullets[i] != null) {
            int x = 0;
            int y = 0;
            if (i == 0) {
              x = 29;
              y = 3;
            } else if (i - 1 < slots.length) {
              x = slots[i - 1][0];
              y = slots[i - 1][1];
            } else {
              int ii = i - (slots.length + 1);
              x = ii == 0 ? 48 : ii == 1 ? 29 : ii == 3 ? 2 : 10;
              y = ii == 1 ? 57 : ii == 3 ? 30 : ii == 4 ? 11 : 49;
            }

            ir.renderItemIntoGUI(
                ClientUtils.mc().fontRenderer, ClientUtils.mc().renderEngine, bullets[i], x, y);
          }
        }
        RenderHelper.disableStandardItemLighting();
        GL11.glDisable(GL11.GL_BLEND);
        GL11.glPopMatrix();
      } else if (ClientUtils.mc().thePlayer.getCurrentEquippedItem().getItem() instanceof ItemDrill
          && ClientUtils.mc().thePlayer.getCurrentEquippedItem().getItemDamage() == 0) {
        ItemStack drill = ClientUtils.mc().thePlayer.getCurrentEquippedItem();
        ClientUtils.bindTexture("immersiveengineering:textures/gui/fuelGauge.png");
        GL11.glColor4f(1, 1, 1, 1);
        float dx = event.resolution.getScaledWidth() - 16;
        float dy = event.resolution.getScaledHeight() - 12;
        GL11.glPushMatrix();
        GL11.glTranslated(dx, dy, 0);
        ClientUtils.drawTexturedRect(-24, -68, 33, 81, 179 / 256f, 210 / 256f, 0 / 96f, 80 / 96f);

        GL11.glTranslated(-23, -28, 0);
        FluidStack fuel = ((ItemDrill) drill.getItem()).getFluid(drill);
        //				for(float angle=80; angle>=-80; angle-=20)
        //				{
        float angle =
            80
                - (160
                    * (fuel != null
                        ? fuel.amount / (float) ((ItemDrill) drill.getItem()).getCapacity(drill)
                        : 0));
        GL11.glRotatef(angle, 0, 0, 1);
        ClientUtils.drawTexturedRect(6, -2, 24, 4, 91 / 256f, 123 / 256f, 80 / 96f, 87 / 96f);
        GL11.glRotatef(-angle, 0, 0, 1);
        //				}
        GL11.glTranslated(23, 28, 0);

        ClientUtils.drawTexturedRect(
            -33 - 17, -40 - 26, 33 + 30, 40 + 37, 108 / 256f, 174 / 256f, 0 / 96f, 80 / 96f);

        RenderItem ir = RenderItem.getInstance();
        float scale = 1; // .75f;
        GL11.glScalef(scale, scale, scale);
        ItemStack head = ((ItemDrill) drill.getItem()).getHead(drill);
        if (head != null) {
          ir.renderItemIntoGUI(
              ClientUtils.mc().fontRenderer,
              ClientUtils.mc().renderEngine,
              head,
              (int) (-48 / scale),
              (int) (-36 / scale));
          ir.renderItemOverlayIntoGUI(
              ClientUtils.font(),
              ClientUtils.mc().renderEngine,
              head,
              (int) (-48 / scale),
              (int) (-36 / scale));
          RenderHelper.disableStandardItemLighting();
        }
        GL11.glPopMatrix();
      }
    }
  }
  @Override
  public void updateEntity() {
    if (!formed || pos != 17) return;

    if (!worldObj.isRemote) {
      boolean update = false;
      int prevAmount = tank2.getFluidAmount();
      boolean enabled;
      if (computerControlled) enabled = computerOn;
      else
        enabled =
            !worldObj.isBlockIndirectlyGettingPowered(
                xCoord + (facing == 4 ? -1 : facing == 5 ? 1 : facing == 2 ? -2 : 2),
                yCoord + 1,
                zCoord + (facing == 2 ? -1 : facing == 3 ? 1 : facing == 4 ? 2 : -2));
      if (enabled) {
        RefineryRecipe recipe = getRecipe(true);
        if (recipe != null) {
          int consumed = Config.getInt("refinery_consumption");
          if (energyStorage.extractEnergy(consumed, true) == consumed
              && tank2.fill(recipe.output.copy(), false) == recipe.output.amount) {
            int drain0 =
                tank0.getFluid().isFluidEqual(recipe.input0)
                    ? recipe.input0.amount
                    : recipe.input1.amount;
            int drain1 =
                tank0.getFluid().isFluidEqual(recipe.input0)
                    ? recipe.input1.amount
                    : recipe.input0.amount;
            if (tank0.getFluidAmount() >= drain0 && tank1.getFluidAmount() >= drain1) {
              energyStorage.extractEnergy(consumed, false);
              tank0.drain(drain0, true);
              tank1.drain(drain1, true);
              tank2.fill(recipe.output.copy(), true);
              update = true;
            }
          }
        }
      }
      if (tank2.getFluidAmount() > 0) {
        ItemStack filledContainer = Utils.fillFluidContainer(tank2, inventory[4], inventory[5]);
        if (filledContainer != null) {
          if (inventory[5] != null
              && OreDictionary.itemMatches(inventory[5], filledContainer, true))
            inventory[5].stackSize += filledContainer.stackSize;
          else if (inventory[5] == null) inventory[5] = filledContainer.copy();
          this.decrStackSize(4, filledContainer.stackSize);
          update = true;
        }
        if (tank2.getFluidAmount() > 0) {
          ForgeDirection f = ForgeDirection.getOrientation(facing);
          int out = Math.min(144, tank2.getFluidAmount());
          TileEntity te =
              Utils.getExistingTileEntity(
                  worldObj, xCoord + f.offsetX * 2, yCoord, zCoord + f.offsetZ * 2);
          if (te instanceof IFluidHandler
              && ((IFluidHandler) te).canFill(f.getOpposite(), tank2.getFluid().getFluid())) {
            int accepted =
                ((IFluidHandler) te)
                    .fill(f.getOpposite(), new FluidStack(tank2.getFluid().getFluid(), out), false);
            FluidStack drained = this.tank2.drain(accepted, true);
            ((IFluidHandler) te).fill(f.getOpposite(), drained, true);
          }
        }
      }
      if (tank2.getFluidAmount() != prevAmount) update = true;

      ItemStack emptyContainer = Utils.drainFluidContainer(tank0, inventory[0]);
      if (emptyContainer != null) {
        if (inventory[1] != null && OreDictionary.itemMatches(inventory[1], emptyContainer, true))
          inventory[1].stackSize += emptyContainer.stackSize;
        else if (inventory[1] == null) inventory[1] = emptyContainer.copy();
        this.decrStackSize(0, emptyContainer.stackSize);
        update = true;
      }
      emptyContainer = Utils.drainFluidContainer(tank1, inventory[2]);
      if (emptyContainer != null) {
        if (inventory[3] != null && OreDictionary.itemMatches(inventory[3], emptyContainer, true))
          inventory[3].stackSize += emptyContainer.stackSize;
        else if (inventory[3] == null) inventory[3] = emptyContainer.copy();
        this.decrStackSize(2, emptyContainer.stackSize);
        update = true;
      }

      if (update) {
        this.markDirty();
        worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
      }
    }
  }
  @Override
  public void updateEntity() {
    if (!formed || pos != 4) return;

    if (worldObj.isRemote) return;
    boolean update = false;
    int consumed = Config.getInt("bottlingMachine_consumption");
    for (int i = 0; i < inventory.length; i++)
      if (inventory[i] != null) {
        if (this.energyStorage.extractEnergy(consumed, true) == consumed) {
          this.energyStorage.extractEnergy(consumed, false);
          if (process[i]++ <= 72) {
            ItemStack filled = getFilledItem(inventory[i], false);
            if (predictedOutput[i] == null
                || !OreDictionary.itemMatches(filled, predictedOutput[i], true))
              predictedOutput[i] = filled;
            if (process[i] == 1) update = true;
            if (filled != null && process[i] > 72)
              inventory[i] = getFilledItem(inventory[i], true).copy();
          }
        }
        if (process[i] > 120) {
          ItemStack output = inventory[i].copy();
          TileEntity invOutput =
              worldObj.getTileEntity(
                  xCoord
                      + (facing == 4
                          ? 1
                          : facing == 5 ? -1 : ((mirrored ? -1 : 1) * (facing == 3 ? 1 : -1))),
                  yCoord + 1,
                  zCoord
                      + (facing == 2
                          ? 1
                          : facing == 3 ? -1 : ((mirrored ? -1 : 1) * (facing == 4 ? 1 : -1))));
          if ((invOutput instanceof ISidedInventory
                  && ((ISidedInventory) invOutput).getAccessibleSlotsFromSide(facing).length > 0)
              || (invOutput instanceof IInventory
                  && ((IInventory) invOutput).getSizeInventory() > 0))
            output = Utils.insertStackIntoInventory((IInventory) invOutput, output, facing);

          if (output != null) {
            ForgeDirection fd = ForgeDirection.getOrientation(facing);
            EntityItem ei =
                new EntityItem(
                    worldObj,
                    xCoord
                        + .5
                        + (facing == 4
                            ? 1
                            : facing == 5 ? -1 : ((mirrored ? -1 : 1) * (facing == 3 ? 1 : -1))),
                    yCoord + 1 + .25,
                    zCoord
                        + .5
                        + (facing == 2
                            ? 1
                            : facing == 3 ? -1 : ((mirrored ? -1 : 1) * (facing == 4 ? 1 : -1))),
                    output.copy());
            ei.motionX = (0.075F * fd.offsetX);
            ei.motionY = 0.025000000372529D;
            ei.motionZ = (0.075F * fd.offsetZ);
            this.worldObj.spawnEntityInWorld(ei);
          }
          process[i] = -1;
          inventory[i] = null;
          predictedOutput[i] = null;
          update = true;
        }
      }

    if (update) {
      this.markDirty();
      worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
    }
  }
  @Override
  public void onUpdate() {
    if (this.ticksExisted == 1 && !worldObj.isRemote)
      IELogger.debug("init tick at " + System.currentTimeMillis());
    super.onUpdate();
    //		if(this.ticksExisted>40)
    //			this.setDead();
    if (worldObj.isRemote) return;

    EntityPlayer player = null;
    if (this.riddenByEntity instanceof EntityPlayer) player = ((EntityPlayer) this.riddenByEntity);

    if (subPoints != null && targetPoint < subPoints.length - 1) {
      double dist = subPoints[targetPoint].distanceTo(Vec3.createVectorHelper(posX, posY, posZ));
      IELogger.debug("dist: " + dist);
      if (dist <= .05) {
        this.posX = subPoints[targetPoint].xCoord;
        this.posY = subPoints[targetPoint].yCoord;
        this.posZ = subPoints[targetPoint].zCoord;
        targetPoint++;
        IELogger.debug("next vertex: " + targetPoint);
        //				double dx = (subPoints[targetPoint].xCoord-posX);//connection.length;
        //				double dy = (subPoints[targetPoint].yCoord-posY);//connection.length;
        //				double dz = (subPoints[targetPoint].zCoord-posZ);//connection.length;
        //				Vec3 moveVec = Vec3.createVectorHelper(dx,dy,dz);
        float speed = 3f;
        if (player != null
            && player.getCurrentEquippedItem() != null
            && player.getCurrentEquippedItem().getItem() instanceof ItemSkyhook)
          speed =
              ((ItemSkyhook) player.getCurrentEquippedItem().getItem())
                  .getSkylineSpeed(player.getCurrentEquippedItem());
        Vec3 moveVec =
            SkylineHelper.getSubMovementVector(
                Vec3.createVectorHelper(posX, posY, posZ), subPoints[targetPoint], speed);
        motionX = moveVec.xCoord; // *speed;
        motionY = moveVec.yCoord; // *speed;
        motionZ = moveVec.zCoord; // *speed;
        return;
      }
    }

    if (target != null && targetPoint == subPoints.length - 1) {
      TileEntity end = this.worldObj.getTileEntity(target.posX, target.posY, target.posZ);
      IImmersiveConnectable iicEnd = Utils.toIIC(end, worldObj);
      if (iicEnd == null) {
        this.setDead();
        return;
      }
      Vec3 vEnd = Vec3.createVectorHelper(target.posX, target.posY, target.posZ);
      vEnd = Utils.addVectors(vEnd, iicEnd.getConnectionOffset(connection));

      double gDist = vEnd.distanceTo(Vec3.createVectorHelper(posX, posY, posZ));
      IELogger.debug("distance to goal: " + gDist);
      if (gDist <= .3) {
        reachedTarget(end);
        return;
      } else if (gDist > 5) {
        setDead();
        return;
      }
    }
    this.posX += this.motionX;
    this.posY += this.motionY;
    this.posZ += this.motionZ;
    float f1 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
    this.rotationYaw = (float) (Math.atan2(this.motionZ, this.motionX) * 180.0D / Math.PI) + 90.0F;

    for (this.rotationPitch =
            (float) (Math.atan2((double) f1, this.motionY) * 180.0D / Math.PI) - 90.0F;
        this.rotationPitch - this.prevRotationPitch < -180.0F;
        this.prevRotationPitch -= 360.0F) ;

    while (this.rotationPitch - this.prevRotationPitch >= 180.0F) this.prevRotationPitch += 360.0F;
    while (this.rotationYaw - this.prevRotationYaw < -180.0F) this.prevRotationYaw -= 360.0F;
    while (this.rotationYaw - this.prevRotationYaw >= 180.0F) this.prevRotationYaw += 360.0F;

    this.rotationPitch =
        this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
    this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;

    if (this.isInWater()) {
      for (int j = 0; j < 4; ++j) {
        float f3 = 0.25F;
        this.worldObj.spawnParticle(
            "bubble",
            this.posX - this.motionX * (double) f3,
            this.posY - this.motionY * (double) f3,
            this.posZ - this.motionZ * (double) f3,
            this.motionX,
            this.motionY,
            this.motionZ);
      }
    }

    this.setPosition(this.posX, this.posY, this.posZ);
  }
  public static void renderAllIEConnections(float partial) {
    if (connectionsRendered) return;
    GL11.glPushMatrix();

    GL11.glDisable(GL11.GL_CULL_FACE);
    //		GL11.glDisable(GL11.GL_TEXTURE_2D);
    GL11.glEnable(GL11.GL_BLEND);
    GL11.glDisable(GL11.GL_ALPHA_TEST);
    OpenGlHelper.glBlendFunc(770, 771, 1, 0);
    GL11.glShadeModel(GL11.GL_SMOOTH);
    RenderHelper.enableStandardItemLighting();

    Tessellator.instance.startDrawing(GL11.GL_QUADS);

    EntityLivingBase viewer = ClientUtils.mc().renderViewEntity;
    double dx =
        viewer.lastTickPosX
            + (viewer.posX - viewer.lastTickPosX) * partial; // (double)event.partialTicks;
    double dy =
        viewer.lastTickPosY
            + (viewer.posY - viewer.lastTickPosY) * partial; // (double)event.partialTicks;
    double dz =
        viewer.lastTickPosZ
            + (viewer.posZ - viewer.lastTickPosZ) * partial; // (double)event.partialTicks;

    for (Object o : ClientUtils.mc().renderGlobal.tileEntities)
      if (o instanceof IImmersiveConnectable) {
        TileEntity tile = (TileEntity) o;
        //				int lb = tile.getWorldObj().getLightBrightnessForSkyBlocks(tile.xCoord, tile.yCoord,
        // tile.zCoord, 0);
        //				int lb_j = lb % 65536;
        //				int lb_k = lb / 65536;
        //				OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)lb_j /
        // 1.0F, (float)lb_k / 1.0F);

        Tessellator.instance.setTranslation(tile.xCoord - dx, tile.yCoord - dy, tile.zCoord - dz);
        //				GL11.glTranslated((tile.xCoord+.5-dx), (tile.yCoord+.5-dy), (tile.zCoord+.5-dz));
        ClientUtils.renderAttachedConnections((TileEntity) tile);
        //				GL11.glTranslated(-(tile.xCoord+.5-dx), -(tile.yCoord+.5-dy), -(tile.zCoord+.5-dz));

      }

    Iterator<ImmersiveNetHandler.Connection> it = skyhookGrabableConnections.iterator();
    World world = viewer.worldObj;
    while (it.hasNext()) {
      ImmersiveNetHandler.Connection con = it.next();
      Tessellator.instance.setTranslation(
          con.start.posX - dx, con.start.posY - dy, con.start.posZ - dz);
      double r = con.cableType.getRenderDiameter() / 2;
      ClientUtils.drawConnection(
          con, Utils.toIIC(con.start, world), Utils.toIIC(con.end, world), 0x00ff99, 128, r * 1.75);
    }

    Tessellator.instance.setTranslation(0, 0, 0);
    Tessellator.instance.draw();

    GL11.glDisable(GL11.GL_BLEND);
    GL11.glEnable(GL11.GL_ALPHA_TEST);
    GL11.glEnable(GL11.GL_TEXTURE_2D);
    GL11.glEnable(GL11.GL_CULL_FACE);

    GL11.glPopMatrix();
    connectionsRendered = true;
  }