mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-08 12:34:46 +00:00
Add Recipe Groups to GuiRecipe (#1049)
This commit is contained in:
1 parent
3c472a02cb
commit
a54c414595
16 files changed
+1464
-840
No files matched your search
@@ -8,6 +8,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -16,6 +17,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -322,6 +324,21 @@ public class FavoriteRecipes {
|
||||
return getFavorite(stack) != null;
|
||||
}
|
||||
|
||||
public static void toggleFavoriteItem(ItemStack stack) {
|
||||
if (stack == null) return;
|
||||
|
||||
if (containsManual(stack)) {
|
||||
setFavorite(stack, null);
|
||||
} else {
|
||||
setFavorite(
|
||||
stack,
|
||||
RecipeId.of(
|
||||
stack,
|
||||
FavoriteRecipes.FAVORITE_ITEM,
|
||||
Arrays.asList(new ItemStack(Blocks.fire, 1), stack)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void setFavorite(ItemStack stack, RecipeId recipeId) {
|
||||
if (stack == null) return;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.init.Blocks;
|
||||
@@ -42,7 +43,8 @@ public class PositionedStack implements Cloneable {
|
||||
protected boolean permutated = false;
|
||||
|
||||
protected String acceptsLabel;
|
||||
protected List<String> tooltip;
|
||||
|
||||
protected Function<ItemStack, List<String>> tooltip;
|
||||
protected List<Badge> badges;
|
||||
|
||||
public PositionedStack(Object object, int x, int y, boolean genPerms) {
|
||||
@@ -115,11 +117,15 @@ public class PositionedStack implements Cloneable {
|
||||
}
|
||||
|
||||
public void setTooltip(List<String> tooltip) {
|
||||
this.tooltip = tooltip;
|
||||
this.tooltip = tooltip == null ? null : item -> tooltip;
|
||||
}
|
||||
|
||||
public void setTooltipProvider(Function<ItemStack, List<String>> provider) {
|
||||
this.tooltip = provider;
|
||||
}
|
||||
|
||||
public List<String> getTooltip() {
|
||||
return this.tooltip;
|
||||
return this.tooltip == null ? null : this.tooltip.apply(this.item);
|
||||
}
|
||||
|
||||
public int getChance() {
|
||||
@@ -135,7 +141,6 @@ public class PositionedStack implements Cloneable {
|
||||
PositionedStack pStack = (PositionedStack) super.clone();
|
||||
pStack.items = Arrays.stream(this.items).map(ItemStack::copy).toArray(ItemStack[]::new);
|
||||
pStack.item = this.item == null ? null : this.item.copy();
|
||||
pStack.tooltip = this.tooltip == null ? null : new ArrayList<>(this.tooltip);
|
||||
pStack.badges = this.badges == null ? null : new ArrayList<>(this.badges);
|
||||
return pStack;
|
||||
} catch (CloneNotSupportedException e) {
|
||||
|
||||
@@ -1,121 +1,242 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.inventory.InventoryCrafting;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemDye;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.RecipeFireworks;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.nei.InventoryCraftingDummy;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
|
||||
public class FireworkRecipeHandler extends ShapelessRecipeHandler {
|
||||
|
||||
public class CachedFireworkRecipe extends CachedShapelessRecipe {
|
||||
|
||||
final LinkedList<Object> itemList = new LinkedList<>();
|
||||
|
||||
public final Object[] baseIngredients;
|
||||
public final Object extraIngred;
|
||||
public final int recipeType;
|
||||
public final Object groupId;
|
||||
|
||||
public CachedFireworkRecipe(Object[] base, Object extra, int type) {
|
||||
super(new ItemStack(Items.fireworks));
|
||||
this.baseIngredients = base;
|
||||
this.extraIngred = extra;
|
||||
public CachedFireworkRecipe(List<ItemStack> ingredients, ItemStack result, int type, Object groupId) {
|
||||
super(ingredients, result);
|
||||
this.recipeType = type;
|
||||
|
||||
cycle();
|
||||
}
|
||||
|
||||
public void cycle() {
|
||||
itemList.clear();
|
||||
itemList.addAll(Arrays.asList(baseIngredients));
|
||||
int extras = (cycleticks / 40) % (10 - itemList.size());
|
||||
for (int i = 0; i < extras; i++) itemList.add(extraIngred);
|
||||
setIngredients(itemList);
|
||||
|
||||
List<PositionedStack> ingreds = getIngredients();
|
||||
for (int i = 0; i < 9; i++)
|
||||
inventoryCrafting.setInventorySlotContents(i, i < ingreds.size() ? ingreds.get(i).item : null);
|
||||
|
||||
if (!recipeFireworks.matches(inventoryCrafting, null)) throw new RuntimeException("Invalid Recipe?");
|
||||
setResult(recipeFireworks.getCraftingResult(null));
|
||||
this.groupId = groupId;
|
||||
}
|
||||
}
|
||||
|
||||
// indexed by the "Type" explosion tag
|
||||
private static final ItemStack[] SHAPES = { null, new ItemStack(Items.fire_charge),
|
||||
new ItemStack(Items.gold_nugget), new ItemStack(Items.skull, 1, Short.MAX_VALUE),
|
||||
new ItemStack(Items.feather) };
|
||||
|
||||
private final InventoryCrafting inventoryCrafting = new InventoryCraftingDummy();
|
||||
private final RecipeFireworks recipeFireworks = new RecipeFireworks();
|
||||
|
||||
public final ArrayList<CachedFireworkRecipe> mfireworks = new ArrayList<>();
|
||||
private ItemStack usageIngredient = null;
|
||||
|
||||
public FireworkRecipeHandler() {
|
||||
super();
|
||||
stackorder = new int[][] { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 0, 1 }, { 1, 1 }, { 2, 1 }, { 0, 2 }, { 1, 2 },
|
||||
{ 2, 2 } };
|
||||
loadAllFireworks();
|
||||
}
|
||||
|
||||
private void loadAllFireworks() {
|
||||
// charges
|
||||
Item[] shapes = new Item[] { null, Items.fire_charge, Items.gold_nugget, Items.feather, Items.skull };
|
||||
Item[] effects = new Item[] { null, Items.diamond, Items.glowstone_dust };
|
||||
for (Item shape : shapes)
|
||||
for (Item effect : effects) genRecipe(Items.gunpowder, shape, effect, Items.dye, Items.dye, 0);
|
||||
|
||||
// fireworks
|
||||
genRecipe(Items.gunpowder, Items.paper, Items.firework_charge, 2);
|
||||
genRecipe(Items.gunpowder, Items.gunpowder, Items.paper, Items.firework_charge, 2);
|
||||
genRecipe(Items.gunpowder, Items.gunpowder, Items.gunpowder, Items.paper, Items.firework_charge, 2);
|
||||
|
||||
// setup a valid charge to use for the recolour recipe
|
||||
for (int i = 0; i < 9; i++) inventoryCrafting.setInventorySlotContents(i, null);
|
||||
inventoryCrafting.setInventorySlotContents(0, new ItemStack(Items.gunpowder));
|
||||
inventoryCrafting.setInventorySlotContents(1, new ItemStack(Items.dye));
|
||||
recipeFireworks.matches(inventoryCrafting, null);
|
||||
ItemStack charge = recipeFireworks.getCraftingResult(null);
|
||||
genRecipe(charge, Items.dye, Items.dye, 1);
|
||||
@Override
|
||||
public Object getRecipeGroupId(int recipe) {
|
||||
return this.arecipes.get(recipe) instanceof CachedFireworkRecipe firework ? firework.groupId : null;
|
||||
}
|
||||
|
||||
private void genRecipe(Object... params) {
|
||||
int numIngreds = 0;
|
||||
for (int i = 0; i < params.length - 2; i++) if (params[i] != null) numIngreds++;
|
||||
private void loadChargeRecipes() {
|
||||
final List<List<ItemStack>> dyeSets = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < params.length - 1; i++)
|
||||
if (params[i] instanceof Item) params[i] = new ItemStack((Item) params[i], 1, Short.MAX_VALUE);
|
||||
for (int dye = 0; dye < ItemDye.field_150922_c.length; dye++) {
|
||||
dyeSets.add(Collections.singletonList(new ItemStack(Items.dye, 1, dye)));
|
||||
}
|
||||
|
||||
Object[] ingreds = new Object[numIngreds];
|
||||
for (int i = 0, j = 0; i < params.length - 2; i++) if (params[i] != null) ingreds[j++] = params[i];
|
||||
for (ItemStack shape : SHAPES) {
|
||||
for (int effect = 0; effect < 4; effect++) {
|
||||
addRecipes(getChargeBase(shape, (effect & 1) != 0, (effect & 2) != 0), dyeSets, 0);
|
||||
}
|
||||
}
|
||||
|
||||
mfireworks
|
||||
.add(new CachedFireworkRecipe(ingreds, params[params.length - 2], (Integer) params[params.length - 1]));
|
||||
final ItemStack charge = craft(Arrays.asList(new ItemStack(Items.gunpowder), new ItemStack(Items.dye)));
|
||||
addRecipes(Collections.singletonList(charge), dyeSets, 1);
|
||||
}
|
||||
|
||||
private void loadRocketRecipes() {
|
||||
final ItemStack charge = craft(Arrays.asList(new ItemStack(Items.gunpowder), new ItemStack(Items.dye)));
|
||||
final List<List<ItemStack>> chargeSets = new ArrayList<>();
|
||||
|
||||
for (int count = 0; count < 9; count++) {
|
||||
chargeSets.add(Collections.nCopies(count, charge));
|
||||
}
|
||||
|
||||
for (int flight = 1; flight <= 3; flight++) {
|
||||
addRecipes(getRocketBase(flight), chargeSets, 2);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadExactChargeRecipes(NBTTagCompound explosion) {
|
||||
final byte type = explosion.getByte("Type");
|
||||
|
||||
if (explosion.hasKey("FadeColors")) {
|
||||
final NBTTagCompound baseTag = new NBTTagCompound();
|
||||
final ItemStack charge = new ItemStack(Items.firework_charge);
|
||||
|
||||
baseTag.setTag("Explosion", explosion.copy());
|
||||
baseTag.getCompoundTag("Explosion").removeTag("FadeColors");
|
||||
charge.setTagCompound(baseTag);
|
||||
|
||||
addRecipes(Collections.singletonList(charge), getDyeSets(explosion.getIntArray("FadeColors")), 1);
|
||||
} else if (type >= 0 && type < SHAPES.length) {
|
||||
addRecipes(
|
||||
getChargeBase(SHAPES[type], explosion.getBoolean("Flicker"), explosion.getBoolean("Trail")),
|
||||
getDyeSets(explosion.getIntArray("Colors")),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadExactRocketRecipes(NBTTagCompound fireworks) {
|
||||
final NBTTagList explosions = fireworks.getTagList("Explosions", 10);
|
||||
final List<ItemStack> charges = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < explosions.tagCount(); i++) {
|
||||
final ItemStack charge = new ItemStack(Items.firework_charge);
|
||||
charge.setTagCompound(new NBTTagCompound());
|
||||
charge.getTagCompound().setTag("Explosion", explosions.getCompoundTagAt(i));
|
||||
charges.add(charge);
|
||||
}
|
||||
|
||||
addRecipes(getRocketBase(fireworks.getByte("Flight")), Collections.singletonList(charges), 2);
|
||||
}
|
||||
|
||||
private static List<ItemStack> getChargeBase(ItemStack shape, boolean flicker, boolean trail) {
|
||||
final List<ItemStack> base = new ArrayList<>();
|
||||
base.add(new ItemStack(Items.gunpowder));
|
||||
|
||||
if (shape != null) {
|
||||
base.add(shape);
|
||||
}
|
||||
|
||||
if (flicker) {
|
||||
base.add(new ItemStack(Items.glowstone_dust));
|
||||
}
|
||||
|
||||
if (trail) {
|
||||
base.add(new ItemStack(Items.diamond));
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
private static List<ItemStack> getRocketBase(int flight) {
|
||||
final List<ItemStack> base = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < flight; i++) {
|
||||
base.add(new ItemStack(Items.gunpowder));
|
||||
}
|
||||
|
||||
base.add(new ItemStack(Items.paper));
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
private static List<List<ItemStack>> getDyeSets(int[] colors) {
|
||||
final List<ItemStack> dyes = new ArrayList<>();
|
||||
|
||||
for (int color : colors) {
|
||||
for (int dye = 0; dye < ItemDye.field_150922_c.length; dye++) {
|
||||
if (ItemDye.field_150922_c[dye] == color) {
|
||||
dyes.add(new ItemStack(Items.dye, 1, dye));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dyes.isEmpty() || dyes.size() != colors.length) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (Arrays.stream(colors).distinct().count() > 1) {
|
||||
return Collections.singletonList(dyes);
|
||||
}
|
||||
|
||||
final List<List<ItemStack>> dyeSets = new ArrayList<>();
|
||||
|
||||
for (int count = 1; count < 9; count++) {
|
||||
dyeSets.add(Collections.nCopies(count, dyes.get(0)));
|
||||
}
|
||||
|
||||
return dyeSets;
|
||||
}
|
||||
|
||||
private void addRecipes(List<ItemStack> base, List<List<ItemStack>> extraSets, int type) {
|
||||
final Object groupId = new Object();
|
||||
|
||||
for (List<ItemStack> extras : extraSets) {
|
||||
final List<ItemStack> ingredients = new ArrayList<>(base);
|
||||
ingredients.addAll(extras);
|
||||
|
||||
final ItemStack result = craft(ingredients);
|
||||
|
||||
if (result != null) {
|
||||
final CachedFireworkRecipe recipe = new CachedFireworkRecipe(ingredients, result, type, groupId);
|
||||
|
||||
if (usageIngredient == null || recipe.contains(recipe.ingredients, usageIngredient)) {
|
||||
arecipes.add(recipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ItemStack craft(List<ItemStack> ingredients) {
|
||||
|
||||
if (ingredients.size() > 9) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
inventoryCrafting.setInventorySlotContents(i, i < ingredients.size() ? ingredients.get(i) : null);
|
||||
}
|
||||
|
||||
return recipeFireworks.matches(inventoryCrafting, null) ? recipeFireworks.getCraftingResult(inventoryCrafting)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCraftingRecipes(ItemStack result) {
|
||||
for (CachedFireworkRecipe recipe : mfireworks) {
|
||||
if (recipe.result.item.getItem() == result.getItem()) {
|
||||
recipe.cycle();
|
||||
arecipes.add(recipe);
|
||||
final NBTTagCompound tag = result.getTagCompound();
|
||||
|
||||
if (result.getItem() == Items.firework_charge) {
|
||||
if (tag != null) {
|
||||
loadExactChargeRecipes(tag.getCompoundTag("Explosion"));
|
||||
}
|
||||
|
||||
if (arecipes.isEmpty()) {
|
||||
loadChargeRecipes();
|
||||
}
|
||||
} else if (result.getItem() == Items.fireworks) {
|
||||
if (tag != null) {
|
||||
loadExactRocketRecipes(tag.getCompoundTag("Fireworks"));
|
||||
}
|
||||
|
||||
if (arecipes.isEmpty()) {
|
||||
loadRocketRecipes();
|
||||
}
|
||||
}
|
||||
// show random recolouring recipes as well
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCraftingRecipes(String outputId, Object... results) {
|
||||
if (outputId.equals("crafting") && getClass() == FireworkRecipeHandler.class) {
|
||||
arecipes.addAll(mfireworks);
|
||||
loadChargeRecipes();
|
||||
loadRocketRecipes();
|
||||
} else {
|
||||
super.loadCraftingRecipes(outputId, results);
|
||||
}
|
||||
@@ -123,20 +244,9 @@ public class FireworkRecipeHandler extends ShapelessRecipeHandler {
|
||||
|
||||
@Override
|
||||
public void loadUsageRecipes(ItemStack ingredient) {
|
||||
for (CachedFireworkRecipe recipe : mfireworks) {
|
||||
if (recipe.contains(recipe.ingredients, ingredient)) {
|
||||
recipe.cycle();
|
||||
arecipes.add(recipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
if (!NEIClientUtils.shiftKey()) {
|
||||
cycleticks++;
|
||||
if (cycleticks % 20 == 0) for (CachedRecipe crecipe : arecipes) ((CachedFireworkRecipe) crecipe).cycle();
|
||||
}
|
||||
usageIngredient = ingredient;
|
||||
loadChargeRecipes();
|
||||
loadRocketRecipes();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,11 +257,8 @@ public class FireworkRecipeHandler extends ShapelessRecipeHandler {
|
||||
@Override
|
||||
public List<String> handleTooltip(GuiRecipe<?> gui, List<String> currenttip, int recipe) {
|
||||
currenttip = super.handleTooltip(gui, currenttip, recipe);
|
||||
Point mousepos = GuiDraw.getMousePosition();
|
||||
Point relMouse = new Point(mousepos.x - gui.guiLeft, mousepos.y - gui.guiTop);
|
||||
Point recipepos = gui.getRecipePosition(recipe);
|
||||
if (currenttip.isEmpty() && GuiContainerManager.getStackMouseOver(gui) == null
|
||||
&& new Rectangle(recipepos.x, recipepos.y, 166, 55).contains(relMouse))
|
||||
&& new Rectangle(0, 0, 166, 55).contains(gui.getRecipeMousePosition(recipe)))
|
||||
currenttip.add(
|
||||
NEIClientUtils.translate(
|
||||
"recipe.firework.tooltip" + ((CachedFireworkRecipe) arecipes.get(recipe)).recipeType));
|
||||
|
||||
@@ -52,6 +52,7 @@ import codechicken.nei.guihook.IContainerTooltipHandler;
|
||||
import codechicken.nei.guihook.IGuiClientSide;
|
||||
import codechicken.nei.guihook.IGuiHandleMouseWheel;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.widget.RecipeWidget;
|
||||
import codechicken.nei.scroll.ScrollBar;
|
||||
import codechicken.nei.scroll.ScrollBar.OverflowType;
|
||||
import codechicken.nei.scroll.ScrollBar.ScrollPlace;
|
||||
@@ -523,7 +524,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
}
|
||||
|
||||
public int openTargetRecipe(RecipeId recipeId) {
|
||||
int refIndex = -1;
|
||||
int recipeIndex = -1;
|
||||
int recipetype = 0;
|
||||
|
||||
this.recipeId = recipeId;
|
||||
@@ -537,10 +538,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
recipetype = j;
|
||||
|
||||
if (!this.recipeId.getIngredients().isEmpty()) {
|
||||
refIndex = SearchRecipeHandler.findFirst(
|
||||
recipeIndex = SearchRecipeHandler.findFirst(
|
||||
localHandler,
|
||||
recipeIndex -> this.recipeId
|
||||
.equalsIngredients(localHandler.getIngredientStacks(recipeIndex)));
|
||||
ri -> this.recipeId.equalsIngredients(localHandler.getIngredientStacks(ri)));
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -549,17 +549,17 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
}
|
||||
|
||||
setRecipePage(recipetype);
|
||||
this.handlerPages.gotoRefIndex(Math.max(0, refIndex));
|
||||
this.handlerPages.gotoRecipeIndex(Math.max(0, recipeIndex));
|
||||
|
||||
return refIndex;
|
||||
return recipeIndex;
|
||||
}
|
||||
|
||||
public Recipe getFocusedRecipe() {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
final Widget activeWidget = this.container.getWidgetUnderMouse(mouse.x, mouse.y);
|
||||
|
||||
if (activeWidget instanceof NEIRecipeWidget recipeWidget && recipeWidget.isFocusedRecipe(mouse.x, mouse.y)) {
|
||||
return recipeWidget.getRecipe();
|
||||
if (activeWidget instanceof RecipeWidget recipeWidget && recipeWidget.isFocusedRecipe(mouse.x, mouse.y)) {
|
||||
return Recipe.of(recipeWidget.getRecipeHandlerRef());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -569,7 +569,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
final Widget activeWidget = this.container.getWidgetUnderMouse(mouse.x, mouse.y);
|
||||
|
||||
if (activeWidget instanceof NEIRecipeWidget recipeWidget) {
|
||||
if (activeWidget instanceof RecipeWidget recipeWidget) {
|
||||
final PositionedStack hovered = recipeWidget.getPositionedStackMouseOver(mouse.x, mouse.y);
|
||||
|
||||
if (hovered != null) {
|
||||
@@ -586,7 +586,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
final EmptyContainer slotcontainer = (EmptyContainer) inventorySlots;
|
||||
slotcontainer.setActiveStack(null);
|
||||
|
||||
if (activeWidget instanceof NEIRecipeWidget recipeWidget) {
|
||||
if (activeWidget instanceof RecipeWidget recipeWidget) {
|
||||
final PositionedStack hovered = recipeWidget.getPositionedStackMouseOver(mousex, mousey);
|
||||
|
||||
if (hovered != null) {
|
||||
@@ -844,7 +844,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
List<String> currenttip) {
|
||||
final Widget activeWidget = this.container.getWidgetUnderMouse(mousex, mousey);
|
||||
|
||||
if (activeWidget instanceof NEIRecipeWidget recipeWidget) {
|
||||
if (activeWidget instanceof RecipeWidget recipeWidget) {
|
||||
currenttip = recipeWidget.handleItemTooltip(itemstack, mousex, mousey, currenttip);
|
||||
}
|
||||
|
||||
@@ -891,7 +891,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
this.recipetype = this.currenthandlers.indexOf(this.handler.original);
|
||||
|
||||
setRecipePage(this.recipetype);
|
||||
this.handlerPages.changePage(currentPage);
|
||||
this.handlerPages.changePage(currentPage - this.handlerPages.getCurrentPageIndex());
|
||||
|
||||
refreshContainer();
|
||||
}
|
||||
@@ -1014,8 +1014,23 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
return this.firstGuiGeneral;
|
||||
}
|
||||
|
||||
public Point getRecipePosition(int recipe) {
|
||||
return getRefIndexPosition(getRecipeIndices().indexOf(recipe));
|
||||
public Point getRecipePosition(int recipeIndex) {
|
||||
|
||||
for (Widget widget : this.container.getWidgets()) {
|
||||
if (widget instanceof RecipeWidget recipeWidget && recipeWidget.containsRecipeIndex(recipeIndex)) {
|
||||
return new Point(
|
||||
recipeWidget.x - this.guiLeft,
|
||||
recipeWidget.y - this.guiTop + recipeWidget.getHandlerInfo().getYShift());
|
||||
}
|
||||
}
|
||||
|
||||
return new Point(0, 0);
|
||||
}
|
||||
|
||||
public Point getRecipeMousePosition(int recipeIndex) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
final Point recipePosition = getRecipePosition(recipeIndex);
|
||||
return new Point(mouse.x - this.guiLeft - recipePosition.x, mouse.y - this.guiTop - recipePosition.y);
|
||||
}
|
||||
|
||||
protected Point getRefIndexPosition(int refIndex) {
|
||||
@@ -1027,7 +1042,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
final List<Widget> children = this.container.getWidgets();
|
||||
|
||||
if (refIndex >= 0 && refIndex < children.size()
|
||||
&& children.get(refIndex) instanceof NEIRecipeWidget recipeWidget) {
|
||||
&& children.get(refIndex) instanceof RecipeWidget recipeWidget) {
|
||||
return new Point(
|
||||
recipeWidget.x - this.guiLeft,
|
||||
recipeWidget.y - this.guiTop + recipeWidget.getHandlerInfo().getYShift());
|
||||
|
||||
@@ -15,12 +15,12 @@ public abstract class GuiRecipeButton extends GuiNEIButton {
|
||||
public static class UpdateRecipeButtonsEvent extends GuiScreenEvent {
|
||||
|
||||
public List<GuiRecipeButton> buttonList;
|
||||
public NEIRecipeWidget recipeWidget;
|
||||
public RecipeHandlerRef handlerRef;
|
||||
|
||||
public UpdateRecipeButtonsEvent(GuiRecipe<?> gui, NEIRecipeWidget recipeWidget,
|
||||
public UpdateRecipeButtonsEvent(GuiRecipe<?> gui, RecipeHandlerRef handlerRef,
|
||||
List<GuiRecipeButton> buttonList) {
|
||||
super(gui);
|
||||
this.recipeWidget = recipeWidget;
|
||||
this.handlerRef = handlerRef;
|
||||
this.buttonList = new ArrayList<>(buttonList);
|
||||
}
|
||||
|
||||
@@ -29,16 +29,16 @@ public abstract class GuiRecipeButton extends GuiNEIButton {
|
||||
|
||||
public HandlerInfo handlerInfo;
|
||||
|
||||
public Pre(GuiRecipe<?> gui, NEIRecipeWidget recipeWidget, HandlerInfo handlerInfo) {
|
||||
super(gui, recipeWidget, new ArrayList<>());
|
||||
public Pre(GuiRecipe<?> gui, RecipeHandlerRef handlerRef, HandlerInfo handlerInfo) {
|
||||
super(gui, handlerRef, new ArrayList<>());
|
||||
this.handlerInfo = handlerInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Post extends UpdateRecipeButtonsEvent {
|
||||
|
||||
public Post(GuiRecipe<?> gui, NEIRecipeWidget recipeWidget, List<GuiRecipeButton> buttonList) {
|
||||
super(gui, recipeWidget, buttonList);
|
||||
public Post(GuiRecipe<?> gui, RecipeHandlerRef handlerRef, List<GuiRecipeButton> buttonList) {
|
||||
super(gui, handlerRef, buttonList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,14 @@ public interface IRecipeHandler {
|
||||
*/
|
||||
PositionedStack getResultStack(int recipe);
|
||||
|
||||
/**
|
||||
* @param recipe The recipe index to get the group id for.
|
||||
* @return An id shared by every recipe in this recipe's group, or {@code null} if this recipe isn't grouped.
|
||||
*/
|
||||
default Object getRecipeGroupId(int recipe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tick function called for updating progress bars and cycling damage items.
|
||||
*/
|
||||
|
||||
@@ -1,688 +0,0 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.KeyManager;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.NEIServerUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.Widget;
|
||||
import codechicken.nei.api.IGuiContainerOverlay;
|
||||
import codechicken.nei.api.ShortcutInputHandler;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.GuiRecipeButton.UpdateRecipeButtonsEvent;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.debug.DebugHandlerWidget;
|
||||
import codechicken.nei.util.NEIMouseUtils;
|
||||
|
||||
public class NEIRecipeWidget extends Widget {
|
||||
|
||||
protected AcceptsFollowingTooltipLineHandler acceptsFollowingTooltipLineHandler;
|
||||
protected final Map<PositionedStack, Integer> favoriteIndexes = new WeakHashMap<>();
|
||||
protected final Map<PositionedStack, List<ItemStack>> permutations = new WeakHashMap<>();
|
||||
protected final Map<PositionedStack, List<Badge>> badgeCache = new WeakHashMap<>();
|
||||
protected int favoriteRevision = -1;
|
||||
protected boolean update = true;
|
||||
protected int cycleticks = 0;
|
||||
protected int lastcycle = -1;
|
||||
|
||||
protected final RecipeHandlerRef handlerRef;
|
||||
protected final HandlerInfo handlerInfo;
|
||||
|
||||
protected boolean showAsWidget = false;
|
||||
protected List<GuiRecipeButton> recipeButtons = null;
|
||||
|
||||
public NEIRecipeWidget(RecipeHandlerRef handlerRef) {
|
||||
this.handlerRef = handlerRef;
|
||||
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.handlerRef.handler);
|
||||
update();
|
||||
}
|
||||
|
||||
public void setLocation(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
|
||||
if (this.showAsWidget || !this.handlerInfo.isAllowOverflowY()) {
|
||||
final int recipeHeight = this.handlerRef.handler.getRecipeHeight(this.handlerRef.recipeIndex);
|
||||
this.w = Math.max(166, this.handlerInfo.getWidth());
|
||||
this.h = recipeHeight > 0 ? recipeHeight : this.handlerInfo.getHeight();
|
||||
this.h += this.handlerInfo.getYShift();
|
||||
}
|
||||
|
||||
this.update = true;
|
||||
}
|
||||
|
||||
public List<GuiRecipeButton> getRecipeButtons() {
|
||||
|
||||
if (this.recipeButtons == null) {
|
||||
if (!getOutputs().isEmpty()) {
|
||||
this.recipeButtons = initButtons();
|
||||
} else {
|
||||
this.recipeButtons = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(this.recipeButtons);
|
||||
}
|
||||
|
||||
protected List<GuiRecipeButton> getRecipeButtonsIfInit() {
|
||||
|
||||
if (this.recipeButtons == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(this.recipeButtons);
|
||||
}
|
||||
|
||||
public RecipeHandlerRef getRecipeHandlerRef() {
|
||||
return this.handlerRef;
|
||||
}
|
||||
|
||||
public Recipe getRecipe() {
|
||||
return Recipe.of(this.handlerRef);
|
||||
}
|
||||
|
||||
public HandlerInfo getHandlerInfo() {
|
||||
return this.handlerInfo;
|
||||
}
|
||||
|
||||
public void showAsWidget(boolean show) {
|
||||
this.showAsWidget = show;
|
||||
}
|
||||
|
||||
protected List<GuiRecipeButton> initButtons() {
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
|
||||
if (guiRecipe != null) {
|
||||
final UpdateRecipeButtonsEvent.Pre preEvent = new UpdateRecipeButtonsEvent.Pre(
|
||||
guiRecipe,
|
||||
this,
|
||||
this.handlerInfo);
|
||||
|
||||
if (MinecraftForge.EVENT_BUS.post(preEvent)) {
|
||||
return preEvent.buttonList;
|
||||
} else {
|
||||
final UpdateRecipeButtonsEvent.Post postEvent = new UpdateRecipeButtonsEvent.Post(
|
||||
guiRecipe,
|
||||
this,
|
||||
getDefaultButtons());
|
||||
MinecraftForge.EVENT_BUS.post(postEvent);
|
||||
return postEvent.buttonList;
|
||||
}
|
||||
|
||||
} else {
|
||||
return getDefaultButtons();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected List<GuiRecipeButton> getDefaultButtons() {
|
||||
GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
final List<GuiRecipeButton> buttons = new ArrayList<>();
|
||||
final boolean showFavorites = NEIClientConfig.favoritesEnabled() && handlerInfo.getShowFavoritesButton();
|
||||
final boolean showOverlay = handlerInfo.getShowOverlayButton();
|
||||
final int x = Math.min(166, this.w) - GuiRecipeButton.BUTTON_WIDTH;
|
||||
int y = this.h - GuiRecipeButton.BUTTON_HEIGHT - 6;
|
||||
|
||||
if (guiContainer instanceof IGuiContainerOverlay overlay) {
|
||||
guiContainer = overlay.getFirstScreen();
|
||||
}
|
||||
|
||||
if (showOverlay) {
|
||||
buttons.add(new GuiOverlayButton(guiContainer, this.handlerRef, x, y));
|
||||
y -= GuiRecipeButton.BUTTON_HEIGHT + 1;
|
||||
}
|
||||
|
||||
if (showFavorites) {
|
||||
buttons.add(new GuiFavoriteButton(handlerRef, x, y));
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mouseX, int mouseY) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
|
||||
if (this.update) {
|
||||
this.update = false;
|
||||
|
||||
if (!NEIClientUtils.shiftKey() && ((this.cycleticks++) / 20) != this.lastcycle || this.lastcycle == -1) {
|
||||
this.lastcycle = this.cycleticks / 20;
|
||||
updatePermutations();
|
||||
|
||||
for (GuiRecipeButton button : getRecipeButtonsIfInit()) {
|
||||
button.onPermutationsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
for (GuiRecipeButton button : getRecipeButtons()) {
|
||||
button.update();
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glTranslatef(this.x, this.y + yShift, 0);
|
||||
|
||||
this.handlerRef.handler.drawBackground(this.handlerRef.recipeIndex);
|
||||
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
|
||||
GuiContainerManager.enableMatrixStackLogging();
|
||||
|
||||
for (PositionedStack pStack : getInputs()) {
|
||||
|
||||
if (!this.permutations.containsKey(pStack)) {
|
||||
updatePermutationsFor(pStack);
|
||||
}
|
||||
|
||||
drawItem(pStack, mouseX, mouseY, yShift, true);
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getCatalysts()) {
|
||||
|
||||
if (!this.permutations.containsKey(pStack)) {
|
||||
updatePermutationsFor(pStack);
|
||||
}
|
||||
|
||||
drawItem(pStack, mouseX, mouseY, yShift, true);
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getOutputs()) {
|
||||
drawItem(pStack, mouseX, mouseY, yShift, false);
|
||||
}
|
||||
|
||||
GuiContainerManager.disableMatrixStackLogging();
|
||||
|
||||
this.handlerRef.handler.drawForeground(this.handlerRef.recipeIndex);
|
||||
|
||||
final GuiRecipeButton overlayButton = forEachButtons(
|
||||
button -> (this.showAsWidget ? button instanceof GuiOverlayButton : button.contains(mouseX, mouseY))
|
||||
? button
|
||||
: null,
|
||||
null);
|
||||
|
||||
if (overlayButton != null) {
|
||||
NEIClientUtils.gl2DRenderContext(() -> overlayButton.drawItemOverlay());
|
||||
}
|
||||
|
||||
GL11.glTranslatef(-this.x, -this.y - yShift, 0);
|
||||
GL11.glPopAttrib();
|
||||
|
||||
if (!this.showAsWidget) {
|
||||
final Minecraft mc = NEIClientUtils.mc();
|
||||
for (GuiRecipeButton button : getRecipeButtons()) {
|
||||
button.xPosition += this.x;
|
||||
button.yPosition += this.y;
|
||||
button.drawButton(mc, mouseX, mouseY);
|
||||
button.xPosition -= this.x;
|
||||
button.yPosition -= this.y;
|
||||
}
|
||||
}
|
||||
|
||||
DebugHandlerWidget.instance.drawGuiPlaceholder(this);
|
||||
}
|
||||
|
||||
protected void drawItem(PositionedStack pStack, int mouseX, int mouseY, int yShift, boolean input) {
|
||||
pStack.draw(mouseX - this.x, mouseY - this.y - yShift);
|
||||
|
||||
if (this.handlerInfo.getShowBadge()) {
|
||||
drawBadge(pStack, input);
|
||||
}
|
||||
|
||||
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
|
||||
NEIClientUtils.gl2DRenderContext(
|
||||
() -> GuiDraw.drawRect(pStack.relx, pStack.rely, pStack.width, pStack.height, 0x80FFFFFF));
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Badge> getBadges(PositionedStack pStack, boolean input) {
|
||||
|
||||
return this.badgeCache.computeIfAbsent(pStack, k -> {
|
||||
final List<Badge> badges = k.getBadges();
|
||||
|
||||
if (badges == null) {
|
||||
|
||||
if (input) {
|
||||
if (StackInfo.getAmount(pStack.item) == 0) {
|
||||
return Collections.singletonList(Badge.notConsumed());
|
||||
} else if (pStack.getChance() == 0) {
|
||||
return Collections.singletonList(Badge.notConsumedParallel());
|
||||
} else if (pStack.getChance() != PositionedStack.CHANCE_FULL) {
|
||||
return Collections.singletonList(
|
||||
Badge.consumeChance(pStack.getChance() / (float) PositionedStack.CHANCE_FULL));
|
||||
}
|
||||
} else if (pStack.getChance() != PositionedStack.CHANCE_FULL) {
|
||||
return Collections.singletonList(
|
||||
Badge.outputChance(pStack.getChance() / (float) PositionedStack.CHANCE_FULL));
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return badges;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
protected void drawBadge(PositionedStack pStack, boolean input) {
|
||||
final List<Badge> badges = getBadges(pStack, input);
|
||||
|
||||
for (Badge badge : badges) {
|
||||
badge.draw(new Rectangle4i(pStack.relx, pStack.rely, pStack.width, pStack.height));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyPress(int keyID, char keyChar) {
|
||||
|
||||
if (NEIClientConfig.favoritesEnabled()
|
||||
&& KeyManager.isHashDown("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH)) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
PositionedStack pStackOver = null;
|
||||
|
||||
for (PositionedStack pStack : getInputs()) {
|
||||
if (pStack.contains(mouse.x - this.x, mouse.y - this.y - yShift)) {
|
||||
pStackOver = pStack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pStackOver != null) {
|
||||
|
||||
if (FavoriteRecipes.containsManual(pStackOver.item)) {
|
||||
FavoriteRecipes.setFavorite(pStackOver.item, null);
|
||||
} else {
|
||||
FavoriteRecipes.setFavorite(
|
||||
pStackOver.item,
|
||||
RecipeId.of(
|
||||
pStackOver.item,
|
||||
FavoriteRecipes.FAVORITE_ITEM,
|
||||
Arrays.asList(new ItemStack(Blocks.fire, 1), pStackOver.item)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
|
||||
forEachButtons(button -> {
|
||||
|
||||
if (button.contains(mouse.x, mouse.y)) {
|
||||
button.lastKeyTyped(keyChar, keyID);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mouseX, int mouseY, int button) {
|
||||
|
||||
if (ShortcutInputHandler.handleMouseClick(getStackMouseOver(mouseX, mouseY))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == 0) {
|
||||
final Minecraft mc = NEIClientUtils.mc();
|
||||
|
||||
if (forEachButtons(guibutton -> {
|
||||
|
||||
if (guibutton.mousePressed(mc, mouseX, mouseY)) {
|
||||
NEIClientUtils.playClickSound();
|
||||
guibutton.mouseReleased(mouseX, mouseY);
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
if (guiRecipe == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.handlerRef.handler.mouseClicked(guiRecipe, button, this.handlerRef.recipeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(int mouseX, int mouseY, List<String> tooltip) {
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
|
||||
if (guiRecipe == null) {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
final List<String> localTooltip = this.handlerRef.handler
|
||||
.handleTooltip(guiRecipe, tooltip, this.handlerRef.recipeIndex);
|
||||
|
||||
return forEachButtons(
|
||||
button -> button.contains(mouseX, mouseY) ? button.handleTooltip(localTooltip) : null,
|
||||
localTooltip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> tooltip) {
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
|
||||
if (guiRecipe == null) {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
tooltip = this.handlerRef.handler.handleItemTooltip(guiRecipe, itemstack, tooltip, this.handlerRef.recipeIndex);
|
||||
|
||||
if (itemstack != null) {
|
||||
final PositionedStack hovered = getPositionedStackMouseOver(mousex, mousey);
|
||||
|
||||
if (hovered != null) {
|
||||
if (this.handlerInfo.getShowBadge()) {
|
||||
final List<Badge> badges = getBadges(hovered, getOutputs().indexOf(hovered) == -1);
|
||||
|
||||
for (Badge badge : badges) {
|
||||
if (badge.getTooltip() != null && !badge.getTooltip().isEmpty()) {
|
||||
tooltip.addAll(badge.getTooltip());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> customTooltip = hovered.getTooltip();
|
||||
|
||||
if (customTooltip != null && !customTooltip.isEmpty()) {
|
||||
tooltip.addAll(customTooltip);
|
||||
}
|
||||
}
|
||||
|
||||
if (hovered == null || !NEIClientConfig.showCycledIngredientsTooltip()
|
||||
|| this.permutations.getOrDefault(hovered, Collections.emptyList()).size() <= 1
|
||||
|| getOutputs().indexOf(hovered) != -1) {
|
||||
this.acceptsFollowingTooltipLineHandler = null;
|
||||
} else if (this.acceptsFollowingTooltipLineHandler == null
|
||||
|| this.acceptsFollowingTooltipLineHandler.tooltipGUID != hovered) {
|
||||
this.acceptsFollowingTooltipLineHandler = AcceptsFollowingTooltipLineHandler
|
||||
.of(hovered, this.permutations.get(hovered), hovered.item);
|
||||
|
||||
if (this.acceptsFollowingTooltipLineHandler != null && hovered.getAcceptsLabel() != null) {
|
||||
this.acceptsFollowingTooltipLineHandler.setLabel(hovered.getAcceptsLabel());
|
||||
}
|
||||
}
|
||||
} else if (this.acceptsFollowingTooltipLineHandler != null) {
|
||||
this.acceptsFollowingTooltipLineHandler = null;
|
||||
}
|
||||
|
||||
if (this.acceptsFollowingTooltipLineHandler != null) {
|
||||
tooltip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.acceptsFollowingTooltipLineHandler));
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> handleHotkeys(int mouseX, int mouseY, Map<String, String> hotkeys) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
final Map<String, String> buttonHotkeys = forEachButtons(
|
||||
button -> button.contains(mouseX, mouseY) ? button.handleHotkeys(mouseX, mouseY, hotkeys) : null,
|
||||
hotkeys);
|
||||
|
||||
if (this.acceptsFollowingTooltipLineHandler != null) {
|
||||
buttonHotkeys.put(
|
||||
NEIClientUtils.getKeyName(
|
||||
NEIClientUtils.SHIFT_HASH,
|
||||
NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL),
|
||||
NEIClientUtils.translate("recipe.accepts.scroll"));
|
||||
}
|
||||
|
||||
if (getInputs().stream().anyMatch(
|
||||
pStack -> this.permutations.getOrDefault(pStack, Collections.emptyList()).size() > 1
|
||||
&& pStack.contains(mouseX - this.x, mouseY - this.y - yShift))) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("recipe.favorite.toggle"));
|
||||
}
|
||||
|
||||
return buttonHotkeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseWheel(int scroll, int mx, int my) {
|
||||
|
||||
if (forEachButtons(button -> button.contains(mx, my) ? button.mouseScrolled(scroll) : null, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (scrollPermutations(scroll, mx, my)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
|
||||
return guiRecipe != null
|
||||
&& this.handlerRef.handler.mouseScrolled(guiRecipe, scroll, this.handlerRef.recipeIndex);
|
||||
}
|
||||
|
||||
protected boolean scrollPermutations(int scroll, int mx, int my) {
|
||||
if (!NEIClientUtils.shiftKey()) return false;
|
||||
final PositionedStack overStack = getPositionedStackMouseOver(mx, my);
|
||||
|
||||
if (overStack != null && this.permutations.getOrDefault(overStack, Collections.emptyList()).size() > 1) {
|
||||
final List<ItemStack> items = this.permutations.get(overStack);
|
||||
final int stackIndex = indexOf(items, overStack.item);
|
||||
final ItemStack stack = items.get((items.size() - scroll + stackIndex) % items.size());
|
||||
|
||||
Stream.concat(getInputs().stream(), getCatalysts().stream()).filter(pStack -> pStack.containsWithNBT(stack))
|
||||
.forEach(pStack -> pStack.setPermutationToRender(stack));
|
||||
|
||||
if (this.acceptsFollowingTooltipLineHandler != null) {
|
||||
this.acceptsFollowingTooltipLineHandler
|
||||
.setActiveStack(((PositionedStack) this.acceptsFollowingTooltipLineHandler.tooltipGUID).item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int indexOf(List<ItemStack> list, ItemStack stack) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(list.get(i), stack)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected <R> R forEachButtons(Function<GuiRecipeButton, R> callback, R defaultValue) {
|
||||
|
||||
for (GuiRecipeButton button : getRecipeButtonsIfInit()) {
|
||||
button.xPosition += this.x;
|
||||
button.yPosition += this.y;
|
||||
|
||||
final R result = callback.apply(button);
|
||||
|
||||
button.xPosition -= this.x;
|
||||
button.yPosition -= this.y;
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean isFocusedRecipe(int mx, int my) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
|
||||
for (PositionedStack pStackOver : getOutputs()) {
|
||||
if (pStackOver.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public PositionedStack getPositionedStackMouseOver(int mx, int my) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
|
||||
for (PositionedStack pStack : getInputs()) {
|
||||
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getCatalysts()) {
|
||||
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getOutputs()) {
|
||||
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStackMouseOver(int mx, int my) {
|
||||
final PositionedStack pStack = getPositionedStackMouseOver(mx, my);
|
||||
return pStack != null ? pStack.item : null;
|
||||
}
|
||||
|
||||
protected GuiRecipe<?> getGuiRecipe() {
|
||||
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
|
||||
if (guiContainer instanceof GuiRecipe<?>guiRecipe) {
|
||||
return guiRecipe;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected GuiContainer getFirstGui() {
|
||||
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
|
||||
if (guiContainer instanceof GuiRecipe<?>guiRecipe) {
|
||||
return guiRecipe.firstGui;
|
||||
}
|
||||
|
||||
return guiContainer;
|
||||
}
|
||||
|
||||
protected void updatePermutations() {
|
||||
final int rev = FavoriteRecipes.getRevision();
|
||||
|
||||
if (this.favoriteRevision != rev) {
|
||||
this.favoriteRevision = rev;
|
||||
this.acceptsFollowingTooltipLineHandler = null;
|
||||
this.favoriteIndexes.clear();
|
||||
this.permutations.clear();
|
||||
this.badgeCache.clear();
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getInputs()) {
|
||||
updatePermutationsFor(pStack);
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getCatalysts()) {
|
||||
updatePermutationsFor(pStack);
|
||||
}
|
||||
|
||||
if (this.acceptsFollowingTooltipLineHandler != null) {
|
||||
this.acceptsFollowingTooltipLineHandler
|
||||
.setActiveStack(((PositionedStack) this.acceptsFollowingTooltipLineHandler.tooltipGUID).item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updatePermutationsFor(PositionedStack pStack) {
|
||||
List<ItemStack> perms = this.permutations.get(pStack);
|
||||
int favoriteIndex = this.favoriteIndexes.getOrDefault(pStack, -1);
|
||||
boolean needsUpdate = false;
|
||||
|
||||
if (perms == null) {
|
||||
perms = pStack.getFilteredPermutations();
|
||||
favoriteIndex = -1;
|
||||
|
||||
if (perms.size() > 1) {
|
||||
for (ItemStack item : perms) {
|
||||
if (!FavoriteRecipes.containsManual(item)) break;
|
||||
favoriteIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
this.permutations.put(pStack, perms);
|
||||
this.favoriteIndexes.put(pStack, favoriteIndex);
|
||||
needsUpdate = true;
|
||||
}
|
||||
|
||||
if (perms.size() > 1 || needsUpdate) {
|
||||
final int size = favoriteIndex != -1 ? favoriteIndex + 1 : perms.size();
|
||||
|
||||
if (size > 0) {
|
||||
pStack.setPermutationToRender(perms.get(this.lastcycle % size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getInputs() {
|
||||
return this.handlerRef.handler.getIngredientStacks(this.handlerRef.recipeIndex);
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getOutputs() {
|
||||
final PositionedStack pStackResult = this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex);
|
||||
return pStackResult != null ? Arrays.asList(pStackResult)
|
||||
: this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex);
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getCatalysts() {
|
||||
if (this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex) == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,12 +36,12 @@ public class RecipeHandlerRef {
|
||||
.getCraftingHandlers("recipeId", recipeId.getResult(), recipeId);
|
||||
|
||||
for (ICraftingHandler handler : handlers) {
|
||||
int refIndex = SearchRecipeHandler.findFirst(
|
||||
int recipeIndex = SearchRecipeHandler.findFirst(
|
||||
handler,
|
||||
recipeIndex -> recipeId.equalsIngredients(handler.getIngredientStacks(recipeIndex)));
|
||||
index -> recipeId.equalsIngredients(handler.getIngredientStacks(index)));
|
||||
|
||||
if (refIndex >= 0) {
|
||||
return new RecipeHandlerRef(handler, refIndex);
|
||||
if (recipeIndex >= 0) {
|
||||
return new RecipeHandlerRef(handler, recipeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,10 +66,6 @@ public class RecipeHandlerRef {
|
||||
return firstGui != null && firstGui.inventorySlots != null ? firstGui : null;
|
||||
}
|
||||
|
||||
public NEIRecipeWidget getRecipeWidget() {
|
||||
return new NEIRecipeWidget(this);
|
||||
}
|
||||
|
||||
public IOverlayHandler getOverlayHandler(GuiContainer gui) {
|
||||
final GuiContainer firstGui = prepareGui(gui);
|
||||
|
||||
|
||||
@@ -2,10 +2,13 @@ package codechicken.nei.recipe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.Widget;
|
||||
import codechicken.nei.recipe.widget.RecipeWidget;
|
||||
import codechicken.nei.scroll.ScrollContainer;
|
||||
|
||||
public class RecipePageManager {
|
||||
@@ -16,7 +19,6 @@ public class RecipePageManager {
|
||||
|
||||
private String cacheKey = "";
|
||||
private int currentPageIndex = -1;
|
||||
private List<Integer> recipeIndices = new ArrayList<>();
|
||||
private List<List<Widget>> pageWidgets = new ArrayList<>();
|
||||
|
||||
public RecipePageManager(SearchRecipeHandler<?> handler, HandlerInfo handlerInfo, ScrollContainer container) {
|
||||
@@ -52,12 +54,28 @@ public class RecipePageManager {
|
||||
final int width = Math.max(166, this.handlerInfo.getWidth());
|
||||
final List<Integer> searchRecipes = this.handler.getFilteredRecipes();
|
||||
final int numRecipes = searchRecipes.size();
|
||||
final List<List<Integer>> groupedRecipes = new ArrayList<>();
|
||||
final Map<Object, Integer> groupEndIndices = new HashMap<>();
|
||||
List<Widget> widgets = new ArrayList<>();
|
||||
int shiftY = 0;
|
||||
|
||||
for (int i = 0; i < numRecipes; i++) {
|
||||
final int recipeIndex = searchRecipes.get(i);
|
||||
final NEIRecipeWidget widget = RecipeHandlerRef.of(this.handler.original, recipeIndex).getRecipeWidget();
|
||||
final Object groupId = this.handler.original.getRecipeGroupId(recipeIndex);
|
||||
final int groupIndex = groupId != null
|
||||
? groupEndIndices.computeIfAbsent(groupId, k -> groupedRecipes.size())
|
||||
: groupedRecipes.size();
|
||||
|
||||
if (groupedRecipes.size() <= groupIndex) {
|
||||
groupedRecipes.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
groupedRecipes.get(groupIndex).add(recipeIndex);
|
||||
}
|
||||
|
||||
for (int groupIndex = 0; groupIndex < groupedRecipes.size(); groupIndex++) {
|
||||
final RecipeWidget widget = new RecipeWidget(this.handler.original, groupedRecipes.get(groupIndex));
|
||||
|
||||
widget.w = width;
|
||||
|
||||
if (allowOverflowY) {
|
||||
@@ -109,29 +127,21 @@ public class RecipePageManager {
|
||||
}
|
||||
|
||||
this.currentPageIndex = page;
|
||||
this.recipeIndices = new ArrayList<>();
|
||||
|
||||
for (Widget widget : getCurrentPageWidgets()) {
|
||||
if (widget instanceof NEIRecipeWidget recipeWidget) {
|
||||
this.recipeIndices.add(recipeWidget.getRecipeHandlerRef().recipeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
this.container.setVerticalScrollOffset(0);
|
||||
}
|
||||
|
||||
public void gotoRefIndex(int refIndex) {
|
||||
int page = 0;
|
||||
public boolean gotoRecipeIndex(int recipeIndex) {
|
||||
|
||||
for (int i = 0; i < this.pageWidgets.size(); i++) {
|
||||
refIndex -= this.pageWidgets.get(i).size();
|
||||
if (refIndex < 0) {
|
||||
page = i;
|
||||
break;
|
||||
for (int page = 0; page < this.pageWidgets.size(); page++) {
|
||||
for (Widget widget : this.pageWidgets.get(page)) {
|
||||
if (widget instanceof RecipeWidget recipeWidget && recipeWidget.containsRecipeIndex(recipeIndex)) {
|
||||
changePage(page - this.currentPageIndex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changePage(page);
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Widget> getCurrentPageWidgets() {
|
||||
@@ -143,7 +153,7 @@ public class RecipePageManager {
|
||||
int shiftY = 0;
|
||||
|
||||
for (Widget widget : currentWidgets) {
|
||||
if (widget instanceof NEIRecipeWidget recipeWidget) {
|
||||
if (widget instanceof RecipeWidget recipeWidget) {
|
||||
recipeWidget.setLocation(2, shiftY);
|
||||
}
|
||||
shiftY += widget.h;
|
||||
@@ -153,7 +163,20 @@ public class RecipePageManager {
|
||||
}
|
||||
|
||||
public List<Integer> getRecipeIndices() {
|
||||
return this.recipeIndices;
|
||||
|
||||
if (this.pageWidgets.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<Integer> recipeIndices = new ArrayList<>();
|
||||
|
||||
for (Widget widget : this.pageWidgets.get(this.currentPageIndex)) {
|
||||
if (widget instanceof RecipeWidget recipeWidget) {
|
||||
recipeIndices.add(recipeWidget.getRecipeHandlerRef().recipeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return recipeIndices;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import codechicken.nei.drawable.DrawableBuilder;
|
||||
import codechicken.nei.drawable.DrawableResource;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.widget.RecipeWidget;
|
||||
import codechicken.nei.scroll.GuiHelper;
|
||||
import codechicken.nei.util.ReadableNumberConverter;
|
||||
|
||||
@@ -30,7 +31,7 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
|
||||
protected String recipeName = "";
|
||||
protected GuiRecipe<?> gui = null;
|
||||
protected RecipeHandlerRef handlerRef = null;
|
||||
protected NEIRecipeWidget widget = null;
|
||||
protected RecipeWidget widget = null;
|
||||
protected RecipeId recipeId = null;
|
||||
protected boolean createdGui = false;
|
||||
protected long multiplier = 0;
|
||||
@@ -63,13 +64,13 @@ public class RecipeTooltipLineHandler implements ITooltipLineHandler {
|
||||
this.handlerRef = RecipeHandlerRef.of(this.recipeId);
|
||||
this.createdGui = true;
|
||||
|
||||
if (handlerRef != null) {
|
||||
this.widget = new NEIRecipeWidget(this.handlerRef);
|
||||
if (this.handlerRef != null) {
|
||||
this.widget = new RecipeWidget(this.handlerRef);
|
||||
this.widget.showAsWidget(true);
|
||||
this.widget.x = BG_PADDING;
|
||||
this.widget.y = BG_PADDING + 12;
|
||||
|
||||
this.recipeName = this.widget.getRecipeHandlerRef().handler.getRecipeName().trim();
|
||||
this.recipeName = this.handlerRef.handler.getRecipeName().trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,15 +52,10 @@ class SearchRecipeHandler<H extends IRecipeHandler> {
|
||||
|
||||
public static int findFirst(IRecipeHandler handler, IntPredicate predicate) {
|
||||
final IRecipeFilter filter = searchingAvailable(handler) ? GuiRecipe.getRecipeListFilter() : null;
|
||||
int refIndex = -1;
|
||||
|
||||
for (int recipeIndex = 0; recipeIndex < handler.numRecipes(); recipeIndex++) {
|
||||
if (filter == null || mathRecipe(handler, recipeIndex, filter)) {
|
||||
refIndex++;
|
||||
|
||||
if (predicate.test(recipeIndex)) {
|
||||
return refIndex;
|
||||
}
|
||||
if ((filter == null || mathRecipe(handler, recipeIndex, filter)) && predicate.test(recipeIndex)) {
|
||||
return recipeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ import codechicken.nei.recipe.GuiRecipeButton;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.HandlerInfo;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.NEIRecipeWidget;
|
||||
import codechicken.nei.recipe.TemplateRecipeHandler;
|
||||
import codechicken.nei.recipe.TemplateRecipeHandler.RecipeTransferRect;
|
||||
import codechicken.nei.recipe.widget.RecipeWidget;
|
||||
import codechicken.nei.scroll.ScrollContainer;
|
||||
|
||||
public class DebugHandlerWidget extends Widget {
|
||||
@@ -532,7 +532,7 @@ public class DebugHandlerWidget extends Widget {
|
||||
}
|
||||
}
|
||||
|
||||
public void drawGuiPlaceholder(NEIRecipeWidget widget) {
|
||||
public void drawGuiPlaceholder(RecipeWidget widget) {
|
||||
if (!this.showWidget) return;
|
||||
|
||||
NEIClientUtils.gl2DRenderContext(() -> {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package codechicken.nei.recipe.widget;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.NEIServerUtils;
|
||||
|
||||
public class PermutationsCycler<K> {
|
||||
|
||||
protected final Map<K, List<ItemStack>> permutations;
|
||||
protected final Map<K, Integer> favoriteCounts;
|
||||
protected final Function<K, List<ItemStack>> loader;
|
||||
protected Runnable onInvalidate = null;
|
||||
|
||||
protected int favoriteRevision = -1;
|
||||
|
||||
public PermutationsCycler(Map<K, List<ItemStack>> storage, Map<K, Integer> favoriteStorage,
|
||||
Function<K, List<ItemStack>> loader) {
|
||||
this.permutations = storage;
|
||||
this.favoriteCounts = favoriteStorage;
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
public PermutationsCycler<K> onInvalidate(Runnable callback) {
|
||||
this.onInvalidate = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean validate() {
|
||||
final int rev = FavoriteRecipes.getRevision();
|
||||
|
||||
if (this.favoriteRevision != rev) {
|
||||
this.favoriteRevision = rev;
|
||||
this.permutations.clear();
|
||||
this.favoriteCounts.clear();
|
||||
|
||||
if (this.onInvalidate != null) {
|
||||
this.onInvalidate.run();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean contains(K key) {
|
||||
validate();
|
||||
return this.permutations.containsKey(key);
|
||||
}
|
||||
|
||||
public List<ItemStack> get(K key) {
|
||||
validate();
|
||||
List<ItemStack> items = this.permutations.get(key);
|
||||
|
||||
if (items == null) {
|
||||
items = new ArrayList<>(this.loader.apply(key));
|
||||
items.sort(Comparator.comparing(FavoriteRecipes::containsManual).reversed());
|
||||
|
||||
int favoriteCount = 0;
|
||||
|
||||
if (items.size() > 1) {
|
||||
for (ItemStack stack : items) {
|
||||
if (!FavoriteRecipes.containsManual(stack)) break;
|
||||
favoriteCount++;
|
||||
}
|
||||
}
|
||||
|
||||
this.permutations.put(key, items);
|
||||
this.favoriteCounts.put(key, favoriteCount);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public ItemStack getCycled(K key, int cycle) {
|
||||
final List<ItemStack> items = get(key);
|
||||
final int favoriteCount = this.favoriteCounts.getOrDefault(key, 0);
|
||||
final int size = favoriteCount > 0 ? favoriteCount : items.size();
|
||||
|
||||
return size > 0 ? items.get(cycle % size) : null;
|
||||
}
|
||||
|
||||
public static int indexOf(List<ItemStack> list, ItemStack stack) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (NEIServerUtils.areStacksSameType(list.get(i), stack)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package codechicken.nei.recipe.widget;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.HandlerInfo;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
public class RecipeGroup {
|
||||
|
||||
private static final List<Integer> SINGLE_MEMBER = Collections.singletonList(0);
|
||||
|
||||
private final IRecipeHandler handler;
|
||||
private final List<Integer> recipeIndices;
|
||||
private final HandlerInfo handlerInfo;
|
||||
private final PermutationsCycler<Point> slotPermutations;
|
||||
|
||||
private List<Integer> allowedMembers = null;
|
||||
|
||||
public RecipeGroup(IRecipeHandler handler, List<Integer> recipeIndices) {
|
||||
this.handler = handler;
|
||||
this.recipeIndices = new ArrayList<>(recipeIndices);
|
||||
this.handlerInfo = GuiRecipeTab.getHandlerInfo(handler);
|
||||
this.slotPermutations = new PermutationsCycler<Point>(
|
||||
new HashMap<>(),
|
||||
new HashMap<>(),
|
||||
this::collectSlotPermutations).onInvalidate(() -> this.allowedMembers = null);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.recipeIndices.size();
|
||||
}
|
||||
|
||||
public IRecipeHandler getHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
public int getRecipeIndex(int member) {
|
||||
return this.recipeIndices.get(member);
|
||||
}
|
||||
|
||||
public HandlerInfo getHandlerInfo() {
|
||||
return this.handlerInfo;
|
||||
}
|
||||
|
||||
public boolean contains(int recipeIndex) {
|
||||
return this.recipeIndices.contains(recipeIndex);
|
||||
}
|
||||
|
||||
public List<ItemStack> getPermutations(Point slotKey) {
|
||||
return this.slotPermutations.get(slotKey);
|
||||
}
|
||||
|
||||
public List<PositionedStack> getInputs(int member) {
|
||||
return this.handler.getIngredientStacks(getRecipeIndex(member));
|
||||
}
|
||||
|
||||
public List<PositionedStack> getOutputs(int member) {
|
||||
final int recipeIndex = getRecipeIndex(member);
|
||||
final PositionedStack pStackResult = this.handler.getResultStack(recipeIndex);
|
||||
return pStackResult != null ? Arrays.asList(pStackResult) : this.handler.getOtherStacks(recipeIndex);
|
||||
}
|
||||
|
||||
public List<PositionedStack> getCatalysts(int member) {
|
||||
final int recipeIndex = getRecipeIndex(member);
|
||||
|
||||
if (this.handler.getResultStack(recipeIndex) == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return this.handler.getOtherStacks(recipeIndex);
|
||||
}
|
||||
|
||||
public List<PositionedStack> getCyclingStacks(int member) {
|
||||
final List<PositionedStack> stacks = new ArrayList<>(getInputs(member));
|
||||
stacks.addAll(getCatalysts(member));
|
||||
return stacks;
|
||||
}
|
||||
|
||||
public Point getSlotKey(PositionedStack pStack) {
|
||||
return new Point(pStack.relx, pStack.rely);
|
||||
}
|
||||
|
||||
public PositionedStack getSlot(int member, Point slotKey) {
|
||||
|
||||
for (PositionedStack pStack : getCyclingStacks(member)) {
|
||||
if (pStack.relx == slotKey.x && pStack.rely == slotKey.y) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public PositionedStack getOutputSlot(int member, Point slotKey) {
|
||||
|
||||
for (PositionedStack pStack : getOutputs(member)) {
|
||||
if (pStack.relx == slotKey.x && pStack.rely == slotKey.y) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ItemStack> getOutputVariants(Point slotKey) {
|
||||
final List<ItemStack> items = new ArrayList<>();
|
||||
|
||||
for (int member = 0; member < size(); member++) {
|
||||
final PositionedStack slot = getOutputSlot(member, slotKey);
|
||||
|
||||
if (slot != null && PermutationsCycler.indexOf(items, slot.item) == -1) {
|
||||
items.add(slot.item);
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public List<Integer> getAllowedMembers() {
|
||||
this.slotPermutations.validate();
|
||||
|
||||
if (size() == 1) {
|
||||
return SINGLE_MEMBER;
|
||||
}
|
||||
|
||||
if (this.allowedMembers == null) {
|
||||
final Set<Point> favoriteSlots = new HashSet<>();
|
||||
|
||||
for (int member = 0; member < size(); member++) {
|
||||
for (PositionedStack pStack : getCyclingStacks(member)) {
|
||||
final Point slotKey = getSlotKey(pStack);
|
||||
final List<ItemStack> items = getPermutations(slotKey);
|
||||
|
||||
if (items.size() > 1 && items.stream().anyMatch(FavoriteRecipes::containsManual)) {
|
||||
favoriteSlots.add(slotKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.allowedMembers = filterMembers(favoriteSlots);
|
||||
}
|
||||
|
||||
return this.allowedMembers;
|
||||
}
|
||||
|
||||
protected List<Integer> filterMembers(Set<Point> favoriteSlots) {
|
||||
final List<Integer> indices = new ArrayList<>();
|
||||
int minMisses = Integer.MAX_VALUE;
|
||||
|
||||
for (int member = 0; member < size(); member++) {
|
||||
final int misses = countMissingFavorites(member, favoriteSlots);
|
||||
|
||||
if (misses < minMisses) {
|
||||
minMisses = misses;
|
||||
indices.clear();
|
||||
}
|
||||
|
||||
if (misses == minMisses) {
|
||||
indices.add(member);
|
||||
}
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
protected int countMissingFavorites(int member, Set<Point> favoriteSlots) {
|
||||
int misses = 0;
|
||||
|
||||
for (Point slotKey : favoriteSlots) {
|
||||
final PositionedStack slot = getSlot(member, slotKey);
|
||||
|
||||
if (slot != null && slot.getFilteredPermutations().stream().noneMatch(FavoriteRecipes::containsManual)) {
|
||||
misses++;
|
||||
}
|
||||
}
|
||||
|
||||
return misses;
|
||||
}
|
||||
|
||||
protected List<ItemStack> collectSlotPermutations(Point slotKey) {
|
||||
final List<ItemStack> items = new ArrayList<>();
|
||||
|
||||
for (int member = 0; member < size(); member++) {
|
||||
final PositionedStack slot = getSlot(member, slotKey);
|
||||
|
||||
if (slot != null) {
|
||||
for (ItemStack stack : slot.getFilteredPermutations()) {
|
||||
if (PermutationsCycler.indexOf(items, stack) == -1) {
|
||||
items.add(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
package codechicken.nei.recipe.widget;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
import codechicken.lib.gui.GuiDraw;
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.FavoriteRecipes;
|
||||
import codechicken.nei.ItemsTooltipLineHandler;
|
||||
import codechicken.nei.KeyManager;
|
||||
import codechicken.nei.NEIClientConfig;
|
||||
import codechicken.nei.NEIClientUtils;
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.Widget;
|
||||
import codechicken.nei.api.IGuiContainerOverlay;
|
||||
import codechicken.nei.api.ShortcutInputHandler;
|
||||
import codechicken.nei.guihook.GuiContainerManager;
|
||||
import codechicken.nei.recipe.AcceptsFollowingTooltipLineHandler;
|
||||
import codechicken.nei.recipe.Badge;
|
||||
import codechicken.nei.recipe.GuiFavoriteButton;
|
||||
import codechicken.nei.recipe.GuiOverlayButton;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipeButton;
|
||||
import codechicken.nei.recipe.GuiRecipeButton.UpdateRecipeButtonsEvent;
|
||||
import codechicken.nei.recipe.HandlerInfo;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
import codechicken.nei.recipe.debug.DebugHandlerWidget;
|
||||
import codechicken.nei.util.NEIMouseUtils;
|
||||
|
||||
public class RecipeWidget extends Widget {
|
||||
|
||||
protected static final int TICKS_PER_CYCLE = 20;
|
||||
|
||||
protected final RecipeGroup group;
|
||||
protected final IRecipeHandler handler;
|
||||
protected final HandlerInfo handlerInfo;
|
||||
|
||||
protected final PermutationsCycler<PositionedStack> renderPermutations;
|
||||
|
||||
protected final Map<PositionedStack, List<Badge>> badgeCache = new WeakHashMap<>();
|
||||
protected final Map<Integer, List<GuiRecipeButton>> buttonCache = new HashMap<>();
|
||||
|
||||
protected AcceptsFollowingTooltipLineHandler acceptsTooltip;
|
||||
protected ItemsTooltipLineHandler variantsTooltip;
|
||||
protected Point variantsSlotKey;
|
||||
|
||||
protected int activeMember = 0;
|
||||
protected int memberCursor = -1;
|
||||
protected int slotCycle = 0;
|
||||
protected int cycleticks = 0;
|
||||
protected int lastcycle = -1;
|
||||
|
||||
protected boolean showAsWidget = false;
|
||||
protected boolean update = true;
|
||||
|
||||
public RecipeWidget(RecipeHandlerRef handlerRef) {
|
||||
this(handlerRef.handler, Collections.singletonList(handlerRef.recipeIndex));
|
||||
}
|
||||
|
||||
public RecipeWidget(IRecipeHandler handler, List<Integer> recipeIndices) {
|
||||
this(new RecipeGroup(handler, recipeIndices));
|
||||
}
|
||||
|
||||
public RecipeWidget(RecipeGroup group) {
|
||||
this.group = group;
|
||||
this.handler = group.getHandler();
|
||||
this.handlerInfo = group.getHandlerInfo();
|
||||
this.renderPermutations = new PermutationsCycler<PositionedStack>(
|
||||
new WeakHashMap<>(),
|
||||
new WeakHashMap<>(),
|
||||
PositionedStack::getFilteredPermutations).onInvalidate(() -> {
|
||||
this.badgeCache.clear();
|
||||
this.acceptsTooltip = null;
|
||||
});
|
||||
update();
|
||||
}
|
||||
|
||||
public RecipeHandlerRef getRecipeHandlerRef() {
|
||||
return RecipeHandlerRef.of(this.handler, getRecipeIndex());
|
||||
}
|
||||
|
||||
protected int getRecipeIndex() {
|
||||
return this.group.getRecipeIndex(this.activeMember);
|
||||
}
|
||||
|
||||
public boolean containsRecipeIndex(int recipeIndex) {
|
||||
return this.group.contains(recipeIndex);
|
||||
}
|
||||
|
||||
public HandlerInfo getHandlerInfo() {
|
||||
return this.handlerInfo;
|
||||
}
|
||||
|
||||
public void setLocation(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void showAsWidget(boolean show) {
|
||||
this.showAsWidget = show;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
|
||||
if (this.showAsWidget || !this.handlerInfo.isAllowOverflowY()) {
|
||||
int maxHeight = 0;
|
||||
|
||||
for (int member = 0; member < this.group.size(); member++) {
|
||||
final int recipeHeight = this.handler.getRecipeHeight(this.group.getRecipeIndex(member));
|
||||
|
||||
if (recipeHeight > 0) {
|
||||
maxHeight = Math.max(maxHeight, recipeHeight);
|
||||
}
|
||||
}
|
||||
|
||||
this.w = Math.max(166, this.handlerInfo.getWidth());
|
||||
this.h = (maxHeight > 0 ? maxHeight : this.handlerInfo.getHeight()) + this.handlerInfo.getYShift();
|
||||
}
|
||||
|
||||
this.update = true;
|
||||
}
|
||||
|
||||
public List<GuiRecipeButton> getRecipeButtons() {
|
||||
return this.buttonCache.computeIfAbsent(this.activeMember, this::createButtons);
|
||||
}
|
||||
|
||||
protected List<GuiRecipeButton> getRecipeButtonsIfInit() {
|
||||
return this.buttonCache.getOrDefault(this.activeMember, Collections.emptyList());
|
||||
}
|
||||
|
||||
protected List<GuiRecipeButton> createButtons(int member) {
|
||||
|
||||
if (this.group.getOutputs(member).isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections
|
||||
.unmodifiableList(initButtons(RecipeHandlerRef.of(this.handler, this.group.getRecipeIndex(member))));
|
||||
}
|
||||
|
||||
protected List<GuiRecipeButton> initButtons(RecipeHandlerRef ref) {
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
|
||||
if (guiRecipe != null) {
|
||||
final UpdateRecipeButtonsEvent.Pre preEvent = new UpdateRecipeButtonsEvent.Pre(
|
||||
guiRecipe,
|
||||
ref,
|
||||
this.handlerInfo);
|
||||
|
||||
if (MinecraftForge.EVENT_BUS.post(preEvent)) {
|
||||
return preEvent.buttonList;
|
||||
} else {
|
||||
final UpdateRecipeButtonsEvent.Post postEvent = new UpdateRecipeButtonsEvent.Post(
|
||||
guiRecipe,
|
||||
ref,
|
||||
getDefaultButtons(ref));
|
||||
MinecraftForge.EVENT_BUS.post(postEvent);
|
||||
return postEvent.buttonList;
|
||||
}
|
||||
|
||||
} else {
|
||||
return getDefaultButtons(ref);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected List<GuiRecipeButton> getDefaultButtons(RecipeHandlerRef ref) {
|
||||
GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
final List<GuiRecipeButton> buttons = new ArrayList<>();
|
||||
final boolean showFavorites = NEIClientConfig.favoritesEnabled() && handlerInfo.getShowFavoritesButton();
|
||||
final boolean showOverlay = handlerInfo.getShowOverlayButton();
|
||||
final int x = Math.min(166, this.w) - GuiRecipeButton.BUTTON_WIDTH;
|
||||
int y = this.h - GuiRecipeButton.BUTTON_HEIGHT - 6;
|
||||
|
||||
if (guiContainer instanceof IGuiContainerOverlay overlay) {
|
||||
guiContainer = overlay.getFirstScreen();
|
||||
}
|
||||
|
||||
if (showOverlay) {
|
||||
buttons.add(new GuiOverlayButton(guiContainer, ref, x, y));
|
||||
y -= GuiRecipeButton.BUTTON_HEIGHT + 1;
|
||||
}
|
||||
|
||||
if (showFavorites) {
|
||||
buttons.add(new GuiFavoriteButton(ref, x, y));
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(int mouseX, int mouseY) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
|
||||
if (this.update) {
|
||||
this.update = false;
|
||||
|
||||
if (tickCycle()) {
|
||||
|
||||
if (this.showAsWidget || !contains(mouseX, mouseY)) {
|
||||
nextMember();
|
||||
}
|
||||
|
||||
updatePermutations(this.lastcycle);
|
||||
}
|
||||
|
||||
for (GuiRecipeButton button : getRecipeButtons()) {
|
||||
button.update();
|
||||
}
|
||||
}
|
||||
|
||||
final int recipeIndex = getRecipeIndex();
|
||||
|
||||
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
|
||||
GL11.glTranslatef(this.x, this.y + yShift, 0);
|
||||
|
||||
this.handler.drawBackground(recipeIndex);
|
||||
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
GL11.glColor4f(1, 1, 1, 1);
|
||||
|
||||
GuiContainerManager.enableMatrixStackLogging();
|
||||
|
||||
for (PositionedStack pStack : getInputs()) {
|
||||
|
||||
if (!this.renderPermutations.contains(pStack)) {
|
||||
updatePermutationsFor(pStack);
|
||||
}
|
||||
|
||||
drawItem(pStack, mouseX, mouseY, yShift, true);
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getCatalysts()) {
|
||||
|
||||
if (!this.renderPermutations.contains(pStack)) {
|
||||
updatePermutationsFor(pStack);
|
||||
}
|
||||
|
||||
drawItem(pStack, mouseX, mouseY, yShift, true);
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getOutputs()) {
|
||||
drawItem(pStack, mouseX, mouseY, yShift, false);
|
||||
}
|
||||
|
||||
GuiContainerManager.disableMatrixStackLogging();
|
||||
|
||||
this.handler.drawForeground(recipeIndex);
|
||||
|
||||
final GuiRecipeButton overlayButton = forEachButtons(
|
||||
button -> (this.showAsWidget ? button instanceof GuiOverlayButton : button.contains(mouseX, mouseY))
|
||||
? button
|
||||
: null,
|
||||
null);
|
||||
|
||||
if (overlayButton != null) {
|
||||
NEIClientUtils.gl2DRenderContext(() -> overlayButton.drawItemOverlay());
|
||||
}
|
||||
|
||||
GL11.glTranslatef(-this.x, -this.y - yShift, 0);
|
||||
GL11.glPopAttrib();
|
||||
|
||||
if (!this.showAsWidget) {
|
||||
final Minecraft mc = NEIClientUtils.mc();
|
||||
for (GuiRecipeButton button : getRecipeButtons()) {
|
||||
button.xPosition += this.x;
|
||||
button.yPosition += this.y;
|
||||
button.drawButton(mc, mouseX, mouseY);
|
||||
button.xPosition -= this.x;
|
||||
button.yPosition -= this.y;
|
||||
}
|
||||
}
|
||||
|
||||
DebugHandlerWidget.instance.drawGuiPlaceholder(this);
|
||||
}
|
||||
|
||||
protected void drawItem(PositionedStack pStack, int mouseX, int mouseY, int yShift, boolean input) {
|
||||
pStack.draw(mouseX - this.x, mouseY - this.y - yShift);
|
||||
|
||||
if (this.handlerInfo.getShowBadge()) {
|
||||
drawBadge(pStack, input);
|
||||
}
|
||||
|
||||
if (pStack.contains(mouseX - this.x, mouseY - this.y - yShift)) {
|
||||
NEIClientUtils.gl2DRenderContext(
|
||||
() -> GuiDraw.drawRect(pStack.relx, pStack.rely, pStack.width, pStack.height, 0x80FFFFFF));
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Badge> getBadges(PositionedStack pStack, boolean input) {
|
||||
|
||||
return this.badgeCache.computeIfAbsent(pStack, k -> {
|
||||
final List<Badge> badges = k.getBadges();
|
||||
|
||||
if (badges == null) {
|
||||
|
||||
if (input) {
|
||||
if (StackInfo.getAmount(pStack.item) == 0) {
|
||||
return Collections.singletonList(Badge.notConsumed());
|
||||
} else if (pStack.getChance() == 0) {
|
||||
return Collections.singletonList(Badge.notConsumedParallel());
|
||||
} else if (pStack.getChance() != PositionedStack.CHANCE_FULL) {
|
||||
return Collections.singletonList(
|
||||
Badge.consumeChance(pStack.getChance() / (float) PositionedStack.CHANCE_FULL));
|
||||
}
|
||||
} else if (pStack.getChance() != PositionedStack.CHANCE_FULL) {
|
||||
return Collections.singletonList(
|
||||
Badge.outputChance(pStack.getChance() / (float) PositionedStack.CHANCE_FULL));
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return badges;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
protected void drawBadge(PositionedStack pStack, boolean input) {
|
||||
final List<Badge> badges = getBadges(pStack, input);
|
||||
|
||||
for (Badge badge : badges) {
|
||||
badge.draw(new Rectangle4i(pStack.relx, pStack.rely, pStack.width, pStack.height));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleKeyPress(int keyID, char keyChar) {
|
||||
|
||||
if (NEIClientConfig.favoritesEnabled()
|
||||
&& KeyManager.isHashDown("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH)) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
final PositionedStack hovered = getCyclingStackMouseOver(mouse.x, mouse.y);
|
||||
|
||||
if (hovered != null && getPermutations(hovered).size() > 1) {
|
||||
FavoriteRecipes.toggleFavoriteItem(hovered.item);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lastKeyTyped(int keyID, char keyChar) {
|
||||
final Point mouse = GuiDraw.getMousePosition();
|
||||
|
||||
forEachButtons(button -> {
|
||||
|
||||
if (button.contains(mouse.x, mouse.y)) {
|
||||
button.lastKeyTyped(keyChar, keyID);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleClick(int mouseX, int mouseY, int button) {
|
||||
|
||||
if (ShortcutInputHandler.handleMouseClick(getStackMouseOver(mouseX, mouseY))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (button == 0) {
|
||||
final Minecraft mc = NEIClientUtils.mc();
|
||||
|
||||
if (forEachButtons(guibutton -> {
|
||||
|
||||
if (guibutton.mousePressed(mc, mouseX, mouseY)) {
|
||||
NEIClientUtils.playClickSound();
|
||||
guibutton.mouseReleased(mouseX, mouseY);
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
if (guiRecipe == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.handler.mouseClicked(guiRecipe, button, getRecipeIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleTooltip(int mouseX, int mouseY, List<String> tooltip) {
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
|
||||
if (guiRecipe == null) {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
final List<String> buttonTooltip = forEachButtons(
|
||||
button -> button.contains(mouseX, mouseY) ? button.handleTooltip(tooltip) : null,
|
||||
null);
|
||||
|
||||
if (buttonTooltip != null) {
|
||||
return buttonTooltip;
|
||||
}
|
||||
|
||||
return this.handler.handleTooltip(guiRecipe, tooltip, getRecipeIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> handleItemTooltip(ItemStack itemstack, int mousex, int mousey, List<String> tooltip) {
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
|
||||
if (guiRecipe == null) {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
tooltip = this.handler.handleItemTooltip(guiRecipe, itemstack, tooltip, getRecipeIndex());
|
||||
final PositionedStack cycling = itemstack != null ? getCyclingStackMouseOver(mousex, mousey) : null;
|
||||
final PositionedStack hovered = cycling != null ? cycling
|
||||
: itemstack != null ? getOutputStackMouseOver(mousex, mousey) : null;
|
||||
|
||||
if (hovered != null) {
|
||||
if (this.handlerInfo.getShowBadge()) {
|
||||
final List<Badge> badges = getBadges(hovered, cycling != null);
|
||||
|
||||
for (Badge badge : badges) {
|
||||
if (badge.getTooltip() != null && !badge.getTooltip().isEmpty()) {
|
||||
tooltip.addAll(badge.getTooltip());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> customTooltip = hovered.getTooltip();
|
||||
|
||||
if (customTooltip != null && !customTooltip.isEmpty()) {
|
||||
tooltip.addAll(customTooltip);
|
||||
}
|
||||
}
|
||||
|
||||
final Point slotKey = cycling != null ? this.group.getSlotKey(cycling) : null;
|
||||
|
||||
if (slotKey == null || !NEIClientConfig.showCycledIngredientsTooltip()
|
||||
|| this.group.getPermutations(slotKey).size() <= 1) {
|
||||
this.acceptsTooltip = null;
|
||||
} else if (this.acceptsTooltip == null || !slotKey.equals(this.acceptsTooltip.tooltipGUID)) {
|
||||
this.acceptsTooltip = AcceptsFollowingTooltipLineHandler
|
||||
.of(slotKey, this.group.getPermutations(slotKey), cycling.item);
|
||||
|
||||
if (this.acceptsTooltip != null && cycling.getAcceptsLabel() != null) {
|
||||
this.acceptsTooltip.setLabel(cycling.getAcceptsLabel());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.acceptsTooltip != null) {
|
||||
tooltip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.acceptsTooltip));
|
||||
}
|
||||
|
||||
final Point outputKey = cycling == null && hovered != null && this.group.size() > 1
|
||||
? this.group.getSlotKey(hovered)
|
||||
: null;
|
||||
|
||||
if (outputKey == null || !NEIClientConfig.showCycledIngredientsTooltip()) {
|
||||
this.variantsTooltip = null;
|
||||
this.variantsSlotKey = null;
|
||||
} else if (!outputKey.equals(this.variantsSlotKey)) {
|
||||
final List<ItemStack> variants = this.group.getOutputVariants(outputKey);
|
||||
|
||||
this.variantsSlotKey = outputKey;
|
||||
this.variantsTooltip = variants.size() > 1
|
||||
? new ItemsTooltipLineHandler(NEIClientUtils.translate("recipe.group"), variants, false, 4)
|
||||
: null;
|
||||
|
||||
if (this.variantsTooltip != null) {
|
||||
this.variantsTooltip.setActiveStack(hovered.item);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.variantsTooltip != null) {
|
||||
tooltip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.variantsTooltip));
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> handleHotkeys(int mouseX, int mouseY, Map<String, String> hotkeys) {
|
||||
final Map<String, String> buttonHotkeys = forEachButtons(
|
||||
button -> button.contains(mouseX, mouseY) ? button.handleHotkeys(mouseX, mouseY, hotkeys) : null,
|
||||
hotkeys);
|
||||
final PositionedStack hovered = getCyclingStackMouseOver(mouseX, mouseY);
|
||||
|
||||
if (hovered != null && getPermutations(hovered).size() > 1) {
|
||||
hotkeys.put(
|
||||
KeyManager.getKeyName("bookmark.favorite_item", NEIClientUtils.SHIFT_HASH),
|
||||
NEIClientUtils.translate("recipe.favorite.toggle"));
|
||||
}
|
||||
|
||||
if (hovered == null && this.group.size() > 1 && getOutputStackMouseOver(mouseX, mouseY) != null) {
|
||||
hotkeys.put(
|
||||
NEIClientUtils.getKeyName(
|
||||
NEIClientUtils.SHIFT_HASH,
|
||||
NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL),
|
||||
NEIClientUtils.translate("recipe.group.scroll", this.activeMember + 1, this.group.size()));
|
||||
}
|
||||
|
||||
if (hovered != null && this.acceptsTooltip != null) {
|
||||
buttonHotkeys.put(
|
||||
NEIClientUtils.getKeyName(
|
||||
NEIClientUtils.SHIFT_HASH,
|
||||
NEIMouseUtils.MOUSE_BTN_NONE + NEIMouseUtils.MOUSE_SCROLL),
|
||||
NEIClientUtils.translate("recipe.accepts.scroll"));
|
||||
}
|
||||
|
||||
return buttonHotkeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMouseWheel(int scroll, int mx, int my) {
|
||||
|
||||
if (forEachButtons(button -> button.contains(mx, my) ? button.mouseScrolled(scroll) : null, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NEIClientUtils.shiftKey()) {
|
||||
final PositionedStack hovered = getCyclingStackMouseOver(mx, my);
|
||||
|
||||
if (hovered != null && scrollPermutations(scroll, hovered)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hovered == null && getOutputStackMouseOver(mx, my) != null && scrollMembers(scroll)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final GuiRecipe<?> guiRecipe = getGuiRecipe();
|
||||
return guiRecipe != null && this.handler.mouseScrolled(guiRecipe, scroll, getRecipeIndex());
|
||||
}
|
||||
|
||||
protected boolean scrollPermutations(int scroll, PositionedStack hovered) {
|
||||
final Point slotKey = this.group.getSlotKey(hovered);
|
||||
final List<ItemStack> items = this.group.getPermutations(slotKey);
|
||||
|
||||
if (items.size() <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<PositionedStack> slots = new ArrayList<>(this.group.size());
|
||||
|
||||
for (int member = 0; member < this.group.size(); member++) {
|
||||
slots.add(this.group.getSlot(member, slotKey));
|
||||
}
|
||||
|
||||
final int anchor = getCursorMember();
|
||||
final int currentItem = PermutationsCycler.indexOf(items, hovered.item);
|
||||
final List<int[]> variants = new ArrayList<>();
|
||||
int current = 0;
|
||||
|
||||
for (int itemIndex = 0; itemIndex < items.size(); itemIndex++) {
|
||||
final ItemStack stack = items.get(itemIndex);
|
||||
int member = containsStack(slots.get(this.activeMember), stack) ? this.activeMember : -1;
|
||||
|
||||
for (int offset = 0; member == -1 && offset < slots.size(); offset++) {
|
||||
if (containsStack(slots.get((anchor + offset) % slots.size()), stack)) {
|
||||
member = (anchor + offset) % slots.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (member != -1) {
|
||||
if (itemIndex == currentItem) {
|
||||
current = variants.size();
|
||||
}
|
||||
variants.add(new int[] { itemIndex, member });
|
||||
}
|
||||
}
|
||||
|
||||
if (variants.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int[] next = variants.get(((current - scroll) % variants.size() + variants.size()) % variants.size());
|
||||
switchMember(next[1], items.get(next[0]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean scrollMembers(int scroll) {
|
||||
final int size = this.group.size();
|
||||
|
||||
if (size <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switchMember(((this.activeMember - scroll) % size + size) % size, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void switchMember(int member, ItemStack stack) {
|
||||
final Map<Point, ItemStack> rendered = new HashMap<>();
|
||||
|
||||
for (PositionedStack pStack : getCyclingStacks()) {
|
||||
rendered.put(this.group.getSlotKey(pStack), pStack.item);
|
||||
}
|
||||
|
||||
setActiveMember(member);
|
||||
|
||||
for (PositionedStack pStack : getCyclingStacks()) {
|
||||
final ItemStack renderStack = containsStack(pStack, stack) ? stack
|
||||
: rendered.get(this.group.getSlotKey(pStack));
|
||||
|
||||
if (containsStack(pStack, renderStack)) {
|
||||
pStack.setPermutationToRender(renderStack);
|
||||
}
|
||||
}
|
||||
|
||||
updateTooltipActiveStack();
|
||||
notifyPermutationsChanged();
|
||||
}
|
||||
|
||||
protected boolean tickCycle() {
|
||||
|
||||
if (!NEIClientUtils.shiftKey() && (this.cycleticks++ / TICKS_PER_CYCLE) != this.lastcycle
|
||||
|| this.lastcycle == -1) {
|
||||
this.lastcycle = this.cycleticks / TICKS_PER_CYCLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void nextMember() {
|
||||
final List<Integer> allowed = this.group.getAllowedMembers();
|
||||
|
||||
this.memberCursor = (this.memberCursor + 1) % allowed.size();
|
||||
setActiveMember(allowed.get(this.memberCursor));
|
||||
}
|
||||
|
||||
protected int getCursorMember() {
|
||||
final List<Integer> allowed = this.group.getAllowedMembers();
|
||||
return allowed.get(Math.max(0, this.memberCursor) % allowed.size());
|
||||
}
|
||||
|
||||
protected void updatePermutations(int cycle) {
|
||||
this.slotCycle = cycle;
|
||||
|
||||
for (PositionedStack pStack : getCyclingStacks()) {
|
||||
updatePermutationsFor(pStack);
|
||||
}
|
||||
|
||||
updateTooltipActiveStack();
|
||||
notifyPermutationsChanged();
|
||||
}
|
||||
|
||||
protected void updateTooltipActiveStack() {
|
||||
|
||||
if (this.acceptsTooltip != null && this.acceptsTooltip.tooltipGUID instanceof Point slotKey) {
|
||||
final PositionedStack pStack = this.group.getSlot(this.activeMember, slotKey);
|
||||
|
||||
if (pStack != null) {
|
||||
this.acceptsTooltip.setActiveStack(pStack.item);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.variantsTooltip != null) {
|
||||
final PositionedStack pStack = this.group.getOutputSlot(this.activeMember, this.variantsSlotKey);
|
||||
|
||||
if (pStack != null) {
|
||||
this.variantsTooltip.setActiveStack(pStack.item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void updatePermutationsFor(PositionedStack pStack) {
|
||||
|
||||
if (!this.renderPermutations.contains(pStack) || this.renderPermutations.get(pStack).size() > 1) {
|
||||
final ItemStack stack = this.renderPermutations.getCycled(pStack, this.slotCycle);
|
||||
|
||||
if (stack != null) {
|
||||
pStack.setPermutationToRender(stack);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void setActiveMember(int member) {
|
||||
|
||||
if (member != this.activeMember) {
|
||||
this.activeMember = member;
|
||||
|
||||
for (GuiRecipeButton button : getRecipeButtonsIfInit()) {
|
||||
button.update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean containsStack(PositionedStack pStack, ItemStack stack) {
|
||||
return pStack != null && stack != null
|
||||
&& PermutationsCycler.indexOf(pStack.getFilteredPermutations(), stack) != -1;
|
||||
}
|
||||
|
||||
protected List<ItemStack> getPermutations(PositionedStack pStack) {
|
||||
return this.group.getPermutations(this.group.getSlotKey(pStack));
|
||||
}
|
||||
|
||||
protected void notifyPermutationsChanged() {
|
||||
for (GuiRecipeButton button : getRecipeButtonsIfInit()) {
|
||||
button.onPermutationsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
protected <R> R forEachButtons(Function<GuiRecipeButton, R> callback, R defaultValue) {
|
||||
|
||||
for (GuiRecipeButton button : getRecipeButtonsIfInit()) {
|
||||
button.xPosition += this.x;
|
||||
button.yPosition += this.y;
|
||||
|
||||
final R result = callback.apply(button);
|
||||
|
||||
button.xPosition -= this.x;
|
||||
button.yPosition -= this.y;
|
||||
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean isFocusedRecipe(int mx, int my) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
|
||||
for (PositionedStack pStackOver : getOutputs()) {
|
||||
if (pStackOver.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public PositionedStack getPositionedStackMouseOver(int mx, int my) {
|
||||
final PositionedStack cycling = getCyclingStackMouseOver(mx, my);
|
||||
return cycling != null ? cycling : getOutputStackMouseOver(mx, my);
|
||||
}
|
||||
|
||||
protected PositionedStack getOutputStackMouseOver(int mx, int my) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
|
||||
for (PositionedStack pStack : getOutputs()) {
|
||||
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected PositionedStack getCyclingStackMouseOver(int mx, int my) {
|
||||
final int yShift = this.handlerInfo.getYShift();
|
||||
|
||||
for (PositionedStack pStack : getInputs()) {
|
||||
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
for (PositionedStack pStack : getCatalysts()) {
|
||||
if (pStack.contains(mx - this.x, my - this.y - yShift)) {
|
||||
return pStack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getStackMouseOver(int mx, int my) {
|
||||
final PositionedStack pStack = getPositionedStackMouseOver(mx, my);
|
||||
return pStack != null ? pStack.item : null;
|
||||
}
|
||||
|
||||
protected GuiRecipe<?> getGuiRecipe() {
|
||||
final GuiContainer guiContainer = NEIClientUtils.getGuiContainer();
|
||||
|
||||
if (guiContainer instanceof GuiRecipe<?>guiRecipe) {
|
||||
return guiRecipe;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getInputs() {
|
||||
return this.group.getInputs(this.activeMember);
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getOutputs() {
|
||||
return this.group.getOutputs(this.activeMember);
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getCatalysts() {
|
||||
return this.group.getCatalysts(this.activeMember);
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getCyclingStacks() {
|
||||
return this.group.getCyclingStacks(this.activeMember);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -682,6 +682,8 @@ nei.recipe.concurrent.usage=Concurrent Usage - Total
|
||||
nei.recipe.concurrent.crafting=Concurrent Crafting - Total
|
||||
nei.recipe.accepts=Accepts following
|
||||
nei.recipe.accepts.scroll=Change Item
|
||||
nei.recipe.group=Variants
|
||||
nei.recipe.group.scroll=Change Variant (%s/%s)
|
||||
|
||||
nei.recipe.overlay=Recipe Overlay
|
||||
nei.recipe.overlay.missing=Missing following
|
||||
|
||||
Reference in new issue
Block a user