Skip to content

Commit

Permalink
Initial Fix
Browse files Browse the repository at this point in the history
Fixes #251
  • Loading branch information
OroArmor committed Mar 30, 2024
1 parent dcf6b4e commit 782dcb0
Show file tree
Hide file tree
Showing 18 changed files with 332 additions and 112 deletions.
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,111 @@
/*
* 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 #register(Identifier)} allows mods to create their own remainder locations.
*
* <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 = register(new Identifier("minecraft:crafting"));

/**
* Remainder location for the furnace fuel slot in the different furnace types.
*/
RecipeRemainderLocation FURNACE_FUEL = register(new Identifier("minecraft:furnace_fuel"));

/**
* Remainder location for the furnace ingredient slot in the different furnace types.
*/
RecipeRemainderLocation FURNACE_INGREDIENT = register(new Identifier("minecraft:furnace_ingredient"));

/**
* Remainder location for the dye slot in looms.
*/
RecipeRemainderLocation LOOM_DYE = register(new Identifier("minecraft:loom_dye"));

/**
* Remainder location for the potion addition in brewing stands.
*/
RecipeRemainderLocation POTION_ADDITION = register(new Identifier("minecraft:potion_addition"));

/**
* Remainder location for the input to the stone cutter.
*/
RecipeRemainderLocation STONE_CUTTER_INPUT = register(new Identifier("minecraft:stone_cutter_input"));

/**
* Remainder location for the smithing template slot.
*/
RecipeRemainderLocation SMITHING_TEMPLATE = register(new Identifier("minecraft:smithing_template"));

/**
* Remainder location for the smithing base slot.
*/
RecipeRemainderLocation SMITHING_BASE = register(new Identifier("minecraft:smithing_base"));

/**
* Remainder location for the smithing ingredient slot.
*/
RecipeRemainderLocation SMITHING_INGREDIENT = register(new Identifier("minecraft:smithing_ingredient"));

/**
* The default locations for a recipe remainder.
*/
RecipeRemainderLocation[] DEFAULT_LOCATIONS = {CRAFTING, FURNACE_FUEL};

/**
* Registers a new remainder location.
* @param id the id for the location
* @return a new remainder location
*/
@Contract("null -> fail; _ -> new")
static RecipeRemainderLocation register(Identifier id) {
record RecipeRemainderLocationImpl(Identifier id) implements RecipeRemainderLocation {
}

Objects.requireNonNull(id, "`id` must not be null.");

var location = new RecipeRemainderLocationImpl(id);
if (!RecipeRemainderLogicHandlerImpl.LOCATIONS.add(location)) {
throw new IllegalArgumentException(String.format("Cannot add `%s` as a recipe remainder location twice!", id));
}

return location;
}

/**
*
* @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

0 comments on commit 782dcb0

Please sign in to comment.