/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fluids;
import net.minecraft.block.BlockDispenser;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.stats.StatList;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.text.translation.I18n;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.event.entity.player.FillBucketEvent;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
import net.minecraftforge.fluids.capability.wrappers.FluidBucketWrapper;
import net.minecraftforge.fml.common.eventhandler.Event;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.items.ItemHandlerHelper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* A universal bucket that can hold any liquid
*/
public class UniversalBucket extends Item
{
private final int capacity; // how much the bucket holds
@Nonnull
private final ItemStack empty; // empty item to return and recognize when filling
private final boolean nbtSensitive;
public UniversalBucket()
{
this(Fluid.BUCKET_VOLUME, new ItemStack(Items.BUCKET), false);
}
/**
* @param capacity Capacity of the container
* @param empty Item used for filling with the bucket event and returned when emptied
* @param nbtSensitive Whether the empty item is NBT sensitive (usually true if empty and full are the same items)
*/
public UniversalBucket(int capacity, @Nonnull ItemStack empty, boolean nbtSensitive)
{
this.capacity = capacity;
this.empty = empty;
this.nbtSensitive = nbtSensitive;
this.setMaxStackSize(1);
this.setCreativeTab(CreativeTabs.MISC);
BlockDispenser.DISPENSE_BEHAVIOR_REGISTRY.putObject(this, DispenseFluidContainer.getInstance());
}
@Override
public boolean hasContainerItem(@Nonnull ItemStack stack)
{
return !getEmpty().isEmpty();
}
@Nonnull
@Override
public ItemStack getContainerItem(@Nonnull ItemStack itemStack)
{
if (!getEmpty().isEmpty())
{
// Create a copy such that the game can't mess with it
return getEmpty().copy();
}
return super.getContainerItem(itemStack);
}
@SideOnly(Side.CLIENT)
@Override
public void getSubItems(@Nonnull Item itemIn, @Nullable CreativeTabs tab, @Nonnull NonNullList<ItemStack> subItems)
{
for (Fluid fluid : FluidRegistry.getRegisteredFluids().values())
{
if (fluid != FluidRegistry.WATER && fluid != FluidRegistry.LAVA && !fluid.getName().equals("milk"))
{
// add all fluids that the bucket can be filled with
FluidStack fs = new FluidStack(fluid, getCapacity());
ItemStack stack = new ItemStack(this);
IFluidHandlerItem fluidHandler = new FluidBucketWrapper(stack);
if (fluidHandler.fill(fs, true) == fs.amount)
{
ItemStack filled = fluidHandler.getContainer();
subItems.add(filled);
}
}
}
}
@Override
@Nonnull
public String getItemStackDisplayName(@Nonnull ItemStack stack)
{
FluidStack fluidStack = getFluid(stack);
if (fluidStack == null)
{
if(!getEmpty().isEmpty())
{
return getEmpty().getDisplayName();
}
return super.getItemStackDisplayName(stack);
}
String unloc = this.getUnlocalizedNameInefficiently(stack);
if (I18n.canTranslate(unloc + "." + fluidStack.getFluid().getName()))
{
return I18n.translateToLocal(unloc + "." + fluidStack.getFluid().getName());
}
return I18n.translateToLocalFormatted(unloc + ".name", fluidStack.getLocalizedName());
}
@Override
@Nonnull
public ActionResult<ItemStack> onItemRightClick(@Nonnull World world, @Nonnull EntityPlayer player, @Nonnull EnumHand hand)
{
ItemStack itemstack = player.getHeldItem(hand);
FluidStack fluidStack = getFluid(itemstack);
// empty bucket shouldn't exist, do nothing since it should be handled by the bucket event
if (fluidStack == null)
{
return ActionResult.newResult(EnumActionResult.PASS, itemstack);
}
// clicked on a block?
RayTraceResult mop = this.rayTrace(world, player, false);
if(mop == null || mop.typeOfHit != RayTraceResult.Type.BLOCK)
{
return ActionResult.newResult(EnumActionResult.PASS, itemstack);
}
BlockPos clickPos = mop.getBlockPos();
// can we place liquid there?
if (world.isBlockModifiable(player, clickPos))
{
// the block adjacent to the side we clicked on
BlockPos targetPos = clickPos.offset(mop.sideHit);
// can the player place there?
if (player.canPlayerEdit(targetPos, mop.sideHit, itemstack))
{
// try placing liquid
FluidActionResult result = FluidUtil.tryPlaceFluid(player, world, targetPos, itemstack, fluidStack);
if (result.isSuccess() && !player.capabilities.isCreativeMode)
{
// success!
player.addStat(StatList.getObjectUseStats(this));
itemstack.shrink(1);
ItemStack drained = result.getResult();
ItemStack emptyStack = !drained.isEmpty() ? drained.copy() : new ItemStack(this);
// check whether we replace the item or add the empty one to the inventory
if (itemstack.isEmpty())
{
return ActionResult.newResult(EnumActionResult.SUCCESS, emptyStack);
}
else
{
// add empty bucket to player inventory
ItemHandlerHelper.giveItemToPlayer(player, emptyStack);
return ActionResult.newResult(EnumActionResult.SUCCESS, itemstack);
}
}
}
}
// couldn't place liquid there2
return ActionResult.newResult(EnumActionResult.FAIL, itemstack);
}
@SubscribeEvent(priority = EventPriority.LOW) // low priority so other mods can handle their stuff first
public void onFillBucket(FillBucketEvent event)
{
if (event.getResult() != Event.Result.DEFAULT)
{
// event was already handled
return;
}
// not for us to handle
ItemStack emptyBucket = event.getEmptyBucket();
if (emptyBucket.isEmpty() ||
!emptyBucket.isItemEqual(getEmpty()) ||
(isNbtSensitive() && ItemStack.areItemStackTagsEqual(emptyBucket, getEmpty())))
{
return;
}
// needs to target a block
RayTraceResult target = event.getTarget();
if (target == null || target.typeOfHit != RayTraceResult.Type.BLOCK)
{
return;
}
World world = event.getWorld();
BlockPos pos = target.getBlockPos();
ItemStack singleBucket = emptyBucket.copy();
singleBucket.setCount(1);
FluidActionResult filledResult = FluidUtil.tryPickUpFluid(singleBucket, event.getEntityPlayer(), world, pos, target.sideHit);
if (filledResult.isSuccess())
{
event.setResult(Event.Result.ALLOW);
event.setFilledBucket(filledResult.getResult());
}
else
{
// cancel event, otherwise the vanilla minecraft ItemBucket would
// convert it into a water/lava bucket depending on the blocks material
event.setCanceled(true);
}
}
@Nonnull
public static ItemStack getFilledBucket(@Nonnull UniversalBucket item, Fluid fluid)
{
ItemStack bucket = new ItemStack(item);
if (FluidRegistry.getBucketFluids().contains(fluid))
{
// fill the container
NBTTagCompound tag = new NBTTagCompound();
FluidStack fluidStack = new FluidStack(fluid, item.getCapacity());
fluidStack.writeToNBT(tag);
bucket.setTagCompound(tag);
}
else if (fluid == FluidRegistry.WATER)
{
return new ItemStack(Items.WATER_BUCKET);
}
else if (fluid == FluidRegistry.LAVA)
{
return new ItemStack(Items.LAVA_BUCKET);
}
return bucket;
}
@Nullable
public FluidStack getFluid(@Nonnull ItemStack container)
{
return FluidStack.loadFluidStackFromNBT(container.getTagCompound());
}
public int getCapacity()
{
return capacity;
}
@Nonnull
public ItemStack getEmpty()
{
return empty;
}
public boolean isNbtSensitive()
{
return nbtSensitive;
}
@Override
public ICapabilityProvider initCapabilities(@Nonnull ItemStack stack, NBTTagCompound nbt)
{
return new FluidBucketWrapper(stack);
}
}