245 lines
6.9 KiB
Java
245 lines
6.9 KiB
Java
package net.minecraft.recipebook;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.world.entity.player.Inventory;
|
|
import net.minecraft.world.entity.player.StackedItemContents;
|
|
import net.minecraft.world.inventory.RecipeBookMenu;
|
|
import net.minecraft.world.inventory.Slot;
|
|
import net.minecraft.world.item.Item;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.crafting.Recipe;
|
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
|
import net.minecraft.world.item.crafting.RecipeInput;
|
|
|
|
public class ServerPlaceRecipe<R extends Recipe<?>> {
|
|
private static final int ITEM_NOT_FOUND = -1;
|
|
private final Inventory inventory;
|
|
private final ServerPlaceRecipe.CraftingMenuAccess<R> menu;
|
|
private final boolean useMaxItems;
|
|
private final int gridWidth;
|
|
private final int gridHeight;
|
|
private final List<Slot> inputGridSlots;
|
|
private final List<Slot> slotsToClear;
|
|
|
|
public static <I extends RecipeInput, R extends Recipe<I>> RecipeBookMenu.PostPlaceAction placeRecipe(
|
|
ServerPlaceRecipe.CraftingMenuAccess<R> menu,
|
|
int gridWidth,
|
|
int gridHeight,
|
|
List<Slot> inputGridSlots,
|
|
List<Slot> slotsToClear,
|
|
Inventory inventory,
|
|
RecipeHolder<R> recipe,
|
|
boolean useMaxItems,
|
|
boolean isCreative
|
|
) {
|
|
ServerPlaceRecipe<R> serverPlaceRecipe = new ServerPlaceRecipe<>(menu, inventory, useMaxItems, gridWidth, gridHeight, inputGridSlots, slotsToClear);
|
|
if (!isCreative && !serverPlaceRecipe.testClearGrid()) {
|
|
return RecipeBookMenu.PostPlaceAction.NOTHING;
|
|
} else {
|
|
StackedItemContents stackedItemContents = new StackedItemContents();
|
|
inventory.fillStackedContents(stackedItemContents);
|
|
menu.fillCraftSlotsStackedContents(stackedItemContents);
|
|
return serverPlaceRecipe.tryPlaceRecipe(recipe, stackedItemContents);
|
|
}
|
|
}
|
|
|
|
private ServerPlaceRecipe(
|
|
ServerPlaceRecipe.CraftingMenuAccess<R> menu,
|
|
Inventory inventory,
|
|
boolean useMaxItems,
|
|
int gridWidth,
|
|
int gridHeight,
|
|
List<Slot> inputGridSlots,
|
|
List<Slot> slotsToClear
|
|
) {
|
|
this.menu = menu;
|
|
this.inventory = inventory;
|
|
this.useMaxItems = useMaxItems;
|
|
this.gridWidth = gridWidth;
|
|
this.gridHeight = gridHeight;
|
|
this.inputGridSlots = inputGridSlots;
|
|
this.slotsToClear = slotsToClear;
|
|
}
|
|
|
|
private RecipeBookMenu.PostPlaceAction tryPlaceRecipe(RecipeHolder<R> recipe, StackedItemContents stackedItemContents) {
|
|
if (stackedItemContents.canCraft(recipe.value(), null)) {
|
|
this.placeRecipe(recipe, stackedItemContents);
|
|
this.inventory.setChanged();
|
|
return RecipeBookMenu.PostPlaceAction.NOTHING;
|
|
} else {
|
|
this.clearGrid();
|
|
this.inventory.setChanged();
|
|
return RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE;
|
|
}
|
|
}
|
|
|
|
private void clearGrid() {
|
|
for (Slot slot : this.slotsToClear) {
|
|
ItemStack itemStack = slot.getItem().copy();
|
|
this.inventory.placeItemBackInInventory(itemStack, false);
|
|
slot.set(itemStack);
|
|
}
|
|
|
|
this.menu.clearCraftingContent();
|
|
}
|
|
|
|
private void placeRecipe(RecipeHolder<R> recipe, StackedItemContents stackedItemContents) {
|
|
boolean bl = this.menu.recipeMatches(recipe);
|
|
int i = stackedItemContents.getBiggestCraftableStack(recipe.value(), null);
|
|
if (bl) {
|
|
for (Slot slot : this.inputGridSlots) {
|
|
ItemStack itemStack = slot.getItem();
|
|
if (!itemStack.isEmpty() && Math.min(i, itemStack.getMaxStackSize()) < itemStack.getCount() + 1) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int j = this.calculateAmountToCraft(i, bl);
|
|
List<Holder<Item>> list = new ArrayList();
|
|
if (stackedItemContents.canCraft(recipe.value(), j, list::add)) {
|
|
int k = clampToMaxStackSize(j, list);
|
|
if (k != j) {
|
|
list.clear();
|
|
if (!stackedItemContents.canCraft(recipe.value(), k, list::add)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.clearGrid();
|
|
PlaceRecipeHelper.placeRecipe(
|
|
this.gridWidth, this.gridHeight, recipe.value(), recipe.value().placementInfo().slotsToIngredientIndex(), (integer, jx, kx, l) -> {
|
|
if (integer != -1) {
|
|
Slot slotx = (Slot)this.inputGridSlots.get(jx);
|
|
Holder<Item> holder = (Holder<Item>)list.get(integer);
|
|
int m = k;
|
|
|
|
while (m > 0) {
|
|
m = this.moveItemToGrid(slotx, holder, m);
|
|
if (m == -1) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
private static int clampToMaxStackSize(int amount, List<Holder<Item>> items) {
|
|
for (Holder<Item> holder : items) {
|
|
amount = Math.min(amount, holder.value().getDefaultMaxStackSize());
|
|
}
|
|
|
|
return amount;
|
|
}
|
|
|
|
private int calculateAmountToCraft(int max, boolean recipeMatches) {
|
|
if (this.useMaxItems) {
|
|
return max;
|
|
} else if (recipeMatches) {
|
|
int i = Integer.MAX_VALUE;
|
|
|
|
for (Slot slot : this.inputGridSlots) {
|
|
ItemStack itemStack = slot.getItem();
|
|
if (!itemStack.isEmpty() && i > itemStack.getCount()) {
|
|
i = itemStack.getCount();
|
|
}
|
|
}
|
|
|
|
if (i != Integer.MAX_VALUE) {
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
private int moveItemToGrid(Slot slot, Holder<Item> item, int count) {
|
|
ItemStack itemStack = slot.getItem();
|
|
int i = this.inventory.findSlotMatchingCraftingIngredient(item, itemStack);
|
|
if (i == -1) {
|
|
return -1;
|
|
} else {
|
|
ItemStack itemStack2 = this.inventory.getItem(i);
|
|
ItemStack itemStack3;
|
|
if (count < itemStack2.getCount()) {
|
|
itemStack3 = this.inventory.removeItem(i, count);
|
|
} else {
|
|
itemStack3 = this.inventory.removeItemNoUpdate(i);
|
|
}
|
|
|
|
int j = itemStack3.getCount();
|
|
if (itemStack.isEmpty()) {
|
|
slot.set(itemStack3);
|
|
} else {
|
|
itemStack.grow(j);
|
|
}
|
|
|
|
return count - j;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Places the output of the recipe into the player's inventory.
|
|
*/
|
|
private boolean testClearGrid() {
|
|
List<ItemStack> list = Lists.<ItemStack>newArrayList();
|
|
int i = this.getAmountOfFreeSlotsInInventory();
|
|
|
|
for (Slot slot : this.inputGridSlots) {
|
|
ItemStack itemStack = slot.getItem().copy();
|
|
if (!itemStack.isEmpty()) {
|
|
int j = this.inventory.getSlotWithRemainingSpace(itemStack);
|
|
if (j == -1 && list.size() <= i) {
|
|
for (ItemStack itemStack2 : list) {
|
|
if (ItemStack.isSameItem(itemStack2, itemStack)
|
|
&& itemStack2.getCount() != itemStack2.getMaxStackSize()
|
|
&& itemStack2.getCount() + itemStack.getCount() <= itemStack2.getMaxStackSize()) {
|
|
itemStack2.grow(itemStack.getCount());
|
|
itemStack.setCount(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!itemStack.isEmpty()) {
|
|
if (list.size() >= i) {
|
|
return false;
|
|
}
|
|
|
|
list.add(itemStack);
|
|
}
|
|
} else if (j == -1) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private int getAmountOfFreeSlotsInInventory() {
|
|
int i = 0;
|
|
|
|
for (ItemStack itemStack : this.inventory.getNonEquipmentItems()) {
|
|
if (itemStack.isEmpty()) {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
public interface CraftingMenuAccess<T extends Recipe<?>> {
|
|
void fillCraftSlotsStackedContents(StackedItemContents stackedItemContents);
|
|
|
|
void clearCraftingContent();
|
|
|
|
boolean recipeMatches(RecipeHolder<T> recipe);
|
|
}
|
|
}
|