diff --git a/patches/minecraft/net/minecraft/world/inventory/GrindstoneMenu.java.patch b/patches/minecraft/net/minecraft/world/inventory/GrindstoneMenu.java.patch index 0d498a63db..8f31fefb4a 100644 --- a/patches/minecraft/net/minecraft/world/inventory/GrindstoneMenu.java.patch +++ b/patches/minecraft/net/minecraft/world/inventory/GrindstoneMenu.java.patch @@ -1,5 +1,53 @@ --- a/net/minecraft/world/inventory/GrindstoneMenu.java +++ b/net/minecraft/world/inventory/GrindstoneMenu.java +@@ -34,6 +_,7 @@ + } + }; + private final ContainerLevelAccess f_39561_; ++ private int xp = -1; + + public GrindstoneMenu(int p_39563_, Inventory p_39564_) { + this(p_39563_, p_39564_, ContainerLevelAccess.f_39287_); +@@ -44,12 +_,12 @@ + this.f_39561_ = p_39568_; + this.m_38897_(new Slot(this.f_39560_, 0, 49, 19) { + public boolean m_5857_(ItemStack p_39607_) { +- return p_39607_.m_41763_() || p_39607_.m_150930_(Items.f_42690_) || p_39607_.m_41793_(); ++ return true; //Allow all items in the slot, not just repairable + } + }); + this.m_38897_(new Slot(this.f_39560_, 1, 49, 40) { + public boolean m_5857_(ItemStack p_39616_) { +- return p_39616_.m_41763_() || p_39616_.m_150930_(Items.f_42690_) || p_39616_.m_41793_(); ++ return true; //Allow all items in the slot, not just repairable + } + }); + this.m_38897_(new Slot(this.f_39559_, 2, 129, 34) { +@@ -58,6 +_,7 @@ + } + + public void m_142406_(Player p_150574_, ItemStack p_150575_) { ++ if (net.minecraftforge.common.ForgeHooks.onGrindstoneTake(GrindstoneMenu.this.f_39560_, p_39568_, this::m_39631_)) return; + p_39568_.m_39292_((p_39634_, p_39635_) -> { + if (p_39634_ instanceof ServerLevel) { + ExperienceOrb.m_147082_((ServerLevel)p_39634_, Vec3.m_82512_(p_39635_), this.m_39631_(p_39634_)); +@@ -70,6 +_,7 @@ + } + + private int m_39631_(Level p_39632_) { ++ if (xp > -1) return xp; + int l = 0; + l += this.m_39636_(GrindstoneMenu.this.f_39560_.m_8020_(0)); + l += this.m_39636_(GrindstoneMenu.this.f_39560_.m_8020_(1)); +@@ -122,6 +_,8 @@ + ItemStack itemstack1 = this.f_39560_.m_8020_(1); + boolean flag = !itemstack.m_41619_() || !itemstack1.m_41619_(); + boolean flag1 = !itemstack.m_41619_() && !itemstack1.m_41619_(); ++ this.xp = net.minecraftforge.common.ForgeHooks.onGrindstoneChange(itemstack, itemstack1, this.f_39559_, this.xp); ++ if (this.xp != Integer.MIN_VALUE) return; + if (!flag) { + this.f_39559_.m_6836_(0, ItemStack.f_41583_); + } else { @@ -143,12 +_,13 @@ } diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 72b1e13f8c..ce444a6eef 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -9,6 +9,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Matcher; @@ -37,18 +38,29 @@ import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.core.*; import net.minecraft.network.chat.*; import net.minecraft.network.chat.contents.LiteralContents; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.packs.PackType; import net.minecraft.tags.BlockTags; import net.minecraft.tags.TagEntry; import net.minecraft.tags.TagKey; import net.minecraft.util.datafix.fixes.StructuresBecomeConfiguredFix; +import net.minecraft.world.Difficulty; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.ExperienceOrb; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.DefaultAttributes; -import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.monster.EnderMan; +import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.item.*; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.RecipeType; @@ -65,11 +77,6 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.world.item.enchantment.EnchantmentHelper; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.MobSpawnType; -import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.server.level.ServerPlayer; @@ -86,7 +93,6 @@ import net.minecraft.world.item.alchemy.Potion; import net.minecraft.world.item.alchemy.PotionUtils; import net.minecraft.stats.Stats; import net.minecraft.world.phys.Vec3; -import net.minecraft.world.*; import net.minecraft.world.level.BaseSpawner; import net.minecraft.world.level.block.entity.FurnaceBlockEntity; import net.minecraft.world.level.block.entity.BlockEntity; @@ -107,6 +113,7 @@ import net.minecraftforge.common.util.MavenVersionStringHelper; import net.minecraftforge.event.AnvilUpdateEvent; import net.minecraftforge.event.DifficultyChangeEvent; import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.GrindstoneEvent; import net.minecraftforge.event.ItemAttributeModifierEvent; import net.minecraftforge.event.ModMismatchEvent; import net.minecraftforge.event.ServerChatEvent; @@ -734,6 +741,37 @@ public class ForgeHooks return e.getBreakChance(); } + public static int onGrindstoneChange(@NotNull ItemStack top, @NotNull ItemStack bottom, Container outputSlot, int xp) + { + GrindstoneEvent.OnplaceItem e = new GrindstoneEvent.OnplaceItem(top, bottom, xp); + if (MinecraftForge.EVENT_BUS.post(e)) return e.getXp(); + if (e.getOutput().isEmpty()) return Integer.MIN_VALUE; + + outputSlot.setItem(0, e.getOutput()); + return e.getXp(); + } + + public static boolean onGrindstoneTake(Container inputSlots, ContainerLevelAccess access, Function xpFunction) + { + access.execute((l,p) -> { + int xp = xpFunction.apply(l); + GrindstoneEvent.OnTakeItem e = new GrindstoneEvent.OnTakeItem(inputSlots.getItem(0), inputSlots.getItem(1), xp); + if (MinecraftForge.EVENT_BUS.post(e)) + { + return; + } + if (l instanceof ServerLevel) + { + ExperienceOrb.award((ServerLevel)l, Vec3.atCenterOf(p), e.getXp()); + } + l.levelEvent(1042, p, 0); + inputSlots.setItem(0, e.getNewTopItem()); + inputSlots.setItem(1, e.getNewBottomItem()); + inputSlots.setChanged(); + }); + return true; + } + private static ThreadLocal craftingPlayer = new ThreadLocal(); public static void setCraftingPlayer(Player player) { diff --git a/src/main/java/net/minecraftforge/event/GrindstoneEvent.java b/src/main/java/net/minecraftforge/event/GrindstoneEvent.java new file mode 100644 index 0000000000..c363ee3fda --- /dev/null +++ b/src/main/java/net/minecraftforge/event/GrindstoneEvent.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.event; + +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.eventbus.api.Cancelable; +import net.minecraftforge.eventbus.api.Event; + +public abstract class GrindstoneEvent extends Event +{ + private final ItemStack top; + private final ItemStack bottom; + private int xp; + + protected GrindstoneEvent(ItemStack top, ItemStack bottom, int xp) + { + this.top = top; + this.bottom = bottom; + this.xp = xp; + } + + /** + * @return The item in the top input grindstone slot.
+ */ + public ItemStack getTopItem() + { + return top; + } + + /** + * @return The item in the bottom input grindstone slot.
+ */ + public ItemStack getBottomItem() + { + return bottom; + } + + /** + * This is the experience amount determined by the event, not the vanilla behavior.
+ * If you are the first receiver of this event, it is guaranteed to be -1.
+ * The value must be set to a positive value to override vanilla behavior.
+ * if the value is equal to -1, the vanilla behavior for calculating experience will run.
+ * @return The experience amount given to the player.
+ */ + public int getXp() + { + return xp; + } + + /** + * Sets the experience amount.
+ * @param xp The experience amount given to the player.
+ */ + public void setXp(int xp) + { + this.xp = xp; + } + + /** + * This event is {@link Cancelable}
+ * {@link OnplaceItem} is fired when the inputs to a grindstone are changed.
+ * It is called from {@link GrindstoneMenu#createResult()}.
+ * If the event is canceled, vanilla behavior will not run, and the output will be set to {@link ItemStack#EMPTY}.
+ * If the event is not canceled, but the output is not empty, it will set the output and not run vanilla behavior.
+ * if the output is empty, and the event is not canceled, vanilla behavior will execute.
+ * if the amount of experience is larger than or equal 0, the vanilla behavior for calculating experience will not run.
+ */ + @Cancelable + public static class OnplaceItem extends GrindstoneEvent + { + private ItemStack output; + + public OnplaceItem(ItemStack top, ItemStack bottom, int xp) + { + super(top, bottom, xp); + this.output = ItemStack.EMPTY; + } + + /** + * This is the output as determined by the event, not by the vanilla behavior between these two items.
+ * If you are the first receiver of this event, it is guaranteed to be empty.
+ * It will only be non-empty if changed by an event handler.
+ * If this event is cancelled, this output stack is discarded.
+ * @return The item to set in the output grindstone slot.
+ */ + public ItemStack getOutput() + { + return output; + } + + /** + * Sets the output slot to a specific itemstack. + * @param output The stack to change the output to. + */ + public void setOutput(ItemStack output) + { + this.output = output; + } + } + + /** + * This event is {@link Cancelable}
+ * {@link OnTakeItem} is fired when the output in a grindstone are is taken.
+ * It is called from {@link GrindstoneMenu#GrindstoneMenu(int, Inventory)}.
+ * If the event is canceled, vanilla behavior will not run, and no inputs will be consumed.
+ * if the amount of experience is larger than or equal 0, the vanilla behavior for calculating experience will not run.
+ */ + @Cancelable + public static class OnTakeItem extends GrindstoneEvent + { + private ItemStack newTop = ItemStack.EMPTY; + private ItemStack newBottom = ItemStack.EMPTY; + + public OnTakeItem(ItemStack top, ItemStack bottom, int xp) + { + super(top, bottom, xp); + } + + /** + * @return The item in that will be in the top input grindstone slot after the event.
+ */ + public ItemStack getNewTopItem() + { + return newTop; + } + + /** + * @return The item in that will be in the bottom input grindstone slot after the event.
+ */ + public ItemStack getNewBottomItem() + { + return newBottom; + } + + /** + * Sets the itemstack in the top slot.
+ * @param newTop + */ + public void setNewTopItem(ItemStack newTop) + { + this.newTop = newTop; + } + + /** + * Sets the itemstack in the bottom slot.
+ * @param newBottom + */ + public void setNewBottomItem(ItemStack newBottom) + { + this.newBottom = newBottom; + } + + /** + * This is the experience amount that will be returned by the event.
+ * @return The experience amount given to the player.
+ */ + public int getXp() + { + return super.getXp(); + } + } +}