package micdoodle8.mods.galacticraft.core.util; import micdoodle8.mods.galacticraft.core.GCFluids; import micdoodle8.mods.galacticraft.core.GCItems; import micdoodle8.mods.galacticraft.core.items.ItemCanisterGeneric; import micdoodle8.mods.galacticraft.core.tile.TileEntityFluidTank; import micdoodle8.mods.galacticraft.planets.asteroids.items.AsteroidsItems; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.BlockPos; import net.minecraft.util.EnumFacing; import net.minecraft.util.MathHelper; import net.minecraftforge.common.ForgeModContainer; import net.minecraftforge.fluids.*; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.items.ItemHandlerHelper; import java.lang.reflect.Field; import java.lang.reflect.Method; public class FluidUtil { private static boolean oldFluidIDMethod = true; private static Class<?> fluidStackClass = null; private static Method getFluidMethod = null; private static Field fluidIdField = null; /** * This looks for any type of filled or partly filled container of "fuel" * * @param var4 The ItemStack being examined * @return True if it's a container with "fuel" */ public static boolean isFuelContainerAny(ItemStack var4) { if (var4.getItem() instanceof ItemCanisterGeneric) { return var4.getItem() == GCItems.fuelCanister && var4.getItemDamage() < var4.getMaxDamage(); } FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(var4); return liquid != null && FluidUtil.testFuel(FluidRegistry.getFluidName(liquid)); } /** * Tests whether the named liquid is fuel / fuelgc * or rocket fuel from another mod, for compatibility (includes RotaryCraft Jet Fuel) * * @param name - which MUST be in lowercase, as always returned by FluidRegistry.getFluidName * @return true if a type of recognised fuel, false if not */ public static boolean testFuel(String name) { if (name.startsWith("fuel")) { return true; } if (name.contains("rocket") && name.contains("fuel")) { return true; } if (name.equals("rc jet fuel")) { return true; } return false; } public static boolean isFuel(FluidStack fluid) { return fluid != null && testFuel(FluidRegistry.getFluidName(fluid)); } public static boolean isFluidFuzzy(FluidStack fluid, String name) { return fluid != null && fluid.getFluid() != null && fluid.getFluid().getName().startsWith(name); } public static boolean isFluidStrict(FluidStack fluid, String name) { return fluid != null && fluid.getFluid() != null && fluid.getFluid().getName().equals(name); } /** * Fill Galacticraft entities (e.g. rockets, buggies) with Galacticraft fuel. * For legacy reasons, accepts either "fuel" or "fuelgc". * Auto-converts either one to the same type of fuel as is already contained. * * @param tank The tank * @param liquid A FluidStack being the fuel offered * @param doFill True if this is not a simulation / tank capacity test * @return The amount filled */ public static int fillWithGCFuel(FluidTank tank, FluidStack liquid, boolean doFill) { if (liquid != null && testFuel(FluidRegistry.getFluidName(liquid))) { final FluidStack liquidInTank = tank.getFluid(); //If the tank is empty, fill it with the current type of GC fuel if (liquidInTank == null) { return tank.fill(new FluidStack(GCFluids.fluidFuel, liquid.amount), doFill); } //If the tank already contains something, fill it with more of the same if (liquidInTank.amount < tank.getCapacity()) { return tank.fill(new FluidStack(liquidInTank, liquid.amount), doFill); } } return 0; } /** * This looks for any type of filled or partly filled container of "oil" * or the alternative fluid "oilgc" which may be present if Buildcraft was installed on top of a GC world * * @param var4 The ItemStack being examined * @return True if it's a container with "oil" */ public static boolean isOilContainerAny(ItemStack var4) { if (var4.getItem() instanceof ItemCanisterGeneric) { return var4.getItem() == GCItems.oilCanister && var4.getItemDamage() < var4.getMaxDamage(); } FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(var4); return liquid != null && FluidRegistry.getFluidName(liquid).startsWith("oil"); } /** * This looks for any type of filled or partly filled container of "methane" gas * * @param var4 The ItemStack being examined * @return True if it's a container with "methane" */ public static boolean isMethaneContainerAny(ItemStack var4) { if (var4.getItem() instanceof ItemCanisterGeneric) { return var4.getItem() == AsteroidsItems.methaneCanister && var4.getItemDamage() < var4.getMaxDamage(); } FluidStack stack = FluidContainerRegistry.getFluidForFilledItem(var4); return stack != null && stack.getFluid() != null && stack.getFluid().getName().toLowerCase().contains("methane"); } /** * This looks for any type of completely full container * Used, for example, in canExtractItem() logic * * @param var4 The ItemStack being examined * @return True if it's a full container */ public static boolean isFullContainer(ItemStack var4) { if (var4.getItem() instanceof ItemCanisterGeneric) { return var4.getItemDamage() == 1; } FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(var4); return liquid != null; } /** * This tries to fill the given container (at inventory[slot]) with fluid from the specified tank * If successful, it places the resulting filled container in inventory[slot] * <p> * Note: this deals with the issue where FluidContainerRegistry.fillFluidContainer() returns null for failed fills * * @param tank The tank to take the fluid from * @param liquid The type of liquid in that tank (the calling method will normally have checked this already) * @param inventory * @param slot * @param canisterType The type of canister to return, if it's a canister being filled (pre-matched with the liquid type) */ public static void tryFillContainer(FluidTank tank, FluidStack liquid, ItemStack[] inventory, int slot, Item canisterType) { ItemStack slotItem = inventory[slot]; boolean isCanister = slotItem.getItem() instanceof ItemCanisterGeneric; final int amountToFill = Math.min(liquid.amount, isCanister ? slotItem.getItemDamage() - 1 : FluidContainerRegistry.BUCKET_VOLUME); if (amountToFill <= 0 || (isCanister && slotItem.getItem() != canisterType && slotItem.getItemDamage() != ItemCanisterGeneric.EMPTY)) { return; } if (isCanister) { inventory[slot] = new ItemStack(canisterType, 1, slotItem.getItemDamage() - amountToFill); tank.drain(amountToFill, true); } else if (amountToFill == FluidContainerRegistry.BUCKET_VOLUME) { inventory[slot] = FluidContainerRegistry.fillFluidContainer(liquid, inventory[slot]); if (inventory[slot] == null) { //Failed to fill container: restore item that was there before inventory[slot] = slotItem; } else { tank.drain(amountToFill, true); } } } /** * @param tank * @param inventory * @param slot */ public static void tryFillContainerFuel(FluidTank tank, ItemStack[] inventory, int slot) { if (FluidUtil.isValidContainer(inventory[slot])) { FluidStack liquid = tank.getFluid(); if (liquid != null && liquid.amount > 0) { String liquidname = liquid.getFluid().getName(); //Test for the GC fuels (though anything similarly named would also pass here) if (liquidname.startsWith("fuel")) { //Make sure it is the current GC fuel if (!liquidname.equals(GCFluids.fluidFuel.getName())) { liquid = new FluidStack(GCFluids.fluidFuel, liquid.amount); } //But match any existing fuel fluid in the container ItemStack stack = inventory[slot]; //(No null check necessary here: it cannot be a null ItemStack thanks to the .isValidContainer() check above if (stack.getItem() instanceof IFluidContainerItem) { FluidStack existingFluid = ((IFluidContainerItem) stack.getItem()).getFluid(stack); if (existingFluid != null && !existingFluid.getFluid().getName().equals(GCFluids.fluidFuel.getName())) { liquid = new FluidStack(existingFluid, liquid.amount); } } FluidUtil.tryFillContainer(tank, liquid, inventory, slot, GCItems.fuelCanister); } } } } /** * This tries to empty the container (at inventory[slot]) into the specified tank * forcing the tank contents to Fluid: desiredLiquid even if the container had * something different (useful for different types of fuel, for example). * If successful, it replaces inventory[slot] with the corresponding empty container * <p> * * @param tank The tank to fill with the fluid * @param desiredLiquid The type of liquid intended for that tank * @param inventory * @param slot * @param amountOffered The amount in the container being offered */ public static void loadFromContainer(FluidTank tank, Fluid desiredLiquid, ItemStack[] inventory, int slot, int amountOffered) { ItemStack slotItem = inventory[slot]; if (slotItem.getItem() instanceof ItemCanisterGeneric) { int originalDamage = slotItem.getItemDamage(); int used = tank.fill(new FluidStack(desiredLiquid, ItemCanisterGeneric.EMPTY - originalDamage), true); if (originalDamage + used >= ItemCanisterGeneric.EMPTY) { inventory[slot] = new ItemStack(GCItems.oilCanister, 1, ItemCanisterGeneric.EMPTY); } else { inventory[slot] = new ItemStack(slotItem.getItem(), 1, originalDamage + used); } } else { if (tank.getFluid() == null || amountOffered <= tank.getCapacity() - tank.getFluid().amount) { tank.fill(new FluidStack(desiredLiquid, amountOffered), true); if (FluidContainerRegistry.isFilledContainer(slotItem)) { final int bucketCount = slotItem.stackSize; if (FluidContainerRegistry.isBucket(slotItem)) { if (bucketCount > 1) { tank.fill(new FluidStack(desiredLiquid, (bucketCount - 1) * FluidContainerRegistry.BUCKET_VOLUME), true); } inventory[slot] = new ItemStack(Items.bucket, bucketCount); } else { ItemStack emptyStack = FluidContainerRegistry.drainFluidContainer(slotItem); if (bucketCount > 1) { tank.fill(new FluidStack(desiredLiquid, (bucketCount - 1) * FluidContainerRegistry.getContainerCapacity(slotItem)), true); if (emptyStack != null) { emptyStack.stackSize = bucketCount; } } inventory[slot] = emptyStack; } } else { slotItem.stackSize--; if (slotItem.stackSize == 0) { inventory[slot] = null; } } } } } /** * Tests for any type of container with some space in it * It can be either an empty container, or a Galacticraft canister * of the appropriate type, either empty or at least with some capacity remaining * Will return true for any type of empty Forge fluid container including vanilla buckets * * @param var4 * @param canisterType * @return */ public static boolean isEmptyContainer(ItemStack var4, Item canisterType) { if (var4.getItem() instanceof ItemCanisterGeneric) { return var4.getItemDamage() == ItemCanisterGeneric.EMPTY || (var4.getItem() == canisterType && var4.getItemDamage() > 1); } return FluidContainerRegistry.isEmptyContainer(var4); } /** * Tests for any empty container which can accept the specified fluid * Either Galacticraft canisters or Forge containers * * @param var4 * @param canisterType * @return */ public static boolean isEmptyContainerFor(ItemStack var4, FluidStack targetFluid) { if (var4.getItem() instanceof ItemCanisterGeneric) { if (var4.getItemDamage() == ItemCanisterGeneric.EMPTY) { return true; } if (var4.getItemDamage() == 1) { return false; } return fluidsSame(((ItemCanisterGeneric) var4.getItem()).getFluid(var4), targetFluid); } if (FluidContainerRegistry.isEmptyContainer(var4)) { return true; } return fluidsSame(FluidContainerRegistry.getFluidForFilledItem(var4), targetFluid); } /** * @param fs1 First FluidStack to compare * @param fs2 Second FluidStack to compare * @return True if the FluidStacks are both not null and the same fluid type * False otherwise */ public static boolean fluidsSame(FluidStack fs1, FluidStack fs2) { if (fs1 == null || fs2 == null) { return false; } Fluid f1 = fs1.getFluid(); Fluid f2 = fs2.getFluid(); if (f1 == null || f2 == null || f1.getName() == null) { return false; } return f1.getName().equals(f2.getName()); } /** * Test for any completely empty container of either type * Used, for example, in canExtractItem() logic * * @param var4 The ItemStack being tested * @return True if the container is empty */ public static boolean isEmptyContainer(ItemStack var4) { if (var4.getItem() instanceof ItemCanisterGeneric) { return var4.getItemDamage() == ItemCanisterGeneric.EMPTY; } return FluidContainerRegistry.isEmptyContainer(var4); } /** * Test for any empty specialist gas container * For future use, e.g. for compatibility with Mekanism tank items * * @param var4 The ItemStack being tested * @return True if the container is empty */ public static boolean isEmptyGasContainer(ItemStack var4) { return false; } /** * Test for any container with some level of contents * Used, for example, in shift-clicking items into input item slots * * @param var4 The ItemStack being tested * @return True if the container contains something */ public static boolean isFilledContainer(ItemStack var4) { if (var4.getItem() instanceof ItemCanisterGeneric) { return var4.getItemDamage() < ItemCanisterGeneric.EMPTY; } return FluidContainerRegistry.getFluidForFilledItem(var4) != null; } /** * Test for any container with water * (vanilla or Forge items only, Galacticraft does not have a water container) * * @param var4 The ItemStack being tested * @return True if the container contains water */ public static boolean isWaterContainer(ItemStack var4) { FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(var4); return liquid != null && liquid.getFluid() != null && liquid.getFluid().getName().equals("water"); } /** * Test for any container type at all * Used, for example, in isItemValidForSlot() logic * * @param slotItem * @return True if it is a container; False if it is null or not a container */ public static boolean isValidContainer(ItemStack slotItem) { return slotItem != null && slotItem.stackSize == 1 && (slotItem.getItem() instanceof ItemCanisterGeneric || FluidContainerRegistry.isContainer(slotItem)); } /** * Returns the used (empty) container, for example an empty bucket * Used, for example, in isItemValidForSlot() logic * * @param slotItem * @return True if it is a container; False if it is null or not a container */ public static ItemStack getUsedContainer(ItemStack container) { if (FluidContainerRegistry.isBucket(container) && FluidContainerRegistry.isFilledContainer(container)) { return new ItemStack(Items.bucket, container.stackSize); } else { container.stackSize--; if (container.stackSize == 0) { return null; } return container; } } public static FluidStack getFluidContained(ItemStack container) { if (container == null) { return null; } if (container.getItem() instanceof ItemCanisterGeneric) { ItemCanisterGeneric canister = (ItemCanisterGeneric) container.getItem(); return new FluidStack(FluidRegistry.getFluid(canister.getAllowedFluid()), ItemCanisterGeneric.EMPTY - container.getItemDamage()); } return FluidContainerRegistry.getFluidForFilledItem(container); } @SideOnly(Side.CLIENT) public static boolean isInsideOfFluid(Entity entity, Fluid fluid) { double d0 = entity.posY + entity.getEyeHeight(); int i = MathHelper.floor_double(entity.posX); int j = MathHelper.floor_float(MathHelper.floor_double(d0)); int k = MathHelper.floor_double(entity.posZ); BlockPos pos = new BlockPos(i, j, k); Block block = entity.worldObj.getBlockState(pos).getBlock(); if (block != null && block instanceof IFluidBlock && ((IFluidBlock) block).getFluid() != null && ((IFluidBlock) block).getFluid().getName().equals(fluid.getName())) { double filled = ((IFluidBlock) block).getFilledPercentage(entity.worldObj, pos); if (filled < 0) { filled *= -1; return d0 > j + (1 - filled); } else { return d0 < j + filled; } } else { return false; } } public static boolean interactWithTank(ItemStack container, EntityPlayer playerIn, TileEntityFluidTank tank, EnumFacing side) { if (container == null || playerIn.worldObj.isRemote) { return true; } ItemStack result; if (container.getItem() instanceof ItemCanisterGeneric) { if ((result = FluidUtil.tryEmptyCanister(container, tank, side)) != null || (result = FluidUtil.tryFillCanister(container, tank, side)) != null) { // send inventory updates to client if (playerIn.inventoryContainer != null) { playerIn.inventoryContainer.detectAndSendChanges(); } } return true; } //The following code deals with a bug in 1.8.9 (filling a bucket from a tank with less than 1000mB returns an empty bucket, but clears out the tank) //This code may not be required in 1.10.2+ int slot = playerIn.inventory.currentItem; if ((result = FluidUtil.tryFillBucket(container, tank, side)) != null || (result = net.minecraftforge.fluids.FluidUtil.tryEmptyBucket(container, tank, side)) != null) { if (!playerIn.capabilities.isCreativeMode) { playerIn.inventory.decrStackSize(slot, 1); ItemHandlerHelper.giveItemToPlayer(playerIn, result, slot); } if (playerIn.inventoryContainer != null) { playerIn.inventoryContainer.detectAndSendChanges(); } return true; } else { // Code for IFluidContainerItems - unchanged from Forge ItemStack copy = container.copy(); boolean changedBucket = false; if (ItemStack.areItemsEqual(container, FluidContainerRegistry.EMPTY_BUCKET) && FluidRegistry.isUniversalBucketEnabled()) { container = new ItemStack(ForgeModContainer.getInstance().universalBucket, copy.stackSize); changedBucket = true; } if (net.minecraftforge.fluids.FluidUtil.tryFillFluidContainerItem(container, tank, side, playerIn) || net.minecraftforge.fluids.FluidUtil.tryEmptyFluidContainerItem(container, tank, side, playerIn)) { if (playerIn.capabilities.isCreativeMode) { playerIn.inventory.setInventorySlotContents(slot, copy); } else { if (changedBucket && container.stackSize != copy.stackSize) { copy.stackSize = container.stackSize; playerIn.inventory.setInventorySlotContents(slot, copy); } else { if (copy.stackSize > 1) { playerIn.inventory.setInventorySlotContents(slot, container); } else { playerIn.inventory.setInventorySlotContents(slot, null); ItemHandlerHelper.giveItemToPlayer(playerIn, container, slot); } } } if (playerIn.inventoryContainer != null) { playerIn.inventoryContainer.detectAndSendChanges(); } return true; } } return false; } /** * The Forge original version of this is bugged! (Drains tank even if less than 1000mB in tank) */ private static ItemStack tryFillBucket(ItemStack bucket, TileEntityFluidTank tank, EnumFacing side) { if (!FluidContainerRegistry.isEmptyContainer(bucket)) { return null; } FluidTankInfo[] info = tank.getTankInfo(side); if (info == null || info.length == 0) { return null; } int capacity = FluidContainerRegistry.getContainerCapacity(info[0].fluid, bucket); FluidStack liquid = tank.drain(side, capacity, false); if (liquid != null && liquid.amount == capacity) //Only proceed and drain the tank if it can actually fill this bucket { tank.drain(side, capacity, true); return FluidContainerRegistry.fillFluidContainer(liquid, bucket); } return null; } /** * This is how to do it properly. Nice and simple, and high performance. */ private static ItemStack tryFillCanister(ItemStack canister, TileEntityFluidTank tank, EnumFacing side) { int currCapacity = canister.getItemDamage() - 1; if (currCapacity <= 0) { return null; } FluidStack liquid = tank.drain(side, currCapacity, false); int transferred = ((ItemCanisterGeneric)canister.getItem()).fill(canister, liquid, true); if (transferred > 0) { liquid = tank.drain(side, transferred, true); return canister; } return null; } private static ItemStack tryEmptyCanister(ItemStack canister, TileEntityFluidTank tank, EnumFacing side) { int currContents = ItemCanisterGeneric.EMPTY - canister.getItemDamage(); if (currContents <= 0) { return null; } FluidStack liquid = ((ItemFluidContainer)canister.getItem()).drain(canister, currContents, false); int transferred = tank.fill(side, liquid, true); if (transferred > 0) { ((ItemFluidContainer)canister.getItem()).drain(canister, transferred, true); return canister; } return null; } }