Add List View to Recipe (#1041)

This commit is contained in:
slprime authored and GitHub committed 2026-09-23 00:14:39 +03:00
1 parent c9db437dd1
commit 939845a9ed
7 files changed
+654 -38

No files matched your search

@@ -0,0 +1,125 @@
package codechicken.nei;
import static codechicken.lib.gui.GuiDraw.fontRenderer;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.guihook.GuiContainerManager;
public class ItemsListTooltipLineHandler extends ItemsTooltipLineHandler {
protected static final int NAME_MARGIN = 2;
protected final List<String> names = new ArrayList<>();
protected int tailCount = 0;
public ItemsListTooltipLineHandler(String label, List<ItemStack> items, int maxRows) {
super(label, items, true, maxRows);
this.rows = Math.min(maxRows, this.length);
this.count = this.length > this.rows ? this.rows - 1 : this.length;
int width = fontRenderer.getStringWidth(this.label) + 15;
for (int index = 0; index < this.count; index++) {
final String name = this.items.get(index).getDisplayName();
this.names.add(name);
width = Math.max(width, SLOT_SIZE + NAME_MARGIN + fontRenderer.getStringWidth(name));
}
if (this.count < this.length) {
final int tail = this.length - this.count;
width = Math.max(width, Math.min(tail, MAX_COLUMNS) * SLOT_SIZE);
final int fit = width / SLOT_SIZE;
if (tail <= fit) {
this.tailCount = tail;
} else {
final String text = "+" + (tail - fit);
this.tailCount = Math
.max(0, fit - (int) Math.ceil((float) (fontRenderer.getStringWidth(text) - 2) / SLOT_SIZE));
}
}
this.size.width = this.length > 0 ? width : 0;
this.size.height = this.length > 0 ? this.rows * SLOT_SIZE + fontRenderer.FONT_HEIGHT + 2 + MARGIN_TOP : 0;
}
@Override
public void setLabel(String label) {
this.label = label;
this.size.width = Math.max(this.size.width, fontRenderer.getStringWidth(this.label) + 15);
}
@Override
public void draw(int x, int y) {
if (this.length == 0) return;
y += MARGIN_TOP;
fontRenderer.drawStringWithShadow(this.labelColor + this.label + ":", x, y, 0);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_LIGHTING_BIT);
RenderHelper.enableGUIStandardItemLighting();
final int xTranslation = x;
final int yTranslation = y + fontRenderer.FONT_HEIGHT + 2;
final int zTranslation = GuiContainerManager.TOOLTIP_Z_OFFSET;
GL11.glTranslatef(xTranslation, yTranslation, zTranslation);
for (int index = 0; index < this.count; index++) {
drawSlot(0, index * SLOT_SIZE, index);
}
for (int index = 0; index < this.tailCount; index++) {
drawSlot(index * SLOT_SIZE, this.count * SLOT_SIZE, this.count + index);
}
NEIClientUtils.gl2DRenderContext(() -> {
final int textShift = ICON_OFFSET + Math.round((ICON_SIZE - fontRenderer.FONT_HEIGHT) / 2f);
for (int index = 0; index < this.count; index++) {
fontRenderer.drawStringWithShadow(
EnumChatFormatting.GRAY + this.names.get(index),
SLOT_SIZE + NAME_MARGIN,
index * SLOT_SIZE + textShift,
0xFFFFFF);
}
final int hidden = this.length - this.count - this.tailCount;
if (hidden > 0) {
final String text = "+" + hidden;
fontRenderer.drawStringWithShadow(
EnumChatFormatting.GRAY + text,
this.size.width - fontRenderer.getStringWidth(text) - 2,
this.count * SLOT_SIZE + textShift,
0xFFFFFF);
}
});
GL11.glTranslatef(-xTranslation, -yTranslation, -zTranslation);
GL11.glPopAttrib();
}
protected void drawSlot(int x, int y, int index) {
if (this.activeStackIndex == index) {
NEIClientUtils.gl2DRenderContext(() -> GuiDraw.drawRect(x, y, SLOT_SIZE, SLOT_SIZE, 0x66555555));
}
drawStackWithAmount(x, y, index);
}
}
@@ -4,6 +4,7 @@ import static codechicken.lib.gui.GuiDraw.fontRenderer;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -17,22 +18,151 @@ import org.lwjgl.opengl.GL12;
import codechicken.lib.gui.GuiDraw;
import codechicken.lib.gui.GuiDraw.ITooltipLineHandler;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.NEIClientUtils.Alignment;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.ReadableNumberConverter;
public class ItemsTooltipLineHandler implements ITooltipLineHandler {
@FunctionalInterface
public interface AmountRenderer {
void draw(int x, int y, ItemStack stack, long amount);
}
public static class NoAmountRenderer implements AmountRenderer {
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
GuiContainerManager.drawItem(x, y, stack, true, "");
}
protected static void drawAmountText(String text, Rectangle4i rect, int color, Alignment alignment) {
NEIClientUtils.drawNEIOverlayText(text, rect, 1, color, true, alignment);
}
}
public static class TotalAmountRenderer extends NoAmountRenderer {
private static class AmountText {
public final String text;
public final Alignment alignment;
public AmountText(String text, Alignment alignment) {
this.text = text;
this.alignment = alignment;
}
}
private final Map<ItemStack, AmountText> cache = new IdentityHashMap<>();
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
super.draw(x, y, stack, amount);
drawAmount(new Rectangle4i(x, y, SLOT_SIZE - 2, SLOT_SIZE - 2), stack, amount);
}
protected void drawAmount(Rectangle4i rect, ItemStack stack, long amount) {
final AmountText amountText = this.cache.computeIfAbsent(stack, key -> {
final boolean isFluidDisplay = StackInfo.isFluidDisplayItem(key);
final String text = formatAmount(key, amount);
return new AmountText(
isFluidDisplay && !text.isEmpty() ? text + "L" : text,
isFluidDisplay ? Alignment.BottomLeft : Alignment.BottomRight);
});
if (!amountText.text.isEmpty()) {
drawAmountText(amountText.text, rect, 0xFFFFFF, amountText.alignment);
}
}
protected String formatAmount(ItemStack stack, long amount) {
return amount == 0 ? "" : ReadableNumberConverter.INSTANCE.toWideReadableForm(amount);
}
}
public static class StacksAmountRenderer extends NoAmountRenderer {
private static class StacksText {
public final String stacks;
public final String remainder;
public final int color;
public StacksText(String stacks, String remainder, int color) {
this.stacks = stacks;
this.remainder = remainder;
this.color = color;
}
}
private final Map<ItemStack, StacksText> cache = new IdentityHashMap<>();
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
super.draw(x, y, stack, amount);
drawAmount(new Rectangle4i(x, y, SLOT_SIZE - 2, SLOT_SIZE - 2), stack, amount);
}
protected void drawAmount(Rectangle4i rect, ItemStack stack, long amount) {
final StacksText stacksText = this.cache.computeIfAbsent(stack, key -> formatAmount(key, amount));
if (!stacksText.stacks.isEmpty()) {
drawAmountText(stacksText.stacks, rect, stacksText.color, Alignment.TopLeft);
}
if (!stacksText.remainder.isEmpty()) {
drawAmountText(stacksText.remainder, rect, stacksText.color, Alignment.BottomRight);
}
}
private StacksText formatAmount(ItemStack stack, long amount) {
final long stackSize = StackInfo.isFluidDisplayItem(stack) ? FLUID_STACK_SIZE
: Math.max(1, stack.getMaxStackSize());
final long stacks = amount / stackSize;
final long remainder = amount % stackSize;
final String prefix = getPrefix(stack);
return new StacksText(
stacks > 0 ? prefix + "x" + ReadableNumberConverter.INSTANCE.toWideReadableForm(stacks) : "",
remainder > 0
? (stacks > 0 ? "+" : prefix)
+ ReadableNumberConverter.INSTANCE.toWideReadableForm(remainder)
: "",
getColor(stack));
}
protected String getPrefix(ItemStack stack) {
return "";
}
protected int getColor(ItemStack stack) {
return 0xFFFFFF;
}
}
protected static final int SLOT_SIZE = 18;
protected static final int ICON_SIZE = 16;
protected static final int ICON_OFFSET = 1;
protected static final int MAX_COLUMNS = 11;
protected static final int MARGIN_TOP = 2;
protected static final int FLUID_STACK_SIZE = 144;
protected final List<ItemStack> items;
protected final List<Long> amounts = new ArrayList<>();
protected AmountRenderer amountRenderer;
protected String label;
protected EnumChatFormatting labelColor = EnumChatFormatting.GRAY;
protected List<ItemStack> items;
protected int activeStackIndex = -1;
protected Dimension size = new Dimension();
protected boolean saveStackSize = false;
protected int columns = 0;
protected int count = 0;
protected int rows = 0;
@@ -45,7 +175,7 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
public ItemsTooltipLineHandler(String label, List<ItemStack> items, boolean saveStackSize, int maxRows) {
this.label = label;
this.items = groupingItemStacks(items);
this.saveStackSize = saveStackSize;
this.amountRenderer = saveStackSize ? new TotalAmountRenderer() : new NoAmountRenderer();
this.length = this.items.size();
if (this.length > 0) {
@@ -69,6 +199,11 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
}
public ItemsTooltipLineHandler setAmountRenderer(AmountRenderer amountRenderer) {
this.amountRenderer = amountRenderer;
return this;
}
public void setActiveStack(ItemStack activeStack) {
final ItemStack realStack = items.stream().filter(stack -> NEIClientUtils.areStacksSameType(stack, activeStack))
.findFirst().orElse(null);
@@ -126,19 +261,15 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
}
for (int index = 0; index < this.count && index + indexShift < this.items.size(); index++) {
ItemStack drawStack = this.items.get(index + indexShift);
int col = index % this.columns;
int row = index / this.columns;
String stackSize = !this.saveStackSize || drawStack.stackSize == 0 ? ""
: ReadableNumberConverter.INSTANCE.toWideReadableForm(drawStack.stackSize);
if (this.activeStackIndex == index + indexShift) {
NEIClientUtils.gl2DRenderContext(
() -> GuiDraw.drawRect(col * SLOT_SIZE - 1, row * SLOT_SIZE - 1, 18, 18, 0x66555555));
() -> GuiDraw.drawRect(col * SLOT_SIZE, row * SLOT_SIZE, SLOT_SIZE, SLOT_SIZE, 0x66555555));
}
drawItem(col * SLOT_SIZE, row * SLOT_SIZE, drawStack, stackSize);
drawStackWithAmount(col * SLOT_SIZE, row * SLOT_SIZE, index + indexShift);
}
if (this.count < this.items.size()) {
@@ -157,25 +288,23 @@ public class ItemsTooltipLineHandler implements ITooltipLineHandler {
GL11.glPopAttrib();
}
protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) {
GuiContainerManager.drawItem(x, y, drawStack, true, stackSize);
protected void drawStackWithAmount(int x, int y, int index) {
this.amountRenderer.draw(x + ICON_OFFSET, y + ICON_OFFSET, this.items.get(index), this.amounts.get(index));
}
private List<ItemStack> groupingItemStacks(List<ItemStack> items) {
final List<ItemStack> result = new ArrayList<>();
for (Map.Entry<NBTTagCompound, Long> entry : ItemStackAmount.of(items).entrySet()) {
final ItemStack stack = StackInfo.loadFromNBT(entry.getKey(), Math.max(0, entry.getValue()));
if (stack != null && StackInfo.isFluidDisplayItem(stack)) {
stack.stackSize = 0;
}
result.add(stack);
result.add(StackInfo.loadFromNBT(entry.getKey(), 0));
this.amounts.add(Math.max(0, entry.getValue()));
}
if (result.isEmpty()) {
result.addAll(items);
for (ItemStack stack : items) {
result.add(stack);
this.amounts.add(0L);
}
}
return result;
@@ -67,6 +67,7 @@ import codechicken.nei.recipe.GuiRecipeButton.UpdateRecipeButtonsEvent;
import codechicken.nei.recipe.GuiRecipeTab;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.RecipeItemsTooltipLineHandler;
import codechicken.nei.recipe.RecipeTooltipLineHandler;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.EmptyContainer;
@@ -134,6 +135,7 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
private RecipeTooltipLineHandler recipeTooltipLineHandler;
private AcceptsFollowingTooltipLineHandler acceptsFollowingTooltipLineHandler;
private RecipeItemsTooltipLineHandler recipeItemsTooltipLineHandler;
private final CraftingTreeState uiState = new CraftingTreeState();
private final CraftingTreeToolbar toolbar;
@@ -939,12 +941,15 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
if (toolbarTooltip != null) {
currenttip.add(toolbarTooltip);
} else if (this.hoveredBadgeHandler != null) {
final RecipeId recipeId = this.hoveredBadgeHandler.prefItem != null
? this.hoveredBadgeHandler.prefItem.recipeId
: null;
final long multiplier = this.hoveredBadgeHandler.multiplier;
final ItemTreeSlot node = this.hoveredBadgeHandler;
final RecipeId recipeId = node.prefItem != null ? node.prefItem.recipeId : null;
if (recipeId != null && NEIClientUtils.shiftKey()) {
addRecipeItemsTooltipLine(recipeId, node.multiplier, currenttip);
} else {
addRecipeTooltipLine(recipeId, node.multiplier, currenttip);
}
addRecipeTooltipLine(recipeId, multiplier, currenttip);
} else {
if (this.statsPanelVisible) {
@@ -960,6 +965,14 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
EnumChatFormatting.GRAY + NEIClientUtils.translate(
"bookmark.tree.stats.handler_iterations",
handlerStatsSlot.stats.iterations));
} else if (widget instanceof StatsPanel.ItemStatsSlot itemStatsSlot && itemStatsSlot.recipeId != null) {
if (NEIClientUtils.shiftKey()) {
addRecipeItemsTooltipLine(itemStatsSlot.recipeId, itemStatsSlot.multiplier, currenttip);
} else {
currenttip.add(itemStatsSlot.stack.getDisplayName() + GuiDraw.TOOLTIP_LINESPACE);
addRecipeTooltipLine(itemStatsSlot.recipeId, itemStatsSlot.multiplier, currenttip);
}
}
}
@@ -968,6 +981,19 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
return currenttip;
}
private void addRecipeItemsTooltipLine(RecipeId recipeId, long multiplier, List<String> currenttip) {
if (this.recipeItemsTooltipLineHandler == null
|| this.recipeItemsTooltipLineHandler.getMultiplier() != multiplier
|| !this.recipeItemsTooltipLineHandler.getRecipeId().equals(recipeId)) {
this.recipeItemsTooltipLineHandler = new RecipeItemsTooltipLineHandler(recipeId, multiplier)
.setUseInventory(true);
}
currenttip.add(this.recipeItemsTooltipLineHandler.getTitle() + GuiDraw.TOOLTIP_LINESPACE);
currenttip.add(GuiDraw.TOOLTIP_HANDLER + GuiDraw.getTipLineId(this.recipeItemsTooltipLineHandler));
}
private void addRecipeTooltipLine(RecipeId recipeId, long multiplier, List<String> currenttip) {
if (recipeId == null) {
@@ -1078,14 +1104,6 @@ public class GuiCraftingTree extends GuiContainer implements INEIGuiHandler, IGu
} else {
this.acceptsFollowingTooltipLineHandler = null;
if (this.statsPanelVisible) {
final Widget widget = this.statsPanel.getWidgetUnderMouse(mousex, mousey);
if (widget instanceof StatsPanel.ItemStatsSlot itemStatsSlot) {
addRecipeTooltipLine(itemStatsSlot.recipeId, itemStatsSlot.multiplier, currenttip);
}
}
}
return currenttip;
@@ -88,6 +88,10 @@ class StatsPanel extends ScrollContainer {
this(stack, stackSize, icon, null, 1);
}
public ItemStack getStack() {
return StackInfo.withAmount(this.stack, stackSize);
}
@Override
public void draw(int mx, int my) {
@@ -519,7 +523,7 @@ class StatsPanel extends ScrollContainer {
public ItemStack getStackMouseOver(int mouseX, int mouseY) {
final Widget widget = getWidgetUnderMouse(mouseX, mouseY);
return widget instanceof ItemStatsSlot slot ? slot.stack : null;
return widget instanceof ItemStatsSlot slot && slot.recipeId == null ? slot.getStack() : null;
}
public void setHighlight(SearchResult searchResult) {
@@ -20,6 +20,7 @@ public class AcceptsFollowingTooltipLineHandler extends ItemsTooltipLineHandler
super(NEIClientUtils.translate("recipe.accepts"), items, false, maxRows);
this.tooltipGUID = tooltipGUID;
setActiveStack(activeStack);
setAmountRenderer(new FavoriteAmountRenderer());
}
public static AcceptsFollowingTooltipLineHandler of(Object tooltipGUID, List<ItemStack> items,
@@ -38,12 +39,15 @@ public class AcceptsFollowingTooltipLineHandler extends ItemsTooltipLineHandler
return null;
}
@Override
protected void drawItem(int x, int y, ItemStack drawStack, String stackSize) {
super.drawItem(x, y, drawStack, stackSize);
protected static class FavoriteAmountRenderer extends NoAmountRenderer {
if (FavoriteRecipes.containsManual(drawStack)) {
NEIClientUtils.drawNEIOverlayText("F", x, y);
@Override
public void draw(int x, int y, ItemStack stack, long amount) {
super.draw(x, y, stack, amount);
if (FavoriteRecipes.containsManual(stack)) {
NEIClientUtils.drawNEIOverlayText("F", x, y);
}
}
}
@@ -0,0 +1,332 @@
package codechicken.nei.recipe;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumChatFormatting;
import codechicken.lib.gui.GuiDraw.ITooltipLineHandler;
import codechicken.lib.vec.Rectangle4i;
import codechicken.nei.ItemStackAmount;
import codechicken.nei.ItemsListTooltipLineHandler;
import codechicken.nei.ItemsTooltipLineHandler;
import codechicken.nei.ItemsTooltipLineHandler.StacksAmountRenderer;
import codechicken.nei.ItemsTooltipLineHandler.TotalAmountRenderer;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IGuiContainerOverlay;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.util.ReadableNumberConverter;
public class RecipeItemsTooltipLineHandler implements ITooltipLineHandler {
protected static class ChanceTotalAmountRenderer extends TotalAmountRenderer {
protected final Set<ItemStack> approximate;
public ChanceTotalAmountRenderer(Set<ItemStack> approximate) {
this.approximate = approximate;
}
@Override
protected String formatAmount(ItemStack stack, long amount) {
final String text = super.formatAmount(stack, amount);
if (!this.approximate.contains(stack)) {
return text;
} else if (!text.isEmpty()) {
return EnumChatFormatting.GOLD + "~" + text;
} else if (amount == 0) {
return EnumChatFormatting.GOLD + "~0";
}
return text;
}
}
protected static class ChanceStacksAmountRenderer extends StacksAmountRenderer {
protected static final int APPROXIMATE_COLOR = 0xFFAA00;
protected final Set<ItemStack> approximate;
public ChanceStacksAmountRenderer(Set<ItemStack> approximate) {
this.approximate = approximate;
}
@Override
protected void drawAmount(Rectangle4i rect, ItemStack stack, long amount) {
if (amount == 0) {
Badge.notConsumed().draw(rect);
} else {
super.drawAmount(rect, stack, amount);
}
}
@Override
protected String getPrefix(ItemStack stack) {
return this.approximate.contains(stack) ? "~" : "";
}
@Override
protected int getColor(ItemStack stack) {
return this.approximate.contains(stack) ? APPROXIMATE_COLOR : super.getColor(stack);
}
}
protected static final int MAX_ROWS = 15;
protected final RecipeId recipeId;
protected final long multiplier;
protected boolean useInventory = false;
protected final List<ItemsTooltipLineHandler> lines = new ArrayList<>();
protected final Dimension size = new Dimension();
protected String title = "";
protected boolean created = false;
public RecipeItemsTooltipLineHandler(RecipeId recipeId, long multiplier) {
this.recipeId = recipeId;
this.multiplier = multiplier;
}
public RecipeItemsTooltipLineHandler setUseInventory(boolean useInventory) {
this.useInventory = useInventory;
this.created = false;
return this;
}
public RecipeId getRecipeId() {
return this.recipeId;
}
public long getMultiplier() {
return this.multiplier;
}
public boolean isUseInventory() {
return this.useInventory;
}
public String getTitle() {
ensureCreated();
return this.title;
}
@Override
public Dimension getSize() {
ensureCreated();
return this.size;
}
protected void ensureCreated() {
if (!this.created) {
this.created = true;
createLines();
}
}
@Override
public void draw(int x, int y) {
if (this.size.height == 0) return;
for (ItemsTooltipLineHandler line : this.lines) {
line.draw(x, y);
y += line.getSize().height;
}
}
protected static int getIngredientOrder(ItemStack stack) {
return StackInfo.isFluidDisplayItem(stack) ? 2 : stack.stackSize == 0 ? 0 : 1;
}
protected void createLines() {
this.lines.clear();
this.size.setSize(0, 0);
final RecipeHandlerRef handlerRef = RecipeHandlerRef.of(this.recipeId);
if (handlerRef == null) {
return;
}
final Recipe recipe = Recipe.of(handlerRef);
final Set<NBTTagCompound> approximateResults = new HashSet<>();
final Set<NBTTagCompound> approximateIngredients = new HashSet<>();
final List<ItemStack> results = multiplyItems(recipe.getResults(), false, approximateResults);
final List<ItemStack> ingredients = multiplyItems(recipe.getIngredients(), true, approximateIngredients);
final ItemStackAmount ingredientsAmount = ItemStackAmount.of(ingredients);
ingredients.sort(Comparator.comparingInt(RecipeItemsTooltipLineHandler::getIngredientOrder));
this.title = handlerRef.handler.getRecipeName().trim() + " (x"
+ ReadableNumberConverter.INSTANCE.toWideReadableForm(this.multiplier)
+ ")";
addResultLine(results, approximateResults);
addRequiredLine(ingredients, approximateIngredients);
if (this.useInventory) {
final List<ItemStack> needed = subtractItems(ingredientsAmount, getInventoryItems());
final ItemStackAmount neededAmount = ItemStackAmount.of(needed);
final ItemStackAmount consumedAmount = ItemStackAmount.of(ingredientsAmount);
consumedAmount.removeIf(entry -> entry.getValue() <= 0);
if (!neededAmount.equals(consumedAmount)) {
addNeededLine(
needed,
approximateIngredients,
needed.size() == 1 || ingredientsAmount.size() + neededAmount.size() <= MAX_ROWS);
}
}
int width = 0;
int height = 0;
for (ItemsTooltipLineHandler line : this.lines) {
width = Math.max(width, line.getSize().width);
height += line.getSize().height;
}
this.size.setSize(width, height);
}
protected void addResultLine(List<ItemStack> items, Set<NBTTagCompound> approximate) {
final String label = NEIClientUtils.translate("recipe.items.results");
final ItemsTooltipLineHandler line = items.size() == 1 ? new ItemsListTooltipLineHandler(label, items, MAX_ROWS)
: new ItemsTooltipLineHandler(label, items);
if (line.isEmpty()) {
return;
}
line.setLabelColor(EnumChatFormatting.GREEN);
final Set<ItemStack> approximateItems = Collections.newSetFromMap(new IdentityHashMap<>());
for (ItemStack stack : line.getItems()) {
if (approximate.contains(StackInfo.itemStackToNBT(stack, false))) {
approximateItems.add(stack);
}
}
line.setAmountRenderer(new ChanceTotalAmountRenderer(approximateItems));
this.lines.add(line);
}
protected void addRequiredLine(List<ItemStack> items, Set<NBTTagCompound> approximate) {
final String label = NEIClientUtils.translate("recipe.items.ingredients");
final ItemsTooltipLineHandler line = new ItemsListTooltipLineHandler(label, items, MAX_ROWS);
if (line.isEmpty()) {
return;
}
line.setLabelColor(EnumChatFormatting.YELLOW);
final Set<ItemStack> approximateItems = Collections.newSetFromMap(new IdentityHashMap<>());
for (ItemStack stack : line.getItems()) {
if (approximate.contains(StackInfo.itemStackToNBT(stack, false))) {
approximateItems.add(stack);
}
}
line.setAmountRenderer(new ChanceStacksAmountRenderer(approximateItems));
this.lines.add(line);
}
protected void addNeededLine(List<ItemStack> items, Set<NBTTagCompound> approximate, boolean vertical) {
final String label = NEIClientUtils.translate("recipe.items.ingredients_needed");
final ItemsTooltipLineHandler line = vertical ? new ItemsListTooltipLineHandler(label, items, MAX_ROWS)
: new ItemsTooltipLineHandler(label, items);
if (line.isEmpty()) {
return;
}
line.setLabelColor(EnumChatFormatting.RED);
final Set<ItemStack> approximateItems = Collections.newSetFromMap(new IdentityHashMap<>());
for (ItemStack stack : line.getItems()) {
if (approximate.contains(StackInfo.itemStackToNBT(stack, false))) {
approximateItems.add(stack);
}
}
line.setAmountRenderer(new ChanceStacksAmountRenderer(approximateItems));
this.lines.add(line);
}
protected List<ItemStack> multiplyItems(List<RecipeIngredient> items, boolean ingredients,
Set<NBTTagCompound> approximate) {
final ItemStackAmount chanceAmounts = new ItemStackAmount();
final List<ItemStack> result = new ArrayList<>();
for (RecipeIngredient item : items) {
chanceAmounts.add(item.getItemStack(), (long) item.getAmount() * item.getChance());
}
for (Map.Entry<NBTTagCompound, Long> entry : chanceAmounts.entrySet()) {
final long amount = entry.getValue() * this.multiplier;
final long entryAmount = ingredients
? (amount + PositionedStack.CHANCE_FULL - 1) / PositionedStack.CHANCE_FULL
: amount / PositionedStack.CHANCE_FULL;
final ItemStack stack = StackInfo.loadFromNBT(entry.getKey(), entryAmount);
if (amount % PositionedStack.CHANCE_FULL != 0) {
approximate.add(entry.getKey());
}
result.add(stack);
}
return result;
}
protected static ItemStackAmount getInventoryItems() {
GuiScreen screen = NEIClientUtils.mc().currentScreen;
if (screen instanceof IGuiContainerOverlay overlay) {
screen = overlay.getFirstScreen();
}
if (screen instanceof GuiContainer container) {
return AutoCraftingManager.getInventoryItems(container);
}
return new ItemStackAmount();
}
protected static List<ItemStack> subtractItems(ItemStackAmount items, ItemStackAmount subtrahend) {
final List<ItemStack> result = new ArrayList<>();
for (Map.Entry<NBTTagCompound, Long> entry : items.entrySet()) {
final ItemStack stack = StackInfo.loadFromNBT(entry.getKey(), 0);
final long amount = entry.getValue() - subtrahend.getOrDefault(stack, 0L);
if (amount > 0) {
result.add(StackInfo.withAmount(stack, amount));
}
}
return result;
}
}
@@ -667,6 +667,10 @@ nei.recipe.badge.ncp.tooltip=Considered in parallel calculation, but not get con
nei.recipe.badge.chance.consume=Consume Chance: %s
nei.recipe.badge.chance.output=Output Chance: %s
nei.recipe.items.results=Results
nei.recipe.items.ingredients=Ingredients
nei.recipe.items.ingredients_needed=Ingredients Needed
# Deprecated
nei.recipe.fuel.required=%s required
# Deprecated