mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-12 08:27:37 +00:00
543 lines
18 KiB
Java
543 lines
18 KiB
Java
package codechicken.nei.recipe;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
import java.util.stream.Collectors;
|
|
|
|
import net.minecraft.item.Item;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraftforge.fluids.FluidContainerRegistry;
|
|
|
|
import com.google.common.base.Objects;
|
|
import com.google.gson.JsonArray;
|
|
import com.google.gson.JsonElement;
|
|
import com.google.gson.JsonObject;
|
|
import com.google.gson.JsonPrimitive;
|
|
|
|
import codechicken.nei.NEIClientUtils;
|
|
import codechicken.nei.NEIServerUtils;
|
|
import codechicken.nei.PositionedStack;
|
|
import codechicken.nei.api.ItemInfo;
|
|
import codechicken.nei.util.NBTJson;
|
|
|
|
public class Recipe {
|
|
|
|
public static class RecipeId {
|
|
|
|
private static final String KEY_HANDLER_NAME = "handlerName";
|
|
private static final String KEY_INGREDIENTS = "ingredients";
|
|
private static final String KEY_RESULT = "result";
|
|
|
|
private final String handlerName;
|
|
private final List<ItemStack> ingredients;
|
|
private final ItemStack result;
|
|
|
|
protected RecipeId(ItemStack result, String handlerName, List<ItemStack> ingredients) {
|
|
this.result = result != null ? StackInfo.withAmount(result, 0) : null;
|
|
this.handlerName = handlerName;
|
|
this.ingredients = ingredients;
|
|
|
|
if (!isShapedRecipe()) {
|
|
// sort to ignore ingredients position in shapeless recipes
|
|
this.ingredients.sort(Comparator.comparing(StackInfo::getItemStackGUID));
|
|
}
|
|
}
|
|
|
|
public static RecipeId of(Object result, String handlerName, Iterable<?> ingredients) {
|
|
return new RecipeId(extractItem(result), handlerName, extractIngredients(ingredients));
|
|
}
|
|
|
|
public static RecipeId of(IRecipeHandler handler, int recipeIndex) {
|
|
final List<PositionedStack> ingredients = handler.getIngredientStacks(recipeIndex);
|
|
final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
|
|
PositionedStack pStackResult = handler.getResultStack(recipeIndex);
|
|
|
|
if (pStackResult == null) {
|
|
for (PositionedStack otherStack : handler.getOtherStacks(recipeIndex)) {
|
|
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|
|
|| StackInfo.getFluid(otherStack.items[0]) != null) {
|
|
pStackResult = otherStack;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return new RecipeId(extractItem(pStackResult), handlerName, extractIngredients(ingredients));
|
|
}
|
|
|
|
public static RecipeId of(JsonObject json) {
|
|
String handlerName = null;
|
|
List<ItemStack> ingredients = new ArrayList<>();
|
|
ItemStack result = null;
|
|
|
|
if (json.get(KEY_HANDLER_NAME) != null) {
|
|
handlerName = json.get(KEY_HANDLER_NAME).getAsString();
|
|
}
|
|
|
|
if (json.get(KEY_INGREDIENTS) != null) {
|
|
ingredients = convertJsonArrayToItems(json.getAsJsonArray(KEY_INGREDIENTS));
|
|
}
|
|
|
|
if (json.get(KEY_RESULT) != null) {
|
|
result = StackInfo.loadFromNBT((NBTTagCompound) NBTJson.toNbt(json.getAsJsonObject(KEY_RESULT)));
|
|
}
|
|
|
|
return new RecipeId(result, handlerName, ingredients);
|
|
}
|
|
|
|
public String getHandlerName() {
|
|
return this.handlerName;
|
|
}
|
|
|
|
public ItemStack getResult() {
|
|
return this.result;
|
|
}
|
|
|
|
public List<ItemStack> getIngredients() {
|
|
return new ArrayList<>(this.ingredients);
|
|
}
|
|
|
|
protected static List<ItemStack> extractIngredients(Iterable<?> items) {
|
|
final List<ItemStack> list = new ArrayList<>();
|
|
|
|
for (Object item : items) {
|
|
final ItemStack stack = extractItem(item);
|
|
|
|
if (stack != null) {
|
|
list.add(prepareIngredient(stack.copy()));
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
protected static ItemStack extractItem(Object item) {
|
|
|
|
if (item instanceof PositionedStack positionedStack) {
|
|
item = StackInfo.getItemStackWithMinimumDamage(positionedStack.items);
|
|
}
|
|
|
|
if (item instanceof RecipeIngredient ingr) {
|
|
item = StackInfo.getItemStackWithMinimumDamage(ingr.getPermutations().toArray(new ItemStack[0]));
|
|
}
|
|
|
|
if (item instanceof NBTTagCompound nbTag) {
|
|
return StackInfo.loadFromNBT(nbTag);
|
|
}
|
|
|
|
if (item instanceof ItemStack stack) {
|
|
return stack;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public boolean equalsIngredients(List<PositionedStack> stacks) {
|
|
|
|
if (ingredients.size() != stacks.size()) {
|
|
return false;
|
|
}
|
|
|
|
if (isShapedRecipe()) {
|
|
for (int index = 0; index < stacks.size(); index++) {
|
|
if (!containsWithNBT(stacks.get(index), Collections.singletonList(this.ingredients.get(index)))) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
for (PositionedStack pStack : stacks) {
|
|
if (!containsWithNBT(pStack, this.ingredients)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static boolean containsWithNBT(PositionedStack pStack, List<ItemStack> ingrStacks) {
|
|
for (ItemStack item : pStack.items) {
|
|
final ItemStack pItem = prepareIngredient(item);
|
|
if (ingrStacks.stream().anyMatch(stack -> StackInfo.equalItemAndNBT(pItem, stack, true))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static ItemStack prepareIngredient(ItemStack stack) {
|
|
|
|
// hack: remove metadata for "Data Stick"
|
|
if (stack.hasTagCompound() && stack.getItemDamage() == 32708
|
|
&& "gregtech:gt.metaitem.01".equals(Item.itemRegistry.getNameForObject(stack.getItem()))) {
|
|
return new ItemStack(stack.getItem(), stack.getItemDamage());
|
|
}
|
|
|
|
// hack: remove metadata for "Circuit Imprint"
|
|
if (stack.hasTagCompound() && stack.getItemDamage() == 0
|
|
&& "bartworks:gt.bwMetaGeneratedItem0"
|
|
.equals(Item.itemRegistry.getNameForObject(stack.getItem()))) {
|
|
return new ItemStack(stack.getItem(), stack.getItemDamage());
|
|
}
|
|
|
|
return stack;
|
|
}
|
|
|
|
public boolean isShapedRecipe() {
|
|
return this.handlerName.equals("codechicken.nei.recipe.ShapedRecipeHandler")
|
|
|| this.handlerName.equals("fox.spiteful.avaritia.compat.nei.ExtremeShapedRecipeHandler");
|
|
}
|
|
|
|
public JsonObject toJsonObject() {
|
|
final JsonObject json = new JsonObject();
|
|
|
|
if (this.handlerName != null && !"".equals(this.handlerName)) {
|
|
json.add(KEY_HANDLER_NAME, new JsonPrimitive(this.handlerName));
|
|
json.add(KEY_INGREDIENTS, convertItemsToJsonArray(this.ingredients));
|
|
|
|
if (this.result != null) { // old format without result
|
|
json.add(KEY_RESULT, NBTJson.toJsonObject(StackInfo.itemStackToNBT(this.result)));
|
|
}
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
protected JsonArray convertItemsToJsonArray(List<ItemStack> items) {
|
|
final JsonArray arr = new JsonArray();
|
|
|
|
for (ItemStack stack : items) {
|
|
arr.add(NBTJson.toJsonObject(StackInfo.itemStackToNBT(stack)));
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
protected static List<ItemStack> convertJsonArrayToItems(JsonArray arr) {
|
|
final List<ItemStack> items = new ArrayList<>();
|
|
|
|
for (JsonElement elem : arr) {
|
|
final ItemStack nbt = StackInfo.loadFromNBT((NBTTagCompound) NBTJson.toNbt(elem));
|
|
|
|
if (nbt == null) {
|
|
return new ArrayList<>();
|
|
}
|
|
|
|
items.add(nbt);
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hashCode(this.handlerName, this.ingredients.size());
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object anObject) {
|
|
if (this == anObject) {
|
|
return true;
|
|
}
|
|
|
|
if (anObject instanceof RecipeId anRecipeId) {
|
|
|
|
if (!this.handlerName.equals(anRecipeId.handlerName)) {
|
|
return false;
|
|
}
|
|
|
|
if (this.ingredients.size() != anRecipeId.ingredients.size()) {
|
|
return false;
|
|
}
|
|
|
|
if (isShapedRecipe() && this.result != null
|
|
&& anRecipeId.result != null
|
|
&& !StackInfo.equalItemAndNBT(this.result, anRecipeId.result, true)) {
|
|
return false;
|
|
}
|
|
|
|
for (int idx = 0; idx < this.ingredients.size(); idx++) {
|
|
if (!StackInfo.equalItemAndNBT(this.ingredients.get(idx), anRecipeId.ingredients.get(idx), true)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public RecipeId copy() {
|
|
return new RecipeId(this.result, this.handlerName, this.ingredients);
|
|
}
|
|
|
|
}
|
|
|
|
public static class RecipeIngredient {
|
|
|
|
protected final ItemStack[] items;
|
|
protected int activeIndex = 0;
|
|
|
|
protected int amount = 0;
|
|
protected int chance = PositionedStack.CHANCE_FULL;
|
|
|
|
protected int relx;
|
|
protected int rely;
|
|
protected int width;
|
|
protected int height;
|
|
|
|
public RecipeIngredient(int relx, int rely, int width, int height, List<ItemStack> items, int chance,
|
|
int activeIndex) {
|
|
this.items = items.stream().map(ItemStack::copy).toArray(size -> new ItemStack[size]);
|
|
this.amount = StackInfo.getAmount(this.items[activeIndex]);
|
|
this.activeIndex = activeIndex;
|
|
this.relx = relx;
|
|
this.rely = rely;
|
|
this.width = width;
|
|
this.height = height;
|
|
this.chance = chance;
|
|
}
|
|
|
|
public static RecipeIngredient of(PositionedStack positionedStack) {
|
|
final List<ItemStack> stacks = Arrays.asList(positionedStack.items).stream()
|
|
.filter(item -> !ItemInfo.isHidden(item)).collect(Collectors.toList());
|
|
int activeIndex = 0;
|
|
|
|
if (stacks.isEmpty()) {
|
|
stacks.addAll(Arrays.asList(positionedStack.items));
|
|
}
|
|
|
|
for (int i = 0; i < stacks.size(); i++) {
|
|
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(positionedStack.item, stacks.get(i))) {
|
|
activeIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return of(
|
|
positionedStack.relx,
|
|
positionedStack.rely,
|
|
positionedStack.width,
|
|
positionedStack.height,
|
|
stacks,
|
|
positionedStack.getChance(),
|
|
activeIndex);
|
|
}
|
|
|
|
public static RecipeIngredient of(int relx, int rely, int width, int height, List<ItemStack> stacks, int chance,
|
|
int activeIndex) {
|
|
return new RecipeIngredient(relx, rely, width, height, stacks, chance, activeIndex);
|
|
}
|
|
|
|
public int getAmount() {
|
|
return this.amount;
|
|
}
|
|
|
|
public int getChance() {
|
|
return this.chance;
|
|
}
|
|
|
|
public ItemStack getItemStack() {
|
|
return this.items[this.activeIndex];
|
|
}
|
|
|
|
public void setActiveIndex(int activeIndex) {
|
|
this.activeIndex = Math.max(0, Math.min(activeIndex, this.items.length - 1));
|
|
this.amount = StackInfo.getAmount(this.items[this.activeIndex]);
|
|
}
|
|
|
|
public boolean contains(ItemStack stackA) {
|
|
final ItemStack stack = StackInfo.withAmount(stackA, 0);
|
|
return getPermutations().stream().anyMatch(
|
|
stackB -> NEIClientUtils.areStacksSameTypeWithNBT(StackInfo.withAmount(stackB, 0), stack));
|
|
}
|
|
|
|
public List<ItemStack> getPermutations() {
|
|
return Arrays.asList(this.items);
|
|
}
|
|
|
|
public RecipeIngredient copy() {
|
|
return new RecipeIngredient(
|
|
this.relx,
|
|
this.rely,
|
|
this.width,
|
|
this.height,
|
|
getPermutations().stream().map(ItemStack::copy).collect(Collectors.toList()),
|
|
this.chance,
|
|
this.activeIndex);
|
|
}
|
|
}
|
|
|
|
private final String handlerName;
|
|
private final RecipeIngredient[] results;
|
|
private final RecipeIngredient[] ingredients;
|
|
private RecipeId recipeId = null;
|
|
|
|
private Recipe(String handlerName, List<RecipeIngredient> ingredients, List<RecipeIngredient> results) {
|
|
this.handlerName = handlerName;
|
|
this.ingredients = ingredients.toArray(new RecipeIngredient[ingredients.size()]);
|
|
this.results = results.toArray(new RecipeIngredient[results.size()]);
|
|
}
|
|
|
|
public static Recipe of(IRecipeHandler handler, int recipeIndex) {
|
|
final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
|
|
final List<RecipeIngredient> ingredients = new ArrayList<>();
|
|
final List<RecipeIngredient> results = new ArrayList<>();
|
|
|
|
for (PositionedStack positionedStack : handler.getIngredientStacks(recipeIndex)) {
|
|
ingredients.add(RecipeIngredient.of(positionedStack));
|
|
}
|
|
|
|
if (handler.getResultStack(recipeIndex) != null) {
|
|
results.add(RecipeIngredient.of(handler.getResultStack(recipeIndex)));
|
|
} else {
|
|
for (PositionedStack positionedStack : handler.getOtherStacks(recipeIndex)) {
|
|
results.add(RecipeIngredient.of(positionedStack));
|
|
}
|
|
}
|
|
|
|
return new Recipe(handlerName, ingredients, results);
|
|
}
|
|
|
|
public static Recipe of(List<?> results, String handlerName, List<?> ingredients) {
|
|
return new Recipe(handlerName, extractItems(ingredients), extractItems(results));
|
|
}
|
|
|
|
public static Recipe of(RecipeHandlerRef handlerRef) {
|
|
return Recipe.of(handlerRef.handler, handlerRef.recipeIndex);
|
|
}
|
|
|
|
public static Recipe of(RecipeId recipeId) {
|
|
if (recipeId == null) {
|
|
return null;
|
|
}
|
|
|
|
final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(recipeId);
|
|
|
|
if (handlerRef != null) {
|
|
return Recipe.of(handlerRef);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected static List<RecipeIngredient> extractItems(List<?> items) {
|
|
final List<RecipeIngredient> list = new ArrayList<>();
|
|
|
|
for (Object item : items) {
|
|
RecipeIngredient ingr = extractItem(item);
|
|
|
|
if (ingr != null) {
|
|
list.add(ingr);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
protected static RecipeIngredient extractItem(Object item) {
|
|
|
|
if (item instanceof PositionedStack positionedStack) {
|
|
return RecipeIngredient.of(positionedStack);
|
|
}
|
|
|
|
if (item instanceof NBTTagCompound nbTag) {
|
|
item = StackInfo.loadFromNBT(nbTag);
|
|
}
|
|
|
|
if (item instanceof ItemStack stack) {
|
|
return RecipeIngredient.of(0, 0, 16, 16, Arrays.asList(stack), PositionedStack.CHANCE_FULL, 0);
|
|
}
|
|
|
|
if (item instanceof RecipeIngredient ingr) {
|
|
return ingr.copy();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public String getHandlerName() {
|
|
return this.handlerName;
|
|
}
|
|
|
|
public List<RecipeIngredient> getResults() {
|
|
return Arrays.asList(this.results);
|
|
}
|
|
|
|
public List<RecipeIngredient> getIngredients() {
|
|
return Arrays.asList(this.ingredients);
|
|
}
|
|
|
|
public ItemStack getResult() {
|
|
return getResult(null);
|
|
}
|
|
|
|
public ItemStack getResult(ItemStack stack) {
|
|
|
|
if (this.results.length == 0) {
|
|
return null;
|
|
}
|
|
|
|
if (stack == null && this.results.length > 1) {
|
|
for (RecipeIngredient ingr : this.results) {
|
|
if (!FluidContainerRegistry.isContainer(ingr.getItemStack())
|
|
|| StackInfo.getFluid(ingr.getItemStack()) != null) {
|
|
stack = ingr.getItemStack();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stack == null) {
|
|
stack = this.results[0].getItemStack();
|
|
}
|
|
|
|
int stackSize = 0;
|
|
|
|
for (RecipeIngredient result : this.results) {
|
|
if (result.contains(stack)) {
|
|
stackSize += result.getAmount();
|
|
}
|
|
}
|
|
|
|
return StackInfo.withAmount(stack, stackSize);
|
|
}
|
|
|
|
public RecipeId getRecipeId() {
|
|
|
|
if (this.recipeId == null) {
|
|
this.recipeId = RecipeId.of(getResult(), this.handlerName, getIngredients());
|
|
}
|
|
|
|
return this.recipeId;
|
|
}
|
|
|
|
public void setCustomRecipeId(RecipeId recipeId) {
|
|
this.recipeId = recipeId;
|
|
}
|
|
|
|
public Recipe copy() {
|
|
final List<RecipeIngredient> ingredients = new ArrayList<>();
|
|
final List<RecipeIngredient> results = new ArrayList<>();
|
|
|
|
for (RecipeIngredient ingr : this.ingredients) {
|
|
ingredients.add(ingr.copy());
|
|
}
|
|
|
|
for (RecipeIngredient res : this.results) {
|
|
results.add(res.copy());
|
|
}
|
|
|
|
Recipe recipe = new Recipe(this.handlerName, ingredients, results);
|
|
recipe.recipeId = this.recipeId;
|
|
|
|
return recipe;
|
|
}
|
|
|
|
}
|