Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Recipe Remainder API #366

Merged
merged 3 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.quiltmc.qsl.item.setting.api;

import java.util.Map;

import org.quiltmc.qsl.item.setting.impl.CustomItemSettingImpl;

/**
Expand All @@ -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.
*
* <p>
* The setting is currently used in the following places:
* <ul>
* <li>Crafting Table</li>
* <li>Crafting</li>
* <li>Furnace Fuel</li>
* <li>Furnace Ingredient</li>
* <li>Loom Dye Input</li>
* <li>Brewing Stand Ingredient</li>
* <li>Smithing Table Addition</li>
* <li>Stone Cutter Ingredient</li>
* <li>Loom Dye</li>
* <li>Brewing Ingredient</li>
* <li>Smithing Template</li>
* <li>Smithing Base</li>
* <li>Smithing Ingredient</li>
* <li>Stone Cutter Input</li>
* </ul>
*/
public static final CustomItemSetting<RecipeRemainderProvider> RECIPE_REMAINDER_PROVIDER = CustomItemSettingImpl.RECIPE_REMAINDER_PROVIDER;
public static final CustomItemSetting<Map<RecipeRemainderLocation, RecipeRemainderProvider>> RECIPE_REMAINDER_PROVIDER = CustomItemSettingImpl.RECIPE_REMAINDER_PROVIDER;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,6 +33,7 @@
/**
* Quilt's version of {@link Item.Settings}.
* Adds additional methods and hooks not found in the original class.
*
* <p>
* To use it, simply replace {@code new Item.Settings()} with {@code new QuiltItemSettings()}.
*/
Expand Down Expand Up @@ -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<Map<RecipeRemainderLocation, RecipeRemainderProvider>>) 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) -> {
Expand All @@ -113,7 +166,7 @@ public QuiltItemSettings recipeDamageRemainder(int by) {
}

return copy;
});
}, locations);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.
*
* <p> 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 = getOrCreate(new Identifier("minecraft:crafting"));
OroArmor marked this conversation as resolved.
Show resolved Hide resolved

/**
* Remainder location for the furnace fuel slot in the different furnace types.
*/
RecipeRemainderLocation FURNACE_FUEL = 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"));

/**
* The default locations for a recipe remainder.
*/
RecipeRemainderLocation[] DEFAULT_LOCATIONS = {CRAFTING, FURNACE_FUEL};
OroArmor marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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);
}

/**
*
* @return the id for the location.
*/
Identifier id();
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ 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) {
ItemStack remainder = CustomItemSettingImpl.RECIPE_REMAINDER_PROVIDER
.get(original.getItem())
.getOrDefault(location, (_original, _recipe) -> ItemStack.EMPTY)
.getRecipeRemainder(
original,
recipe
);

return remainder.isEmpty() ? ItemStack.EMPTY : remainder;
}
Expand All @@ -60,62 +64,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<ItemStack> 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<ItemStack> 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<ItemStack> inventory, int index, Consumer<ItemStack> 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<ItemStack> inventory, int index, Consumer<ItemStack> 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<ItemStack> 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<ItemStack> 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);
}
}
Loading
Loading