mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-13 17:04:03 +00:00
Smart-Fallback & Alchemic Chemistry Set Fix (#935)
Co-authored-by: FFS <FFS@OhFuckOff> Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
This commit is contained in:
3 files changed
+253
-6
No files matched your search
@@ -197,7 +197,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
return ingredSlots;
|
||||
}
|
||||
|
||||
private void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
|
||||
protected void moveIngredients(GuiContainer gui, List<IngredientDistribution> assignedIngredients, int multiplier) {
|
||||
final Slot craftingSlot = getCraftingResultSlot(gui);
|
||||
|
||||
for (Slot slot : gui.inventorySlots.inventorySlots) {
|
||||
@@ -249,7 +249,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateRecipeQuantity(List<IngredientDistribution> assignedIngredients) {
|
||||
protected int calculateRecipeQuantity(List<IngredientDistribution> assignedIngredients) {
|
||||
int quantity = Integer.MAX_VALUE;
|
||||
|
||||
for (IngredientDistribution distrib : assignedIngredients) {
|
||||
@@ -275,7 +275,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
private Slot[][] assignIngredSlots(GuiContainer gui, List<PositionedStack> ingredients,
|
||||
protected Slot[][] assignIngredSlots(GuiContainer gui, List<PositionedStack> ingredients,
|
||||
List<IngredientDistribution> assignedIngredients) {
|
||||
Slot[][] recipeSlots = mapIngredSlots(gui, ingredients); // setup the slot map
|
||||
|
||||
@@ -320,7 +320,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
return recipeSlots;
|
||||
}
|
||||
|
||||
private List<IngredientDistribution> assignIngredients(List<PositionedStack> ingredients,
|
||||
protected List<IngredientDistribution> assignIngredients(List<PositionedStack> ingredients,
|
||||
List<DistributedIngred> ingredStacks) {
|
||||
ArrayList<IngredientDistribution> assignedIngredients = new ArrayList<>();
|
||||
for (PositionedStack posstack : ingredients) // assign what we need and have
|
||||
@@ -358,7 +358,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
return assignedIngredients;
|
||||
}
|
||||
|
||||
private void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
|
||||
protected void findInventoryQuantities(GuiContainer gui, List<DistributedIngred> ingredStacks) {
|
||||
for (Slot slot : gui.inventorySlots.inventorySlots) /* work out how much we have to go round */ {
|
||||
if (slot.getHasStack() && canMoveFrom(slot, gui)) {
|
||||
final ItemStack pstack = slot.getStack();
|
||||
@@ -390,7 +390,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private List<DistributedIngred> getPermutationIngredients(List<PositionedStack> ingredients) {
|
||||
protected List<DistributedIngred> getPermutationIngredients(List<PositionedStack> ingredients) {
|
||||
ArrayList<DistributedIngred> ingredStacks = new ArrayList<>();
|
||||
for (PositionedStack posstack : ingredients) /* work out what we need */ {
|
||||
for (ItemStack pstack : posstack.items) {
|
||||
|
||||
@@ -77,9 +77,24 @@ public class RecipeHandlerRef {
|
||||
return this.handler.getOverlayHandler(firstGui, this.recipeIndex);
|
||||
}
|
||||
|
||||
if (firstGui != null && canUseSmartOverlay(firstGui)) {
|
||||
return SmartOverlayHandler.INSTANCE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean canUseSmartOverlay(GuiContainer gui) {
|
||||
if (!(this.handler instanceof TemplateRecipeHandler templateHandler)
|
||||
|| templateHandler.getOverlayIdentifier() == null
|
||||
|| templateHandler.getGuiClass() == null
|
||||
|| !templateHandler.getGuiClass().isAssignableFrom(gui.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SmartOverlayHandler.canHandle(gui, this.handler, this.recipeIndex);
|
||||
}
|
||||
|
||||
public boolean canFillCraftingGrid(GuiContainer gui) {
|
||||
final IOverlayHandler overlayHandler = getOverlayHandler(gui);
|
||||
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.inventory.SlotCrafting;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.FastTransferManager;
|
||||
import codechicken.nei.PositionedStack;
|
||||
|
||||
public class SmartOverlayHandler extends DefaultOverlayHandler {
|
||||
|
||||
public static final SmartOverlayHandler INSTANCE = new SmartOverlayHandler();
|
||||
|
||||
private static final int MAX_RECIPE_SLOTS = 12;
|
||||
private static final int MAX_COST_DELTA = 16 * 16;
|
||||
|
||||
private SmartOverlayHandler() {}
|
||||
|
||||
public static boolean canHandle(GuiContainer gui, IRecipeHandler handler, int recipeIndex) {
|
||||
return INSTANCE.resolveSlots(gui, handler.getIngredientStacks(recipeIndex)) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int transferRecipe(GuiContainer gui, IRecipeHandler handler, int recipeIndex, int multiplier) {
|
||||
final List<PositionedStack> ingredients = handler.getIngredientStacks(recipeIndex);
|
||||
final List<DistributedIngred> ingredStacks = getPermutationIngredients(ingredients);
|
||||
final Slot[][] recipeSlots = resolveSlots(gui, ingredients);
|
||||
|
||||
if (recipeSlots == null || !clearMappedSlots(gui, recipeSlots)) return 0;
|
||||
|
||||
findInventoryQuantities(gui, ingredStacks);
|
||||
|
||||
final List<IngredientDistribution> assignedIngredients = assignIngredients(ingredients, ingredStacks);
|
||||
if (assignedIngredients == null) return 0;
|
||||
|
||||
assignIngredSlots(gui, ingredients, assignedIngredients);
|
||||
multiplier = Math.min(multiplier == 0 ? 64 : multiplier, calculateRecipeQuantity(assignedIngredients));
|
||||
|
||||
moveIngredients(gui, assignedIngredients, Math.max(1, multiplier));
|
||||
|
||||
return assignedIngredients.stream().anyMatch(distrib -> distrib.distrib.distributed == 0) ? 0 : multiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canFillCraftingGrid(GuiContainer firstGui, IRecipeHandler handler, int recipeIndex) {
|
||||
return canHandle(firstGui, handler, recipeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<Slot> getCraftMatrixSlots(GuiContainer gui, IRecipeHandler handler) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot[][] mapIngredSlots(GuiContainer gui, List<PositionedStack> ingredients) {
|
||||
Slot[][] exactSlots = super.mapIngredSlots(gui, ingredients);
|
||||
if (!hasEmptyMappings(exactSlots)) {
|
||||
return exactSlots;
|
||||
}
|
||||
|
||||
Slot[][] smartSlots = resolveSlots(gui, ingredients);
|
||||
return smartSlots == null ? exactSlots : smartSlots;
|
||||
}
|
||||
|
||||
private boolean clearMappedSlots(GuiContainer gui, Slot[][] recipeSlots) {
|
||||
Set<Slot> cleared = new HashSet<>();
|
||||
|
||||
for (Slot[] slots : recipeSlots) {
|
||||
for (Slot slot : slots) {
|
||||
if (cleared.add(slot) && slot.getHasStack() && slot.canTakeStack(gui.mc.thePlayer)) {
|
||||
FastTransferManager.clickSlot(gui, slot.slotNumber, 0, 1);
|
||||
if (slot.getHasStack()) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FastTransferManager.dropHeldItem(gui);
|
||||
return gui.mc.thePlayer.inventory.getItemStack() == null;
|
||||
}
|
||||
|
||||
private Slot[][] resolveSlots(GuiContainer gui, List<PositionedStack> ingredients) {
|
||||
if (ingredients == null || ingredients.isEmpty() || ingredients.size() > MAX_RECIPE_SLOTS) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Slot> candidates = new ArrayList<>();
|
||||
for (Slot slot : gui.inventorySlots.inventorySlots) {
|
||||
if (isDestinationCandidate(slot)) {
|
||||
candidates.add(slot);
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.size() < ingredients.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Assignment best = new Assignment();
|
||||
Assignment secondBest = new Assignment();
|
||||
searchAssignments(
|
||||
ingredients,
|
||||
candidates,
|
||||
new boolean[candidates.size()],
|
||||
new Slot[ingredients.size()],
|
||||
0,
|
||||
0,
|
||||
best,
|
||||
secondBest);
|
||||
|
||||
if (best.slots == null || secondBest.cost - best.cost <= MAX_COST_DELTA) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Slot[][] resolved = new Slot[ingredients.size()][];
|
||||
for (int i = 0; i < ingredients.size(); i++) {
|
||||
resolved[i] = new Slot[] { best.slots[i] };
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private void searchAssignments(List<PositionedStack> ingredients, List<Slot> candidates, boolean[] used,
|
||||
Slot[] assigned, int ingredientIndex, int cost, Assignment best, Assignment secondBest) {
|
||||
if (cost >= secondBest.cost) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ingredientIndex == ingredients.size()) {
|
||||
if (cost < best.cost) {
|
||||
secondBest.copyFrom(best);
|
||||
best.set(cost, assigned);
|
||||
} else if (cost < secondBest.cost) {
|
||||
secondBest.set(cost, assigned);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PositionedStack ingredient = ingredients.get(ingredientIndex);
|
||||
for (int i = 0; i < candidates.size(); i++) {
|
||||
if (used[i]) continue;
|
||||
|
||||
Slot slot = candidates.get(i);
|
||||
int slotCost = getSlotCost(ingredient, slot);
|
||||
if (slotCost < 0) continue;
|
||||
|
||||
used[i] = true;
|
||||
assigned[ingredientIndex] = slot;
|
||||
searchAssignments(
|
||||
ingredients,
|
||||
candidates,
|
||||
used,
|
||||
assigned,
|
||||
ingredientIndex + 1,
|
||||
cost + slotCost,
|
||||
best,
|
||||
secondBest);
|
||||
assigned[ingredientIndex] = null;
|
||||
used[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
private int getSlotCost(PositionedStack ingredient, Slot slot) {
|
||||
boolean acceptsAnyPermutation = false;
|
||||
for (ItemStack permutation : ingredient.items) {
|
||||
if (canUseSlot(slot, permutation)) {
|
||||
acceptsAnyPermutation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!acceptsAnyPermutation) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dx = slot.xDisplayPosition - (ingredient.relx + offsetx);
|
||||
int dy = slot.yDisplayPosition - (ingredient.rely + offsety);
|
||||
int cost = dx * dx + dy * dy;
|
||||
|
||||
if (slot.getHasStack()) {
|
||||
cost += 10_000;
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
private boolean canUseSlot(Slot slot, ItemStack stack) {
|
||||
if (stack == null || stack.stackSize <= 0 || !slot.isItemValid(stack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!slot.inventory.isItemValidForSlot(slot.getSlotIndex(), stack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !slot.getHasStack() || canStack(slot.getStack(), stack);
|
||||
}
|
||||
|
||||
private boolean isDestinationCandidate(Slot slot) {
|
||||
return slot != null && !(slot.inventory instanceof InventoryPlayer) && !(slot instanceof SlotCrafting);
|
||||
}
|
||||
|
||||
private boolean hasEmptyMappings(Slot[][] recipeSlots) {
|
||||
for (Slot[] recipeSlot : recipeSlots) {
|
||||
if (recipeSlot.length == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class Assignment {
|
||||
|
||||
int cost = Integer.MAX_VALUE;
|
||||
Slot[] slots;
|
||||
|
||||
void set(int cost, Slot[] slots) {
|
||||
this.cost = cost;
|
||||
this.slots = slots.clone();
|
||||
}
|
||||
|
||||
void copyFrom(Assignment other) {
|
||||
this.cost = other.cost;
|
||||
this.slots = other.slots == null ? null : other.slots.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user