mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-08 12:34:46 +00:00
Add List View to Recipe (#1041)
This commit is contained in:
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
|
||||
|
||||
Reference in new issue
Block a user