diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltCustomItemSettings.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltCustomItemSettings.java index ad03948db7..d93525b4b5 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltCustomItemSettings.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltCustomItemSettings.java @@ -16,6 +16,8 @@ package org.quiltmc.qsl.item.setting.api; +import java.util.Map; + import org.quiltmc.qsl.item.setting.impl.CustomItemSettingImpl; /** @@ -36,17 +38,20 @@ private QuiltCustomItemSettings() {} /** * The {@link CustomItemSetting} in charge of handing {@link RecipeRemainderProvider}s. This setting should be used when implementing custom crafting systems to properly handle remainders. + * *

* The setting is currently used in the following places: *

*/ - public static final CustomItemSetting RECIPE_REMAINDER_PROVIDER = CustomItemSettingImpl.RECIPE_REMAINDER_PROVIDER; + public static final CustomItemSetting> RECIPE_REMAINDER_PROVIDER = CustomItemSettingImpl.RECIPE_REMAINDER_PROVIDER; } diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltItemSettings.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltItemSettings.java index f11b9d2783..91f22c38f6 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltItemSettings.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/QuiltItemSettings.java @@ -17,6 +17,8 @@ package org.quiltmc.qsl.item.setting.api; +import java.util.Map; + import org.jetbrains.annotations.Contract; import net.minecraft.entity.EquipmentSlot; @@ -31,6 +33,7 @@ /** * Quilt's version of {@link Item.Settings}. * Adds additional methods and hooks not found in the original class. + * *

* To use it, simply replace {@code new Item.Settings()} with {@code new QuiltItemSettings()}. */ @@ -69,33 +72,83 @@ public QuiltItemSettings customDamage(CustomDamageHandler handler) { /** * Sets the stack-aware recipe remainder provider of the item. + * Defaults to setting both crafting and furnace fuel remainder, like vanilla. + * + * @param provider the {@link RecipeRemainderProvider} for the item */ public QuiltItemSettings recipeRemainder(RecipeRemainderProvider provider) { - return this.customSetting(QuiltCustomItemSettings.RECIPE_REMAINDER_PROVIDER, provider); + return this.recipeRemainder(provider, RecipeRemainderLocation.DEFAULT_LOCATIONS); } /** * Sets the stack-aware recipe remainder to damage the item by 1 every time it is used in crafting. + * Defaults to setting both crafting and furnace fuel remainder, like vanilla. */ public QuiltItemSettings recipeDamageRemainder() { - return this.recipeDamageRemainder(1); + return this.recipeDamageRemainder(1, RecipeRemainderLocation.DEFAULT_LOCATIONS); } /** * Sets the stack-aware recipe remainder to return the item itself. + * Defaults to setting both crafting and furnace fuel remainder, like vanilla. */ public QuiltItemSettings recipeSelfRemainder() { - return this.recipeDamageRemainder(0); + return this.recipeDamageRemainder(0, RecipeRemainderLocation.DEFAULT_LOCATIONS); } /** * Sets the stack-aware recipe remainder to damage the item by a certain amount every time it is used in crafting. + * Defaults to setting both crafting and furnace fuel remainder, like vanilla. * * @param by the amount */ public QuiltItemSettings recipeDamageRemainder(int by) { + return this.recipeDamageRemainder(by, RecipeRemainderLocation.DEFAULT_LOCATIONS); + } + + /** + * Sets the stack-aware recipe remainder provider of the item. + * + * @param provider the {@link RecipeRemainderProvider} for the item + * @param locations the {@link RecipeRemainderLocation locations} for the remainder + */ + public QuiltItemSettings recipeRemainder(RecipeRemainderProvider provider, RecipeRemainderLocation... locations) { + for (var location : locations) { + ((CustomItemSettingImpl>) QuiltCustomItemSettings.RECIPE_REMAINDER_PROVIDER) + .get(this) + .put(location, provider); + } + + return this; + } + + /** + * Sets the stack-aware recipe remainder to damage the item by 1 every time it is used in crafting. + * + * @param locations the {@link RecipeRemainderLocation locations} for the remainder + */ + public QuiltItemSettings recipeDamageRemainder(RecipeRemainderLocation... locations) { + return this.recipeDamageRemainder(1, locations); + } + + /** + * Sets the stack-aware recipe remainder to return the item itself. + * + * @param locations the {@link RecipeRemainderLocation locations} for the remainder + */ + public QuiltItemSettings recipeSelfRemainder(RecipeRemainderLocation... locations) { + return this.recipeDamageRemainder(0, locations); + } + + /** + * Sets the stack-aware recipe remainder to damage the item by a certain amount every time it is used in crafting. + * + * @param by the amount + * @param locations the {@link RecipeRemainderLocation location} for the remainder + */ + public QuiltItemSettings recipeDamageRemainder(int by, RecipeRemainderLocation... locations) { if (by == 0) { - return this.recipeRemainder((original, recipe) -> original.copy()); + return this.recipeRemainder((original, recipe) -> original.copy(), locations); } return this.recipeRemainder((original, recipe) -> { @@ -113,7 +166,7 @@ public QuiltItemSettings recipeDamageRemainder(int by) { } return copy; - }); + }, locations); } /** diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderLocation.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderLocation.java new file mode 100644 index 0000000000..245edfd552 --- /dev/null +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderLocation.java @@ -0,0 +1,123 @@ +/* + * Copyright 2024 The Quilt Project + * + * 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.item.setting.api; + +import java.util.Objects; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; + +import net.minecraft.util.Identifier; + +import org.quiltmc.qsl.item.setting.impl.RecipeRemainderLogicHandlerImpl; + +/** + * Contains the different recipe remainder locations that QSL supports. + * Calling {@link #getOrCreate(Identifier)} allows mods to create their own remainder locations or get remainder locations without needing to compile against the other mod. + * The hierarchy of recipe remainder locations is: {@link #DEFAULT_LOCATIONS} < any location < {@link #ALL_LOCATIONS}. + * + *

This class should not be extended. + */ +@ApiStatus.NonExtendable +public interface RecipeRemainderLocation { + /** + * Remainder location for Vanilla crafting. Used in Crafting Tables and the inventory crafting screen. + */ + RecipeRemainderLocation CRAFTING = addToDefaultLocations(getOrCreate(new Identifier("minecraft:crafting"))); + + /** + * Remainder location for the furnace fuel slot in the different furnace types. + */ + RecipeRemainderLocation FURNACE_FUEL = addToDefaultLocations(getOrCreate(new Identifier("minecraft:furnace_fuel"))); + + /** + * Remainder location for the furnace ingredient slot in the different furnace types. + */ + RecipeRemainderLocation FURNACE_INGREDIENT = getOrCreate(new Identifier("minecraft:furnace_ingredient")); + + /** + * Remainder location for the dye slot in looms. + */ + RecipeRemainderLocation LOOM_DYE = getOrCreate(new Identifier("minecraft:loom_dye")); + + /** + * Remainder location for the potion addition in brewing stands. + */ + RecipeRemainderLocation POTION_ADDITION = getOrCreate(new Identifier("minecraft:potion_addition")); + + /** + * Remainder location for the input to the stone cutter. + */ + RecipeRemainderLocation STONE_CUTTER_INPUT = getOrCreate(new Identifier("minecraft:stone_cutter_input")); + + /** + * Remainder location for the smithing template slot. + */ + RecipeRemainderLocation SMITHING_TEMPLATE = getOrCreate(new Identifier("minecraft:smithing_template")); + + /** + * Remainder location for the smithing base slot. + */ + RecipeRemainderLocation SMITHING_BASE = getOrCreate(new Identifier("minecraft:smithing_base")); + + /** + * Remainder location for the smithing ingredient slot. + */ + RecipeRemainderLocation SMITHING_INGREDIENT = getOrCreate(new Identifier("minecraft:smithing_ingredient")); + + /** + * Remainder location for the default locations. This starts with {@link #CRAFTING} and {@link #FURNACE_FUEL}. + */ + RecipeRemainderLocation DEFAULT_LOCATIONS = getOrCreate(new Identifier("quilt:default")); + + /** + * Remainder location for all locations. Using this will override any other locations that is specified. + */ + RecipeRemainderLocation ALL_LOCATIONS = getOrCreate(new Identifier("quilt:all")); + + /** + * Gets a new remainder location if it already exists, creating it otherwise. + * @param id the id for the location + * @return the remainder location + */ + @Contract("null -> fail; _ -> new") + static RecipeRemainderLocation getOrCreate(Identifier id) { + record RecipeRemainderLocationImpl(Identifier id) implements RecipeRemainderLocation { + } + + Objects.requireNonNull(id, "`id` must not be null."); + + return RecipeRemainderLogicHandlerImpl.LOCATIONS.computeIfAbsent(id, RecipeRemainderLocationImpl::new); + } + + /** + * @param location the location to add to the default locations + */ + @Contract("null -> fail") + static RecipeRemainderLocation addToDefaultLocations(RecipeRemainderLocation location) { + Objects.requireNonNull(location, "`location` must not be null"); + + RecipeRemainderLogicHandlerImpl.DEFAULT_LOCATIONS.add(location); + return location; + } + + /** + * + * @return the id for the location. + */ + Identifier id(); +} diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderLogicHandler.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderLogicHandler.java index 7782e800a3..ff9a2ab4b0 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderLogicHandler.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderLogicHandler.java @@ -16,6 +16,7 @@ package org.quiltmc.qsl.item.setting.api; +import java.util.Map; import java.util.function.Consumer; import org.jetbrains.annotations.ApiStatus; @@ -44,14 +45,32 @@ public interface RecipeRemainderLogicHandler { * Gets the stack-aware remainder of the provided {@link ItemStack} for the provided {@link Recipe}. * * @param original the stack to decrement - * @param recipe the recipe being used + * @param recipe the recipe being used + * @param location the remainder location * @return the recipe remainder */ - static ItemStack getRemainder(ItemStack original, @Nullable Recipe recipe) { - ItemStack remainder = CustomItemSettingImpl.RECIPE_REMAINDER_PROVIDER.get(original.getItem()).getRecipeRemainder( - original, - recipe - ); + static ItemStack getRemainder(ItemStack original, @Nullable Recipe recipe, RecipeRemainderLocation location) { + Map providers = CustomItemSettingImpl.RECIPE_REMAINDER_PROVIDER + .get(original.getItem()); + + RecipeRemainderProvider provider = (_original, _recipe) -> ItemStack.EMPTY; + + if (RecipeRemainderLogicHandlerImpl.DEFAULT_LOCATIONS.contains(location)) { + provider = providers.get(RecipeRemainderLocation.DEFAULT_LOCATIONS); + } + + if (providers.containsKey(location)) { + provider = providers.get(location); + } + + if (providers.containsKey(RecipeRemainderLocation.ALL_LOCATIONS)) { + provider = providers.get(RecipeRemainderLocation.ALL_LOCATIONS); + } + + ItemStack remainder = provider.getRecipeRemainder( + original, + recipe + ); return remainder.isEmpty() ? ItemStack.EMPTY : remainder; } @@ -60,62 +79,65 @@ static ItemStack getRemainder(ItemStack original, @Nullable Recipe recipe) { * Handles the recipe remainder logic for crafts without a {@link PlayerEntity player} present. * Excess items that cannot be returned to a slot are dropped in the world. * - * @param input the original item stack - * @param amount the amount by which to decrease the stack - * @param recipe the recipe being used + * @param input the original item stack + * @param amount the amount by which to decrease the stack + * @param recipe the recipe being used + * @param location the remainder location * @param inventory the inventory - * @param index the index of the original stack in the inventory - * @param world the world - * @param location the location to drop excess remainders + * @param index the index of the original stack in the inventory + * @param world the world + * @param pos the location to drop excess remainders */ - @Contract(mutates = "param1, param4, param6") - static void handleRemainderForNonPlayerCraft(ItemStack input, int amount, @Nullable Recipe recipe, DefaultedList inventory, int index, World world, BlockPos location) { - handleRemainderForNonPlayerCraft(input, amount, recipe, inventory, index, remainder -> ItemScatterer.spawn(world, location.getX(), location.getY(), location.getZ(), remainder)); + @Contract(mutates = "param1, param5, param7") + static void handleRemainderForNonPlayerCraft(ItemStack input, int amount, @Nullable Recipe recipe, RecipeRemainderLocation location, DefaultedList inventory, int index, World world, BlockPos pos) { + handleRemainderForNonPlayerCraft(input, amount, recipe, location, inventory, index, remainder -> ItemScatterer.spawn(world, pos.getX(), pos.getY(), pos.getZ(), remainder)); } /** * Handles the recipe remainder logic for crafts without a {@link PlayerEntity player} present. * Excess items that cannot be returned to a slot are handled by the provided {@link Consumer consumer}. * - * @param input the original item stack - * @param amount the amount by which to decrease the stack - * @param recipe the recipe being used + * @param input the original item stack + * @param amount the amount by which to decrease the stack + * @param recipe the recipe being used + * @param location the remainder location * @param inventory the inventory - * @param index the index of the original stack in the inventory - * @param failure callback that is run if excess items could not be returned to a slot + * @param index the index of the original stack in the inventory + * @param failure callback that is run if excess items could not be returned to a slot */ - @Contract(mutates = "param1, param4, param6") - static void handleRemainderForNonPlayerCraft(ItemStack input, int amount, @Nullable Recipe recipe, DefaultedList inventory, int index, Consumer failure) { - RecipeRemainderLogicHandlerImpl.handleRemainderForNonPlayerCraft(input, amount, recipe, inventory, index, failure); + @Contract(mutates = "param1, param5, param7") + static void handleRemainderForNonPlayerCraft(ItemStack input, int amount, @Nullable Recipe recipe, RecipeRemainderLocation location, DefaultedList inventory, int index, Consumer failure) { + RecipeRemainderLogicHandlerImpl.handleRemainderForNonPlayerCraft(input, amount, recipe, location, inventory, index, failure); } /** - * @see RecipeRemainderLogicHandler#handleRemainderForNonPlayerCraft(ItemStack, int, Recipe, DefaultedList, int, World, BlockPos) + * @see RecipeRemainderLogicHandler#handleRemainderForNonPlayerCraft(ItemStack, int, Recipe, RecipeRemainderLocation, DefaultedList, int, World, BlockPos) */ - @Contract(mutates = "param1, param3, param5") - static void handleRemainderForNonPlayerCraft(ItemStack input, @Nullable Recipe recipe, DefaultedList inventory, int index, World world, BlockPos location) { - handleRemainderForNonPlayerCraft(input, 1, recipe, inventory, index, world, location); + @Contract(mutates = "param1, param4, param6") + static void handleRemainderForNonPlayerCraft(ItemStack input, @Nullable Recipe recipe, RecipeRemainderLocation location, DefaultedList inventory, int index, World world, BlockPos pos) { + handleRemainderForNonPlayerCraft(input, 1, recipe, location, inventory, index, world, pos); } /** * Handles the recipe remainder logic for crafts within a {@link net.minecraft.screen.ScreenHandler screen handler}. * Excess items that cannot be returned to a slot are {@linkplain net.minecraft.entity.player.PlayerInventory#offerOrDrop(ItemStack) offered to the player or dropped}. * - * @param slot the slot of the original stack - * @param amount the amount by which to decrease the stack - * @param recipe the recipe being used - * @param player the player performing the craft + * @param slot the slot of the original stack + * @param amount the amount by which to decrease the stack + * @param recipe the recipe being used + * @param location the remainder location + * @param player the player performing the craft */ - @Contract(mutates = "param1, param4") - static void handleRemainderForScreenHandler(Slot slot, int amount, @Nullable Recipe recipe, PlayerEntity player) { - RecipeRemainderLogicHandlerImpl.handleRemainderForScreenHandler(slot, amount, recipe, player); + @Contract(mutates = "param1, param5") + static void handleRemainderForScreenHandler(Slot slot, int amount, @Nullable Recipe recipe, RecipeRemainderLocation location, PlayerEntity player) { + RecipeRemainderLogicHandlerImpl.handleRemainderForScreenHandler(slot, amount, recipe, location, player); } /** - * @see RecipeRemainderLogicHandler#handleRemainderForScreenHandler(Slot, int, Recipe, PlayerEntity) + * @see RecipeRemainderLogicHandler#handleRemainderForScreenHandler(Slot, int, Recipe, RecipeRemainderLocation, PlayerEntity) */ - @Contract(mutates = "param1, param3") - static void handleRemainderForScreenHandler(Slot slot, @Nullable Recipe recipe, PlayerEntity player) { - handleRemainderForScreenHandler(slot, 1, recipe, player); + @Contract(mutates = "param1, param4") + static void handleRemainderForScreenHandler(Slot slot, @Nullable Recipe recipe, RecipeRemainderLocation location, PlayerEntity player) { + handleRemainderForScreenHandler(slot, 1, recipe, location, player); } } diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderProvider.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderProvider.java index 5dfe6630f5..00333eaa23 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderProvider.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/api/RecipeRemainderProvider.java @@ -30,6 +30,7 @@ * The recipe remainder is an {@link ItemStack} instead of an {@link Item}. * This can be used to allow your item to get damaged instead of * getting removed when used in crafting. + * *

* Recipe remainder providers can be set with {@link QuiltItemSettings#recipeRemainder(RecipeRemainderProvider)}. */ @@ -45,10 +46,10 @@ public interface RecipeRemainderProvider { @Contract(value = "_, _ -> new") ItemStack getRecipeRemainder(ItemStack original, @Nullable Recipe recipe); - static DefaultedList getRemainingStacks(Inventory inventory, Recipe recipe, DefaultedList defaultedList) { + static DefaultedList getRemainingStacks(Inventory inventory, Recipe recipe, RecipeRemainderLocation location, DefaultedList defaultedList) { for (int i = 0; i < defaultedList.size(); ++i) { ItemStack stack = inventory.getStack(i); - ItemStack remainder = RecipeRemainderLogicHandler.getRemainder(stack, recipe); + ItemStack remainder = RecipeRemainderLogicHandler.getRemainder(stack, recipe, location); if (!remainder.isEmpty()) { defaultedList.set(i, remainder); diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/CustomItemSettingImpl.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/CustomItemSettingImpl.java index d44b80e988..a23eb30c88 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/CustomItemSettingImpl.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/CustomItemSettingImpl.java @@ -28,11 +28,11 @@ import org.jetbrains.annotations.ApiStatus; import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; import org.quiltmc.qsl.item.setting.api.CustomDamageHandler; import org.quiltmc.qsl.item.setting.api.CustomItemSetting; import org.quiltmc.qsl.item.setting.api.EquipmentSlotProvider; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderProvider; @ApiStatus.Internal @@ -41,7 +41,7 @@ public class CustomItemSettingImpl implements CustomItemSetting { public static final CustomItemSetting CUSTOM_DAMAGE_HANDLER = CustomItemSetting.create(() -> null); @SuppressWarnings("ConstantConditions") - public static final CustomItemSetting RECIPE_REMAINDER_PROVIDER = new CustomItemSettingImpl<>(() -> (original, recipe) -> original.getItem().hasRecipeRemainder() ? original.getItem().getRecipeRemainder().getDefaultStack() : ItemStack.EMPTY) { + public static final CustomItemSetting> RECIPE_REMAINDER_PROVIDER = new CustomItemSettingImpl<>(HashMap::new) { @Override public void apply(Item.Settings settings, Item item) { if (item.hasRecipeRemainder()) { @@ -78,6 +78,13 @@ public void set(Item.Settings settings, T value) { CUSTOM_SETTINGS.computeIfAbsent(settings, s -> new HashSet<>()).add(this); } + public T get(Item.Settings settings) { + Objects.requireNonNull(settings); + + CUSTOM_SETTINGS.computeIfAbsent(settings, s -> new HashSet<>()).add(this); + return this.customSettings.computeIfAbsent(settings, _setting -> this.defaultValue.get()); + } + public void apply(Item.Settings settings, Item item) { Objects.requireNonNull(settings); diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/RecipeRemainderLogicHandlerImpl.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/RecipeRemainderLogicHandlerImpl.java index be978fa52f..77499c6b00 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/RecipeRemainderLogicHandlerImpl.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/impl/RecipeRemainderLogicHandlerImpl.java @@ -16,6 +16,10 @@ package org.quiltmc.qsl.item.setting.impl; +import java.util.HashMap; +import java.util.Map; +import java.util.HashSet; +import java.util.Set; import java.util.function.Consumer; import org.jetbrains.annotations.ApiStatus; @@ -27,13 +31,18 @@ import net.minecraft.recipe.Recipe; import net.minecraft.screen.slot.Slot; import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.Identifier; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderLogicHandler; @ApiStatus.Internal public final class RecipeRemainderLogicHandlerImpl implements RecipeRemainderLogicHandler { + public static final Map LOCATIONS = new HashMap<>(); + public static final Set DEFAULT_LOCATIONS = new HashSet<>(); + /** - * {@return {@code true} if returning the item to the inventory was successful, or {@code false} if additional handling for the remainder is needed} + * @return {@code true} if returning the item to the inventory was successful, or {@code false} if additional handling for the remainder is needed */ @Contract(mutates = "param1, param2") private static boolean tryReturnItemToInventory(ItemStack remainder, DefaultedList inventory, int index) { @@ -47,7 +56,7 @@ private static boolean tryReturnItemToInventory(ItemStack remainder, DefaultedLi } /** - * {@return {@code true} if returning the item to the slot was successful, or {@code false} if additional handling for the remainder is needed} + * @return {@code true} if returning the item to the slot was successful, or {@code false} if additional handling for the remainder is needed */ @Contract(mutates = "param1, param2") private static boolean tryReturnItemToSlot(ItemStack remainder, Slot slot) { @@ -61,7 +70,7 @@ private static boolean tryReturnItemToSlot(ItemStack remainder, Slot slot) { } /** - * {@return {@code true} if the remainder stack was fully merged into the base stack, or {@code false} otherwise} + * @return {@code true} if the remainder stack was fully merged into the base stack, or {@code false} otherwise */ @Contract(mutates = "param1, param2") private static boolean tryMergeStacks(ItemStack base, ItemStack remainder) { @@ -78,30 +87,30 @@ private static boolean tryMergeStacks(ItemStack base, ItemStack remainder) { } @Contract(mutates = "param1") - private static ItemStack decrementWithRemainder(ItemStack original, int amount, @Nullable Recipe recipe) { + private static ItemStack decrementWithRemainder(ItemStack original, int amount, @Nullable Recipe recipe, RecipeRemainderLocation location) { if (original.isEmpty()) { return ItemStack.EMPTY; } - ItemStack remainder = RecipeRemainderLogicHandler.getRemainder(original, recipe); + ItemStack remainder = RecipeRemainderLogicHandler.getRemainder(original, recipe, location); original.decrement(amount); return remainder; } - @Contract(mutates = "param1, param4, param6") - public static void handleRemainderForNonPlayerCraft(ItemStack input, int amount, @Nullable Recipe recipe, DefaultedList inventory, int index, Consumer failure) { - ItemStack remainder = decrementWithRemainder(input, amount, recipe); + @Contract(mutates = "param1, param5, param7") + public static void handleRemainderForNonPlayerCraft(ItemStack input, int amount, @Nullable Recipe recipe, RecipeRemainderLocation location, DefaultedList inventory, int index, Consumer failure) { + ItemStack remainder = decrementWithRemainder(input, amount, recipe, location); if (!tryReturnItemToInventory(remainder, inventory, index)) { failure.accept(remainder); } } - @Contract(mutates = "param1, param4") - public static void handleRemainderForScreenHandler(Slot slot, int amount, @Nullable Recipe recipe, PlayerEntity player) { - ItemStack remainder = decrementWithRemainder(slot.getStack(), amount, recipe); + @Contract(mutates = "param1, param5") + public static void handleRemainderForScreenHandler(Slot slot, int amount, @Nullable Recipe recipe, RecipeRemainderLocation location, PlayerEntity player) { + ItemStack remainder = decrementWithRemainder(slot.getStack(), amount, recipe, location); if (!tryReturnItemToSlot(remainder, slot)) { player.getInventory().offerOrDrop(remainder); diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/AbstractFurnaceBlockEntityMixin.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/AbstractFurnaceBlockEntityMixin.java index 1fe7a7cf54..d460cf1440 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/AbstractFurnaceBlockEntityMixin.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/AbstractFurnaceBlockEntityMixin.java @@ -44,6 +44,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderLogicHandler; @Mixin(AbstractFurnaceBlockEntity.class) @@ -81,7 +82,7 @@ private static void checkMismatchedRemaindersCanDrop(DynamicRegistryManager regi ItemStack original = inventory.get(INPUT_SLOT).copy(); if (!original.isEmpty()) { - ItemStack remainder = RecipeRemainderLogicHandler.getRemainder(original, recipeHolder.value()).copy(); + ItemStack remainder = RecipeRemainderLogicHandler.getRemainder(original, recipeHolder.value(), RecipeRemainderLocation.FURNACE_INGREDIENT).copy(); original.decrement(1); if (!remainder.isEmpty() && ItemStack.canCombine(original, remainder)) { @@ -112,6 +113,7 @@ private static void setFuelRemainder(ItemStack fuelStack, int amount, World worl fuelStack, amount, recipe, + RecipeRemainderLocation.FURNACE_FUEL, cast.inventory, FUEL_SLOT, blockEntity.getWorld(), @@ -130,6 +132,7 @@ private static void setInputRemainder(ItemStack inputStack, int amount, DynamicR inputStack, amount, recipeHolder == null ? null : recipeHolder.value(), + RecipeRemainderLocation.FURNACE_INGREDIENT, inventory, INPUT_SLOT, remainder -> { // consumer only called when there are excess remainder items that can be dropped into the world diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/BrewingStandBlockEntityMixin.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/BrewingStandBlockEntityMixin.java index 6ba60b2032..54ecedad5d 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/BrewingStandBlockEntityMixin.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/BrewingStandBlockEntityMixin.java @@ -28,6 +28,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderLogicHandler; @Mixin(BrewingStandBlockEntity.class) @@ -44,6 +45,7 @@ private static void applyRecipeRemainder(ItemStack ingredient, int amount, World ingredient, amount, null, + RecipeRemainderLocation.POTION_ADDITION, inventory, INGREDIENT_SLOT, world, diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/LoomOutputSlotMixin.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/LoomOutputSlotMixin.java index 9402476334..6cdf35fba0 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/LoomOutputSlotMixin.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/LoomOutputSlotMixin.java @@ -25,6 +25,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.screen.slot.Slot; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderLogicHandler; @Mixin(targets = {"net.minecraft.screen.LoomScreenHandler$C_ntobwfpp"}) @@ -39,6 +40,7 @@ public ItemStack getRecipeRemainder(Slot slot, int amount, PlayerEntity player, slot, amount, null, + RecipeRemainderLocation.LOOM_DYE, player ); diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/RecipeManagerMixin.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/RecipeManagerMixin.java index df2345eece..439259bea5 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/RecipeManagerMixin.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/RecipeManagerMixin.java @@ -33,6 +33,7 @@ import net.minecraft.util.collection.DefaultedList; import net.minecraft.world.World; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderProvider; @Mixin(RecipeManager.class) @@ -40,7 +41,7 @@ public class RecipeManagerMixin { @Inject(method = "getRemainingStacks", at = @At(value = "RETURN", ordinal = 0), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) public > void interceptGetRemainingStacks(RecipeType recipeType, C inventory, World world, CallbackInfoReturnable> cir, Optional> optionalRecipe) { cir.setReturnValue( - RecipeRemainderProvider.getRemainingStacks(inventory, optionalRecipe.get().value(), cir.getReturnValue()) + RecipeRemainderProvider.getRemainingStacks(inventory, optionalRecipe.get().value(), RecipeRemainderLocation.CRAFTING, cir.getReturnValue()) ); } } diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/SmithingScreenHandlerMixin.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/SmithingScreenHandlerMixin.java index 06d8f422e2..0b938fc958 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/SmithingScreenHandlerMixin.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/SmithingScreenHandlerMixin.java @@ -34,6 +34,7 @@ import net.minecraft.screen.ScreenHandlerType; import net.minecraft.screen.SmithingScreenHandler; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderLogicHandler; @Mixin(SmithingScreenHandler.class) @@ -48,12 +49,18 @@ public SmithingScreenHandlerMixin(@Nullable ScreenHandlerType screenHandlerTy super(screenHandlerType, i, playerInventory, screenHandlerContext); } - @Redirect(method = "decrementStack", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;decrement(I)V")) - private void applyRecipeRemainder(ItemStack instance, int amount, int slot) { + @Redirect(method = "onTakeOutput", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/SmithingScreenHandler;decrementStack(I)V")) + private void applyRecipeRemainderToIngredient(SmithingScreenHandler instance, int slot) { RecipeRemainderLogicHandler.handleRemainderForScreenHandler( this.getSlot(slot), - amount, + 1, this.currentRecipe.value(), + switch (slot) { + case SmithingScreenHandler.TEMPLATE_SLOT -> RecipeRemainderLocation.SMITHING_TEMPLATE; + case SmithingScreenHandler.BASE_SLOT -> RecipeRemainderLocation.SMITHING_BASE; + case SmithingScreenHandler.ADDITIONAL_SLOT -> RecipeRemainderLocation.SMITHING_INGREDIENT; + default -> throw new IllegalStateException("Unexpected value: " + slot); + }, this.player ); } diff --git a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/StonecutterOutputSlotMixin.java b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/StonecutterOutputSlotMixin.java index 81897a0ed0..106bce0281 100644 --- a/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/StonecutterOutputSlotMixin.java +++ b/library/item/item_setting/src/main/java/org/quiltmc/qsl/item/setting/mixin/recipe_remainder/StonecutterOutputSlotMixin.java @@ -30,6 +30,7 @@ import net.minecraft.screen.StonecutterScreenHandler; import net.minecraft.screen.slot.Slot; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; import org.quiltmc.qsl.item.setting.api.RecipeRemainderLogicHandler; @Mixin(targets = {"net.minecraft.screen.StonecutterScreenHandler$C_biccipxg"}) @@ -53,6 +54,7 @@ public ItemStack getRecipeRemainder(Slot slot, int amount, PlayerEntity player, slot, amount, recipe, + RecipeRemainderLocation.STONE_CUTTER_INPUT, player ); diff --git a/library/item/item_setting/src/testmod/java/org/quiltmc/qsl/item/test/RecipeRemainderTests.java b/library/item/item_setting/src/testmod/java/org/quiltmc/qsl/item/test/RecipeRemainderTests.java index 762f80abdf..dfc5003c25 100644 --- a/library/item/item_setting/src/testmod/java/org/quiltmc/qsl/item/test/RecipeRemainderTests.java +++ b/library/item/item_setting/src/testmod/java/org/quiltmc/qsl/item/test/RecipeRemainderTests.java @@ -29,6 +29,7 @@ import org.quiltmc.loader.api.ModContainer; import org.quiltmc.qsl.base.api.entrypoint.ModInitializer; import org.quiltmc.qsl.item.setting.api.QuiltItemSettings; +import org.quiltmc.qsl.item.setting.api.RecipeRemainderLocation; public class RecipeRemainderTests implements ModInitializer { // Static field so we can use it in BrewingRecipeRegistryMixin @@ -37,73 +38,56 @@ public class RecipeRemainderTests implements ModInitializer { new Identifier(QuiltItemSettingsTests.NAMESPACE, "potion_ingredient_remainder"), new Item( new QuiltItemSettings().recipeRemainder( - (original, recipe) -> new ItemStack(Items.BLAZE_POWDER) + (original, recipe) -> new ItemStack(Items.BLAZE_POWDER), RecipeRemainderLocation.POTION_ADDITION ) ) ); @Override public void onInitialize(ModContainer mod) { + // TODO: figure out a way to test these better. Maybe a gametest? Item hammerItem = new Item(new QuiltItemSettings().maxDamage(16).recipeDamageRemainder()); Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "hammer"), hammerItem); - Item furnaceInputRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> { - if (recipe != null && recipe.getType() == RecipeType.SMELTING) { - return Items.DIAMOND.getDefaultStack(); - } - - return ItemStack.EMPTY; - })); + Item furnaceInputRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> Items.DIAMOND.getDefaultStack(), RecipeRemainderLocation.FURNACE_INGREDIENT)); Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "weird_ore"), furnaceInputRemainder); Item furnaceInputSelfRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> { - if (recipe != null && recipe.getType() == RecipeType.SMELTING) { - var remainder = original.copy(); - remainder.setCount(2); - return remainder; - } - - return ItemStack.EMPTY; - })); + var remainder = original.copy(); + remainder.setCount(2); + return remainder; + }, RecipeRemainderLocation.FURNACE_INGREDIENT)); Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "infinite_ore"), furnaceInputSelfRemainder); Item furnaceFuelSelfRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> { var remainder = original.copy(); - if (recipe != null) { - if (recipe.getType() == RecipeType.SMELTING) { - remainder.setCount(1); - } else if (recipe.getType() == RecipeType.SMOKING) { - remainder.setCount(2); - } else if (recipe.getType() == RecipeType.BLASTING) { - remainder.setCount(3); - } - - return remainder; + if (recipe == null) { + // noop + } else if (recipe.getType() == RecipeType.SMELTING) { + remainder.setCount(1); + } else if (recipe.getType() == RecipeType.SMOKING) { + remainder.setCount(2); + } else if (recipe.getType() == RecipeType.BLASTING) { + remainder.setCount(3); } - return ItemStack.EMPTY; - })); + return remainder; + }, RecipeRemainderLocation.FURNACE_FUEL)); Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "infinite_fuel"), furnaceFuelSelfRemainder); - Item smithingInputRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> { - if (recipe != null && recipe.getType() == RecipeType.SMITHING) { - return original.getItem().getDefaultStack(); - } - - return Items.NETHERITE_SCRAP.getDefaultStack(); - })); + Item smithingInputRemainder = new Item(new QuiltItemSettings().recipeSelfRemainder(RecipeRemainderLocation.SMITHING_INGREDIENT)); Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "infinite_netherite"), smithingInputRemainder); - Item loomInputRemainder = new DyeItem(DyeColor.RED, new QuiltItemSettings().maxDamage(100).recipeDamageRemainder()); - Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "infinite_dye"), loomInputRemainder); + Item smithingTemplateRemainder = new Item(new QuiltItemSettings().maxDamage(100).recipeDamageRemainder(1, RecipeRemainderLocation.SMITHING_TEMPLATE)); + Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "infinite_netherite_template"), smithingTemplateRemainder); - Item cuttingInputRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> { - if (recipe != null && recipe.getType() == RecipeType.STONECUTTING) { - return Items.STONE.getDefaultStack(); - } + Item smithingBaseRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> new ItemStack(Items.LEATHER), RecipeRemainderLocation.SMITHING_BASE)); + Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "leaving_leather_base"), smithingBaseRemainder); + + Item loomInputRemainder = new DyeItem(DyeColor.RED, new QuiltItemSettings().maxDamage(100).recipeDamageRemainder(RecipeRemainderLocation.LOOM_DYE)); + Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "reusable_dye"), loomInputRemainder); - return ItemStack.EMPTY; - })); + Item cuttingInputRemainder = new Item(new QuiltItemSettings().recipeRemainder((original, recipe) -> Items.STONE.getDefaultStack(), RecipeRemainderLocation.STONE_CUTTER_INPUT)); Registry.register(Registries.ITEM, new Identifier(QuiltItemSettingsTests.NAMESPACE, "infinite_stone"), cuttingInputRemainder); } } diff --git a/library/item/item_setting/src/testmod/resources/data/quilt_item_content_registry/attachments/minecraft/item/fuel_time.json b/library/item/item_setting/src/testmod/resources/data/quilt/attachments/minecraft/item/fuel_times.json similarity index 100% rename from library/item/item_setting/src/testmod/resources/data/quilt_item_content_registry/attachments/minecraft/item/fuel_time.json rename to library/item/item_setting/src/testmod/resources/data/quilt/attachments/minecraft/item/fuel_times.json diff --git a/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_base_test.json b/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_base_test.json new file mode 100644 index 0000000000..f25c6aacb7 --- /dev/null +++ b/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_base_test.json @@ -0,0 +1,15 @@ +{ + "type": "minecraft:smithing_transform", + "base": { + "item": "quilt_item_setting_testmod:leaving_leather_base" + }, + "addition": { + "item": "minecraft:netherite_ingot" + }, + "result": { + "item": "minecraft:netherite_chestplate" + }, + "template": { + "item": "minecraft:netherite_upgrade_smithing_template" + } +} diff --git a/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_test.json b/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_ingredient_test.json similarity index 100% rename from library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_test.json rename to library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_ingredient_test.json diff --git a/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_template_test.json b/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_template_test.json new file mode 100644 index 0000000000..61f89a9e05 --- /dev/null +++ b/library/item/item_setting/src/testmod/resources/data/quilt_item_setting_testmod/recipes/smithing_template_test.json @@ -0,0 +1,15 @@ +{ + "type": "minecraft:smithing_transform", + "base": { + "item": "minecraft:gold_block" + }, + "addition": { + "item": "minecraft:netherite_ingot" + }, + "result": { + "item": "minecraft:netherite_block" + }, + "template": { + "item": "quilt_item_setting_testmod:infinite_netherite_template" + } +}