diff --git a/src/main/java/net/minecraftforge/client/model/ModelLoader.java b/src/main/java/net/minecraftforge/client/model/ModelLoader.java index b565ce713..453b1c1b3 100644 --- a/src/main/java/net/minecraftforge/client/model/ModelLoader.java +++ b/src/main/java/net/minecraftforge/client/model/ModelLoader.java @@ -220,6 +220,13 @@ public class ModelLoader extends ModelBakery private void loadItems() { registerVariantNames(); + + // register model for the universal bucket, if it exists + if(FluidRegistry.isUniversalBucketEnabled()) + { + setBucketModelDefinition(ForgeModContainer.getInstance().universalBucket); + } + List itemVariants = Lists.newArrayList(); for(Item item : GameData.getItemRegistry().typeSafeIterable()) { diff --git a/src/main/java/net/minecraftforge/common/ForgeModContainer.java b/src/main/java/net/minecraftforge/common/ForgeModContainer.java index 7f58f9625..dfe2346f5 100644 --- a/src/main/java/net/minecraftforge/common/ForgeModContainer.java +++ b/src/main/java/net/minecraftforge/common/ForgeModContainer.java @@ -27,6 +27,8 @@ import net.minecraftforge.common.config.Property; import net.minecraftforge.common.network.ForgeNetworkHandler; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.items.CapabilityItemHandler; +import net.minecraftforge.fluids.UniversalBucket; +import net.minecraftforge.fml.common.registry.GameRegistry; import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.RecipeSorter; import net.minecraftforge.server.command.ForgeCommand; @@ -80,6 +82,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC } private URL updateJSONUrl = null; + public UniversalBucket universalBucket; public ForgeModContainer() { @@ -320,6 +323,15 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC { ForgeVersion.startVersionCheck(); } + + // Add and register the forge universal bucket, if it's enabled + if(FluidRegistry.isUniversalBucketEnabled()) + { + universalBucket = new UniversalBucket(); + universalBucket.setUnlocalizedName("forge.bucketFilled"); + GameRegistry.registerItem(universalBucket, "bucketFilled"); + MinecraftForge.EVENT_BUS.register(universalBucket); + } } @Subscribe diff --git a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java index b1a2e6f22..d04e33582 100644 --- a/src/main/java/net/minecraftforge/fluids/FluidRegistry.java +++ b/src/main/java/net/minecraftforge/fluids/FluidRegistry.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import net.minecraftforge.fml.common.LoaderState; import org.apache.logging.log4j.Level; import net.minecraft.block.Block; @@ -19,6 +20,7 @@ import com.google.common.base.Strings; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -48,6 +50,9 @@ public abstract class FluidRegistry static BiMap defaultFluidName = HashBiMap.create(); static Map delegates = Maps.newHashMap(); + static boolean universalBucketEnabled = false; + static Set bucketFluids = Sets.newHashSet(); + public static final Fluid WATER = new Fluid("water", new ResourceLocation("blocks/water_still"), new ResourceLocation("blocks/water_flow")) { @Override public String getLocalizedName(FluidStack fs) { @@ -247,6 +252,57 @@ public abstract class FluidRegistry return ImmutableMap.copyOf(fluidIDs); } + /** + * Enables the universal bucket in forge. + * Has to be called before pre-initialization. + * Actually just call it statically in your mod class. + */ + public static void enableUniversalBucket() + { + if (Loader.instance().hasReachedState(LoaderState.PREINITIALIZATION)) + { + FMLLog.getLogger().log(Level.ERROR, "Trying to activate the universal filled bucket too late. Call it statically in your Mods class. Mod: {}", Loader.instance().activeModContainer().getName()); + } + else + { + universalBucketEnabled = true; + } + } + + public static boolean isUniversalBucketEnabled() + { + return universalBucketEnabled; + } + + /** + * Registers a fluid with the universal bucket. + * This only has an effect if the universal bucket is enabled. + * @param fluid The fluid that the bucket shall be able to hold + * @return True if the fluid was added successfully, false if it already was registered or couldn't be registered with the bucket. + */ + public static boolean addBucketForFluid(Fluid fluid) + { + if(fluid == null) { + return false; + } + // register unregistered fluids + if (!isFluidRegistered(fluid)) + { + registerFluid(fluid); + } + return bucketFluids.add(fluid); + } + + /** + * All fluids registered with the universal bucket + * @return An immutable set containing the fluids + */ + public static Set getBucketFluids() + { + return ImmutableSet.copyOf(bucketFluids); + } + + public static Fluid lookupFluidForBlock(Block block) { if (fluidBlocks == null) diff --git a/src/main/java/net/minecraftforge/fluids/FluidUtil.java b/src/main/java/net/minecraftforge/fluids/FluidUtil.java new file mode 100644 index 000000000..7368df3a9 --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/FluidUtil.java @@ -0,0 +1,563 @@ +package net.minecraftforge.fluids; + +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ReportedException; +import net.minecraft.world.World; +import net.minecraftforge.common.ForgeModContainer; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.ItemHandlerHelper; +import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; + +import java.util.concurrent.Callable; + +public class FluidUtil +{ + + private FluidUtil() + { + } + + /** Returns true if intercation was successful. */ + public static boolean interactWithTank(ItemStack stack, EntityPlayer player, IFluidHandler tank, EnumFacing side) + { + if (stack == null || player.worldObj.isRemote) + { + return true; + } + + ItemStack result; + + // regular bucket? + int slot = player.inventory.currentItem; + if ((result = FluidUtil.tryFillBucket(stack, tank, side)) != null || + (result = FluidUtil.tryEmptyBucket(stack, tank, side)) != null) + { + // "use up" the input item if the player is not in creative + if (!player.capabilities.isCreativeMode) + { + player.inventory.decrStackSize(slot, 1); + ItemHandlerHelper.giveItemToPlayer(player, result, slot); + } + // send inventory updates to client + if (player.inventoryContainer != null) + { + player.inventoryContainer.detectAndSendChanges(); + } + return true; + } + // IFluidContainerItems + else + { + // copy of the original item for creative mode + ItemStack copy = stack.copy(); + boolean changedBucket = false; + // convert to fluidcontainer-bucket if it's a regular empty bucket + if (ItemStack.areItemsEqual(stack, FluidContainerRegistry.EMPTY_BUCKET) && FluidRegistry.isUniversalBucketEnabled()) + { + // try using the forge fluid bucket if it's enabled + stack = new ItemStack(ForgeModContainer.getInstance().universalBucket, copy.stackSize); + changedBucket = true; + } + + // try filling an empty fluidcontainer or emptying a filled fluidcontainer + if (FluidUtil.tryFillFluidContainerItem(stack, tank, side, player) || + FluidUtil.tryEmptyFluidContainerItem(stack, tank, side, player)) + { + if (player.capabilities.isCreativeMode) + { + // reset the stack that got modified + player.inventory.setInventorySlotContents(slot, copy); + } + else + { + // we passed in multiple stacksize and it changed, that means the new items are in the inventory + // but we have to readjust the old ones back + if (changedBucket && stack.stackSize != copy.stackSize) + { + copy.stackSize = stack.stackSize; + // replace the previously changed buckets that were not used back + player.inventory.setInventorySlotContents(slot, copy); + } + // we have the new stack now, but since we changed it from its original we have to set the contents anew + else + { + // if the original stack was multiple, replace it + if (copy.stackSize > 1) + { + player.inventory.setInventorySlotContents(slot, stack); + } + // otherwise reinsert it into the inventory + else + { + player.inventory.setInventorySlotContents(slot, null); + ItemHandlerHelper.giveItemToPlayer(player, stack, slot); + } + } + } + // send inventory updates to client + if (player.inventoryContainer != null) + { + player.inventoryContainer.detectAndSendChanges(); + } + return true; + } + } + + return false; + } + + /** + * Fill an empty bucket from the given tank. Uses the FluidContainerRegistry. + * + * @param bucket The empty bucket + * @param tank The tank to fill the bucket from + * @param side Side to access the tank from + * @return The filled bucket or null if the liquid couldn't be taken from the tank. + */ + public static ItemStack tryFillBucket(ItemStack bucket, IFluidHandler tank, EnumFacing side) + { + FluidTankInfo[] info = tank.getTankInfo(side); + // check for fluid in the tank + if (info == null || info.length == 0) + { + return null; + } + // check if we actually have an empty bucket + if (!FluidContainerRegistry.isEmptyContainer(bucket)) + { + return null; + } + // fluid in the tank + FluidStack inTank = info[0].fluid; + // drain one bucket if possible + FluidStack liquid = tank.drain(side, FluidContainerRegistry.getContainerCapacity(inTank, bucket), false); + if (liquid != null && liquid.amount > 0) + { + // success, return filled bucket + tank.drain(side, FluidContainerRegistry.getContainerCapacity(liquid, bucket), true); + return FluidContainerRegistry.fillFluidContainer(liquid, bucket); + } + + return null; + } + + /** + * Takes a filled bucket and tries to empty it into the given tank. Uses the FluidContainerRegistry. + * + * @param bucket The filled bucket + * @param tank The tank to fill with the bucket + * @param side Side to access the tank from + * @return The empty bucket if successful, null if the tank couldn't be filled. + */ + public static ItemStack tryEmptyBucket(ItemStack bucket, IFluidHandler tank, EnumFacing side) + { + // not a filled bucket + if (!FluidContainerRegistry.isFilledContainer(bucket)) + { + return null; + } + + // try filling the fluid from the bucket into the tank + FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(bucket); + if (tank.canFill(side, liquid.getFluid())) + { + // how much can we put into the tank? + int amount = tank.fill(side, liquid, false); + // not everything? + if (amount == liquid.amount) + { + // success, fully filled it into the tank, return empty bucket + tank.fill(side, liquid, true); + return FluidContainerRegistry.drainFluidContainer(bucket); + } + } + + return null; + } + + /** + * Takes an IFluidContainerItem and tries to fill it from the given tank. + * + * @param container The IFluidContainerItem Itemstack to fill. WILL BE MODIFIED! + * @param tank The tank to fill from + * @param side Side to access the tank from + * @param player The player that tries to fill the bucket. Needed if the input itemstack has a stacksize > 1 to determine where the filled container goes. + * @return True if the IFluidContainerItem was filled successfully, false otherwise. The passed container will have been modified to accomodate for anything done in this method. New Itemstacks might have been added to the players inventory. + */ + public static boolean tryFillFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, EntityPlayer player) + { + return tryFillFluidContainerItem(container, tank, side, new PlayerMainInvWrapper(player.inventory), -1, player); + } + + public static boolean tryEmptyFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, EntityPlayer player) + { + return tryEmptyFluidContainerItem(container, tank, side, new PlayerMainInvWrapper(player.inventory), -1, player); + } + + /** + * Takes an IFluidContainerItem and tries to fill it from the given tank. + * If the input itemstack has a stacksize >1 new itemstacks will be created and inserted into the given inventory. + * If the inventory does not accept it, it will be given to the player or dropped at the players feet. + * If player is null in this case, the action will be aborted. + * To support buckets that are not in the FluidContainerRegistry but implement IFluidContainerItem be sure to convert + * the empty bucket to your empty bucket variant before passing it to this function. + * + * @param container The IFluidContainerItem Itemstack to fill. WILL BE MODIFIED! + * @param tank The tank to fill from + * @param side Side to access the tank from + * @param inventory An inventory where any additionally created item (filled container if multiple empty are present) are put + * @param max Maximum amount to take from the tank. Uses IFluidContainerItem capacity if <= 0 + * @param player The player that gets the items the inventory can't take. Can be null, only used if the inventory cannot take the filled stack. + * @return True if the IFluidContainerItem was filled successfully, false otherwise. The passed container will have been modified to accomodate for anything done in this method. New Itemstacks might have been added to the players inventory. + */ + public static boolean tryFillFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, IItemHandler inventory, int max, EntityPlayer player) + { + if (!(container.getItem() instanceof IFluidContainerItem)) + { + // not a fluid container + return false; + } + + IFluidContainerItem fluidContainer = (IFluidContainerItem) container.getItem(); + if (fluidContainer.getFluid(container) != null) + { + // not empty + return false; + } + + // if no maximum is given, fill fully + if (max <= 0) + { + max = fluidContainer.getCapacity(container); + } + // check how much liquid we can drain from the tank + FluidStack liquid = tank.drain(side, max, false); + if (liquid != null && liquid.amount > 0) + { + // check which itemstack shall be altered by the fill call + if (container.stackSize > 1) + { + // create a copy of the container and fill it + ItemStack toFill = container.copy(); + toFill.stackSize = 1; + int filled = fluidContainer.fill(toFill, liquid, false); + if (filled > 0) + { + // This manipulates the container Itemstack! + filled = fluidContainer.fill(toFill, liquid, true); + } + else + { + // IFluidContainer does not accept the fluid/amount + return false; + } + + // check if we can give the itemstack to the inventory + ItemStack remainder = ItemHandlerHelper.insertItemStacked(inventory, toFill, true); + if (remainder != null && player == null) + { + // couldn't add to the inventory and don't have a player to drop the item at + return false; + } + remainder = ItemHandlerHelper.insertItemStacked(inventory, toFill, false); + // give it to the player or drop it at his feet + if (remainder != null && player != null) + { + ItemHandlerHelper.giveItemToPlayer(player, remainder); + } + + // the result has been given to the player, drain the tank since everything is ok + tank.drain(side, filled, true); + + // decrease its stacksize to accommodate the filled one (it was >1 from the check above) + container.stackSize--; + } + // just 1 empty container to fill, no special treatment needed + else + { + int filled = fluidContainer.fill(container, liquid, false); + if (filled > 0) + { + // This manipulates the container Itemstack! + filled = fluidContainer.fill(container, liquid, true); + } + else + { + // IFluidContainer does not accept the fluid/amount + return false; + } + tank.drain(side, filled, true); + } + + return true; + } + + return false; + } + + /** + * Takes an IFluidContainerItem and tries to empty it into the given tank. + * If the input itemstack has a stacksize >1 new itemstacks will be created and inserted into the given inventory. + * If the inventory does not accept it, it will be given to the player or dropped at the players feet. + * If player is null in this case, the action will be aborted. + * + * @param container The IFluidContainerItem Itemstack to empty. WILL BE MODIFIED! + * @param tank The tank to fill + * @param side Side to access the tank from + * @param inventory An inventory where any additionally created item (filled container if multiple empty are present) are put + * @param max Maximum amount to take from the tank. Uses IFluidContainerItem capacity if <= 0 + * @param player The player that gets the items the inventory can't take. Can be null, only used if the inventory cannot take the emptied stack. + * @return True if the container successfully emptied at least 1 mb into the tank, false otherwise. The passed container itemstack will be modified to accommodate for the liquid transaction. + */ + public static boolean tryEmptyFluidContainerItem(ItemStack container, IFluidHandler tank, EnumFacing side, IItemHandler inventory, int max, EntityPlayer player) + { + if (!(container.getItem() instanceof IFluidContainerItem)) + { + // not a fluid container + return false; + } + + IFluidContainerItem fluidContainer = (IFluidContainerItem) container.getItem(); + if (fluidContainer.getFluid(container) != null) + { + // drain out of the fluidcontainer + if (max <= 0) + { + max = fluidContainer.getCapacity(container); + } + FluidStack drained = fluidContainer.drain(container, max, false); + if (drained != null) + { + // check how much we can fill into the tank + int filled = tank.fill(side, drained, false); + if (filled > 0) + { + // verify that the new amount can also be drained (buckets can only extract full amounts for example) + drained = fluidContainer.drain(container, filled, false); + // actually transfer the liquid if everything went well + if (drained != null && drained.amount == filled) + { + // more than 1 filled itemstack, ensure that we can insert the changed container + if (container.stackSize > 1) + { + // create a copy of the container and drain it + ItemStack toEmpty = container.copy(); + toEmpty.stackSize = 1; + drained = fluidContainer.drain(toEmpty, filled, true); // modifies the container! + + // try adding the drained container to the inventory + ItemStack remainder = ItemHandlerHelper.insertItemStacked(inventory, toEmpty, true); + if (remainder != null && player == null) + { + // couldn't add to the inventory and don't have a player to drop the item at + return false; + } + remainder = ItemHandlerHelper.insertItemStacked(inventory, toEmpty, false); + // give it to the player or drop it at his feet + if (remainder != null && player != null) + { + ItemHandlerHelper.giveItemToPlayer(player, remainder); + } + + // the result has been given to the player, fill the tank since everything is ok + tank.fill(side, drained, true); + + // decrease its stacksize to accommodate the filled one (it was >1 from the check above) + container.stackSize--; + } + // itemstack of size 1, no special treatment needed + else + { + // This manipulates the container Itemstack! + drained = fluidContainer.drain(container, filled, true); // modifies the container! + tank.fill(side, drained, true); + } + return true; + } + } + } + } + + return false; + } + + private static boolean insertItemIntoo(ItemStack stack, IInventory inventory, World world, BlockPos pos, boolean isCreative) + { + if (stack == null) + { + return false; + } + // add it to the inventory + + if (inventory != null && addItemStackToInventory(stack, inventory, isCreative)) + { + if (world != null && pos != null) + { + world.playSoundEffect(pos.getX(), pos.getY(), pos.getZ(), "random.pop", 0.2F, ((world.rand.nextFloat() - world.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); + } + return true; + } + else if (world != null && pos != null) + { + double d0 = pos.getY() + 0.5d; + EntityItem entityitem = new EntityItem(world, pos.getX(), d0, pos.getZ(), stack); + entityitem.setPickupDelay(40); + + entityitem.motionX = 0; + //entityitem.motionY = 0; + entityitem.motionZ = 0; + + if (!world.isRemote) + { + world.spawnEntityInWorld(entityitem); + } + return true; + } + return false; + } + + // generalized copy of InventoryPlayer.addItemStackToInventory without regarding itemstack sizes > 1 + private static boolean addItemStackToInventory(final ItemStack itemstack, IInventory inventory, boolean isCreative) + { + if (itemstack != null && itemstack.stackSize == 1 && itemstack.getItem() != null) + { + try + { + int sizeInventory = inventory.getSizeInventory(); + // player inventory requires hardcoding because we don't want to add to the armor slots + if (inventory instanceof InventoryPlayer) + { + sizeInventory -= 4; + } + if (itemstack.isItemDamaged()) + { + int j = 0; + for (; j < sizeInventory; ++j) + { + if (inventory.getStackInSlot(j) == null) + { + break; + } + } + + // found empty slot + if (j < sizeInventory) + { + ItemStack copy = ItemStack.copyItemStack(itemstack); + copy.animationsToGo = 5; + inventory.setInventorySlotContents(j, copy); + itemstack.stackSize = 0; + return true; + } + else if (isCreative) + { + itemstack.stackSize = 0; + return true; + } + else + { + return false; + } + } + else + { + int origSize = itemstack.stackSize; + // go through the inventory and try to fill up already existing items + for (int i = 0; i < sizeInventory; i++) + { + ItemStack slot = inventory.getStackInSlot(i); + if (slot != null && slot.getItem() == itemstack.getItem() && + slot.isStackable() && slot.stackSize < slot.getMaxStackSize() && + slot.stackSize < inventory.getInventoryStackLimit() && + (!slot.getHasSubtypes() || slot.getMetadata() == itemstack.getMetadata()) && + ItemStack.areItemStackTagsEqual(slot, itemstack)) + { + // stackable + int dif = itemstack.stackSize; + if (dif > slot.getMaxStackSize() - slot.stackSize) + { + dif = slot.getMaxStackSize() - slot.stackSize; + } + if (dif > inventory.getInventoryStackLimit()) + { + dif = inventory.getInventoryStackLimit(); + } + slot.stackSize += dif; + slot.animationsToGo = 5; + itemstack.stackSize -= dif; + inventory.setInventorySlotContents(i, slot); + + if (itemstack.stackSize <= 0) + { + break; + } + } + } + + if (itemstack.stackSize > 0) + { + // find empty slot + for (int i = 0; i < sizeInventory; i++) + { + if (inventory.getStackInSlot(i) == null) + { + ItemStack slot = ItemStack.copyItemStack(itemstack); + if (slot.stackSize > inventory.getInventoryStackLimit()) + { + slot.stackSize = inventory.getInventoryStackLimit(); + } + slot.animationsToGo = 5; + + inventory.setInventorySlotContents(i, slot); + itemstack.stackSize -= slot.stackSize; + } + + if (itemstack.stackSize <= 0) + { + break; + } + } + } + + if (itemstack.stackSize > 0 && isCreative) + { + itemstack.stackSize = 0; + return true; + } + else + { + return itemstack.stackSize < origSize; + } + } + } + catch (Throwable throwable) + { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Adding item to inventory"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Item being added"); + crashreportcategory.addCrashSection("Item ID", Integer.valueOf(Item.getIdFromItem(itemstack.getItem()))); + crashreportcategory.addCrashSection("Item data", Integer.valueOf(itemstack.getMetadata())); + crashreportcategory.addCrashSectionCallable("Item name", new Callable() + { + public String call() throws Exception + { + return itemstack.getDisplayName(); + } + }); + throw new ReportedException(crashreport); + } + } + else + { + return false; + } + } +} diff --git a/src/main/java/net/minecraftforge/fluids/UniversalBucket.java b/src/main/java/net/minecraftforge/fluids/UniversalBucket.java new file mode 100644 index 000000000..c8af1a2bc --- /dev/null +++ b/src/main/java/net/minecraftforge/fluids/UniversalBucket.java @@ -0,0 +1,338 @@ +package net.minecraftforge.fluids; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.stats.StatList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockPos; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.event.entity.player.FillBucketEvent; +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 java.util.List; + +/** + * A universal bucket that can hold any liquid + */ +public class UniversalBucket extends Item implements IFluidContainerItem +{ + + public final int capacity; // how much the bucket holds + public final ItemStack empty; // empty item to return and recognize when filling + public final boolean nbtSensitive; + + public UniversalBucket() + { + this(FluidContainerRegistry.BUCKET_VOLUME, FluidContainerRegistry.EMPTY_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, ItemStack empty, boolean nbtSensitive) + { + this.capacity = capacity; + this.empty = empty; + this.nbtSensitive = nbtSensitive; + + this.setMaxStackSize(1); + + this.setCreativeTab(CreativeTabs.tabMisc); + } + + @SideOnly(Side.CLIENT) + @Override + public void getSubItems(Item itemIn, CreativeTabs tab, List subItems) + { + for (Fluid fluid : FluidRegistry.getRegisteredFluids().values()) + { + // add all fluids that the bucket can be filled with + FluidStack fs = new FluidStack(fluid, capacity); + ItemStack stack = new ItemStack(this); + if (fill(stack, fs, true) == fs.amount) + { + subItems.add(stack); + } + } + } + + @Override + public String getItemStackDisplayName(ItemStack stack) + { + FluidStack fluidStack = getFluid(stack); + if (fluidStack == null) + { + if(empty != null) + { + return empty.getDisplayName(); + } + return super.getItemStackDisplayName(stack); + } + + String unloc = this.getUnlocalizedNameInefficiently(stack); + + if (StatCollector.canTranslate(unloc + "." + fluidStack.getFluid().getName())) + { + return StatCollector.translateToFallback(unloc + "." + fluidStack.getFluid().getName()); + } + + return StatCollector.translateToLocalFormatted(unloc + ".name", fluidStack.getLocalizedName()); + } + + @Override + public ItemStack onItemRightClick(ItemStack itemstack, World world, EntityPlayer player) + { + FluidStack fluidStack = getFluid(itemstack); + // empty bucket shouldn't exist, do nothing since it should be handled by the bucket event + if (fluidStack == null) + { + return itemstack; + } + + // clicked on a block? + MovingObjectPosition mop = this.getMovingObjectPositionFromPlayer(world, player, false); + if (mop != null && mop.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) + { + 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 + if (this.tryPlaceFluid(fluidStack.getFluid().getBlock(), world, targetPos) + && !player.capabilities.isCreativeMode) + { + // success! + player.triggerAchievement(StatList.objectUseStats[Item.getIdFromItem(this)]); + + itemstack.stackSize--; + ItemStack emptyStack = empty != null ? empty.copy() : new ItemStack(this); + + // check whether we replace the item or add the empty one to the inventory + if (itemstack.stackSize <= 0) + { + return emptyStack; + } + else + { + // add empty bucket to player inventory + ItemHandlerHelper.giveItemToPlayer(player, emptyStack); + return itemstack; + } + } + } + } + } + + // couldn't place liquid there2 + return itemstack; + } + + + public boolean tryPlaceFluid(Block block, World worldIn, BlockPos pos) + { + if (block == null) + { + return false; + } + + Material material = worldIn.getBlockState(pos).getBlock().getMaterial(); + boolean isSolid = material.isSolid(); + + // can only place in air or non-solid blocks + if (!worldIn.isAirBlock(pos) && isSolid) + { + return false; + } + + // water goes poof? + if (worldIn.provider.doesWaterVaporize() && (block == Blocks.flowing_water || block == Blocks.water)) + { + int i = pos.getX(); + int j = pos.getY(); + int k = pos.getZ(); + worldIn.playSoundEffect((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + + 0.5F), "random.fizz", 0.5F, + 2.6F + (worldIn.rand.nextFloat() - worldIn.rand.nextFloat()) * 0.8F); + + for (int l = 0; l < 8; ++l) + { + worldIn.spawnParticle(EnumParticleTypes.SMOKE_LARGE, + (double) i + Math.random(), + (double) j + Math.random(), (double) k + Math.random(), 0.0D, 0.0D, 0.0D); + } + } + else + { + if (!worldIn.isRemote && !isSolid && !material.isLiquid()) + { + worldIn.destroyBlock(pos, true); + } + + worldIn.setBlockState(pos, block.getDefaultState(), 3); + } + return true; + } + + @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 + if (event.current == null || + !event.current.isItemEqual(empty) || + (nbtSensitive && ItemStack.areItemStackTagsEqual(event.current, empty))) + { + return; + } + + // needs to target a block + if (event.target == null || event.target.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) + { + return; + } + + World world = event.world; + BlockPos pos = event.target.getBlockPos(); + IBlockState state = world.getBlockState(pos); + // Note that water and lava are NOT an instance of IFluidBlock! They are therefore not handled by this code! + if (state.getBlock() instanceof IFluidBlock) + { + IFluidBlock fluidBlock = (IFluidBlock) state.getBlock(); + if (fluidBlock.canDrain(world, pos)) + { + FluidStack drained = fluidBlock.drain(world, pos, false); + // check if it fits exactly + if (drained != null && drained.amount == capacity) + { + // check if the container accepts it + ItemStack filledBucket = new ItemStack(this); + int filled = this.fill(filledBucket, drained, false); + if (filled == drained.amount) + { + // actually transfer the fluid + drained = fluidBlock.drain(world, pos, true); + this.fill(filledBucket, drained, true); + + // set it as the result + event.setResult(Event.Result.ALLOW); + event.result = filledBucket; + } + else + { + // cancel event, otherwise the vanilla minecraft ItemBucket would + // convert it into a water/lava bucket depending on the blocks material + event.setCanceled(true); + } + } + } + } + } + + public static ItemStack getFilledBucket(UniversalBucket item, Fluid fluid) + { + ItemStack stack = new ItemStack(item); + item.fill(stack, new FluidStack(fluid, item.capacity), true); + return stack; + } + + /* FluidContainer Management */ + + @Override + public FluidStack getFluid(ItemStack container) + { + return FluidStack.loadFluidStackFromNBT(container.getTagCompound()); + } + + @Override + public int getCapacity(ItemStack container) + { + return capacity; + } + + @Override + public int fill(ItemStack container, FluidStack resource, boolean doFill) + { + // has to be exactly 1, must be handled from the caller + if (container.stackSize != 1) + { + return 0; + } + + // can only fill exact capacity + if (resource == null || resource.amount != capacity) + { + return 0; + } + // registered in the registry? + if (!FluidRegistry.getBucketFluids().contains(resource.getFluid())) + { + return 0; + } + // fill the container + if (doFill) + { + NBTTagCompound tag = container.getTagCompound(); + if (tag == null) + { + tag = new NBTTagCompound(); + } + resource.writeToNBT(tag); + container.setTagCompound(tag); + } + return capacity; + } + + @Override + public FluidStack drain(ItemStack container, int maxDrain, boolean doDrain) + { + // can only drain everything at once + if (maxDrain < capacity) + { + return null; + } + + FluidStack fluidStack = getFluid(container); + if (doDrain && fluidStack != null) + { + if(empty != null) + { + container.setItem(empty.getItem()); + container.setTagCompound(empty.getTagCompound()); + container.setItemDamage(empty.getItemDamage()); + } + else { + container.stackSize = 0; + } + } + + return fluidStack; + } +} diff --git a/src/main/java/net/minecraftforge/items/IItemHandler.java b/src/main/java/net/minecraftforge/items/IItemHandler.java index 2955837ea..1e457f82f 100644 --- a/src/main/java/net/minecraftforge/items/IItemHandler.java +++ b/src/main/java/net/minecraftforge/items/IItemHandler.java @@ -34,12 +34,14 @@ public interface IItemHandler /** * Inserts an ItemStack into the given slot and return the remainder. + * The ItemStack should not be modified in this function! * Note: This behaviour is subtly different from IFluidHandlers.fill() * * @param slot Slot to insert into. - * @param stack ItemStack to insert + * @param stack ItemStack to insert. * @param simulate If true, the insertion is only simulated - * @return The remaining ItemStack that was not inserted (if the entire stack is accepted, then return null) + * @return The remaining ItemStack that was not inserted (if the entire stack is accepted, then return null). + * May be the same as the input ItemStack if unchanged, otherwise a new ItemStack. **/ ItemStack insertItem(int slot, ItemStack stack, boolean simulate); diff --git a/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java b/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java index e01e2060d..433b3f48e 100644 --- a/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java +++ b/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java @@ -1,7 +1,11 @@ package net.minecraftforge.items; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.items.wrapper.PlayerMainInvWrapper; public class ItemHandlerHelper { @@ -32,12 +36,138 @@ public class ItemHandlerHelper return (aTag != null || bTag == null) && (aTag == null || aTag.equals(bTag)); } + /** + * A relaxed version of canItemStacksStack that stacks itemstacks with different metadata if they don't have subtypes. + * This usually only applies when players pick up items. + */ + public static boolean canItemStacksStackRelaxed(ItemStack a, ItemStack b) + { + if (a == null || b == null || a.getItem() != b.getItem()) + return false; + + if (!a.isStackable()) + return false; + + // Metadata value only matters when the item has subtypes + // Vanilla stacks non-subtype items with different metadata together + // e.g. a stick with metadata 0 and a stick with metadata 1 stack + if (a.getHasSubtypes() && a.getMetadata() != b.getMetadata()) + return false; + + final NBTTagCompound aTag = a.getTagCompound(); + final NBTTagCompound bTag = b.getTagCompound(); + return (aTag != null || bTag == null) && (aTag == null || aTag.equals(bTag)); + } + public static ItemStack copyStackWithSize(ItemStack itemStack, int size) { if (size == 0) return null; ItemStack copy = ItemStack.copyItemStack(itemStack); - copy.stackSize = size; + if (copy != null) + copy.stackSize = size; return copy; } + + /** + * Inserts the ItemStack into the inventory, filling up already present stacks first. + * This is equivalent to the behaviour of a player picking up an item. + * Note: This function stacks items without subtypes with different metadata together. + */ + public static ItemStack insertItemStacked(IItemHandler inventory, ItemStack stack, boolean simulate) + { + if (inventory == null || stack == null) + return stack; + + // not stackable -> just insert into a new slot + if (!stack.isStackable()) + { + return insertItem(inventory, stack, simulate); + } + + int sizeInventory = inventory.getSlots(); + + // go through the inventory and try to fill up already existing items + for (int i = 0; i < sizeInventory; i++) + { + ItemStack slot = inventory.getStackInSlot(i); + if (canItemStacksStackRelaxed(slot, stack)) + { + stack = inventory.insertItem(i, stack, simulate); + + if (stack == null) + { + break; + } + } + } + + // insert remainder into empty slots + if (stack != null) + { + // find empty slot + for (int i = 0; i < sizeInventory; i++) + { + if (inventory.getStackInSlot(i) == null) + { + stack = inventory.insertItem(i, stack, simulate); + if (stack == null) + { + break; + } + } + } + } + + return stack; + } + + /** giveItemToPlayer without preferred slot */ + public static void giveItemToPlayer(EntityPlayer player, ItemStack stack) { + giveItemToPlayer(player, stack, -1); + } + + /** + * Inserts the given itemstack into the players inventory. + * If the inventory can't hold it, the item will be dropped in the world at the players position. + * + * @param player The player to give the item to + * @param stack The itemstack to insert + */ + public static void giveItemToPlayer(EntityPlayer player, ItemStack stack, int preferredSlot) + { + IItemHandler inventory = new PlayerMainInvWrapper(player.inventory); + World world = player.worldObj; + + // try adding it into the inventory + ItemStack remainder = null; + // insert into preferred slot first + if(preferredSlot >= 0) + { + remainder = inventory.insertItem(preferredSlot, stack, false); + } + // then into the inventory in general + if(remainder != null) + { + remainder = insertItemStacked(inventory, stack, false); + } + + // play sound if something got picked up + if (remainder == null || remainder.stackSize != stack.stackSize) + { + world.playSoundEffect(player.posX, player.posY, player.posZ, + "random.pop", 0.2F, ((world.rand.nextFloat() - world.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); + } + + // drop remaining itemstack into the world + if (remainder != null && !world.isRemote) + { + EntityItem entityitem = new EntityItem(world, player.posX, player.posY + 0.5, player.posZ, stack); + entityitem.setPickupDelay(40); + entityitem.motionX = 0; + entityitem.motionZ = 0; + + world.spawnEntityInWorld(entityitem); + } + } } diff --git a/src/main/java/net/minecraftforge/items/wrapper/CombinedInvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/CombinedInvWrapper.java new file mode 100644 index 000000000..4ffc4bb22 --- /dev/null +++ b/src/main/java/net/minecraftforge/items/wrapper/CombinedInvWrapper.java @@ -0,0 +1,102 @@ +package net.minecraftforge.items.wrapper; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.items.IItemHandlerModifiable; + +// combines multiple IItemHandlerModifiable into one interface +public class CombinedInvWrapper implements IItemHandlerModifiable +{ + + protected final IItemHandlerModifiable[] itemHandler; // the handlers + protected final int[] baseIndex; // index-offsets of the different handlers + protected final int slotCount; // numeber of total slots + + public CombinedInvWrapper(IItemHandlerModifiable... itemHandler) + { + this.itemHandler = itemHandler; + this.baseIndex = new int[itemHandler.length]; + int index = 0; + for (int i = 0; i < itemHandler.length; i++) + { + index += itemHandler[i].getSlots(); + baseIndex[i] = index; + } + this.slotCount = index; + } + + // returns the handler index for the slot + protected int getIndexForSlot(int slot) + { + if (slot < 0) + return -1; + + for (int i = 0; i < baseIndex.length; i++) + { + if (slot - baseIndex[i] < 0) + { + return i; + } + } + return -1; + } + + protected IItemHandlerModifiable getHandlerFromIndex(int index) + { + if (index < 0 || index >= itemHandler.length) + { + return (IItemHandlerModifiable)EmptyHandler.INSTANCE; + } + return itemHandler[index]; + } + + protected int getSlotFromIndex(int index) + { + if (index <= 0 || index >= baseIndex.length) + { + return 0; + } + return baseIndex[index - 1]; + } + + @Override + public void setStackInSlot(int slot, ItemStack stack) + { + int index = getIndexForSlot(slot); + IItemHandlerModifiable handler = getHandlerFromIndex(index); + slot = slot - getSlotFromIndex(index); + handler.setStackInSlot(slot, stack); + } + + @Override + public int getSlots() + { + return slotCount; + } + + @Override + public ItemStack getStackInSlot(int slot) + { + int index = getIndexForSlot(slot); + IItemHandlerModifiable handler = getHandlerFromIndex(index); + slot = getSlotFromIndex(index); + return handler.getStackInSlot(slot); + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) + { + int index = getIndexForSlot(slot); + IItemHandlerModifiable handler = getHandlerFromIndex(index); + slot = getSlotFromIndex(index); + return handler.insertItem(slot, stack, simulate); + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) + { + int index = getIndexForSlot(slot); + IItemHandlerModifiable handler = getHandlerFromIndex(index); + slot = getSlotFromIndex(index); + return handler.extractItem(slot, amount, simulate); + } +} diff --git a/src/main/java/net/minecraftforge/items/wrapper/EmptyHandler.java b/src/main/java/net/minecraftforge/items/wrapper/EmptyHandler.java index 4a6d07635..e99314c97 100644 --- a/src/main/java/net/minecraftforge/items/wrapper/EmptyHandler.java +++ b/src/main/java/net/minecraftforge/items/wrapper/EmptyHandler.java @@ -2,8 +2,9 @@ package net.minecraftforge.items.wrapper; import net.minecraft.item.ItemStack; import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.IItemHandlerModifiable; -public class EmptyHandler implements IItemHandler +public class EmptyHandler implements IItemHandlerModifiable { public static IItemHandler INSTANCE = new EmptyHandler(); @@ -30,4 +31,10 @@ public class EmptyHandler implements IItemHandler { return null; } + + @Override + public void setStackInSlot(int slot, ItemStack stack) + { + // nothing to do here + } } diff --git a/src/main/java/net/minecraftforge/items/wrapper/InvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/InvWrapper.java index 3adb77eaf..6b722dc30 100644 --- a/src/main/java/net/minecraftforge/items/wrapper/InvWrapper.java +++ b/src/main/java/net/minecraftforge/items/wrapper/InvWrapper.java @@ -79,6 +79,8 @@ public class InvWrapper implements IItemHandlerModifiable } else { + // copy the stack to not modify the original one + stack = stack.copy(); if (!simulate) { ItemStack copy = stack.splitStack(m); @@ -99,6 +101,8 @@ public class InvWrapper implements IItemHandlerModifiable m = Math.min(stack.getMaxStackSize(), inv.getInventoryStackLimit()); if (m < stack.stackSize) { + // copy the stack to not modify the original one + stack = stack.copy(); if (!simulate) { inv.setInventorySlotContents(slot, stack.splitStack(m)); diff --git a/src/main/java/net/minecraftforge/items/wrapper/PlayerArmorInvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/PlayerArmorInvWrapper.java new file mode 100644 index 000000000..d440f3892 --- /dev/null +++ b/src/main/java/net/minecraftforge/items/wrapper/PlayerArmorInvWrapper.java @@ -0,0 +1,53 @@ +package net.minecraftforge.items.wrapper; + +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +public class PlayerArmorInvWrapper extends InvWrapper +{ + public final InventoryPlayer inventoryPlayer; + public final int offset; + + public PlayerArmorInvWrapper(InventoryPlayer inv) + { + super(inv); + + inventoryPlayer = inv; + offset = inventoryPlayer.mainInventory.length; + } + + @Override + public int getSlots() + { + return inventoryPlayer.armorInventory.length; + } + + @Override + public ItemStack getStackInSlot(int slot) + { + return super.getStackInSlot(slot + offset); + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) + { + // check if it's valid for the armor slot + if (slot < 4 && stack != null && stack.getItem().isValidArmor(stack, 3 - slot, inventoryPlayer.player)) + { + return super.insertItem(slot + offset, stack, simulate); + } + return stack; + } + + @Override + public void setStackInSlot(int slot, ItemStack stack) + { + super.setStackInSlot(slot + offset, stack); + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) + { + return super.extractItem(slot + offset, amount, simulate); + } +} diff --git a/src/main/java/net/minecraftforge/items/wrapper/PlayerInvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/PlayerInvWrapper.java new file mode 100644 index 000000000..b5654705a --- /dev/null +++ b/src/main/java/net/minecraftforge/items/wrapper/PlayerInvWrapper.java @@ -0,0 +1,11 @@ +package net.minecraftforge.items.wrapper; + +import net.minecraft.entity.player.InventoryPlayer; + +public class PlayerInvWrapper extends CombinedInvWrapper +{ + public PlayerInvWrapper(InventoryPlayer inv) + { + super(new PlayerMainInvWrapper(inv), new PlayerArmorInvWrapper(inv)); + } +} diff --git a/src/main/java/net/minecraftforge/items/wrapper/PlayerMainInvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/PlayerMainInvWrapper.java new file mode 100644 index 000000000..a2d996d95 --- /dev/null +++ b/src/main/java/net/minecraftforge/items/wrapper/PlayerMainInvWrapper.java @@ -0,0 +1,89 @@ +package net.minecraftforge.items.wrapper; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +/** + * Exposes the player inventory WITHOUT the armor inventory as IItemHandler. + * Also takes core of inserting/extracting having the same logic as picking up items. + */ +public class PlayerMainInvWrapper extends InvWrapper +{ + + public final InventoryPlayer inventoryPlayer; + + public PlayerMainInvWrapper(InventoryPlayer inv) + { + super(inv); + + inventoryPlayer = inv; + } + + @Override + public int getSlots() + { + return inventoryPlayer.mainInventory.length; + } + + @Override + public void setStackInSlot(int slot, ItemStack stack) + { + // prevent setting of armor inventory + if (slot > getSlots()) + { + return; + } + super.setStackInSlot(slot, stack); + } + + @Override + public ItemStack getStackInSlot(int slot) + { + // prevent getting of armor inventory + if (slot > getSlots()) + { + return null; + } + return super.getStackInSlot(slot); + } + + @Override + public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) + { + // prevent inserting into armor inventory + if (slot > getSlots()) + { + return stack; + } + + ItemStack rest = super.insertItem(slot, stack, simulate); + if (rest == null || rest.stackSize != stack.stackSize) + { + // the stack in the slot changed, animate it + ItemStack inSlot = getStackInSlot(slot); + if(inSlot != null) + { + if (inventoryPlayer.player.worldObj.isRemote) + { + inSlot.animationsToGo = 5; + } + else if(inventoryPlayer.player instanceof EntityPlayerMP) { + inventoryPlayer.player.openContainer.detectAndSendChanges(); + } + } + } + return rest; + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) + { + // prevent extraction from armor inventory + if (slot > getSlots()) + { + return null; + } + return super.extractItem(slot, amount, simulate); + } +} diff --git a/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java b/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java index 4707bdd6d..8d0d61a44 100644 --- a/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java +++ b/src/main/java/net/minecraftforge/items/wrapper/SidedInvWrapper.java @@ -97,6 +97,8 @@ public class SidedInvWrapper implements IItemHandlerModifiable } else { + // copy the stack to not modify the original one + stack = stack.copy(); if (!simulate) { ItemStack copy = stack.splitStack(m); @@ -116,6 +118,8 @@ public class SidedInvWrapper implements IItemHandlerModifiable m = Math.min(stack.getMaxStackSize(), inv.getInventoryStackLimit()); if (m < stack.stackSize) { + // copy the stack to not modify the original one + stack = stack.copy(); if (!simulate) { inv.setInventorySlotContents(slot1, stack.splitStack(m)); diff --git a/src/main/resources/assets/forge/lang/en_US.lang b/src/main/resources/assets/forge/lang/en_US.lang index fed5d696b..507a84a68 100644 --- a/src/main/resources/assets/forge/lang/en_US.lang +++ b/src/main/resources/assets/forge/lang/en_US.lang @@ -148,3 +148,4 @@ fml.menu.mods.normal=Normal fml.menu.mods.search=Search: fml.menu.modoptions=Mod Options... +item.forge.bucketFilled.name=%s Bucket \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/DynBucketTest.java b/src/test/java/net/minecraftforge/debug/DynBucketTest.java index cfa812818..74f8645d6 100644 --- a/src/test/java/net/minecraftforge/debug/DynBucketTest.java +++ b/src/test/java/net/minecraftforge/debug/DynBucketTest.java @@ -1,26 +1,31 @@ package net.minecraftforge.debug; -import java.util.List; - +import net.minecraft.block.BlockContainer; +import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.ItemMeshDefinition; import net.minecraft.client.resources.model.ModelBakery; import net.minecraft.client.resources.model.ModelResourceLocation; 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.network.NetworkManager; +import net.minecraft.network.Packet; +import net.minecraft.network.play.server.S35PacketUpdateTileEntity; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; import net.minecraftforge.client.model.ModelLoader; -import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.debug.ModelFluidDebug.TestFluid; import net.minecraftforge.debug.ModelFluidDebug.TestGas; import net.minecraftforge.event.entity.player.FillBucketEvent; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidContainerRegistry; -import net.minecraftforge.fluids.FluidRegistry; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.IFluidBlock; +import net.minecraftforge.fluids.*; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.SidedProxy; @@ -29,12 +34,19 @@ import net.minecraftforge.fml.common.eventhandler.Event.Result; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.registry.GameRegistry; +import java.util.List; + @Mod(modid = "DynBucketTest", version = "0.1", dependencies = "after:" + ModelFluidDebug.MODID) public class DynBucketTest { public static final Item dynBucket = new DynBucket(); public static final Item dynBottle = new DynBottle(); + static + { + FluidRegistry.enableUniversalBucket(); + } + @SidedProxy public static CommonProxy proxy; @@ -45,7 +57,9 @@ public class DynBucketTest } } - public static class ServerProxy extends CommonProxy {} + public static class ServerProxy extends CommonProxy + { + } public static class ClientProxy extends CommonProxy { @@ -70,27 +84,33 @@ public class DynBucketTest @EventHandler public void preInit(FMLPreInitializationEvent event) { - GameRegistry.registerItem(dynBucket, "dynbucket"); + GameRegistry.registerBlock(new BlockSimpleTank(), "simpletank"); + GameRegistry.registerTileEntity(TileSimpleTank.class, "simpletank"); + + FluidRegistry.addBucketForFluid(FluidRegistry.getFluid(TestFluid.name)); + FluidRegistry.addBucketForFluid(FluidRegistry.getFluid(TestGas.name)); + + //GameRegistry.registerItem(dynBucket, "dynbucket"); GameRegistry.registerItem(dynBottle, "dynbottle"); // register fluid containers int i = 0; //registerFluidContainer(FluidRegistry.WATER, i++); //registerFluidContainer(FluidRegistry.LAVA, i++); - registerFluidContainer(FluidRegistry.getFluid(TestFluid.name), i++); - registerFluidContainer(FluidRegistry.getFluid(TestGas.name), i++); + //registerFluidContainer(FluidRegistry.getFluid(TestFluid.name), i++); + //registerFluidContainer(FluidRegistry.getFluid(TestGas.name), i++); i = 0; - registerFluidContainer2(FluidRegistry.WATER, i++); - registerFluidContainer2(FluidRegistry.LAVA, i++); - registerFluidContainer2(FluidRegistry.getFluid(TestFluid.name), i++); - registerFluidContainer2(FluidRegistry.getFluid(TestGas.name), i++); + //registerFluidContainer2(FluidRegistry.WATER, i++); + //registerFluidContainer2(FluidRegistry.LAVA, i++); + //registerFluidContainer2(FluidRegistry.getFluid(TestFluid.name), i++); + //registerFluidContainer2(FluidRegistry.getFluid(TestGas.name), i++); // Set TestFluidBlocks blockstate to use milk instead of testfluid for the texture to be loaded FluidContainerRegistry.registerFluidContainer(FluidRegistry.getFluid("milk"), new ItemStack(Items.milk_bucket), FluidContainerRegistry.EMPTY_BUCKET); proxy.setupModels(); - MinecraftForge.EVENT_BUS.register(this); + //MinecraftForge.EVENT_BUS.register(this); } private void registerFluidContainer(Fluid fluid, int meta) @@ -153,25 +173,158 @@ public class DynBucketTest } } - public static class DynBottle extends Item + public static class DynBottle extends UniversalBucket { public DynBottle() { + super(250, new ItemStack(Items.glass_bottle), true); setUnlocalizedName("dynbottle"); - setMaxStackSize(1); + setMaxStackSize(16); setHasSubtypes(true); setCreativeTab(CreativeTabs.tabMisc); } + } + + // simple tank copied from tinkers construct + public static class BlockSimpleTank extends BlockContainer + { + + protected BlockSimpleTank() + { + super(Material.rock); + setCreativeTab(CreativeTabs.tabMisc); + } @Override - public void getSubItems(Item itemIn, CreativeTabs tab, List subItems) + public TileEntity createNewTileEntity(World worldIn, int meta) { - for (int i = 0; i < 4; i++) + return new TileSimpleTank(); + } + + @Override + public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumFacing side, float hitX, float hitY, float hitZ) + { + TileEntity te = worldIn.getTileEntity(pos); + if (!(te instanceof IFluidHandler)) { - ItemStack bucket = new ItemStack(this, 1, i); - if (FluidContainerRegistry.isFilledContainer(bucket)) - subItems.add(bucket); + return false; + } + IFluidHandler tank = (IFluidHandler) te; + side = side.getOpposite(); + + ItemStack stack = playerIn.getHeldItem(); + if (stack == null) + { + sendText(playerIn, tank, side); + return false; + } + + // do the thing with the tank and the buckets + if (FluidUtil.interactWithTank(stack, playerIn, tank, side)) + { + return true; + } + else + { + sendText(playerIn, tank, side); + } + + // prevent interaction of the item if it's a fluidcontainer. Prevents placing liquids when interacting with the tank + return FluidContainerRegistry.isFilledContainer(stack) || stack.getItem() instanceof IFluidContainerItem; + } + + private void sendText(EntityPlayer player, IFluidHandler tank, EnumFacing side) + { + if (player.worldObj.isRemote) + { + String text; + if (tank.getTankInfo(side).length > 0 && tank.getTankInfo(side)[0] != null && tank.getTankInfo(side)[0].fluid != null) + { + text = tank.getTankInfo(side)[0].fluid.amount + "x " + tank.getTankInfo(side)[0].fluid.getLocalizedName(); + } else + { + text = "empty"; + } + player.addChatMessage(new ChatComponentText(text)); } } } + + public static class TileSimpleTank extends TileEntity implements IFluidHandler + { + FluidTank tank = new FluidTank(4000); + + @Override + public int fill(EnumFacing from, FluidStack resource, boolean doFill) + { + int filled = tank.fill(resource, doFill); + if(doFill && filled > 0) { + worldObj.markBlockForUpdate(pos); + } + return filled; + } + + @Override + public FluidStack drain(EnumFacing from, FluidStack resource, boolean doDrain) + { + // not used in this test + return null; + } + + @Override + public FluidStack drain(EnumFacing from, int maxDrain, boolean doDrain) + { + FluidStack drained = tank.drain(maxDrain, doDrain); + if(doDrain && drained != null) { + worldObj.markBlockForUpdate(pos); + } + return drained; + } + + @Override + public boolean canFill(EnumFacing from, Fluid fluid) + { + return tank.getFluidAmount() == 0 || + (tank.getFluid().getFluid() == fluid && tank.getFluidAmount() < tank.getCapacity()); + } + + @Override + public boolean canDrain(EnumFacing from, Fluid fluid) + { + return tank.getFluidAmount() > 0; + } + + @Override + public FluidTankInfo[] getTankInfo(EnumFacing from) + { + return new FluidTankInfo[]{new FluidTankInfo(tank)}; + } + + @Override + public void readFromNBT(NBTTagCompound tags) + { + super.readFromNBT(tags); + tank.readFromNBT(tags); + } + + @Override + public void writeToNBT(NBTTagCompound tags) + { + super.writeToNBT(tags); + tank.writeToNBT(tags); + } + + @Override + public Packet getDescriptionPacket() { + NBTTagCompound tag = new NBTTagCompound(); + writeToNBT(tag); + return new S35PacketUpdateTileEntity(this.getPos(), this.getBlockMetadata(), tag); + } + + @Override + public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { + super.onDataPacket(net, pkt); + readFromNBT(pkt.getNbtCompound()); + } + } }