diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c6b2ea..b72fd10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Forge Recommended Versioning](https://mcforge.readthedocs.io/en/latest/conventions/versioning/). +## [1.20.4-3.1.2.0] - 2024-01-31 +### Added +- new ingredient type "bucketlib:block" to specify buckets with blocks in a recipe +- new ingredient type "bucketlib:entity" to specify buckets with entities in a recipe + +### Fixed +- JEI showed vanilla bucket in recipes of specific "bucketlib:empty" ingredients (thanks to FreeFull for the report) + ## [1.20.4-3.1.1.0] - 2024-01-31 ### Added - new ingredient type "bucketlib:empty" to specify an empty bucket in a recipe (thanks to FreeFull for the idea) #34 diff --git a/common/src/main/java/de/cech12/bucketlib/api/item/UniversalBucketItem.java b/common/src/main/java/de/cech12/bucketlib/api/item/UniversalBucketItem.java index fb357f4..e0e5fb3 100644 --- a/common/src/main/java/de/cech12/bucketlib/api/item/UniversalBucketItem.java +++ b/common/src/main/java/de/cech12/bucketlib/api/item/UniversalBucketItem.java @@ -262,7 +262,7 @@ public InteractionResultHolder use(@Nonnull Level level, @Nonnull Pla InteractionResult interactionResult = fakeStack.useOn(new UseOnContext(player, interactionHand, blockHitResult)); player.setItemInHand(interactionHand, itemstack); if (interactionResult.consumesAction()) { - return new InteractionResultHolder<>(interactionResult, BucketLibUtil.createEmptyResult(itemstack, player, BucketLibUtil.removeBlock(itemstack), interactionHand)); + return new InteractionResultHolder<>(interactionResult, BucketLibUtil.createEmptyResult(itemstack, player, BucketLibUtil.removeBlock(itemstack, true), interactionHand)); } } } @@ -402,8 +402,20 @@ public ItemStack getCraftingRemainingItem(ItemStack itemStack) { if (BucketLibUtil.isAffectedByInfinityEnchantment(itemStack)) { return itemStack.copy(); } - // AFAIK this method is only used by fluid handling. Other things like entities, blocks, etc. should not be affected by this. - return BucketLibUtil.removeFluid(itemStack); + //remove everything from bucket + ItemStack result = itemStack.copy(); + boolean damaged = BucketLibUtil.containsFluid(result); //damaging is done by fluid handler + if (BucketLibUtil.containsBlock(result)) { + result = BucketLibUtil.removeBlock(result, !damaged); + damaged = true; + } + if (BucketLibUtil.containsEntityType(result)) { + result = BucketLibUtil.removeEntityType(result, !damaged); + } + if (BucketLibUtil.containsFluid(result) || BucketLibUtil.containsMilk(result)) { + result = BucketLibUtil.removeFluid(result); + } + return result; } private boolean getBooleanProperty(Supplier config, boolean defaultValue) { diff --git a/common/src/main/java/de/cech12/bucketlib/item/UniversalBucketDispenseBehaviour.java b/common/src/main/java/de/cech12/bucketlib/item/UniversalBucketDispenseBehaviour.java index d3530c6..adf77e6 100644 --- a/common/src/main/java/de/cech12/bucketlib/item/UniversalBucketDispenseBehaviour.java +++ b/common/src/main/java/de/cech12/bucketlib/item/UniversalBucketDispenseBehaviour.java @@ -86,7 +86,7 @@ private ItemStack emptyBucket(@Nonnull BlockSource source, @Nonnull ItemStack st Block placeBlock = BucketLibUtil.getBlock(stack); if (placeBlock != null && placeBlock.asItem() instanceof DispensibleContainerItem dispensibleContainerItem) { if (dispensibleContainerItem.emptyContents(null, level, placePosition, null)) { - return BucketLibUtil.removeBlock(stack); + return BucketLibUtil.removeBlock(stack, true); } } } else if (BucketLibUtil.containsEntityType(stack)) { diff --git a/common/src/main/java/de/cech12/bucketlib/util/BucketLibUtil.java b/common/src/main/java/de/cech12/bucketlib/util/BucketLibUtil.java index 04ea2e5..d645f54 100644 --- a/common/src/main/java/de/cech12/bucketlib/util/BucketLibUtil.java +++ b/common/src/main/java/de/cech12/bucketlib/util/BucketLibUtil.java @@ -276,9 +276,9 @@ public static ItemStack addBlock(ItemStack itemStack, Block block) { return itemStack.copy(); } - public static ItemStack removeBlock(ItemStack itemStack) { + public static ItemStack removeBlock(ItemStack itemStack, boolean damage) { if (!containsMilk(itemStack)) { - return removeContent(itemStack); + return removeContent(itemStack, damage); } return itemStack.copy(); } diff --git a/common/src/main/java/de/cech12/bucketlib/util/RegistryUtil.java b/common/src/main/java/de/cech12/bucketlib/util/RegistryUtil.java index a443ce7..f4db0e1 100644 --- a/common/src/main/java/de/cech12/bucketlib/util/RegistryUtil.java +++ b/common/src/main/java/de/cech12/bucketlib/util/RegistryUtil.java @@ -60,6 +60,15 @@ public static List getBucketEntities() { return bucketEntities; } + public static BucketEntity getBucketEntity(EntityType entityType) { + for (BucketEntity bucketEntity : getBucketEntities()) { + if (bucketEntity.entityType() == entityType) { + return bucketEntity; + } + } + return null; + } + public record BucketBlock(Block block, SolidBucketItem bucketItem) {} public record BucketEntity(EntityType entityType, Fluid fluid, MobBucketItem bucketItem) {} diff --git a/common/src/main/java/de/cech12/bucketlib/util/WorldInteractionUtil.java b/common/src/main/java/de/cech12/bucketlib/util/WorldInteractionUtil.java index 56e5abd..535bf67 100644 --- a/common/src/main/java/de/cech12/bucketlib/util/WorldInteractionUtil.java +++ b/common/src/main/java/de/cech12/bucketlib/util/WorldInteractionUtil.java @@ -107,7 +107,7 @@ public static InteractionResultHolder tryPlaceIntoCauldron(Level leve player.getAbilities().instabuild = previousInstabuildValue; player.setItemInHand(interactionHand, itemstack); if (interactionResult.consumesAction()) { - return new InteractionResultHolder<>(interactionResult, BucketLibUtil.createEmptyResult(itemstack, player, BucketLibUtil.removeBlock(itemstack), interactionHand, true)); + return new InteractionResultHolder<>(interactionResult, BucketLibUtil.createEmptyResult(itemstack, player, BucketLibUtil.removeBlock(itemstack, true), interactionHand, true)); } } } diff --git a/forge/src/main/java/de/cech12/bucketlib/BucketLibMod.java b/forge/src/main/java/de/cech12/bucketlib/BucketLibMod.java index 5e33252..b5300ac 100644 --- a/forge/src/main/java/de/cech12/bucketlib/BucketLibMod.java +++ b/forge/src/main/java/de/cech12/bucketlib/BucketLibMod.java @@ -3,7 +3,9 @@ import de.cech12.bucketlib.api.BucketLib; import de.cech12.bucketlib.api.BucketLibApi; import de.cech12.bucketlib.api.BucketLibTags; +import de.cech12.bucketlib.api.crafting.BlockIngredient; import de.cech12.bucketlib.api.crafting.EmptyIngredient; +import de.cech12.bucketlib.api.crafting.EntityIngredient; import de.cech12.bucketlib.api.crafting.FluidIngredient; import de.cech12.bucketlib.api.crafting.MilkIngredient; import de.cech12.bucketlib.api.item.UniversalBucketItem; @@ -49,9 +51,11 @@ public class BucketLibMod { static { RECIPE_SERIALIZERS.register("bucket_dyeing", () -> BucketDyeingRecipe.Serializer.INSTANCE); + INGREDIENT_SERIALIZERS.register("block", () -> BlockIngredient.SERIALIZER); + INGREDIENT_SERIALIZERS.register("empty", () -> EmptyIngredient.SERIALIZER); + INGREDIENT_SERIALIZERS.register("entity", () -> EntityIngredient.SERIALIZER); INGREDIENT_SERIALIZERS.register("fluid", () -> FluidIngredient.SERIALIZER); INGREDIENT_SERIALIZERS.register("milk", () -> MilkIngredient.SERIALIZER); - INGREDIENT_SERIALIZERS.register("empty", () -> EmptyIngredient.SERIALIZER); } private static final Logger LOGGER = LogManager.getLogger(); diff --git a/forge/src/main/java/de/cech12/bucketlib/api/crafting/BlockIngredient.java b/forge/src/main/java/de/cech12/bucketlib/api/crafting/BlockIngredient.java new file mode 100644 index 0000000..3708d9b --- /dev/null +++ b/forge/src/main/java/de/cech12/bucketlib/api/crafting/BlockIngredient.java @@ -0,0 +1,158 @@ +package de.cech12.bucketlib.api.crafting; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import de.cech12.bucketlib.BucketLibMod; +import de.cech12.bucketlib.util.BucketLibUtil; +import de.cech12.bucketlib.util.RegistryUtil; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; +import net.minecraftforge.common.crafting.ingredients.AbstractIngredient; +import net.minecraftforge.common.crafting.ingredients.IIngredientSerializer; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.tags.ITagManager; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +public class BlockIngredient extends AbstractIngredient { + + protected final Block block; + protected final TagKey tag; + private ItemStack[] matchingStacks; + + private BlockIngredient(Block block, TagKey tag) { + super(Stream.of()); + this.block = block; + this.tag = tag; + } + + public BlockIngredient(Optional blockOptional, Optional> tagOptional) { + this(blockOptional.map(ForgeRegistries.BLOCKS::getValue).orElse(null), tagOptional.orElse(null)); + } + + public BlockIngredient(Block block) { + this(block, null); + } + + public BlockIngredient(TagKey tag) { + this(null, tag); + } + + @Override + public boolean test(ItemStack itemStack) { + if (itemStack == null || itemStack.isEmpty()) { + return false; + } + Iterable blockIterator; + if (this.block != null) { + blockIterator = List.of(this.block); + } else { + blockIterator = Objects.requireNonNull(ForgeRegistries.BLOCKS.tags()).getTag(this.tag); + } + for (Block block : blockIterator) { + RegistryUtil.BucketBlock bucketBlock = RegistryUtil.getBucketBlock(block); + if (bucketBlock != null) { + if (itemStack.getItem() == bucketBlock.bucketItem()) { + return true; + } + return BucketLibUtil.getBlock(itemStack) == block; + } + } + return false; + } + + @Override + @Nonnull + public ItemStack[] getItems() { + if (this.matchingStacks == null) { + ArrayList stacks = new ArrayList<>(); + List blocks = new ArrayList<>(); + ITagManager blockTags = ForgeRegistries.BLOCKS.tags(); + if (this.tag != null && blockTags != null) { + blockTags.getTag(this.tag).forEach(blocks::add); + } else if (this.block != null) { + blocks.add(this.block); + } + List bucketBlocks = RegistryUtil.getBucketBlocks().stream().filter(bucketBlock -> blocks.contains(bucketBlock.block())).toList(); + //vanilla buckets + for (RegistryUtil.BucketBlock bucketBlock : bucketBlocks) { + stacks.add(new ItemStack(bucketBlock.bucketItem())); + } + //bucket lib buckets + for (RegistryUtil.BucketBlock bucketBlock : bucketBlocks) { + BucketLibMod.getRegisteredBuckets().forEach(bucket -> { + if (bucket.canHoldBlock(bucketBlock.block())) { + stacks.add(BucketLibUtil.addBlock(new ItemStack(bucket), bucketBlock.block())); + } + }); + } + this.matchingStacks = stacks.toArray(new ItemStack[0]); + } + return this.matchingStacks; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean isSimple() { + return false; + } + + @Override + protected void invalidate() { + this.matchingStacks = null; + } + + @Override + @Nonnull + public IIngredientSerializer serializer() { + return SERIALIZER; + } + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + ResourceLocation.CODEC.optionalFieldOf("block").forGetter(i -> Optional.ofNullable(ForgeRegistries.BLOCKS.getKey(i.block))), + TagKey.codec(ForgeRegistries.BLOCKS.getRegistryKey()).optionalFieldOf("tag").forGetter(i -> Optional.ofNullable(i.tag)) + ).apply(builder, BlockIngredient::new) + ); + + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { + + @Override + public Codec codec() { + return CODEC; + } + + @Override + public BlockIngredient read(FriendlyByteBuf buffer) { + String block = buffer.readUtf(); + String tagId = buffer.readUtf(); + if (!tagId.isEmpty()) { + TagKey tag = TagKey.create(ForgeRegistries.BLOCKS.getRegistryKey(), new ResourceLocation(tagId)); + return new BlockIngredient(tag); + } + if (block.isEmpty()) { + throw new IllegalArgumentException("Cannot create a block ingredient with no block or tag."); + } + return new BlockIngredient(ForgeRegistries.BLOCKS.getValue(new ResourceLocation(block))); + } + + @Override + public void write(@Nonnull FriendlyByteBuf buffer, @Nonnull BlockIngredient ingredient) { + buffer.writeUtf(ingredient.block != null ? Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(ingredient.block)).toString() : ""); + buffer.writeUtf(ingredient.tag != null ? ingredient.tag.location().toString() : ""); + } + }; +} diff --git a/forge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java b/forge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java index 951de3b..ac57d90 100644 --- a/forge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java +++ b/forge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java @@ -70,7 +70,9 @@ public boolean test(ItemStack itemStack) { public ItemStack[] getItems() { if (this.matchingStacks == null) { ArrayList stacks = new ArrayList<>(); - stacks.add(new ItemStack(Items.BUCKET)); + if (this.item == null && this.tag == null) { + stacks.add(new ItemStack(Items.BUCKET)); + } BucketLibMod.getRegisteredBuckets().forEach(universalBucketItem -> { ItemStack universalBucketItemStack = new ItemStack(universalBucketItem); if (this.item != null && universalBucketItem == this.item diff --git a/forge/src/main/java/de/cech12/bucketlib/api/crafting/EntityIngredient.java b/forge/src/main/java/de/cech12/bucketlib/api/crafting/EntityIngredient.java new file mode 100644 index 0000000..89891a2 --- /dev/null +++ b/forge/src/main/java/de/cech12/bucketlib/api/crafting/EntityIngredient.java @@ -0,0 +1,164 @@ +package de.cech12.bucketlib.api.crafting; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import de.cech12.bucketlib.BucketLibMod; +import de.cech12.bucketlib.util.BucketLibUtil; +import de.cech12.bucketlib.util.RegistryUtil; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.material.Fluids; +import net.minecraftforge.common.crafting.ingredients.AbstractIngredient; +import net.minecraftforge.common.crafting.ingredients.IIngredientSerializer; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.tags.ITagManager; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +public class EntityIngredient extends AbstractIngredient { + + protected final EntityType entityType; + protected final TagKey> tag; + private ItemStack[] matchingStacks; + + private EntityIngredient(EntityType entityType, TagKey> tag) { + super(Stream.of()); + this.entityType = entityType; + this.tag = tag; + } + + public EntityIngredient(Optional entityTypeOptional, Optional>> tagOptional) { + this(entityTypeOptional.map(ForgeRegistries.ENTITY_TYPES::getValue).orElse(null), tagOptional.orElse(null)); + } + + public EntityIngredient(EntityType entityType) { + this(entityType, null); + } + + public EntityIngredient(TagKey> tag) { + this(null, tag); + } + + @Override + public boolean test(ItemStack itemStack) { + if (itemStack == null || itemStack.isEmpty()) { + return false; + } + Iterable> entityTypeIterator; + if (this.entityType != null) { + entityTypeIterator = List.of(this.entityType); + } else { + entityTypeIterator = Objects.requireNonNull(ForgeRegistries.ENTITY_TYPES.tags()).getTag(this.tag); + } + for (EntityType entityType : entityTypeIterator) { + RegistryUtil.BucketEntity bucketEntity = RegistryUtil.getBucketEntity(entityType); + if (bucketEntity != null) { + if (itemStack.getItem() == bucketEntity.bucketItem()) { + return true; + } + return BucketLibUtil.getEntityType(itemStack) == entityType; + } + } + return false; + } + + @Override + @Nonnull + public ItemStack[] getItems() { + if (this.matchingStacks == null) { + ArrayList stacks = new ArrayList<>(); + List> entityTypes = new ArrayList<>(); + ITagManager> entityTypeTags = ForgeRegistries.ENTITY_TYPES.tags(); + if (this.tag != null && entityTypeTags != null) { + entityTypeTags.getTag(this.tag).forEach(entityTypes::add); + } else if (this.entityType != null) { + entityTypes.add(this.entityType); + } + List bucketEntities = RegistryUtil.getBucketEntities().stream().filter(bucketEntity -> entityTypes.contains(bucketEntity.entityType())).toList(); + //vanilla buckets + for (RegistryUtil.BucketEntity bucketEntity : bucketEntities) { + stacks.add(new ItemStack(bucketEntity.bucketItem())); + } + //bucket lib buckets + for (RegistryUtil.BucketEntity bucketEntity : bucketEntities) { + BucketLibMod.getRegisteredBuckets().forEach(bucket -> { + if (bucket.canHoldFluid(bucketEntity.fluid()) && bucket.canHoldEntity(bucketEntity.entityType())) { + ItemStack filledBucket = new ItemStack(bucket); + if (bucketEntity.fluid() != Fluids.EMPTY) { + filledBucket = BucketLibUtil.addFluid(filledBucket, bucketEntity.fluid()); + } + filledBucket = BucketLibUtil.addEntityType(filledBucket, bucketEntity.entityType()); + stacks.add(filledBucket); + } + }); + } + this.matchingStacks = stacks.toArray(new ItemStack[0]); + } + return this.matchingStacks; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean isSimple() { + return false; + } + + @Override + protected void invalidate() { + this.matchingStacks = null; + } + + @Override + @Nonnull + public IIngredientSerializer serializer() { + return SERIALIZER; + } + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + ResourceLocation.CODEC.optionalFieldOf("entity").forGetter(i -> Optional.ofNullable(ForgeRegistries.ENTITY_TYPES.getKey(i.entityType))), + TagKey.codec(ForgeRegistries.ENTITY_TYPES.getRegistryKey()).optionalFieldOf("tag").forGetter(i -> Optional.ofNullable(i.tag)) + ).apply(builder, EntityIngredient::new) + ); + + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { + + @Override + public Codec codec() { + return CODEC; + } + + @Override + public EntityIngredient read(FriendlyByteBuf buffer) { + String block = buffer.readUtf(); + String tagId = buffer.readUtf(); + if (!tagId.isEmpty()) { + TagKey> tag = TagKey.create(ForgeRegistries.ENTITY_TYPES.getRegistryKey(), new ResourceLocation(tagId)); + return new EntityIngredient(tag); + } + if (block.isEmpty()) { + throw new IllegalArgumentException("Cannot create an entity ingredient with no entity or tag."); + } + return new EntityIngredient(ForgeRegistries.ENTITY_TYPES.getValue(new ResourceLocation(block))); + } + + @Override + public void write(@Nonnull FriendlyByteBuf buffer, @Nonnull EntityIngredient ingredient) { + buffer.writeUtf(ingredient.entityType != null ? Objects.requireNonNull(ForgeRegistries.ENTITY_TYPES.getKey(ingredient.entityType)).toString() : ""); + buffer.writeUtf(ingredient.tag != null ? ingredient.tag.location().toString() : ""); + } + }; +} diff --git a/forge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java b/forge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java index 9277aef..7dd14dd 100644 --- a/forge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java +++ b/forge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java @@ -3,6 +3,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import de.cech12.bucketlib.BucketLibMod; +import de.cech12.bucketlib.util.BucketLibUtil; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; @@ -79,30 +80,27 @@ public boolean test(ItemStack itemStack) { public ItemStack[] getItems() { if (this.matchingStacks == null) { ArrayList stacks = new ArrayList<>(); - BucketLibMod.getRegisteredBuckets().forEach(universalBucketItem -> { - ItemStack stack = new ItemStack(universalBucketItem); - List fluids = new ArrayList<>(); - ITagManager fluidTags = ForgeRegistries.FLUIDS.tags(); - if (this.tag != null && fluidTags != null) { - fluidTags.getTag(this.tag).forEach(fluids::add); - } else if (this.fluid != null) { - fluids.add(this.fluid); + List fluids = new ArrayList<>(); + ITagManager fluidTags = ForgeRegistries.FLUIDS.tags(); + if (this.tag != null && fluidTags != null) { + fluidTags.getTag(this.tag).forEach(fluids::add); + } else if (this.fluid != null) { + fluids.add(this.fluid); + } + for (Fluid fluid : fluids) { + //vanilla bucket + Item bucketItem = fluid.getBucket(); + if (!(bucketItem instanceof BucketItem) || ((BucketItem) bucketItem).getFluid() != fluid) { + continue; //skip fluids that have no vanilla bucket } - for (Fluid fluid : fluids) { - Item bucketItem = fluid.getBucket(); - if (!(bucketItem instanceof BucketItem) || ((BucketItem) bucketItem).getFluid() != fluid) { - continue; + stacks.add(new ItemStack(bucketItem)); + //bucket lib buckets + BucketLibMod.getRegisteredBuckets().forEach(universalBucketItem -> { + if (universalBucketItem.canHoldFluid(fluid)) { + stacks.add(BucketLibUtil.addFluid(new ItemStack(universalBucketItem), fluid)); } - stacks.add(new ItemStack(fluid.getBucket())); - FluidStack fluidStack = new FluidStack(fluid, FluidType.BUCKET_VOLUME); - FluidUtil.getFluidHandler(stack).ifPresent(fluidHandler -> { - int filledAmount = fluidHandler.fill(fluidStack, IFluidHandler.FluidAction.EXECUTE); - if (filledAmount == FluidType.BUCKET_VOLUME) { - stacks.add(fluidHandler.getContainer()); - } - }); - } - }); + }); + } this.matchingStacks = stacks.toArray(new ItemStack[0]); } return this.matchingStacks; diff --git a/gradle.properties b/gradle.properties index 2c62be8..99bb19e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ # Project group=de.cech12.bucketlib -mod_version=3.1.1.0 +mod_version=3.1.2.0 mod_id=bucketlib mod_name=BucketLib mod_author=Cech12 diff --git a/neoforge/src/main/java/de/cech12/bucketlib/BucketLibMod.java b/neoforge/src/main/java/de/cech12/bucketlib/BucketLibMod.java index 4ddde89..a84f11e 100644 --- a/neoforge/src/main/java/de/cech12/bucketlib/BucketLibMod.java +++ b/neoforge/src/main/java/de/cech12/bucketlib/BucketLibMod.java @@ -3,7 +3,9 @@ import de.cech12.bucketlib.api.BucketLib; import de.cech12.bucketlib.api.BucketLibApi; import de.cech12.bucketlib.api.BucketLibTags; +import de.cech12.bucketlib.api.crafting.BlockIngredient; import de.cech12.bucketlib.api.crafting.EmptyIngredient; +import de.cech12.bucketlib.api.crafting.EntityIngredient; import de.cech12.bucketlib.api.crafting.FluidIngredient; import de.cech12.bucketlib.api.crafting.MilkIngredient; import de.cech12.bucketlib.api.item.UniversalBucketItem; @@ -57,9 +59,11 @@ public class BucketLibMod { static { RECIPE_SERIALIZERS.register("bucket_dyeing", () -> BucketDyeingRecipe.Serializer.INSTANCE); + INGREDIENT_TYPES.register("block", () -> BlockIngredient.TYPE); + INGREDIENT_TYPES.register("empty", () -> EmptyIngredient.TYPE); + INGREDIENT_TYPES.register("entity", () -> EntityIngredient.TYPE); INGREDIENT_TYPES.register("fluid", () -> FluidIngredient.TYPE); INGREDIENT_TYPES.register("milk", () -> MilkIngredient.TYPE); - INGREDIENT_TYPES.register("empty", () -> EmptyIngredient.TYPE); } private static final Logger LOGGER = LogManager.getLogger(); diff --git a/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/BlockIngredient.java b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/BlockIngredient.java new file mode 100644 index 0000000..1f14a73 --- /dev/null +++ b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/BlockIngredient.java @@ -0,0 +1,123 @@ +package de.cech12.bucketlib.api.crafting; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import de.cech12.bucketlib.BucketLibMod; +import de.cech12.bucketlib.util.BucketLibUtil; +import de.cech12.bucketlib.util.RegistryUtil; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; +import net.neoforged.neoforge.common.crafting.IngredientType; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public class BlockIngredient extends Ingredient { + + protected final Block block; + protected final TagKey tag; + private ItemStack[] matchingStacks; + + private BlockIngredient(Block block, TagKey tag) { + super(Stream.of()); + this.block = block; + this.tag = tag; + } + + public BlockIngredient(Optional blockOptional, Optional> tagOptional) { + this(blockOptional.map(BuiltInRegistries.BLOCK::get).orElse(null), tagOptional.orElse(null)); + } + + public BlockIngredient(Block block) { + this(block, null); + } + + public BlockIngredient(TagKey tag) { + this(null, tag); + } + + @Override + public boolean test(ItemStack itemStack) { + if (itemStack == null || itemStack.isEmpty()) { + return false; + } + List bucketBlocks; + if (this.block != null) { + RegistryUtil.BucketBlock bucketBlock = RegistryUtil.getBucketBlock(this.block); + if (bucketBlock == null) { + return false; + } + bucketBlocks = List.of(bucketBlock); + } else { + bucketBlocks = RegistryUtil.getBucketBlocks().stream().filter(bucketBlock -> bucketBlock.block().defaultBlockState().is(this.tag)).toList(); + } + for (RegistryUtil.BucketBlock bucketBlock : bucketBlocks) { + if (itemStack.getItem() == bucketBlock.bucketItem()) { + return true; + } + return BucketLibUtil.getBlock(itemStack) == bucketBlock.block(); + } + return false; + } + + @Override + @Nonnull + public ItemStack[] getItems() { + if (this.matchingStacks == null) { + ArrayList stacks = new ArrayList<>(); + List blocks = new ArrayList<>(); + Optional> blockTag = Optional.empty(); + if (this.tag != null) { + blockTag = BuiltInRegistries.BLOCK.getTag(this.tag); + } + if (blockTag.isPresent()) { + blockTag.get().forEach(block -> blocks.add(block.value())); + } else if (this.block != null) { + blocks.add(this.block); + } + List bucketBlocks = RegistryUtil.getBucketBlocks().stream().filter(bucketBlock -> blocks.contains(bucketBlock.block())).toList(); + //vanilla buckets + for (RegistryUtil.BucketBlock bucketBlock : bucketBlocks) { + stacks.add(new ItemStack(bucketBlock.bucketItem())); + } + //bucket lib buckets + for (RegistryUtil.BucketBlock bucketBlock : bucketBlocks) { + BucketLibMod.getRegisteredBuckets().forEach(bucket -> { + if (bucket.canHoldBlock(bucketBlock.block())) { + stacks.add(BucketLibUtil.addBlock(new ItemStack(bucket), bucketBlock.block())); + } + }); + } + this.matchingStacks = stacks.toArray(new ItemStack[0]); + } + return this.matchingStacks; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean isSimple() { + return false; + } + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + ResourceLocation.CODEC.optionalFieldOf("block").forGetter(i -> Optional.of(BuiltInRegistries.BLOCK.getKey(i.block))), + TagKey.codec(BuiltInRegistries.BLOCK.key()).optionalFieldOf("tag").forGetter(i -> Optional.ofNullable(i.tag)) + ).apply(builder, BlockIngredient::new) + ); + + public static final IngredientType TYPE = new IngredientType<>(CODEC); + +} diff --git a/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java index d105247..ef5ebf3 100644 --- a/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java +++ b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/EmptyIngredient.java @@ -67,7 +67,9 @@ public boolean test(ItemStack itemStack) { public ItemStack[] getItems() { if (this.matchingStacks == null) { ArrayList stacks = new ArrayList<>(); - stacks.add(new ItemStack(Items.BUCKET)); + if (this.item == null && this.tag == null) { + stacks.add(new ItemStack(Items.BUCKET)); + } BucketLibMod.getRegisteredBuckets().forEach(universalBucketItem -> { ItemStack universalBucketItemStack = new ItemStack(universalBucketItem); if (this.item != null && universalBucketItem == this.item diff --git a/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/EntityIngredient.java b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/EntityIngredient.java new file mode 100644 index 0000000..cdf5a46 --- /dev/null +++ b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/EntityIngredient.java @@ -0,0 +1,129 @@ +package de.cech12.bucketlib.api.crafting; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import de.cech12.bucketlib.BucketLibMod; +import de.cech12.bucketlib.util.BucketLibUtil; +import de.cech12.bucketlib.util.RegistryUtil; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.neoforge.common.crafting.IngredientType; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +public class EntityIngredient extends Ingredient { + + protected final EntityType entityType; + protected final TagKey> tag; + private ItemStack[] matchingStacks; + + private EntityIngredient(EntityType entityType, TagKey> tag) { + super(Stream.of()); + this.entityType = entityType; + this.tag = tag; + } + + public EntityIngredient(Optional blockOptional, Optional>> tagOptional) { + this(blockOptional.map(BuiltInRegistries.ENTITY_TYPE::get).orElse(null), tagOptional.orElse(null)); + } + + public EntityIngredient(EntityType entityType) { + this(entityType, null); + } + + public EntityIngredient(TagKey> tag) { + this(null, tag); + } + + @Override + public boolean test(ItemStack itemStack) { + if (itemStack == null || itemStack.isEmpty()) { + return false; + } + List bucketEntities; + if (this.entityType != null) { + RegistryUtil.BucketEntity bucketEntity = RegistryUtil.getBucketEntity(this.entityType); + if (bucketEntity == null) { + return false; + } + bucketEntities = List.of(bucketEntity); + } else { + bucketEntities = RegistryUtil.getBucketEntities().stream().filter(bucketBlock -> bucketBlock.entityType().is(this.tag)).toList(); + } + for (RegistryUtil.BucketEntity bucketEntity : bucketEntities) { + if (itemStack.getItem() == bucketEntity.bucketItem()) { + return true; + } + return BucketLibUtil.getEntityType(itemStack) == bucketEntity.entityType(); + } + return false; + } + + @Override + @Nonnull + public ItemStack[] getItems() { + if (this.matchingStacks == null) { + ArrayList stacks = new ArrayList<>(); + List> entityTypes = new ArrayList<>(); + Optional>> entityTag = Optional.empty(); + if (this.tag != null) { + entityTag = BuiltInRegistries.ENTITY_TYPE.getTag(this.tag); + } + if (entityTag.isPresent()) { + entityTag.get().forEach(fluid -> entityTypes.add(fluid.value())); + } else if (this.entityType != null) { + entityTypes.add(this.entityType); + } + List bucketEntities = RegistryUtil.getBucketEntities().stream().filter(bucketEntity -> entityTypes.contains(bucketEntity.entityType())).toList(); + //vanilla buckets + for (RegistryUtil.BucketEntity bucketEntity : bucketEntities) { + stacks.add(new ItemStack(bucketEntity.bucketItem())); + } + //bucket lib buckets + for (RegistryUtil.BucketEntity bucketEntity : bucketEntities) { + BucketLibMod.getRegisteredBuckets().forEach(bucket -> { + if (bucket.canHoldFluid(bucketEntity.fluid()) && bucket.canHoldEntity(bucketEntity.entityType())) { + ItemStack filledBucket = new ItemStack(bucket); + if (bucketEntity.fluid() != Fluids.EMPTY) { + filledBucket = BucketLibUtil.addFluid(filledBucket, bucketEntity.fluid()); + } + filledBucket = BucketLibUtil.addEntityType(filledBucket, bucketEntity.entityType()); + stacks.add(filledBucket); + } + }); + } + this.matchingStacks = stacks.toArray(new ItemStack[0]); + } + return this.matchingStacks; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean isSimple() { + return false; + } + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + ResourceLocation.CODEC.optionalFieldOf("entity").forGetter(i -> Optional.of(BuiltInRegistries.ENTITY_TYPE.getKey(i.entityType))), + TagKey.codec(BuiltInRegistries.ENTITY_TYPE.key()).optionalFieldOf("tag").forGetter(i -> Optional.ofNullable(i.tag)) + ).apply(builder, EntityIngredient::new) + ); + + public static final IngredientType TYPE = new IngredientType<>(CODEC); + +} diff --git a/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java index ebfe13d..f1ab6db 100644 --- a/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java +++ b/neoforge/src/main/java/de/cech12/bucketlib/api/crafting/FluidIngredient.java @@ -3,6 +3,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import de.cech12.bucketlib.BucketLibMod; +import de.cech12.bucketlib.util.BucketLibUtil; import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; @@ -75,33 +76,30 @@ public boolean test(ItemStack itemStack) { public ItemStack[] getItems() { if (this.matchingStacks == null) { ArrayList stacks = new ArrayList<>(); - BucketLibMod.getRegisteredBuckets().forEach(universalBucketItem -> { - ItemStack stack = new ItemStack(universalBucketItem); - List fluids = new ArrayList<>(); - Optional> fluidTag = Optional.empty(); - if (this.tag != null) { - fluidTag = BuiltInRegistries.FLUID.getTag(this.tag); + List fluids = new ArrayList<>(); + Optional> fluidTag = Optional.empty(); + if (this.tag != null) { + fluidTag = BuiltInRegistries.FLUID.getTag(this.tag); + } + if (fluidTag.isPresent()) { + fluidTag.get().forEach(fluid -> fluids.add(fluid.value())); + } else if (this.fluid != null) { + fluids.add(this.fluid); + } + for (Fluid fluid : fluids) { + //vanilla bucket + Item bucketItem = fluid.getBucket(); + if (!(bucketItem instanceof BucketItem) || ((BucketItem) bucketItem).getFluid() != fluid) { + continue; //skip fluids that have no vanilla bucket } - if (fluidTag.isPresent()) { - fluidTag.get().forEach(fluid -> fluids.add(fluid.value())); - } else if (this.fluid != null) { - fluids.add(this.fluid); - } - for (Fluid fluid : fluids) { - Item bucketItem = fluid.getBucket(); - if (!(bucketItem instanceof BucketItem) || ((BucketItem) bucketItem).getFluid() != fluid) { - continue; + stacks.add(new ItemStack(bucketItem)); + //bucket lib buckets + BucketLibMod.getRegisteredBuckets().forEach(universalBucketItem -> { + if (universalBucketItem.canHoldFluid(fluid)) { + stacks.add(BucketLibUtil.addFluid(new ItemStack(universalBucketItem), fluid)); } - stacks.add(new ItemStack(fluid.getBucket())); - FluidStack fluidStack = new FluidStack(fluid, FluidType.BUCKET_VOLUME); - FluidUtil.getFluidHandler(stack).ifPresent(fluidHandler -> { - int filledAmount = fluidHandler.fill(fluidStack, IFluidHandler.FluidAction.EXECUTE); - if (filledAmount == FluidType.BUCKET_VOLUME) { - stacks.add(fluidHandler.getContainer()); - } - }); - } - }); + }); + } this.matchingStacks = stacks.toArray(new ItemStack[0]); } return this.matchingStacks;