Nearly fixed recipe matching

This commit is contained in:
TheDarkDnKTv 2021-02-06 08:29:40 +02:00
parent 701baaed72
commit 1b0a3f4e71
12 changed files with 146 additions and 87 deletions

View file

@ -1,5 +1,7 @@
package gregtechmod.api.interfaces;
import java.util.List;
import gregtechmod.api.recipe.Recipe;
import net.minecraft.item.ItemStack;
@ -27,10 +29,10 @@ public interface IRecipeWorkable {
/**
* Array of input slot ids
*/
public int[] getInputSlots();
public List<ItemStack> getInputItems();
/**
* Array of output slot ids
*/
public int[] getOutputSlots();
public List<ItemStack> getOutputItems();
}

View file

@ -97,12 +97,12 @@ public class GT_MetaTileEntity_E_Furnace extends GT_MetaTileEntity_BasicMachine
}
@Override
public int[] getInputSlots() {
public List<ItemStack> getInputItems() {
return new int[] {1, 2};
}
@Override
public int[] getOutputSlots() {
public List<ItemStack> getOutputItems() {
return new int[] {3, 4};
}

View file

@ -95,7 +95,7 @@ public abstract class BasicFluidWorkable extends GT_MetaTileEntity_BasicTank imp
}
if (result == null) {
result = Recipe.findEqualRecipe(false, recipeLogic.recipeMap, getBaseMetaTileEntity(), getInputSlots());
result = Recipe.findEqualRecipe(false, recipeLogic.recipeMap, getBaseMetaTileEntity(), getInputItems());
}
return result;
@ -156,9 +156,9 @@ public abstract class BasicFluidWorkable extends GT_MetaTileEntity_BasicTank imp
@Override
public boolean spaceForOutput(Recipe recipe) {
ItemStack[] outputs = recipe.getOutputs();
if (outputs.length <= getOutputSlots().length) {
if (outputs.length <= getOutputItems().length) {
List<ItemStack> slots = new ArrayList<>();
for (int i : getOutputSlots()) slots.add(mInventory[i]);
for (int i : getOutputItems()) slots.add(mInventory[i]);
for (int i = 0; i < outputs.length && i < slots.size(); i++) {
if (slots.get(i) != null && outputs[i] != null) {
if (!GT_Utility.areStacksEqual(slots.get(i), outputs[i]) || slots.get(i).stackSize + outputs[i].stackSize > slots.get(i).getMaxStackSize()) {

View file

@ -12,7 +12,7 @@ import gregtechmod.api.recipe.RecipeLogic;
import gregtechmod.api.util.GT_OreDictUnificator;
import gregtechmod.api.util.GT_Utility;
import gregtechmod.api.util.InfoBuilder;
import gregtechmod.api.util.ListAdapter;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
@ -138,9 +138,9 @@ public abstract class GT_MetaTileEntity_BasicMachine extends MetaTileEntity impl
@Override
public boolean spaceForOutput(Recipe recipe) {
ItemStack[] outputs = recipe.getOutputs();
if (outputs.length <= getOutputSlots().length) {
if (outputs.length <= getOutputItems().length) {
List<ItemStack> slots = new ArrayList<>();
for (int i : getOutputSlots()) slots.add(mInventory[i]);
for (int i : getOutputItems()) slots.add(mInventory[i]);
for (int i = 0; i < outputs.length && i < slots.size(); i++) {
if (slots.get(i) != null && outputs[i] != null) {
if (!GT_Utility.areStacksEqual(slots.get(i), outputs[i]) || slots.get(i).stackSize + outputs[i].stackSize > slots.get(i).getMaxStackSize()) {
@ -157,12 +157,12 @@ public abstract class GT_MetaTileEntity_BasicMachine extends MetaTileEntity impl
return false;
}
public int[] getInputSlots() {
return new int[] {1, 2};
public List<ItemStack> getInputItems() {
return new ListAdapter<>(mInventory, 1, 2);
}
public int[] getOutputSlots() {
return new int[] {3, 4};
public List<ItemStack> getOutputItems() {
return new ListAdapter<>(mInventory, 3, 4);
}
/** Fallback to the regular Machine Outside Texture */

View file

@ -25,10 +25,14 @@ import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.MultimapBuilder.ListMultimapBuilder;
import net.minecraft.init.Blocks;
@ -69,72 +73,109 @@ public class Recipe {
this.chancedOutputs = new ArrayList<>(chancedOutputs);
}
public boolean match(ItemStack...machineInputs) {
assert machineInputs != null : "Recipe check failed, machineInputs = null";
assert machineInputs.length > 0 : "Recipe check failed, machineInputs size < 1";
List<ItemStack> inputs = Arrays.stream(machineInputs)
.filter(stack -> GT_Utility.isStackValid(stack))
.collect(Collectors.toList());
return match(null, inputs);
}
/**
* Default match method
* @param decrease if set, itemstacks size will dercease
* @param input collection of input stacks even nulls
* @return true if recipe matches for machine <b>input</b>
*/
public boolean matches(boolean decrease, List<ItemStack> input) {
assert input != null : "Input can not be null!";
assert input.isEmpty() : "Input can not be empty!";
assert input.size() >= inputs.size() : "Can not be less inputs of machine than recipe has!";
public boolean match(boolean decrease, IGregTechTileEntity tile, int[] inputSlots) {
assert tile != null : "Recipe check failed, tile = null";
assert inputSlots != null : "Recipe check failed, inputSlots = null";
assert inputSlots.length > 0 : "Recipe check failed, inputSlots size < 1";
boolean result;
if (shaped)
result = checkShaped(decrease, input);
else
result = checkShapeless(decrease, input);
List<ItemStackKey> decreaseList = new ArrayList<>();
Map<ItemStack, Integer> slotStacks = new HashMap<>();
for (int i : inputSlots) {
ItemStack stack = tile.getStackInSlot(i);
if (stack != null) slotStacks.put(stack.copy(), i);
}
boolean success = this.match(key -> decreaseList.add(key), slotStacks.keySet());
if (success && decrease) {
ListMultimap<ItemStackKey, Integer> slotAligment = ListMultimapBuilder.hashKeys().arrayListValues().build();
slotStacks.entrySet().forEach(e -> slotAligment.put(ItemStackKey.from(e.getKey()), e.getValue()));
for (ItemStackKey recipeKey : decreaseList) {
List<Integer> slots = new ArrayList<>(slotAligment.values());
tile.decrStackSize(slots.get(0), recipeKey.getStackSize());
}
}
return success;
return result;
}
/**
* Matching recipe to following input, in case of successfully found item in recipe input map will execute actionPerfomer
*/
private boolean match(Consumer<ItemStackKey> actionPerfomer, Collection<ItemStack> inputs) {
if (inputs.size() >= mInputs.length) {
start:
for (ItemStack[] recipeSlot : mInputs) {
List<ItemStackKey> variants = Arrays.stream(recipeSlot)
.map(s -> ItemStackKey.from(s))
.collect(Collectors.toList());
Iterator<ItemStack> machineSlot = new ArrayList<>(inputs).iterator();
while (machineSlot.hasNext()) {
int idx = -1;
ItemStack slot = machineSlot.next();
if ((idx = variants.indexOf(ItemStackKey.from(slot))) >= 0) {
if (variants.get(idx).get().stackSize <= slot.stackSize) {
if (actionPerfomer != null)
actionPerfomer.accept(variants.get(idx));
machineSlot.remove();
continue start;
}
}
}
return false;
private boolean checkShapeless(boolean decrease, List<ItemStack> inputs) {
Pair<Boolean, Integer[]> items = this.matchItems(inputs);
if (items.getKey()) {
for (int i = 0; decrease && i < inputs.size(); i++) {
ItemStack current = inputs.get(i);
int newSize = items.getValue()[i];
if (current == null || newSize == current.stackSize) continue;
if (newSize > 0)
current.stackSize = newSize;
else
inputs.set(i, null);
}
} else return false;
return true;
}
return false;
}
/**
* Will check if recipe matches for shaped recipe
* @param actionPerfomer will calls every loop, will put in a ItemStack of slot and amount needed for recipe
*/
private boolean checkShaped(boolean decrease, List<ItemStack> inputs) {
for (int i = 0; i < this.inputs.size(); i++) {
Ingredient ingr = this.inputs.get(i);
ItemStack current = inputs.get(i);
if (!ingr.match(current) || current.stackSize < ingr.getCount())
return false;
}
for (int i = 0; decrease && i < this.inputs.size(); i++) { // Check this, not sure about working
int toConsume = this.inputs.get(i).getCount();
ItemStack current = inputs.get(i);
if (current.stackSize == toConsume) {
inputs.set(i, null);
} else {
current.stackSize -= toConsume;
}
}
return true;
}
/**
* @param input
* @return boolean means if it could be consumed, Integer[] is mapping of item amounts per slot to change
*/
private Pair<Boolean, Integer[]> matchItems(List<ItemStack> input) {
Integer[] itemAmountInSlots = new Integer[input.size()];
for (int i = 0; i < input.size(); i++) {
ItemStack itemInSlot = input.get(i);
itemAmountInSlots[i] = itemInSlot == null ? 0 : itemInSlot.stackSize;
}
for (Ingredient ingr : inputs) {
int ingrAmount = ingr.getCount();
boolean consumed = false;
if (ingrAmount == 0) {
ingrAmount = 1;
consumed = true;
}
for (int i = 0; i < input.size(); i++) {
ItemStack inputStack = input.get(i);
if (inputStack == null || !ingr.match(inputStack))
continue;
int toConsume = Math.min(itemAmountInSlots[i], ingrAmount);
ingrAmount -= toConsume;
if (!consumed)
itemAmountInSlots[i] -= toConsume;
if (ingrAmount == 0)
break;
}
if (ingrAmount > 0)
return Pair.of(false, itemAmountInSlots);
}
return Pair.of(true, itemAmountInSlots);
}
private final void addToMap(Map<Integer, List<Recipe>> aMap) {
for (ItemStack[] tStacks : mInputs) {
for (ItemStack tStack : tStacks) if (tStack != null) {
@ -200,6 +241,10 @@ public class Recipe {
return EUt;
}
public boolean isShaped() {
return shaped;
}
/**
* @return list containing only 100% chanced outputs
*/

View file

@ -123,16 +123,16 @@ public class RecipeLogic {
protected Recipe findRecipe() {
if (customRecipeProvider == null) {
return Recipe.findEqualRecipe(true, recipeMap, getMachine().getBaseMetaTileEntity(), getMachine().getInputSlots());
return Recipe.findEqualRecipe(true, recipeMap, getMachine().getBaseMetaTileEntity(), getMachine().getInputItems());
} else return customRecipeProvider.get();
}
protected boolean match(Recipe recipe) {
return recipe.match(false, getMachine().getBaseMetaTileEntity(), getMachine().getInputSlots());
return recipe.match(false, getMachine().getBaseMetaTileEntity(), getMachine().getInputItems());
}
protected void consumeInputs(Recipe recipe) {
recipe.match(true, getMachine().getBaseMetaTileEntity(), getMachine().getInputSlots());
recipe.match(true, getMachine().getBaseMetaTileEntity(), getMachine().getInputItems());
}
protected void moveItems() {
@ -143,8 +143,8 @@ public class RecipeLogic {
// Slot 4 = right Output
// Slot 5 = battery Slot in most cases
IInventory inv = getMachine().getBaseMetaTileEntity();
int[] in = getMachine().getInputSlots();
int[] out = getMachine().getOutputSlots();
int[] in = getMachine().getInputItems();
int[] out = getMachine().getOutputItems();
if (in.length > 1) GT_Utility.moveStackFromSlotAToSlotB(inv, inv, in[0], in[1], (byte)64, (byte)1, (byte)64, (byte)1);
if (out.length > 1) GT_Utility.moveStackFromSlotAToSlotB(inv, inv, out[0], out[1], (byte)64, (byte)1, (byte)64, (byte)1);
}
@ -165,9 +165,9 @@ public class RecipeLogic {
protected void endRecipe(Recipe recipe) {
ItemStack[] outputs = recipe.getOutputs();
if (outputs.length <= getMachine().getOutputSlots().length) {
if (outputs.length <= getMachine().getOutputItems().length) {
for (ItemStack out : outputs) {
for (int i : getMachine().getOutputSlots()) {
for (int i : getMachine().getOutputItems()) {
if (out != null && getMachine().getBaseMetaTileEntity().addStackToSlot(i, out.copy())) {
break;
}
@ -182,7 +182,7 @@ public class RecipeLogic {
}
protected boolean isInputNonEmpty() {
for (int i : getMachine().getInputSlots()) {
for (int i : getMachine().getInputItems()) {
ItemStack s = getMachine().getStackInSlot(i);
if (s != null && s.stackSize > 0) return true;
}

View file

@ -14,15 +14,23 @@ public final class ItemStackKey {
public final boolean isWildcard;
public static ItemStackKey from(ItemStack stack) {
return from(stack, false);
return from(stack, stack.stackSize, false);
}
public static ItemStackKey from(ItemStack stack, boolean isWildcard) {
return from(stack, stack.stackSize, isWildcard);
}
public static ItemStackKey from(ItemStack stack, int count) {
return from(stack, count, false);
}
public static ItemStackKey from(ItemStack stack, int count, boolean isWildcard) {
Objects.requireNonNull(stack);
Objects.requireNonNull(stack.getItem());
ItemStack stackCopy = stack.copy();
stackCopy.stackSize = 1;
return new ItemStackKey(stackCopy, isWildcard, stack.stackSize);
return new ItemStackKey(stackCopy, isWildcard, count);
}
private ItemStackKey(ItemStack stack, boolean isWildcard, int stackSize) {

View file

@ -1,5 +1,7 @@
package gregtechmod.common.tileentities.machines;
import java.util.List;
import gregtechmod.api.interfaces.IGregTechTileEntity;
import gregtechmod.api.metatileentity.MetaTileEntity;
import gregtechmod.api.metatileentity.implementations.BasicFluidWorkable;
@ -39,8 +41,8 @@ public class GT_MetaTileEntity_Centrifuge extends BasicFluidWorkable {
@Override public int getStackDisplaySlot() {return 6;}
@Override public int getInvSize() {return 7;}
@Override public void onRightclick(EntityPlayer aPlayer) {getBaseMetaTileEntity().openGUI(aPlayer, 146);}
@Override public int[] getInputSlots() { return new int[] {0, 1}; }
@Override public int[] getOutputSlots() { return new int[] {2, 3, 4, 5}; };
@Override public List<ItemStack> getInputItems() { return new int[] {0, 1}; }
@Override public List<ItemStack> getOutputItems() { return new int[] {2, 3, 4, 5}; };
@Override
public int getTextureIndex(byte aSide, byte aFacing, boolean aActive, boolean aRedstone) {

View file

@ -38,12 +38,12 @@ public class GT_MetaTileEntity_ChemicalReactor extends GT_MetaTileEntity_BasicMa
}
@Override
public int[] getInputSlots() {
public List<ItemStack> getInputItems() {
return new int[] {0, 1};
}
@Override
public int[] getOutputSlots() {
public List<ItemStack> getOutputItems() {
return new int[] {2};
}

View file

@ -1,5 +1,7 @@
package gregtechmod.common.tileentities.machines;
import java.util.List;
import gregtechmod.api.interfaces.IGregTechTileEntity;
import gregtechmod.api.metatileentity.MetaTileEntity;
import gregtechmod.api.metatileentity.implementations.BasicFluidWorkable;
@ -25,8 +27,8 @@ public class GT_MetaTileEntity_Electrolyzer extends BasicFluidWorkable {
@Override public int getOutputSlot() {return 2;}
@Override public int getStackDisplaySlot() {return 6;}
@Override public void onRightclick(EntityPlayer aPlayer) {getBaseMetaTileEntity().openGUI(aPlayer, 109);}
@Override public int[] getInputSlots() { return new int[] {0, 1}; }
@Override public int[] getOutputSlots() { return new int[] {2, 3, 4, 5}; };
@Override public List<ItemStack> getInputItems() { return new int[] {0, 1}; }
@Override public List<ItemStack> getOutputItems() { return new int[] {2, 3, 4, 5}; };
@Override
public MetaTileEntity newMetaEntity(IGregTechTileEntity aTileEntity) {

View file

@ -30,7 +30,7 @@ public class GT_MetaTileEntity_AlloySmelter extends GT_MetaTileEntity_BasicMachi
recipeLogic = new RecipeLogic(recipeMap, this) {
@Override
protected void moveItems() {
GT_Utility.moveStackFromSlotAToSlotB(getBaseMetaTileEntity(), getBaseMetaTileEntity(), getOutputSlots()[0], getOutputSlots()[1], (byte)64, (byte)1, (byte)64, (byte)1);
GT_Utility.moveStackFromSlotAToSlotB(getBaseMetaTileEntity(), getBaseMetaTileEntity(), getOutputItems()[0], getOutputItems()[1], (byte)64, (byte)1, (byte)64, (byte)1);
}
};
}