diff --git a/library/item/enchantment/build.gradle b/library/item/enchantment/build.gradle new file mode 100644 index 0000000000..85406a7614 --- /dev/null +++ b/library/item/enchantment/build.gradle @@ -0,0 +1,18 @@ +plugins { + id("qsl.module") +} + +qslModule { + name = "Quilt Enchantment API" + moduleName = "enchantment" + id = "quilt_enchantment" + description = "An API for custom enchantments." + library = "item" + moduleDependencies { + core { + api("qsl_base") + testmodOnly("resource_loader") + } + } + accessWidener() +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/EnchantmentEvents.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/EnchantmentEvents.java new file mode 100644 index 0000000000..89e4ad5d4a --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/EnchantmentEvents.java @@ -0,0 +1,70 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api; + +import java.util.List; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.EnchantmentLevelEntry; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.AnvilScreenHandler; + +import org.quiltmc.qsl.base.api.event.Event; +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; + +public final class EnchantmentEvents { + /** + * An event that is called after random enchantments are generated (e.g. for an enchantment table), but before they are selected to be applied on an item. + * @see EnchantmentHelper#getPossibleEntries(int, ItemStack, boolean) + */ + public static final Event MODIFY_POSSIBLE_ENCHANTMENTS = Event.create(ModifyPossibleEnchantments.class, callbacks -> (possibleEnchantments, context) -> { + for (var callback : callbacks) { + callback.modifyPossibleEntries(possibleEnchantments, context); + } + }); + + /** + * An event that is called when applying an {@link Enchantment} to an {@link Item} in an anvil. + * @see AnvilScreenHandler#updateResult() + */ + public static final Event ANVIL_APPLICATION = Event.create(AnvilApplication.class, callbacks -> (enchantment, context) -> { + for (var callback : callbacks) { + if (!callback.canApply(enchantment, context)) { + return false; + } + } + + return true; + }); + + @FunctionalInterface + public interface ModifyPossibleEnchantments { + @Contract(mutates = "param1") + void modifyPossibleEntries(List possibleEnchantments, @Nullable EnchantingContext context); + } + + @FunctionalInterface + public interface AnvilApplication { + @Contract(pure = true) + boolean canApply(Enchantment enchantment, @Nullable EnchantingContext context); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantableItem.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantableItem.java new file mode 100644 index 0000000000..82f42ba17d --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantableItem.java @@ -0,0 +1,41 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api; + +import org.jetbrains.annotations.Contract; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +/** + * An interface for extending an {@link Item} with additional control over enchanting. + */ +public interface QuiltEnchantableItem { + /** + * Determines whether the provided enchantment can be applied to this item. + *

+ * This takes highest priority for applying enchantments. + * @param stack the stack containing this item + * @param enchantment the enchantment to apply to this item + * @return {@code true} if the enchantment can be applied, or {@code false} otherwise + */ + @Contract(pure = true) + default boolean canEnchant(ItemStack stack, Enchantment enchantment) { + return enchantment.isAcceptableItem(stack); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantment.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantment.java new file mode 100644 index 0000000000..43e28a2e6f --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantment.java @@ -0,0 +1,85 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api; + +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemStack; + +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; + +/** + * An extension of the default {@link Enchantment} class. + *

+ * Allows for custom weighting in randomized enchanting contexts, as well as custom logic for application in the anvil. + */ +public abstract class QuiltEnchantment extends Enchantment { + public QuiltEnchantment(Rarity rarity, @Nullable EnchantmentTarget type, EquipmentSlot[] slotTypes) { + super(rarity, type, slotTypes); + } + + /** + * Return an integer value that represents the weight of this enchantment given the current context. + *

+ * If 0, then this enchantment won't be added. + * @param context the context of the enchanting + * @return the context-aware weight for the enchantment + */ + public @Range(from = 0, to = Integer.MAX_VALUE) int weightFromContext(EnchantingContext context) { + return this.getRarity().getWeight(); + } + + /** + * Determines if the given enchantment can be applied under the current context. + *

+ * Note: {@link QuiltEnchantableItem#canEnchant(ItemStack, Enchantment)} takes priority. + * @see QuiltEnchantableItem#canEnchant(ItemStack, Enchantment) + * @param context the context of the enchanting + * @return {@code true} if this enchantment can be applied, or {@code false} otherwise + */ + public boolean isAcceptableContext(EnchantingContext context) { + if (!context.ignoresPower() && this.isAcceptablePower(context.getLevel(), context.getPower())) { + return false; + } + + return this.isAcceptableItem(context.getStack()); + } + + /** + * Determines if the provided power is within the valid range for this enchantment at the provided level. + * @param level the level of this enchantment being queried + * @param power the power of the current enchanting context + * @return {@code true} if the power is within the valid range, or {@code false} otherwise + */ + public boolean isAcceptablePower(int level, int power) { + return power >= this.getMinPower(level) && power <= this.getMaxPower(level); + } + + /** + * Determines if the given enchantment should be visible in the creative tab. + * @param visibility in what context the stack visibility is tested + * @return {@code true} if this enchantment should be visible, or {@code false} otherwise + */ + public boolean isVisible(ItemGroup.Visibility visibility) { + return true; + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantmentHelper.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantmentHelper.java new file mode 100644 index 0000000000..5c72975a4f --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/QuiltEnchantmentHelper.java @@ -0,0 +1,61 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.item.ItemStack; +import net.minecraft.util.random.RandomGenerator; + +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; + +/** + * Allows modded systems that enchant items to apply an enchanting context. + */ +public final class QuiltEnchantmentHelper { + private static final ThreadLocal CONTEXT = new ThreadLocal<>(); + + /** + * Set the enchanting context for enchantments to use. + *

+ * Note: Almost all the base values, bar the {@link net.minecraft.world.World world}, are provided to the context + * when using {@link EnchantmentHelper#enchant(RandomGenerator, ItemStack, int, boolean)} or + * {@link EnchantmentHelper#generateEnchantments(RandomGenerator, ItemStack, int, boolean)}. + * @param context the enchanting context + */ + public static void setContext(EnchantingContext context) { + CONTEXT.set(context); + } + + /** + * Gets the current enchanting context. + * @return the enchanting context + */ + public static @Nullable EnchantingContext getContext() { + return CONTEXT.get(); + } + + /** + * Clears the current enchanting context. + *

+ * This should be used to ensure that no information bleeds into other contexts. + */ + public static void clearContext() { + CONTEXT.remove(); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/EnchantingContext.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/EnchantingContext.java new file mode 100644 index 0000000000..9ef5588c5b --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/EnchantingContext.java @@ -0,0 +1,98 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api.context; + +import org.jetbrains.annotations.Contract; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.random.RandomGenerator; + +/** + * A class that contains contextual information about the enchantment process. + */ +public class EnchantingContext { + protected final int level; + protected final int power; + protected final ItemStack stack; + protected final RandomGenerator random; + protected final boolean treasureAllowed; + + public EnchantingContext(int level, int power, ItemStack stack, RandomGenerator random, boolean treasureAllowed) { + this.level = level; + this.power = power; + this.stack = stack; + this.random = random; + this.treasureAllowed = treasureAllowed; + } + + @Contract("_->new") + public EnchantingContext withLevel(int level) { + return new EnchantingContext(level, this.power, this.stack, this.random, this.treasureAllowed); + } + + @Contract("_->new") + public EnchantingContext withPower(int power) { + return new EnchantingContext(this.level, power, this.stack, this.random, this.treasureAllowed); + } + + @Contract("_,_,_->new") + public EnchantingContext withCoreContext(ItemStack stack, RandomGenerator random, boolean treasureAllowed) { + return new EnchantingContext(this.level, this.power, stack, random, treasureAllowed); + } + + /** + * @return an integer representing the current enchantment level being queried (ie. I, II, III, etc + */ + public int getLevel() { + return this.level; + } + + /** + * @return the current power of the enchanting context + */ + public int getPower() { + return this.power; + } + + /** + * @return the item stack for the enchantment to be applied to + */ + public ItemStack getStack() { + return this.stack; + } + + /** + * @return the random used for enchanting + */ + public RandomGenerator getRandom() { + return this.random; + } + + /** + * @return {@code true} if treasure enchantments are allowed, or {@code false} otherwise + */ + public boolean treasureAllowed() { + return this.treasureAllowed; + } + + /** + * @return {@code true} if this context ignores power (i.e. Applying enchantments in an anvil), or {@code false} otherwise + */ + public boolean ignoresPower() { + return this.power <= 0; + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/EntityEnchantingContext.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/EntityEnchantingContext.java new file mode 100644 index 0000000000..790f284fd7 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/EntityEnchantingContext.java @@ -0,0 +1,63 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api.context; + +import org.jetbrains.annotations.Contract; + +import net.minecraft.entity.Entity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.random.RandomGenerator; +import net.minecraft.world.World; + +/** + * A class that contains contextual information about the enchantment process. + *

+ * Contains further information about the entity performing the enchanting. + */ +public class EntityEnchantingContext extends WorldEnchantingContext { + protected final E entity; + + public EntityEnchantingContext(int level, int power, ItemStack stack, World world, RandomGenerator random, boolean treasureAllowed, E entity) { + super(level, power, stack, world, random, treasureAllowed); + this.entity = entity; + } + + @Override + @Contract("_->new") + public EntityEnchantingContext withLevel(int level) { + return new EntityEnchantingContext<>(level, this.power, this.stack, this.world, this.random, this.treasureAllowed, this.entity); + } + + @Override + @Contract("_->new") + public EntityEnchantingContext withPower(int power) { + return new EntityEnchantingContext<>(this.level, power, this.stack, this.world, this.random, this.treasureAllowed, this.entity); + } + + @Override + @Contract("_,_,_->new") + public EnchantingContext withCoreContext(ItemStack stack, RandomGenerator random, boolean treasureAllowed) { + return new EntityEnchantingContext<>(this.level, this.power, stack, this.world, random, treasureAllowed, this.entity); + } + + /** + * @return the entity performing the enchanting + */ + public E getEntity() { + return this.entity; + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/PlayerUsingBlockEnchantingContext.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/PlayerUsingBlockEnchantingContext.java new file mode 100644 index 0000000000..94064ad295 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/PlayerUsingBlockEnchantingContext.java @@ -0,0 +1,71 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api.context; + +import org.jetbrains.annotations.Contract; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.random.RandomGenerator; +import net.minecraft.world.World; + +/** + * A class that contains contextual information about the enchantment process. + *

+ * Contains further information about the player and block performing the enchanting. + */ +public class PlayerUsingBlockEnchantingContext extends EntityEnchantingContext { + protected final BlockPos pos; + + public PlayerUsingBlockEnchantingContext(int level, int power, ItemStack stack, World world, RandomGenerator random, boolean treasureAllowed, PlayerEntity player, BlockPos pos) { + super(level, power, stack, world, random, treasureAllowed, player); + this.pos = pos; + } + + @Override + @Contract("_->new") + public PlayerUsingBlockEnchantingContext withLevel(int level) { + return new PlayerUsingBlockEnchantingContext(level, this.power, this.stack, this.world, this.random, this.treasureAllowed, this.entity, this.pos); + } + + @Override + @Contract("_->new") + public EntityEnchantingContext withPower(int power) { + return new PlayerUsingBlockEnchantingContext(this.level, power, this.stack, this.world, this.random, this.treasureAllowed, this.entity, this.pos); + } + + @Override + @Contract("_,_,_->new") + public EnchantingContext withCoreContext(ItemStack stack, RandomGenerator random, boolean treasureAllowed) { + return new PlayerUsingBlockEnchantingContext(this.level, this.power, stack, this.world, random, treasureAllowed, this.entity, this.pos); + } + + /** + * @return the player performing the enchanting + */ + public PlayerEntity getPlayer() { + return this.getEntity(); + } + + /** + * @return the position of the block being used + */ + public BlockPos getPos() { + return this.pos; + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/WorldEnchantingContext.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/WorldEnchantingContext.java new file mode 100644 index 0000000000..dcf9a99163 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/api/context/WorldEnchantingContext.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.api.context; + +import org.jetbrains.annotations.Contract; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.random.RandomGenerator; +import net.minecraft.world.World; + +/** + * A class that contains contextual information about the enchantment process. + *

+ * Contains further information about the world the enchanting is happening in. + */ +public class WorldEnchantingContext extends EnchantingContext { + protected final World world; + + public WorldEnchantingContext(int level, int power, ItemStack stack, World world, RandomGenerator random, boolean treasureAllowed) { + super(level, power, stack, random, treasureAllowed); + this.world = world; + } + + @Contract("_->new") + public EnchantingContext withLevel(int level) { + return new WorldEnchantingContext(level, this.power, this.stack, this.world, this.random, this.treasureAllowed); + } + + @Contract("_->new") + public EnchantingContext withPower(int power) { + return new WorldEnchantingContext(this.level, power, this.stack, this.world, this.random, this.treasureAllowed); + } + + @Contract("_,_,_->new") + public EnchantingContext withCoreContext(ItemStack stack, RandomGenerator random, boolean treasureAllowed) { + return new WorldEnchantingContext(this.level, this.power, stack, this.world, random, treasureAllowed); + } + + /** + * @return the world in which the item is being enchanted + */ + public World getWorld() { + return this.world; + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/AnvilScreenHandlerMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/AnvilScreenHandlerMixin.java new file mode 100644 index 0000000000..1b41600814 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/AnvilScreenHandlerMixin.java @@ -0,0 +1,127 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.AnvilScreenHandler; +import net.minecraft.screen.ForgingScreenHandler; +import net.minecraft.screen.ScreenHandlerContext; +import net.minecraft.screen.ScreenHandlerType; + +import org.quiltmc.qsl.enchantment.api.EnchantmentEvents; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantableItem; +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; +import org.quiltmc.qsl.enchantment.api.context.PlayerUsingBlockEnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantment; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; + +@Mixin(AnvilScreenHandler.class) +public abstract class AnvilScreenHandlerMixin extends ForgingScreenHandler { + private int quilt$enchantLevel; + + public AnvilScreenHandlerMixin(@Nullable ScreenHandlerType type, int syncId, PlayerInventory playerInventory, ScreenHandlerContext context) { + super(type, syncId, playerInventory, context); + } + + @Inject( + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/item/EnchantedBookItem;getEnchantmentNbt(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/nbt/NbtList;" + ) + ), + method = "updateResult", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/enchantment/EnchantmentHelper;get(Lnet/minecraft/item/ItemStack;)Ljava/util/Map;", + ordinal = 0 + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void setAnvilContext(CallbackInfo ci, ItemStack ignored, int i, int j, int k, ItemStack input) { + this.context.run((world, pos) -> QuiltEnchantmentHelper.setContext(new PlayerUsingBlockEnchantingContext(0, 0, input, world, world.getRandom(), true, this.player, pos))); + } + + @ModifyVariable( + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Ljava/util/Map;getOrDefault(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;" + ), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/enchantment/Enchantment;isAcceptableItem(Lnet/minecraft/item/ItemStack;)Z" + ) + ), + method = "updateResult", + at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;"), + index = 14 + ) + private int captureEnchantLevel(int level) { + return this.quilt$enchantLevel = level; + } + + @Redirect(method = "updateResult", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/Enchantment;isAcceptableItem(Lnet/minecraft/item/ItemStack;)Z")) + private boolean checkWithContext(Enchantment enchantment, ItemStack stack) { + EnchantingContext context = null; + if (QuiltEnchantmentHelper.getContext() != null) { + context = QuiltEnchantmentHelper.getContext().withLevel(this.quilt$enchantLevel); + } + + if (!EnchantmentEvents.ANVIL_APPLICATION.invoker().canApply(enchantment, context)) { + return false; + } + + if (stack.getItem() instanceof QuiltEnchantableItem enchantableItem) { + return enchantableItem.canEnchant(stack, enchantment); + } + + if (enchantment instanceof QuiltEnchantment quiltEnchantment && context != null) { + return quiltEnchantment.isAcceptableContext(context); + } + + return enchantment.isAcceptableItem(stack); + } + + @Redirect(method = "updateResult", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/Enchantment;canCombine(Lnet/minecraft/enchantment/Enchantment;)Z")) + private boolean itemCanCombineOverride(Enchantment newEnchantment, Enchantment oldEnchantment) { + ItemStack stack = this.input.getStack(0); + if (stack.getItem() instanceof QuiltEnchantableItem enchantableItem) { + return enchantableItem.canEnchant(stack, newEnchantment); + } + + return newEnchantment.canCombine(oldEnchantment); + } + + @Inject(method = "updateResult", at = @At("RETURN")) + private void resetAnvilContext(CallbackInfo ci) { + QuiltEnchantmentHelper.clearContext(); + this.quilt$enchantLevel = 0; + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantCommandMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantCommandMixin.java new file mode 100644 index 0000000000..2bb6a45a3a --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantCommandMixin.java @@ -0,0 +1,52 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import java.util.Collection; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.item.ItemStack; +import net.minecraft.server.command.EnchantCommand; + +import org.quiltmc.qsl.enchantment.api.QuiltEnchantableItem; + +@Mixin(EnchantCommand.class) +public class EnchantCommandMixin { + @Unique + private static boolean quilt$execute$enchantableItemOverride = false; + + @Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/Enchantment;isAcceptableItem(Lnet/minecraft/item/ItemStack;)Z")) + private static boolean itemCanEnchantOverride(Enchantment enchantment, ItemStack stack) { + if (stack.getItem() instanceof QuiltEnchantableItem enchantableItem) { + return quilt$execute$enchantableItemOverride = enchantableItem.canEnchant(stack, enchantment); + } + + quilt$execute$enchantableItemOverride = false; + return enchantment.isAcceptableItem(stack); + } + + @Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/EnchantmentHelper;isCompatible(Ljava/util/Collection;Lnet/minecraft/enchantment/Enchantment;)Z")) + private static boolean itemCanEnchantOverride2(Collection existing, Enchantment candidate) { + return quilt$execute$enchantableItemOverride || EnchantmentHelper.isCompatible(existing, candidate); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantRandomlyLootFunctionMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantRandomlyLootFunctionMixin.java new file mode 100644 index 0000000000..58e4316daa --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantRandomlyLootFunctionMixin.java @@ -0,0 +1,59 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.item.ItemStack; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.function.EnchantRandomlyLootFunction; + +import org.quiltmc.qsl.enchantment.api.QuiltEnchantment; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; +import org.quiltmc.qsl.enchantment.api.context.WorldEnchantingContext; + +@Mixin(EnchantRandomlyLootFunction.class) +public class EnchantRandomlyLootFunctionMixin { + @Inject(method = "process", at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/Registry;stream()Ljava/util/stream/Stream;")) + private void setEnchantingLootContext(ItemStack stack, LootContext context, CallbackInfoReturnable cir) { + QuiltEnchantmentHelper.setContext(new WorldEnchantingContext(0, 0, stack, context.getWorld(), context.getRandom(), true)); + } + + @Redirect(method = "process", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;", ordinal = 1)) + private Stream filterWithContext(Stream stream, Predicate predicate) { + return stream.filter(enchantment -> { + if (enchantment instanceof QuiltEnchantment quiltEnchantment) { + return quiltEnchantment.isAcceptableContext(QuiltEnchantmentHelper.getContext()); + } + + return predicate.test(enchantment); + }); + } + + @Inject(method = "process", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;")) + private void removeEnchantingLootContext(ItemStack stack, LootContext context, CallbackInfoReturnable cir) { + QuiltEnchantmentHelper.clearContext(); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantWithLevelsLootFunctionMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantWithLevelsLootFunctionMixin.java new file mode 100644 index 0000000000..38fe4e31ad --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantWithLevelsLootFunctionMixin.java @@ -0,0 +1,48 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.item.ItemStack; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.function.EnchantWithLevelsLootFunction; + +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; +import org.quiltmc.qsl.enchantment.api.context.WorldEnchantingContext; + +@Mixin(EnchantWithLevelsLootFunction.class) +public class EnchantWithLevelsLootFunctionMixin { + @Shadow + @Final + boolean treasureEnchantmentsAllowed; + + @Inject(method = "process", at = @At("HEAD")) + private void setEnchantingLootContext(ItemStack stack, LootContext context, CallbackInfoReturnable cir) { + QuiltEnchantmentHelper.setContext(new WorldEnchantingContext(0, 0, stack, context.getWorld(), context.getRandom(), this.treasureEnchantmentsAllowed)); + } + + @Inject(method = "process", at = @At("RETURN")) + private void removeEnchantingLootContext(ItemStack stack, LootContext context, CallbackInfoReturnable cir) { + QuiltEnchantmentHelper.clearContext(); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantmentHelperMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantmentHelperMixin.java new file mode 100644 index 0000000000..832249b0f9 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantmentHelperMixin.java @@ -0,0 +1,132 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import java.util.Iterator; +import java.util.List; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.enchantment.EnchantmentLevelEntry; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.collection.Weight; +import net.minecraft.util.random.RandomGenerator; + +import org.quiltmc.qsl.enchantment.api.EnchantmentEvents; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantableItem; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantment; +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; + +@Mixin(value = EnchantmentHelper.class) +public abstract class EnchantmentHelperMixin { + @Inject(method = "generateEnchantments", at = @At("HEAD")) + private static void addRandomContext(RandomGenerator random, ItemStack stack, int experienceLevel, boolean treasureAllowed, CallbackInfoReturnable cir) { + if (QuiltEnchantmentHelper.getContext() != null) { + QuiltEnchantmentHelper.setContext(QuiltEnchantmentHelper.getContext().withCoreContext(stack, random, treasureAllowed)); + } else { + QuiltEnchantmentHelper.setContext(new EnchantingContext(0, 0, stack, random, treasureAllowed)); + } + } + + @Redirect(method = "getPossibleEntries", at = @At(value = "INVOKE", target = "Lnet/minecraft/registry/Registry;iterator()Ljava/util/Iterator;")) + private static Iterator removeCustomEnchants(Registry registry, int power, ItemStack stack) { + return registry.stream() + .filter((enchantment) -> !(enchantment instanceof QuiltEnchantment)) + .filter(enchantment -> quilt$checkItemEnchantOverride(stack, enchantment)) + .iterator(); + } + + @Redirect(method = "getPossibleEntries", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/EnchantmentTarget;isAcceptableItem(Lnet/minecraft/item/Item;)Z")) + private static boolean itemCanAcceptOverride(EnchantmentTarget type, Item item, int power, ItemStack stack) { + // From the iterator filtering, we know that if the item is a QuiltEnchantableItem, it accepts the enchantment. + return item instanceof QuiltEnchantableItem || type.isAcceptableItem(item); + } + + @Inject(method = "getPossibleEntries", at = @At("RETURN"), cancellable = true) + private static void handleCustomEnchants(int power, ItemStack stack, boolean treasureAllowed, CallbackInfoReturnable> callback) { + List entries = callback.getReturnValue(); + + Registries.ENCHANTMENT.stream().filter((enchantment) -> enchantment instanceof QuiltEnchantment && enchantment.isAvailableForRandomSelection()).forEach((enchantment) -> { + for (int level = enchantment.getMaxLevel(); level >= enchantment.getMinLevel(); level--) { + boolean validEntry = false; + EnchantmentLevelEntry entry = new EnchantmentLevelEntry(enchantment, level); + + if (quilt$checkItemEnchantOverride(stack, enchantment)) { + if (QuiltEnchantmentHelper.getContext() != null) { + EnchantingContext context = QuiltEnchantmentHelper.getContext().withLevel(level).withPower(power); + + int weight = 0; + if (((QuiltEnchantment) enchantment).isAcceptableContext(context)) { + weight = ((QuiltEnchantment) enchantment).weightFromContext(context); + } + + ((WeightedAbsentAccessor) entry).setWeight(Weight.of(weight)); + validEntry = weight > 0; + } else if ((!enchantment.isTreasure() || treasureAllowed) && (enchantment.isAcceptableItem(stack) || stack.isOf(Items.BOOK))) { + validEntry = true; + } + } + + if (validEntry) { + entries.add(entry); + break; + } + } + }); + + EnchantmentEvents.MODIFY_POSSIBLE_ENCHANTMENTS.invoker().modifyPossibleEntries(entries, QuiltEnchantmentHelper.getContext()); + + callback.setReturnValue(entries); + } + + @Redirect(method = "removeConflicts", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/Enchantment;canCombine(Lnet/minecraft/enchantment/Enchantment;)Z")) + private static boolean removeConflictsWithItemOverride(Enchantment pickedEnchantment, Enchantment possibleEnchantment) { + if (QuiltEnchantmentHelper.getContext() != null) { + ItemStack stack = QuiltEnchantmentHelper.getContext().getStack(); + if (stack.getItem() instanceof QuiltEnchantableItem enchantableItem) { + return enchantableItem.canEnchant(stack, possibleEnchantment); + } + } + + return pickedEnchantment.canCombine(possibleEnchantment); + } + + /** + * @return {@code true} if the item can be enchanted or the item is not a {@link QuiltEnchantableItem}, or {@code false} otherwise + */ + @Unique + private static boolean quilt$checkItemEnchantOverride(ItemStack stack, Enchantment enchantment) { + if (!(stack.getItem() instanceof QuiltEnchantableItem enchantableItem)) { + return true; + } + + return enchantableItem.canEnchant(stack, enchantment); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantmentScreenHandlerMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantmentScreenHandlerMixin.java new file mode 100644 index 0000000000..ecc8f06a59 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/EnchantmentScreenHandlerMixin.java @@ -0,0 +1,74 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.enchantment.EnchantmentLevelEntry; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.EnchantmentScreenHandler; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ScreenHandlerContext; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.util.random.RandomGenerator; + +import org.quiltmc.qsl.enchantment.api.context.PlayerUsingBlockEnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; + +@Mixin(EnchantmentScreenHandler.class) +public abstract class EnchantmentScreenHandlerMixin extends ScreenHandler { + @Shadow + @Final + private ScreenHandlerContext context; + @Shadow + @Final + private RandomGenerator random; + @Unique + private PlayerEntity quilt$player; + + protected EnchantmentScreenHandlerMixin(@Nullable ScreenHandlerType type, int syncId) { + super(type, syncId); + } + + @Inject(method = "(ILnet/minecraft/entity/player/PlayerInventory;Lnet/minecraft/screen/ScreenHandlerContext;)V", at = @At("TAIL")) + public void capturePlayer(int syncId, PlayerInventory playerInventory, ScreenHandlerContext context, CallbackInfo callback) { + this.quilt$player = playerInventory.player; + } + + @Inject(method = "generateEnchantments", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/EnchantmentHelper;generateEnchantments(Lnet/minecraft/util/random/RandomGenerator;Lnet/minecraft/item/ItemStack;IZ)Ljava/util/List;")) + private void setEnchantmentContext(ItemStack stack, int slot, int level, CallbackInfoReturnable> callback) { + // Level and power will be set later when those values are figured out. + this.context.run((world, pos) -> QuiltEnchantmentHelper.setContext(new PlayerUsingBlockEnchantingContext(0, 0, stack, world, this.random, false, this.quilt$player, pos))); + } + + @Inject(method = "generateEnchantments", at = @At("RETURN")) + private void clearEnchantmentContext(ItemStack stack, int slot, int level, CallbackInfoReturnable> callback) { + QuiltEnchantmentHelper.clearContext(); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/ItemGroupsMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/ItemGroupsMixin.java new file mode 100644 index 0000000000..6668b8d583 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/ItemGroupsMixin.java @@ -0,0 +1,46 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentTarget; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemGroups; +import net.minecraft.registry.HolderLookup; + +import org.quiltmc.qsl.enchantment.api.QuiltEnchantment; + +@Mixin(ItemGroups.class) +public class ItemGroupsMixin { + @Redirect(method = "generateMaxEnchantedBookEntries", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;")) + private static Stream filterCustomMaxEnchants(Stream stream, Predicate predicate, ItemGroup.ItemStackCollector collector, HolderLookup enchantmentLookup, Set targets, ItemGroup.Visibility visibility) { + return stream.filter(predicate.or(enchantment -> enchantment instanceof QuiltEnchantment quiltEnchantment && quiltEnchantment.isVisible(visibility))); + } + + @Redirect(method = "generateAllEnchantedBookEntries", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;")) + private static Stream filterCustomAllEnchants(Stream stream, Predicate predicate, ItemGroup.ItemStackCollector collector, HolderLookup enchantmentLookup, Set targets, ItemGroup.Visibility visibility) { + return stream.filter(predicate.or(enchantment -> enchantment instanceof QuiltEnchantment quiltEnchantment && quiltEnchantment.isVisible(visibility))); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/MobEntityMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/MobEntityMixin.java new file mode 100644 index 0000000000..4f204df946 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/MobEntityMixin.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.entity.EntityType; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.random.RandomGenerator; +import net.minecraft.world.World; + +import org.quiltmc.qsl.enchantment.api.context.EntityEnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; + +@Mixin(MobEntity.class) +public abstract class MobEntityMixin extends LivingEntity { + @Shadow + public abstract ItemStack getEquippedStack(EquipmentSlot slot); + + protected MobEntityMixin(EntityType entityType, World world) { + super(entityType, world); + } + + @Inject(method = "enchantMainHandItem", at = @At("HEAD")) + private void setEnchantingContextForMainHand(RandomGenerator random, float power, CallbackInfo ci) { + QuiltEnchantmentHelper.setContext(new EntityEnchantingContext<>(0, 0, this.getMainHandStack(), this.world, this.getRandom(), false, this)); + } + + @Inject(method = "enchantMainHandItem", at = @At("RETURN")) + private void removeEnchantingContextForMainHand(RandomGenerator random, float power, CallbackInfo ci) { + QuiltEnchantmentHelper.clearContext(); + } + + @Inject(method = "enchantEquipment", at = @At("HEAD")) + private void setEnchantingContextForEquipment(RandomGenerator random, float power, EquipmentSlot slot, CallbackInfo ci) { + QuiltEnchantmentHelper.setContext(new EntityEnchantingContext<>(0, 0, this.getEquippedStack(slot), this.world, this.getRandom(), false, this)); + } + + @Inject(method = "enchantEquipment", at = @At("RETURN")) + private void removeEnchantingContextForEquipment(RandomGenerator random, float power, EquipmentSlot slot, CallbackInfo ci) { + QuiltEnchantmentHelper.clearContext(); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/SellEnchantedToolFactoryMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/SellEnchantedToolFactoryMixin.java new file mode 100644 index 0000000000..a110f061f6 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/SellEnchantedToolFactoryMixin.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import net.minecraft.entity.Entity; +import net.minecraft.util.random.RandomGenerator; +import net.minecraft.village.TradeOffer; + +import org.quiltmc.qsl.enchantment.api.context.EntityEnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; + +@Mixin(targets = {"net.minecraft.village.TradeOffers$SellEnchantedToolFactory"}) +public class SellEnchantedToolFactoryMixin { + @Inject(method = "create", at = @At(value = "INVOKE", target = "Lnet/minecraft/enchantment/EnchantmentHelper;enchant(Lnet/minecraft/util/random/RandomGenerator;Lnet/minecraft/item/ItemStack;IZ)Lnet/minecraft/item/ItemStack;")) + private void setEnchantingContext(Entity entity, RandomGenerator random, CallbackInfoReturnable cir) { + QuiltEnchantmentHelper.setContext(new EntityEnchantingContext<>(0, 0, null, entity.world, random, false, entity)); + } + + @Inject(method = "create", at = @At(value = "RETURN")) + private void clearEnchantingContext(Entity entity, RandomGenerator random, CallbackInfoReturnable cir) { + QuiltEnchantmentHelper.clearContext(); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/SkeletonHorseTrapTriggerGoalMixin.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/SkeletonHorseTrapTriggerGoalMixin.java new file mode 100644 index 0000000000..79a956ede3 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/SkeletonHorseTrapTriggerGoalMixin.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.entity.ai.goal.SkeletonHorseTrapTriggerGoal; +import net.minecraft.entity.mob.SkeletonEntity; +import net.minecraft.entity.passive.HorseBaseEntity; +import net.minecraft.world.LocalDifficulty; + +import org.quiltmc.qsl.enchantment.api.context.EntityEnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantmentHelper; + +@Mixin(SkeletonHorseTrapTriggerGoal.class) +public abstract class SkeletonHorseTrapTriggerGoalMixin { + @Inject(method = "getSkeleton", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/SkeletonEntity;initialize(Lnet/minecraft/world/ServerWorldAccess;Lnet/minecraft/world/LocalDifficulty;Lnet/minecraft/entity/SpawnReason;Lnet/minecraft/entity/EntityData;Lnet/minecraft/nbt/NbtCompound;)Lnet/minecraft/entity/EntityData;"), locals = LocalCapture.CAPTURE_FAILHARD) + private void setEntityEnchantingContext(LocalDifficulty localDifficulty, HorseBaseEntity vehicle, CallbackInfoReturnable cir, SkeletonEntity skeleton) { + QuiltEnchantmentHelper.setContext(new EntityEnchantingContext<>(0, 0, null, vehicle.world, skeleton.getRandom(), false, skeleton)); + } + + @Inject(method = "getSkeleton", at = @At("RETURN")) + private void clearEntityEnchantingContext(LocalDifficulty localDifficulty, HorseBaseEntity vehicle, CallbackInfoReturnable cir) { + QuiltEnchantmentHelper.clearContext(); + } +} diff --git a/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/WeightedAbsentAccessor.java b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/WeightedAbsentAccessor.java new file mode 100644 index 0000000000..a97b3a4901 --- /dev/null +++ b/library/item/enchantment/src/main/java/org/quiltmc/qsl/enchantment/mixin/WeightedAbsentAccessor.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.util.collection.Weight; +import net.minecraft.util.collection.Weighted; + +@Mixin(Weighted.Absent.class) +public interface WeightedAbsentAccessor { + @Accessor + @Mutable + void setWeight(Weight weight); +} diff --git a/library/item/enchantment/src/main/resources/quilt_enchantment.accesswidener b/library/item/enchantment/src/main/resources/quilt_enchantment.accesswidener new file mode 100644 index 0000000000..1255a3493f --- /dev/null +++ b/library/item/enchantment/src/main/resources/quilt_enchantment.accesswidener @@ -0,0 +1,4 @@ +accessWidener v2 named + +accessible class net/minecraft/item/ItemGroup$ItemStackCollector +accessible class net/minecraft/item/ItemGroup$Visibility diff --git a/library/item/enchantment/src/main/resources/quilt_enchantment.mixins.json b/library/item/enchantment/src/main/resources/quilt_enchantment.mixins.json new file mode 100644 index 0000000000..941c6f9c16 --- /dev/null +++ b/library/item/enchantment/src/main/resources/quilt_enchantment.mixins.json @@ -0,0 +1,21 @@ +{ + "required": true, + "package": "org.quiltmc.qsl.enchantment.mixin", + "compatibilityLevel": "JAVA_16", + "mixins": [ + "AnvilScreenHandlerMixin", + "EnchantCommandMixin", + "EnchantmentHelperMixin", + "EnchantmentScreenHandlerMixin", + "EnchantRandomlyLootFunctionMixin", + "EnchantWithLevelsLootFunctionMixin", + "ItemGroupsMixin", + "MobEntityMixin", + "SellEnchantedToolFactoryMixin", + "SkeletonHorseTrapTriggerGoalMixin", + "WeightedAbsentAccessor" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/EnchantmentTestMod.java b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/EnchantmentTestMod.java new file mode 100644 index 0000000000..4b3ae666f1 --- /dev/null +++ b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/EnchantmentTestMod.java @@ -0,0 +1,56 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.test; + +import net.minecraft.enchantment.Enchantments; +import net.minecraft.item.Item; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import org.quiltmc.loader.api.ModContainer; +import org.quiltmc.qsl.base.api.entrypoint.ModInitializer; +import org.quiltmc.qsl.enchantment.api.EnchantmentEvents; +import org.quiltmc.qsl.enchantment.api.context.PlayerUsingBlockEnchantingContext; + +public class EnchantmentTestMod implements ModInitializer { + public static final String MOD_ID = "quilt_enchantment_testmod"; + + @Override + public void onInitialize(ModContainer mod) { + Registry.register(Registries.ITEM, new Identifier(MOD_ID, "super_enchantable"), new SuperEnchantableItem(new Item.Settings().maxDamage(32))); + Registry.register(Registries.ENCHANTMENT, new Identifier(MOD_ID, "reaping"), new ReapingEnchantment()); + Registry.register(Registries.ENCHANTMENT, new Identifier(MOD_ID, "pervasive"), new PervasiveEnchantment()); + Registry.register(Registries.ENCHANTMENT, new Identifier(MOD_ID, "merchant_greed"), new MerchantGreedEnchantment()); + + EnchantmentEvents.MODIFY_POSSIBLE_ENCHANTMENTS.register((possibleEnchantments, context) -> { + for (var iter = possibleEnchantments.iterator(); iter.hasNext();) { + var entry = iter.next(); + if (entry.enchantment == Enchantments.SHARPNESS) { + if (context instanceof PlayerUsingBlockEnchantingContext playerContext) { + playerContext.getPlayer().sendMessage(Text.literal("Removed sharpness of level " + entry.level), true); + } + + iter.remove(); + } + } + }); + + EnchantmentEvents.ANVIL_APPLICATION.register((enchantment, context) -> enchantment != Enchantments.SILK_TOUCH); + } +} diff --git a/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/MerchantGreedEnchantment.java b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/MerchantGreedEnchantment.java new file mode 100644 index 0000000000..a5876acd79 --- /dev/null +++ b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/MerchantGreedEnchantment.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.test; + +import org.jetbrains.annotations.Range; + +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.passive.MerchantEntity; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemStack; + +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; +import org.quiltmc.qsl.enchantment.api.context.EntityEnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantment; + +public class MerchantGreedEnchantment extends QuiltEnchantment { + public MerchantGreedEnchantment() { + super(Rarity.COMMON, null, EquipmentSlot.values()); + } + + @Override + public @Range(from = 0, to = Integer.MAX_VALUE) int weightFromContext(EnchantingContext context) { + return 100; + } + + @Override + public boolean isAcceptableItem(ItemStack stack) { + return stack.getItem().isEnchantable(stack); + } + + @Override + public boolean isAcceptableContext(EnchantingContext context) { + return context instanceof EntityEnchantingContext entityEnchantingContext && entityEnchantingContext.getEntity() instanceof MerchantEntity; + } + + @Override + public boolean isVisible(ItemGroup.Visibility visibility) { + return false; + } +} diff --git a/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/PervasiveEnchantment.java b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/PervasiveEnchantment.java new file mode 100644 index 0000000000..51b88670a4 --- /dev/null +++ b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/PervasiveEnchantment.java @@ -0,0 +1,80 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.test; + +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; + +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantment; + +public class PervasiveEnchantment extends QuiltEnchantment { + public PervasiveEnchantment() { + super(Rarity.COMMON, null, EquipmentSlot.values()); + } + + @Override + public int weightFromContext(EnchantingContext context) { + return 20; + } + + @Override + public boolean isAcceptableItem(ItemStack stack) { + return stack.getItem().isEnchantable(stack); + } + + @Override + public boolean isAcceptableContext(EnchantingContext context) { + return true; + } + + @Override + public int getMaxLevel() { + return 3; + } + + @Override + public void onTargetDamaged(LivingEntity user, Entity target, int level) { + super.onTargetDamaged(user, target, level); + + this.infect(target, level); + } + + @Override + public void onUserDamaged(LivingEntity user, Entity attacker, int level) { + super.onUserDamaged(user, attacker, level); + + this.infect(attacker, level); + } + + private void infect(Entity target, int level) { + target.getItemsEquipped().forEach(stack -> { + if (stack.getItem().isEnchantable(stack)) { + if (stack.hasEnchantments()) { + var enchantments = EnchantmentHelper.get(stack); + enchantments.put(this, level); + EnchantmentHelper.set(enchantments, stack); + } else { + stack.addEnchantment(this, level); + } + } + }); + } +} diff --git a/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/ReapingEnchantment.java b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/ReapingEnchantment.java new file mode 100644 index 0000000000..67b0f33e59 --- /dev/null +++ b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/ReapingEnchantment.java @@ -0,0 +1,62 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.test; + +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.item.HoeItem; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +import org.quiltmc.qsl.enchantment.api.context.EnchantingContext; +import org.quiltmc.qsl.enchantment.api.context.PlayerUsingBlockEnchantingContext; +import org.quiltmc.qsl.enchantment.api.QuiltEnchantment; + +public class ReapingEnchantment extends QuiltEnchantment { + public ReapingEnchantment() { + super(Rarity.COMMON, null, new EquipmentSlot[]{ EquipmentSlot.MAINHAND }); + } + + @Override + public int weightFromContext(EnchantingContext context) { + return context.getStack().isOf(Items.IRON_HOE) ? 20 : 10; + } + + @Override + public boolean isAcceptableItem(ItemStack stack) { + return stack.getItem() instanceof HoeItem; + } + + @Override + public boolean isAcceptableContext(EnchantingContext context) { + if (context instanceof PlayerUsingBlockEnchantingContext playerUsingBlockContext) { + return playerUsingBlockContext.getPos().getX() % 2 == 0; + } + + return super.isAcceptableContext(context); + } + + @Override + public boolean isVisible(ItemGroup.Visibility visibility) { + return visibility == ItemGroup.Visibility.PARENT_TAB_ONLY; + } + + @Override + public int getMaxLevel() { + return 3; + } +} diff --git a/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/SuperEnchantableItem.java b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/SuperEnchantableItem.java new file mode 100644 index 0000000000..7a349b4e10 --- /dev/null +++ b/library/item/enchantment/src/testmod/java/org/quiltmc/qsl/enchantment/test/SuperEnchantableItem.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.quiltmc.qsl.enchantment.test; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import org.quiltmc.qsl.enchantment.api.QuiltEnchantableItem; + +public class SuperEnchantableItem extends Item implements QuiltEnchantableItem { + public SuperEnchantableItem(Settings settings) { + super(settings); + } + + @Override + public boolean canEnchant(ItemStack stack, Enchantment enchantment) { + return true; + } + + @Override + public int getEnchantability() { + return 25; + } + + @Override + public boolean isEnchantable(ItemStack stack) { + return true; + } +} diff --git a/library/item/enchantment/src/testmod/resources/assets/quilt_enchantment_testmod/lang/en_us.json b/library/item/enchantment/src/testmod/resources/assets/quilt_enchantment_testmod/lang/en_us.json new file mode 100644 index 0000000000..ca700ee322 --- /dev/null +++ b/library/item/enchantment/src/testmod/resources/assets/quilt_enchantment_testmod/lang/en_us.json @@ -0,0 +1,6 @@ +{ + "item.quilt_enchantment_testmod.super_enchantable": "Super Enchantable", + "enchantment.quilt_enchantment_testmod.reaping": "Reaping", + "enchantment.quilt_enchantment_testmod.pervasive": "Pervasive", + "enchantment.quilt_enchantment_testmod.merchant_greed": "Merchant's Greed" +} diff --git a/library/item/enchantment/src/testmod/resources/quilt.mod.json b/library/item/enchantment/src/testmod/resources/quilt.mod.json new file mode 100644 index 0000000000..560f2c77d6 --- /dev/null +++ b/library/item/enchantment/src/testmod/resources/quilt.mod.json @@ -0,0 +1,22 @@ +{ + "schema_version": 1, + "quilt_loader": { + "group": "org.quiltmc.qsl.content_other", + "id": "quilt_enchantment_testmod", + "version": "1.0.0", + "load_type": "always", + "metadata": { + "name": "Quilt Enchantment API Test Mod", + "license": "Apache-2.0" + }, + "intermediate_mappings": "net.fabricmc:intermediary", + "depends": [ + "quilt_loader" + ], + "entrypoints": { + "init": [ + "org.quiltmc.qsl.enchantment.test.EnchantmentTestMod" + ] + } + } +}