diff --git a/src/main/java/gregtech/api/items/metaitem/stats/IItemBehaviour.java b/src/main/java/gregtech/api/items/metaitem/stats/IItemBehaviour.java index eb2b743d176..55121079639 100644 --- a/src/main/java/gregtech/api/items/metaitem/stats/IItemBehaviour.java +++ b/src/main/java/gregtech/api/items/metaitem/stats/IItemBehaviour.java @@ -38,7 +38,7 @@ default EnumActionResult onItemUseFirst(EntityPlayer player, World world, BlockP default ActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { - return ActionResult.newResult(EnumActionResult.PASS, player.getHeldItem(hand)); + return pass(player.getHeldItem(hand)); } default void addInformation(ItemStack itemStack, List lines) {} @@ -50,8 +50,20 @@ default Multimap getAttributeModifiers(EntityEquipmen } default ActionResult onItemRightClick(World world, EntityPlayer player, EnumHand hand) { - return ActionResult.newResult(EnumActionResult.PASS, player.getHeldItem(hand)); + return pass(player.getHeldItem(hand)); } default void addPropertyOverride(@NotNull Item item) {} + + default ActionResult pass(ItemStack stack) { + return ActionResult.newResult(EnumActionResult.PASS, stack); + } + + default ActionResult success(ItemStack stack) { + return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + } + + default ActionResult fail(ItemStack stack) { + return ActionResult.newResult(EnumActionResult.FAIL, stack); + } } diff --git a/src/main/java/gregtech/api/items/metaitem/stats/ItemFluidContainer.java b/src/main/java/gregtech/api/items/metaitem/stats/ItemFluidContainer.java index b206c683512..f94c0202b74 100644 --- a/src/main/java/gregtech/api/items/metaitem/stats/ItemFluidContainer.java +++ b/src/main/java/gregtech/api/items/metaitem/stats/ItemFluidContainer.java @@ -1,15 +1,54 @@ package gregtech.api.items.metaitem.stats; +import gregtech.api.util.GTUtility; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockLiquid; +import net.minecraft.block.material.Material; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResult; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.FluidUtil; +import net.minecraftforge.fluids.IFluidBlock; +import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandlerItem; +import net.minecraftforge.fluids.capability.wrappers.BlockLiquidWrapper; +import net.minecraftforge.fluids.capability.wrappers.BlockWrapper; +import net.minecraftforge.fluids.capability.wrappers.FluidBlockWrapper; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ItemFluidContainer implements IItemContainerItemProvider, IItemBehaviour { -public class ItemFluidContainer implements IItemContainerItemProvider { + private final boolean isBucket; + + public ItemFluidContainer(boolean isBucket) { + this.isBucket = isBucket; + } + + public ItemFluidContainer() { + this(false); + } @Override public ItemStack getContainerItem(ItemStack itemStack) { - IFluidHandlerItem handler = itemStack.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null); + IFluidHandlerItem handler = FluidUtil.getFluidHandler(itemStack); if (handler != null) { FluidStack drained = handler.drain(1000, false); if (drained == null || drained.amount != 1000) return ItemStack.EMPTY; @@ -18,4 +57,175 @@ public ItemStack getContainerItem(ItemStack itemStack) { } return itemStack; } + + @Override + public void addInformation(ItemStack itemStack, List lines) { + if (isBucket) { + lines.add(I18n.format("behaviour.cell_bucket.tooltip")); + } + } + + @Override + public ActionResult onItemRightClick(World world, EntityPlayer player, EnumHand hand) { + ItemStack stack = player.getHeldItem(hand); + if (!isBucket) return pass(stack); + + ItemStack cellStack = GTUtility.copy(1, stack); + + var cellHandler = FluidUtil.getFluidHandler(cellStack); + if (cellHandler == null) return pass(stack); + + var result = rayTrace(world, player, true); + if (result == null || result.typeOfHit != RayTraceResult.Type.BLOCK) { + return pass(stack); + } + + var pos = result.getBlockPos(); + + // can the player modify the clicked block + if (!world.isBlockModifiable(player, pos)) { + return fail(stack); + } + + // can player edit + if (!player.canPlayerEdit(pos, result.sideHit, cellStack)) { + return fail(stack); + } + + // prioritize filling the cell if there's space, otherwise place fluid + if (fillCell(cellStack, world, pos, result.sideHit, player)) { + addToPlayerInventory(stack, cellHandler.getContainer(), player, hand); + return success(stack); + + } else { + result = rayTrace(world, player, false); + if (result == null || result.typeOfHit != RayTraceResult.Type.BLOCK) { + return pass(stack); + } + pos = result.getBlockPos(); + + if (tryPlace(cellHandler, world, pos.offset(result.sideHit), result.sideHit, player)) { + addToPlayerInventory(stack, cellHandler.getContainer(), player, hand); + return success(stack); + } + } + + return pass(stack); + } + + private static boolean tryPlace(IFluidHandlerItem cellHandler, World world, BlockPos pos, EnumFacing side, + EntityPlayer player) { + var cellFluid = cellHandler.drain(Fluid.BUCKET_VOLUME, false); + if (cellFluid == null || !cellFluid.getFluid().canBePlacedInWorld()) + return false; + + IFluidHandler blockHandler = getOrCreate(cellFluid, world, pos, side); + + // check that we can place the fluid at the destination + IBlockState destBlockState = world.getBlockState(pos); + Material destMaterial = destBlockState.getMaterial(); + boolean isDestNonSolid = !destMaterial.isSolid(); + boolean isDestReplaceable = destBlockState.getBlock().isReplaceable(world, pos); + + if (!world.isAirBlock(pos) && !isDestNonSolid && !isDestReplaceable) { + // Non-air, solid, unreplacable block. We can't put fluid here. + return false; + } + + // check vaporize + if (world.provider.doesWaterVaporize() && cellFluid.getFluid().doesVaporize(cellFluid)) { + cellHandler.drain(Fluid.BUCKET_VOLUME, true); + cellFluid.getFluid().vaporize(player, world, pos, cellFluid); + return true; + } + + // fill block + int filled = blockHandler.fill(cellFluid, false); + + if (filled != Fluid.BUCKET_VOLUME) return false; + + playSound(cellFluid, true, player); + boolean consume = !player.isSpectator() && !player.isCreative(); + blockHandler.fill(cellHandler.drain(Fluid.BUCKET_VOLUME, consume), true); + return true; + } + + private static boolean fillCell(ItemStack cellStack, World world, BlockPos pos, EnumFacing side, + EntityPlayer player) { + IFluidHandler blockHandler = FluidUtil.getFluidHandler(world, pos, side); + if (blockHandler == null) return false; + + IFluidHandlerItem cellHandler = FluidUtil.getFluidHandler(cellStack); + if (cellHandler == null) return false; + + FluidStack stack = blockHandler.drain(Fluid.BUCKET_VOLUME, false); + int filled = cellHandler.fill(stack, false); + + if (filled != Fluid.BUCKET_VOLUME) return false; + + playSound(stack, false, player); + boolean consume = !player.isSpectator() && !player.isCreative(); + cellHandler.fill(blockHandler.drain(Fluid.BUCKET_VOLUME, true), consume); + return true; + } + + // copied and adapted from Item.java + @Nullable + private static RayTraceResult rayTrace(World worldIn, EntityPlayer player, boolean hitFluids) { + Vec3d lookPos = player.getPositionVector() + .add(0, player.getEyeHeight(), 0); + + Vec3d lookOffset = player.getLookVec() + .scale(player.getEntityAttribute(EntityPlayer.REACH_DISTANCE).getAttributeValue()); + + return worldIn.rayTraceBlocks(lookPos, lookPos.add(lookOffset), + hitFluids, !hitFluids, false); + } + + @NotNull + private static IFluidHandler createHandler(FluidStack stack, World world, BlockPos pos) { + Block block = stack.getFluid().getBlock(); + if (block instanceof IFluidBlock) { + return new FluidBlockWrapper((IFluidBlock) block, world, pos); + } else if (block instanceof BlockLiquid) { + return new BlockLiquidWrapper((BlockLiquid) block, world, pos); + } else { + return new BlockWrapper(block, world, pos); + } + } + + private static IFluidHandler getOrCreate(FluidStack stack, World world, BlockPos pos, EnumFacing side) { + IFluidHandler handler = FluidUtil.getFluidHandler(world, pos, side); + if (handler != null) return handler; + return createHandler(stack, world, pos); + } + + private static void addToPlayerInventory(ItemStack playerStack, ItemStack resultStack, EntityPlayer player, + EnumHand hand) { + if (playerStack.getCount() > resultStack.getCount()) { + playerStack.shrink(resultStack.getCount()); + if (!player.inventory.addItemStackToInventory(resultStack) && !player.world.isRemote) { + EntityItem dropItem = player.entityDropItem(resultStack, 0); + if (dropItem != null) dropItem.setPickupDelay(0); + } + } else { + player.setHeldItem(hand, resultStack); + } + } + + /** + * Play the appropriate fluid interaction sound for the fluid.
+ * Must be called on server to work correctly + **/ + private static void playSound(FluidStack fluid, boolean fill, EntityPlayer player) { + if (fluid == null || player.world.isRemote) return; + SoundEvent soundEvent; + if (fill) { + soundEvent = fluid.getFluid().getFillSound(fluid); + } else { + soundEvent = fluid.getFluid().getEmptySound(fluid); + } + player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ, + soundEvent, SoundCategory.PLAYERS, 1.0F, 1.0F); + } } diff --git a/src/main/java/gregtech/common/items/MetaItem1.java b/src/main/java/gregtech/common/items/MetaItem1.java index cdf8d42deda..7b82be08d94 100644 --- a/src/main/java/gregtech/common/items/MetaItem1.java +++ b/src/main/java/gregtech/common/items/MetaItem1.java @@ -191,46 +191,51 @@ public void registerSubItems() { // Fluid Cells: ID 78-88 FLUID_CELL = addItem(78, "fluid_cell") .addComponents(new FilteredFluidStats(1000, 1800, true, false, false, false, false), - new ItemFluidContainer()) + new ItemFluidContainer(true)) .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS); FLUID_CELL_UNIVERSAL = addItem(79, "fluid_cell.universal") .addComponents(new FilteredFluidStats(1000, 1800, true, false, false, false, true), - new ItemFluidContainer()) + new ItemFluidContainer(true)) .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_STEEL = addItem(80, "large_fluid_cell.steel") .addComponents(new FilteredFluidStats(8000, Materials.Steel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, - false, false, true), new ItemFluidContainer()) + false, false, true), + new ItemFluidContainer(true)) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Steel, M * 4))) // ingot * 4 .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_ALUMINIUM = addItem(81, "large_fluid_cell.aluminium") .addComponents(new FilteredFluidStats(32000, Materials.Aluminium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, - false, false, true), new ItemFluidContainer()) + false, false, true), + new ItemFluidContainer(true)) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Aluminium, M * 4))) // ingot * 4 .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_STAINLESS_STEEL = addItem(82, "large_fluid_cell.stainless_steel") .addComponents(new FilteredFluidStats(64000, Materials.StainlessSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, - true, true, false, true), new ItemFluidContainer()) + true, true, false, true), + new ItemFluidContainer(true)) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.StainlessSteel, M * 6))) // ingot * 6 .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_TITANIUM = addItem(83, "large_fluid_cell.titanium") .addComponents(new FilteredFluidStats(128000, Materials.Titanium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, true, - false, false, true), new ItemFluidContainer()) + false, false, true), + new ItemFluidContainer(true)) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.Titanium, M * 6))) // ingot * 6 .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS); FLUID_CELL_LARGE_TUNGSTEN_STEEL = addItem(84, "large_fluid_cell.tungstensteel") .addComponents(new FilteredFluidStats(512000, Materials.TungstenSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, - true, false, false, true), new ItemFluidContainer()) + true, false, false, true), + new ItemFluidContainer(true)) .setMaxStackSize(32) .setMaterialInfo(new ItemMaterialInfo(new MaterialStack(Materials.TungstenSteel, M * 8))) // ingot * 8 .setCreativeTabs(GTCreativeTabs.TAB_GREGTECH_TOOLS); diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang index 4b6a49481b6..59548ad5e33 100644 --- a/src/main/resources/assets/gregtech/lang/en_us.lang +++ b/src/main/resources/assets/gregtech/lang/en_us.lang @@ -4748,6 +4748,7 @@ behaviour.softhammer=Activates and Deactivates Machines behaviour.hammer=Turns on and off Muffling for Machines (by hitting them) behaviour.wrench=Rotates Blocks on Rightclick behaviour.boor.by=by %s +behaviour.cell_bucket.tooltip=Can pickup or place fluids behaviour.paintspray.solvent.tooltip=Can remove color from things behaviour.paintspray.white.tooltip=Can paint things in White behaviour.paintspray.orange.tooltip=Can paint things in Orange